httpbinding.cpp 101 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #pragma warning(disable : 4786)
  14. #ifdef ESPHTTP_EXPORTS
  15. #define esp_http_decl DECL_EXPORT
  16. #else
  17. #define esp_http_decl DECL_IMPORT
  18. #endif
  19. //Jlib
  20. #include "jliball.hpp"
  21. //SCM Interfaces
  22. #include "esp.hpp"
  23. //#include "IAEsp.hpp"
  24. //ESP Core
  25. #include "espthread.hpp"
  26. #include "espbinding.hpp"
  27. #include "bindutil.hpp"
  28. #include "httpbinding.hpp"
  29. #include "htmlpage.hpp"
  30. #include "seclib.hpp"
  31. #include "../../../system/security/shared/secloader.hpp"
  32. #include "../../SOAP/Platform/soapmessage.hpp"
  33. #include "xmlvalidator.hpp"
  34. #include "xsdparser.hpp"
  35. #include "espsecurecontext.hpp"
  36. #include "jsonhelpers.hpp"
  37. #include "dasds.hpp"
  38. #include "daclient.hpp"
  39. #define FILE_UPLOAD "FileUploadAccess"
  40. #define DEFAULT_HTTP_PORT 80
  41. static HINSTANCE getXmlLib()
  42. {
  43. const char* name = SharedObjectPrefix "xmllib" SharedObjectExtension;
  44. HINSTANCE xmllib = LoadSharedObject(name,true,false);
  45. if (!LoadSucceeded(xmllib))
  46. throw MakeStringException(-1,"load %s failed with code %d", name, GetSharedObjectError());
  47. return xmllib;
  48. }
  49. static IXslProcessor* getXmlLibXslProcessor()
  50. {
  51. static Owned<IXslProcessor> processor;
  52. if (!processor)
  53. {
  54. typedef IXslProcessor* (*getXslProcessor_t)();
  55. getXslProcessor_t f = (getXslProcessor_t)GetSharedProcedure(getXmlLib(), "getXslProcessor");
  56. processor.setown(f());
  57. }
  58. return processor.get();
  59. }
  60. static IXmlValidator* getXmlLibXmlValidator()
  61. {
  62. static Owned<IXmlValidator> v;
  63. if (!v)
  64. {
  65. typedef IXmlDomParser* (*getDomParser_t)();
  66. getDomParser_t f = (getDomParser_t)GetSharedProcedure(getXmlLib(), "getXmlDomParser");
  67. Owned<IXmlDomParser> d = f();
  68. v.setown(d->createXmlValidator());
  69. }
  70. return v.get();
  71. }
  72. static IXmlSchema* createXmlSchema(const char* schema)
  73. {
  74. const char* name = SharedObjectPrefix "xmllib" SharedObjectExtension;
  75. HINSTANCE xmllib = LoadSharedObject(name,true,false);
  76. if (!LoadSucceeded(xmllib))
  77. throw MakeStringException(-1,"load %s failed with code %d", name, GetSharedObjectError());
  78. typedef IXmlSchema* (*XmlSchemaCreator)(const char*);
  79. XmlSchemaCreator creator = (XmlSchemaCreator)GetSharedProcedure(xmllib, "createXmlSchemaFromString");
  80. if (!creator)
  81. throw MakeStringException(-1,"load XmlSchema factory failed: createXmlSchemaFromString()");
  82. return creator(schema);
  83. }
  84. EspHttpBinding::EspHttpBinding(IPropertyTree* tree, const char *bindname, const char *procname)
  85. {
  86. Owned<IPropertyTree> proc_cfg = getProcessConfig(tree, procname);
  87. m_viewConfig = proc_cfg ? proc_cfg->getPropBool("@httpConfigAccess") : false;
  88. m_formOptions = proc_cfg ? proc_cfg->getPropBool("@formOptionsAccess") : false;
  89. m_includeSoapTest = true;
  90. m_configFile.set(tree ? tree->queryProp("@config") : "esp.xml");
  91. Owned<IPropertyTree> bnd_cfg = getBindingConfig(tree, bindname, procname);
  92. m_wsdlVer=0.0;
  93. // get the config default version
  94. const char* defVersion = bnd_cfg->queryProp("@defaultServiceVersion");
  95. if (defVersion && *defVersion)
  96. m_defaultSvcVersion.set(defVersion);
  97. if(!bnd_cfg)
  98. {
  99. m_filespath.append(getCFD()).append("files/");
  100. }
  101. else
  102. {
  103. if (!bnd_cfg->getProp("@baseFilesPath", m_filespath))
  104. m_filespath.append(getCFD()).append("./files/");
  105. m_host.set(bnd_cfg->queryProp("@netAddress"));
  106. m_port = bnd_cfg->getPropInt("@port");
  107. const char *realm = bnd_cfg->queryProp("@realm");
  108. if (realm)
  109. m_realm.set(realm);
  110. else
  111. {
  112. StringBuffer xpath;
  113. xpath.appendf("EspBinding[@port='%d']", m_port);
  114. Owned<IPropertyTreeIterator> bindings = proc_cfg->getElements(xpath.str());
  115. for (bindings->first(); bindings->isValid(); bindings->next())
  116. {
  117. const char *type = bindings->query().queryProp("@type");
  118. if (type && !stricmp(type, "ws_smcSoapBinding"))
  119. {
  120. m_realm.set("EclWatch");
  121. break;
  122. }
  123. }
  124. if (!m_realm.length())
  125. {
  126. const char *srvName = bnd_cfg->queryProp("@service");
  127. if (srvName)
  128. {
  129. Owned<IPropertyTree> srv_cfg = getServiceConfig(tree, srvName, procname);
  130. if (srv_cfg)
  131. {
  132. realm = srv_cfg->queryProp("@type");
  133. const char *ssfile=srv_cfg->queryProp("@subservices");
  134. if (ssfile && checkFileExists(ssfile))
  135. m_subservices.setown(createPTreeFromXMLFile(ssfile, ipt_caseInsensitive));
  136. }
  137. }
  138. m_realm.set((realm) ? realm : "EspServices");
  139. }
  140. }
  141. m_wsdlAddress.append(bnd_cfg->queryProp("@wsdlServiceAddress"));
  142. if (!m_wsdlAddress.length())
  143. {
  144. const char *host=getHost();
  145. if (!host || !(*host) || !strcmp(host, ".") || !strcmp(host, "0.0.0.0"))
  146. queryHostIP().getIpText(m_wsdlAddress);
  147. else
  148. m_wsdlAddress.append(host);
  149. if (!strchr(m_wsdlAddress.str(), ':') && m_port!=80 && m_port!=443)
  150. m_wsdlAddress.append(':').append(m_port);
  151. }
  152. if (strnicmp(m_wsdlAddress.str(), "http", 4))
  153. m_wsdlAddress.insert(0, (m_port!=443) ? "http://" : "https://");
  154. Owned<IPropertyTree> authcfg = bnd_cfg->getPropTree("Authenticate");
  155. if(authcfg != NULL)
  156. {
  157. //Instantiate a Security Manager
  158. m_authtype.set(authcfg->queryProp("@type"));
  159. m_authmethod.set(authcfg->queryProp("@method"));
  160. if (!m_authmethod.isEmpty())
  161. {
  162. Owned<IPropertyTree> secMgrCfg;
  163. if(proc_cfg.get() != NULL)
  164. {
  165. VStringBuffer sb("SecurityManagers/SecurityManager[@name='%s']", m_authmethod.str());
  166. Owned<IPropertyTree> smTree;
  167. smTree.setown(proc_cfg->getPropTree(sb.str()));
  168. if (smTree && smTree->hasProp("@type"))
  169. secMgrCfg.setown(smTree->getPropTree(smTree->queryProp("@type")));
  170. }
  171. if (secMgrCfg)
  172. {
  173. //This is a Pluggable Security Manager
  174. m_secmgr.setown(SecLoader::loadPluggableSecManager(bindname, bnd_cfg, secMgrCfg));
  175. m_authmap.setown(m_secmgr->createAuthMap(authcfg));
  176. m_feature_authmap.setown(m_secmgr->createFeatureMap(authcfg));
  177. }
  178. else
  179. {
  180. //Legacy Security Manager
  181. if(stricmp(m_authmethod.str(), "LdapSecurity") == 0)
  182. {
  183. StringBuffer lsname;
  184. authcfg->getProp("@config", lsname);
  185. Owned<IPropertyTree> lscfg = bnd_cfg->getPropTree(StringBuffer(".//ldapSecurity[@name=").appendf("\"%s\"]", lsname.str()).str());
  186. if(lscfg == NULL)
  187. {
  188. if(proc_cfg.get() != NULL)
  189. lscfg.setown(proc_cfg->getPropTree(StringBuffer("ldapSecurity[@name=").appendf("\"%s\"]", lsname.str()).str()));
  190. if(lscfg == NULL)
  191. {
  192. ERRLOG("can't find bnd_cfg for LdapSecurity %s", lsname.str());
  193. throw MakeStringException(-1, "can't find bnd_cfg for LdapSecurity %s", lsname.str());
  194. }
  195. }
  196. m_secmgr.setown(SecLoader::loadSecManager("LdapSecurity", "EspHttpBinding", LINK(lscfg)));
  197. if(m_secmgr.get() == NULL)
  198. {
  199. throw MakeStringException(-1, "error generating SecManager");
  200. }
  201. StringBuffer basednbuf;
  202. authcfg->getProp("@resourcesBasedn", basednbuf);
  203. m_secmgr->setExtraParam("resourcesBasedn", basednbuf.str());
  204. basednbuf.clear();
  205. authcfg->getProp("@workunitsBasedn", basednbuf);
  206. m_secmgr->setExtraParam("workunitsBasedn", basednbuf.str());
  207. m_authmap.setown(m_secmgr->createAuthMap(authcfg));
  208. m_feature_authmap.setown(m_secmgr->createFeatureMap(authcfg));
  209. }
  210. else if(stricmp(m_authmethod.str(), "Local") == 0)
  211. {
  212. m_secmgr.setown(SecLoader::loadSecManager("Local", "EspHttpBinding", NULL));
  213. m_authmap.setown(m_secmgr->createAuthMap(authcfg));
  214. }
  215. IRestartManager* restartManager = dynamic_cast<IRestartManager*>(m_secmgr.get());
  216. if(restartManager!=NULL)
  217. {
  218. IRestartHandler* pHandler = dynamic_cast<IRestartHandler*>(getESPContainer());
  219. if(pHandler!=NULL)
  220. restartManager->setRestartHandler(pHandler);
  221. }
  222. }
  223. }
  224. }
  225. }
  226. if(m_secmgr.get())
  227. {
  228. const char* desc = m_secmgr->getDescription();
  229. if(desc && *desc)
  230. m_challenge_realm.appendf("ESP (Authentication: %s)", desc);
  231. }
  232. if(m_challenge_realm.length() == 0)
  233. m_challenge_realm.append("ESP");
  234. if (!m_secmgr.get() || !daliClientActive())
  235. {
  236. if (!daliClientActive())
  237. {
  238. if (proc_cfg->hasProp("AuthDomains"))
  239. throw MakeStringException(-1, "ESP cannot have an 'AuthDomains' setting because no dali connection is available.");
  240. if (bnd_cfg->hasProp("@authDomain"))
  241. throw MakeStringException(-1, "ESP Binding %s cannot have an '@authDomain' setting because no dali connection is available.", bindname);
  242. }
  243. domainAuthType = AuthPerRequestOnly;
  244. Owned<IPropertyTree> authcfg = proc_cfg->getPropTree("Authentication");
  245. if (authcfg)
  246. {
  247. const char* authmethod = authcfg->queryProp("@method");
  248. if (authmethod && strieq(authmethod, "userNameOnly"))
  249. {
  250. //The @getUserNameUnrestrictedResources contains URLs which may be used by the getUserNameURL page.
  251. //For example, an icon file on the getUserNameURL page.
  252. const char* unrestrictedResources = authcfg->queryProp("@getUserNameUnrestrictedResources");
  253. if (!isEmptyString(unrestrictedResources))
  254. readUnrestrictedResources(unrestrictedResources);
  255. else
  256. readUnrestrictedResources(DEFAULT_UNRESTRICTED_RESOURCES);
  257. const char* getUserNameURL = authcfg->queryProp("@getUserNameURL");
  258. if (!isEmptyString(getUserNameURL))
  259. loginURL.set(getUserNameURL);
  260. else
  261. loginURL.set(DEFAULT_GET_USER_NAME_URL);
  262. domainAuthType = AuthUserNameOnly;
  263. }
  264. }
  265. return;
  266. }
  267. processName.set(procname);
  268. const char* authDomain = bnd_cfg->queryProp("@authDomain");
  269. if (!isEmptyString(authDomain))
  270. domainName.set(authDomain);
  271. else
  272. domainName.set("default");
  273. readAuthDomainCfg(proc_cfg);
  274. if ((domainAuthType == AuthPerSessionOnly) || (domainAuthType == AuthTypeMixed))
  275. {
  276. setSDSSession();
  277. checkSessionTimeoutSeconds = proc_cfg->getPropInt("@checkSessionTimeoutSeconds", ESP_CHECK_SESSION_TIMEOUT);
  278. }
  279. setABoolHash(proc_cfg->queryProp("@urlAlias"), serverAlias);
  280. }
  281. void EspHttpBinding::setSDSSession()
  282. {
  283. espSessionSDSPath.setf("%s/%s[@name=\"%s\"]", PathSessionRoot, PathSessionProcess, processName.get());
  284. Owned<IRemoteConnection> conn = querySDS().connect(espSessionSDSPath.str(), myProcessSession(), RTM_LOCK_WRITE, SESSION_SDS_LOCK_TIMEOUT);
  285. if (!conn)
  286. throw MakeStringException(-1, "Failed to connect SDS ESP Session.");
  287. IPropertyTree* espSession = conn->queryRoot();
  288. VStringBuffer appStr("%s[@port=\"%d\"]", PathSessionApplication, m_port);
  289. IPropertyTree* appSessionTree = espSession->queryBranch(appStr.str());
  290. if (!appSessionTree)
  291. {
  292. IPropertyTree* newAppSessionTree = espSession->addPropTree(PathSessionApplication);
  293. newAppSessionTree->setPropInt("@port", m_port);
  294. }
  295. sessionSDSPath.setf("%s/%s/", espSessionSDSPath.str(), appStr.str());
  296. sessionIDCookieName.setf("%s%d", SESSION_ID_COOKIE, m_port);
  297. }
  298. static int compareLength(char const * const *l, char const * const *r) { return strlen(*l) - strlen(*r); }
  299. void EspHttpBinding::readAuthDomainCfg(IPropertyTree* procCfg)
  300. {
  301. VStringBuffer xpath("AuthDomains/AuthDomain[@domainName=\"%s\"]", domainName.get());
  302. IPropertyTree* authDomainTree = procCfg->queryPropTree(xpath);
  303. if (authDomainTree)
  304. {
  305. const char* authType = authDomainTree->queryProp("@authType");
  306. if (isEmptyString(authType) || strieq(authType, "AuthTypeMixed"))
  307. domainAuthType = AuthTypeMixed;
  308. else if (strieq(authType, "AuthPerSessionOnly"))
  309. domainAuthType = AuthPerSessionOnly;
  310. else
  311. {
  312. domainAuthType = AuthPerRequestOnly;
  313. return;
  314. }
  315. int clientSessionTimeoutMinutes = authDomainTree->getPropInt("@clientSessionTimeoutMinutes", ESP_SESSION_TIMEOUT);
  316. if (clientSessionTimeoutMinutes < 0)
  317. clientSessionTimeoutSeconds = ESP_SESSION_NEVER_TIMEOUT;
  318. else
  319. clientSessionTimeoutSeconds = clientSessionTimeoutMinutes * 60;
  320. //The serverSessionTimeoutMinutes is used to clean the sessions by ESP server after the sessions have been timed out on ESP clients.
  321. //Considering possible network delay, serverSessionTimeoutMinutes should be greater than clientSessionTimeoutMinutes.
  322. int serverSessionTimeoutMinutes = authDomainTree->getPropInt("@serverSessionTimeoutMinutes", 0);
  323. if ((serverSessionTimeoutMinutes < 0) || (clientSessionTimeoutMinutes < 0))
  324. serverSessionTimeoutSeconds = ESP_SESSION_NEVER_TIMEOUT;
  325. else
  326. serverSessionTimeoutSeconds = serverSessionTimeoutMinutes * 60;
  327. if (serverSessionTimeoutSeconds < clientSessionTimeoutSeconds)
  328. serverSessionTimeoutSeconds = 2 * clientSessionTimeoutSeconds;
  329. //The @unrestrictedResources contains URLs which may be used before a user is authenticated.
  330. //For example, an icon file on the login page.
  331. const char* unrestrictedResources = authDomainTree->queryProp("@unrestrictedResources");
  332. if (!isEmptyString(unrestrictedResources))
  333. readUnrestrictedResources(unrestrictedResources);
  334. const char* _loginURL = authDomainTree->queryProp("@logonURL");
  335. if (!isEmptyString(_loginURL))
  336. loginURL.set(_loginURL);
  337. else
  338. loginURL.set(DEFAULT_LOGIN_URL);
  339. const char* _logoutURL = authDomainTree->queryProp("@logoutURL");
  340. if (!isEmptyString(_logoutURL))
  341. {
  342. logoutURL.set(_logoutURL);
  343. domainAuthResources.setValue(logoutURL.get(), true);
  344. }
  345. //Read pre-configured 'invalidURLsAfterAuth'. Separate the comma separated string to a
  346. //list. Store them into BoolHash for quick lookup.
  347. setABoolHash(authDomainTree->queryProp("@invalidURLsAfterAuth"), invalidURLsAfterAuth);
  348. }
  349. else
  350. {//old environment.xml
  351. domainAuthType = AuthTypeMixed;
  352. readUnrestrictedResources(DEFAULT_UNRESTRICTED_RESOURCES);
  353. loginURL.set(DEFAULT_LOGIN_URL);
  354. }
  355. if (!loginURL.isEmpty())
  356. setABoolHash(loginURL.get(), invalidURLsAfterAuth);
  357. setABoolHash("/esp/login", invalidURLsAfterAuth);
  358. domainAuthResourcesWildMatch.sortCompare(compareLength);
  359. }
  360. void EspHttpBinding::readUnrestrictedResources(const char* resources)
  361. {
  362. StringArray resourceArray;
  363. resourceArray.appendListUniq(resources, ",");
  364. ForEachItemIn(i, resourceArray)
  365. {
  366. const char* resource = resourceArray.item(i);
  367. if (isEmptyString(resource))
  368. continue;
  369. if (isWildString(resource))
  370. domainAuthResourcesWildMatch.append(resource);
  371. else
  372. domainAuthResources.setValue(resource, true);
  373. }
  374. }
  375. //Check whether the url is valid or not for redirect after authentication.
  376. bool EspHttpBinding::canRedirectAfterAuth(const char* url) const
  377. {
  378. if (isEmptyString(url))
  379. return false;
  380. bool* found = invalidURLsAfterAuth.getValue(url);
  381. return (!found || !*found);
  382. }
  383. //Use the origin header to check whether the request is a CORS request or not.
  384. bool EspHttpBinding::isCORSRequest(const char* originHeader)
  385. {
  386. if (isEmptyString(originHeader))
  387. return false;
  388. const char* ptr = nullptr;
  389. if (strnicmp(originHeader, "http://", 7) == 0)
  390. ptr = originHeader + 7;
  391. else if (strnicmp(originHeader, "https://", 8) == 0)
  392. ptr = originHeader + 8;
  393. else
  394. return true;
  395. StringBuffer ipStr; //ip or network alias
  396. while(*ptr && *ptr != ':')
  397. {
  398. ipStr.append(*ptr);
  399. ptr++;
  400. }
  401. IpAddress ip(ipStr.str());
  402. if (ip.ipequals(queryHostIP()))
  403. return false;
  404. int port = 0;
  405. if (*ptr && *ptr == ':')
  406. {
  407. ptr++;
  408. while(*ptr && isdigit(*ptr))
  409. {
  410. port = 10*port + (*ptr-'0');
  411. ptr++;
  412. }
  413. }
  414. if (port == 0)
  415. port = DEFAULT_HTTP_PORT;
  416. if (port != getPort())
  417. return true;
  418. bool* found = serverAlias.getValue(ipStr.str());
  419. return (!found || !*found);
  420. }
  421. void EspHttpBinding::setABoolHash(const char* csv, BoolHash& hash) const
  422. {
  423. if (isEmptyString(csv))
  424. return;
  425. StringArray aList;
  426. aList.appendListUniq(csv, ",");
  427. ForEachItemIn(i, aList)
  428. {
  429. const char* s = aList.item(i);
  430. bool* found = hash.getValue(s);
  431. if (!found || !*found)
  432. hash.setValue(s, true);
  433. }
  434. }
  435. StringBuffer &EspHttpBinding::generateNamespace(IEspContext &context, CHttpRequest* request, const char *serv, const char *method, StringBuffer &ns)
  436. {
  437. ns.append("urn:hpccsystems:ws:");
  438. if (serv && *serv)
  439. ns.appendLower(strlen(serv), serv);
  440. return ns;
  441. }
  442. void EspHttpBinding::getSchemaLocation( IEspContext &context, CHttpRequest* request, StringBuffer &schemaLocation )
  443. {
  444. const char* svcName = request->queryServiceName();
  445. const char* method = request->queryServiceMethod();
  446. if ( !svcName || !(*svcName) )
  447. return;
  448. StringBuffer host;
  449. const char* wsdlAddr = request->queryParameters()->queryProp("__wsdl_address");
  450. if (wsdlAddr && *wsdlAddr)
  451. host.append(wsdlAddr);
  452. else
  453. {
  454. host.append(request->queryHost());
  455. if (request->getPort()>0)
  456. host.append(":").append(request->getPort());
  457. }
  458. schemaLocation.appendf("%s/%s/%s?xsd&amp;ver_=%g", host.str(), svcName, method ? method : "", context.getClientVersion());
  459. }
  460. int EspHttpBinding::getMethodDescription(IEspContext &context, const char *serv, const char *method, StringBuffer &page)
  461. {
  462. StringBuffer key(method);
  463. StringAttr *descrStr=desc_map.getValue(key.toUpperCase().str());
  464. page.append(descrStr ? descrStr->get() : "No description available");
  465. return 0;
  466. }
  467. int EspHttpBinding::getMethodHelp(IEspContext &context, const char *serv, const char *method, StringBuffer &page)
  468. {
  469. StringBuffer key(method);
  470. StringAttr *helpStr=help_map.getValue(key.toUpperCase().str());
  471. page.append(helpStr ? helpStr->get() : "No Help available");
  472. return 0;
  473. }
  474. bool EspHttpBinding::isMethodInService(IEspContext& context, const char *servname, const char *methname)
  475. {
  476. StringBuffer serviceQName;
  477. StringBuffer methodQName;
  478. if (!qualifyServiceName(context, servname, methname, serviceQName, &methodQName))
  479. return false;
  480. if (methodQName.length() > 0)
  481. return true;
  482. return isMethodInSubService(context, servname, methname);
  483. }
  484. bool EspHttpBinding::qualifySubServiceName(IEspContext &context, const char *servname, const char *methname, StringBuffer &servQName, StringBuffer *methQName)
  485. {
  486. if (m_subservices)
  487. {
  488. StringBuffer xpath;
  489. xpath.appendf("SubService[@name='%s']/@name", servname);
  490. const char *qname=m_subservices->queryProp(xpath.str());
  491. if (qname)
  492. {
  493. servQName.clear().append(qname);
  494. return qualifyMethodName(context, methname, methQName);
  495. }
  496. }
  497. return false;
  498. }
  499. bool EspHttpBinding::hasSubService(IEspContext &context, const char *name)
  500. {
  501. if (m_subservices)
  502. {
  503. StringBuffer xpath;
  504. xpath.appendf("SubService[@name='%s']", name);
  505. return m_subservices->hasProp(xpath.str());
  506. }
  507. return false;
  508. }
  509. void EspHttpBinding::setRequestPath(const char *path)
  510. {
  511. m_reqPath.clear();
  512. m_reqPath.append(path);
  513. }
  514. bool EspHttpBinding::rootAuthRequired()
  515. {
  516. if(!m_authmap.get())
  517. return false;
  518. if(!stricmp(m_authmethod.str(), "UserDefined") && m_authmap->shouldAuth("/"))
  519. return true;
  520. Owned<ISecResourceList> rlist = m_authmap->getResourceList("/");
  521. if (rlist.get() == NULL)
  522. return false;
  523. return true;
  524. }
  525. bool EspHttpBinding::authRequired(CHttpRequest *request)
  526. {
  527. StringBuffer path;
  528. request->getPath(path);
  529. if(path.length() == 0)
  530. {
  531. throw MakeStringException(-1, "Path is empty for http request");
  532. }
  533. if(m_authmap.get() == NULL)
  534. return false;
  535. if(stricmp(m_authmethod.str(), "UserDefined") == 0 && m_authmap->shouldAuth(path.str()))
  536. return true;
  537. ISecResourceList* rlist = m_authmap->getResourceList(path.str());
  538. if(rlist == NULL)
  539. return false;
  540. request->queryContext()->setResources(rlist);
  541. return true;
  542. }
  543. void EspHttpBinding::populateRequest(CHttpRequest *request)
  544. {
  545. IEspContext* ctx = request->queryContext();
  546. ctx->setSecManger(m_secmgr.getLink());
  547. ctx->setFeatureAuthMap(m_feature_authmap.getLink());
  548. StringBuffer userid, password,realm,peer;
  549. ctx->getUserID(userid);
  550. if(m_secmgr.get() == NULL)
  551. {
  552. return;
  553. }
  554. ISecUser *user = m_secmgr->createUser(userid.str());
  555. if(user == NULL)
  556. {
  557. WARNLOG("Couldn't create ISecUser object for %s", userid.str());
  558. return;
  559. }
  560. ctx->getPassword(password);
  561. user->credentials().setPassword(password.str());
  562. ctx->getRealm(realm);
  563. user->setRealm(realm.str());
  564. ctx->getPeer(peer);
  565. user->setPeer(peer.str());
  566. ctx->setUser(user);
  567. if(m_setting_authmap.get() != NULL)
  568. {
  569. ISecResourceList* settinglist = m_setting_authmap->getResourceList("*");
  570. if(settinglist == NULL)
  571. return ;
  572. if (getEspLogLevel()>=LogMax)
  573. {
  574. StringBuffer s;
  575. DBGLOG("Set security settings: %s", settinglist->toString(s).str());
  576. }
  577. ctx->setSecuritySettings(settinglist);
  578. }
  579. return ;
  580. }
  581. bool EspHttpBinding::doAuth(IEspContext* ctx)
  582. {
  583. if(m_authtype.length() == 0 || stricmp(m_authtype.str(), "Basic") == 0)
  584. {
  585. return basicAuth(ctx);
  586. }
  587. return false;
  588. }
  589. bool EspHttpBinding::basicAuth(IEspContext* ctx)
  590. {
  591. StringBuffer userid;
  592. ctx->getUserID(userid);
  593. if(userid.length() == 0)
  594. {
  595. ctx->setAuthError(EspAuthErrorEmptyUserID);
  596. ctx->AuditMessage(AUDIT_TYPE_ACCESS_FAILURE, "Authentication", "Access Denied: No username provided");
  597. return false;
  598. }
  599. if(stricmp(m_authmethod.str(), "UserDefined") == 0)
  600. return true;
  601. ISecUser *user = ctx->queryUser();
  602. if(user == NULL)
  603. {
  604. WARNLOG("Can't find user in context");
  605. ctx->setAuthError(EspAuthErrorUserNotFoundInContext);
  606. ctx->AuditMessage(AUDIT_TYPE_ACCESS_FAILURE, "Authentication", "Access Denied: No username provided");
  607. return false;
  608. }
  609. if(m_secmgr.get() == NULL)
  610. {
  611. WARNLOG("No mechanism established for authentication");
  612. ctx->setAuthError(EspAuthErrorNoAuthMechanism);
  613. return false;
  614. }
  615. ISecResourceList* rlist = ctx->queryResources();
  616. if(rlist == NULL)
  617. {
  618. WARNLOG("No Security Resource");
  619. ctx->setAuthError(EspAuthErrorEmptySecResource);
  620. return false;
  621. }
  622. bool authenticated = m_secmgr->authorize(*user, rlist, ctx->querySecureContext());
  623. if(!authenticated)
  624. {
  625. const char *desc = nullptr;
  626. if (user->getAuthenticateStatus() == AS_PASSWORD_EXPIRED || user->getAuthenticateStatus() == AS_PASSWORD_VALID_BUT_EXPIRED)
  627. desc = "ESP password is expired";
  628. else
  629. desc = "Access Denied: User or password invalid";
  630. ctx->AuditMessage(AUDIT_TYPE_ACCESS_FAILURE, "Authentication", desc);
  631. ctx->setAuthError(EspAuthErrorNotAuthenticated);
  632. ctx->setRespMsg(desc);
  633. return false;
  634. }
  635. bool authorized = true;
  636. for(int i = 0; i < rlist->count(); i++)
  637. {
  638. ISecResource* curres = rlist->queryResource(i);
  639. if(curres != NULL)
  640. {
  641. int access = (int)curres->getAccessFlags();
  642. int required = (int)curres->getRequiredAccessFlags();
  643. if(access < required)
  644. {
  645. const char *desc=curres->getDescription();
  646. VStringBuffer msg("Access for user '%s' denied to: %s. Access=%d, Required=%d", user->getName(), desc?desc:"<no-desc>", access, required);
  647. ESPLOG(LogMin, "%s", msg.str());
  648. ctx->AuditMessage(AUDIT_TYPE_ACCESS_FAILURE, "Authorization", "Access Denied: Not Authorized", "Resource: %s [%s]", curres->getName(), (desc) ? desc : "");
  649. ctx->setAuthError(EspAuthErrorNotAuthorized);
  650. ctx->setRespMsg(msg.str());
  651. authorized = false;
  652. break;
  653. }
  654. }
  655. }
  656. if(authorized==false)
  657. return false;
  658. ISecPropertyList* securitySettings = ctx->querySecuritySettings();
  659. if(securitySettings == NULL)
  660. return authorized;
  661. m_secmgr->updateSettings(*user,securitySettings, ctx->querySecureContext());
  662. ctx->addTraceSummaryTimeStamp(LogMin, "basicAuth");
  663. return authorized;
  664. }
  665. bool EspHttpBinding::queryCacheSeconds(const char *method, unsigned& cacheSeconds)
  666. {
  667. StringBuffer key(method);
  668. unsigned* value = cacheSecondsMap.getValue(key.toUpperCase().str());
  669. if (!value)
  670. return false;
  671. cacheSeconds = *value;
  672. return true;
  673. }
  674. bool EspHttpBinding::queryCacheGlobal(const char *method)
  675. {
  676. StringBuffer key(method);
  677. bool* cacheGlobal = cacheGlobalMap.getValue(key.toUpperCase().str());
  678. return cacheGlobal && *cacheGlobal;
  679. }
  680. const char* EspHttpBinding::createESPCacheID(CHttpRequest* request, StringBuffer& cacheID)
  681. {
  682. StringBuffer idStr, msgType;
  683. if(request->isSoapMessage())
  684. msgType.set("SOAP");
  685. else if(request->isFormSubmission())
  686. msgType.set("FORM");
  687. if (!queryCacheGlobal(request->queryServiceMethod()))
  688. {
  689. const char* userID = request->queryContext()->queryUserId();
  690. if (!isEmptyString(userID))
  691. idStr.append(userID).append("_");
  692. }
  693. if (!msgType.isEmpty())
  694. idStr.append(msgType.str()).append("_");
  695. idStr.appendf("%s_%s_%s", request->queryServiceName(), request->queryServiceMethod(), request->queryAllParameterString());
  696. cacheID.append(hashc((unsigned char *)idStr.str(), idStr.length(), 0));
  697. return cacheID.str();
  698. }
  699. bool EspHttpBinding::sendFromESPCache(IEspCache* cacheClient, CHttpRequest* request, CHttpResponse* response, const char* cacheID)
  700. {
  701. StringBuffer content, contentType;
  702. if (!cacheClient->readResponseCache(cacheID, content.clear(), contentType.clear()))
  703. ESPLOG(LogMax, "Failed to read from ESP Cache for %s.", request->queryServiceMethod());
  704. if (content.isEmpty() || contentType.isEmpty())
  705. return false;
  706. ESPLOG(LogMax, "Sending from ESP Cache for %s.", request->queryServiceMethod());
  707. response->setContentType(contentType.str());
  708. response->setContent(content.str());
  709. response->send();
  710. return true;
  711. }
  712. void EspHttpBinding::addToESPCache(IEspCache* cacheClient, CHttpRequest* request, CHttpResponse* response, const char* cacheID, unsigned cacheSeconds)
  713. {
  714. StringBuffer content, contentType;
  715. response->getContent(content);
  716. response->getContentType(contentType);
  717. if (cacheClient->cacheResponse(cacheID, cacheSeconds, content.str(), contentType.str()))
  718. ESPLOG(LogMax, "AddTo ESP Cache for %s.", request->queryServiceMethod());
  719. else
  720. ESPLOG(LogMax, "Failed to add ESP Cache for %s.", request->queryServiceMethod());
  721. }
  722. void EspHttpBinding::clearCacheByGroupID(const char *ids)
  723. {
  724. if (isEmptyString(ids))
  725. return;
  726. IEspContainer *espContainer = getESPContainer();
  727. if (!espContainer->hasCacheClient())
  728. return;
  729. ESPLOG(LogMax, "clearCacheByGroupID %s.", ids);
  730. StringArray errorMsgs;
  731. espContainer->clearCacheByGroupID(ids, errorMsgs);
  732. if (errorMsgs.length() > 0)
  733. {
  734. ForEachItemIn(i, errorMsgs)
  735. DBGLOG("%s", errorMsgs.item(i));
  736. }
  737. }
  738. void EspHttpBinding::handleHttpPost(CHttpRequest *request, CHttpResponse *response)
  739. {
  740. StringBuffer cacheID;
  741. unsigned cacheSeconds = 0;
  742. IEspCache *cacheClient = nullptr;
  743. IEspContext &context = *request->queryContext();
  744. IEspContainer *espContainer = getESPContainer();
  745. if (espContainer->hasCacheClient() && (cacheMethods > 0)
  746. && queryCacheSeconds(request->queryServiceMethod(), cacheSeconds)) //ESP cache is needed for this method
  747. {
  748. cacheClient = (IEspCache*) espContainer->queryCacheClient(getCacheGroupID(request->queryServiceMethod()));
  749. if (cacheClient)
  750. createESPCacheID(request, cacheID);
  751. if (!cacheID.isEmpty() && !context.queryRequestParameters()->queryProp("dirty_cache")
  752. && sendFromESPCache(cacheClient, request, response, cacheID.str()))
  753. return;
  754. }
  755. if(request->isSoapMessage())
  756. {
  757. request->queryParameters()->setProp("__wsdl_address", m_wsdlAddress.str());
  758. onSoapRequest(request, response);
  759. }
  760. else if(request->isFormSubmission())
  761. onPostForm(request, response);
  762. else
  763. onPost(request, response);
  764. if (!cacheID.isEmpty())
  765. addToESPCache(cacheClient, request, response, cacheID.str(), cacheSeconds);
  766. }
  767. int EspHttpBinding::onGet(CHttpRequest* request, CHttpResponse* response)
  768. {
  769. IEspContext& context = *request->queryContext();
  770. // At this time, the request is already received and fully passed, and
  771. // the user authenticated
  772. LogLevel level = getEspLogLevel(&context);
  773. if (level >= LogNormal)
  774. DBGLOG("EspHttpBinding::onGet");
  775. response->setVersion(HTTP_VERSION);
  776. response->addHeader("Expires", "0");
  777. response->setStatus(HTTP_STATUS_OK);
  778. sub_service sstype = sub_serv_unknown;
  779. StringBuffer pathEx;
  780. StringBuffer serviceName;
  781. StringBuffer methodName;
  782. StringBuffer paramStr;
  783. request->getEspPathInfo(sstype, &pathEx, &serviceName, &methodName, false);
  784. // adjust version if necessary
  785. if (m_defaultSvcVersion.get() && !context.queryRequestParameters()->queryProp("ver_"))
  786. {
  787. switch(sstype)
  788. {
  789. case sub_serv_root:
  790. case sub_serv_main:
  791. case sub_serv_index:
  792. case sub_serv_xform:
  793. case sub_serv_xsd:
  794. case sub_serv_wsdl:
  795. case sub_serv_soap_builder:
  796. case sub_serv_reqsamplexml:
  797. case sub_serv_respsamplexml:
  798. case sub_serv_respsamplejson:
  799. case sub_serv_reqsamplejson:
  800. context.setClientVersion(atof(m_defaultSvcVersion));
  801. default:
  802. break;
  803. }
  804. }
  805. switch (sstype)
  806. {
  807. case sub_serv_root:
  808. case sub_serv_main:
  809. return onGetRoot(context, request, response);
  810. case sub_serv_config:
  811. return onGetConfig(context, request, response);
  812. case sub_serv_getversion:
  813. return onGetVersion(context, request, response, serviceName.str());
  814. case sub_serv_index:
  815. return onGetIndex(context, request, response, serviceName.str());
  816. case sub_serv_files:
  817. checkInitEclIdeResponse(request, response);
  818. return onGetFile(context, request, response, pathEx.str());
  819. case sub_serv_itext:
  820. return onGetItext(context, request, response, pathEx.str());
  821. case sub_serv_iframe:
  822. return onGetIframe(context, request, response, pathEx.str());
  823. case sub_serv_content:
  824. return onGetContent(context, request, response, serviceName.str(), methodName.str());
  825. case sub_serv_method:
  826. return onGetService(context, request, response, serviceName.str(), methodName.str(), pathEx.str());
  827. case sub_serv_form:
  828. return onGetForm(context, request, response, serviceName.str(), methodName.str());
  829. case sub_serv_xform:
  830. return onGetXForm(context, request, response, serviceName.str(), methodName.str());
  831. case sub_serv_result:
  832. return onGetResult(context, request, response, serviceName.str(), methodName.str(), pathEx.str());
  833. case sub_serv_wsdl:
  834. return onGetWsdl(context, request, response, serviceName.str(), methodName.str());
  835. case sub_serv_xsd:
  836. return onGetXsd(context, request, response, serviceName.str(), methodName.str());
  837. case sub_serv_instant_query:
  838. return onGetInstantQuery(context, request, response, serviceName.str(), methodName.str());
  839. case sub_serv_soap_builder:
  840. return onGetSoapBuilder(context, request, response, serviceName.str(), methodName.str());
  841. case sub_serv_reqsamplexml:
  842. return onGetReqSampleXml(context, request, response, serviceName.str(), methodName.str());
  843. case sub_serv_respsamplexml:
  844. return onGetRespSampleXml(context, request, response, serviceName.str(), methodName.str());
  845. case sub_serv_respsamplejson:
  846. return onGetRespSampleJson(context, request, response, serviceName.str(), methodName.str());
  847. case sub_serv_reqsamplejson:
  848. return onGetReqSampleJson(context, request, response, serviceName.str(), methodName.str());
  849. case sub_serv_query:
  850. return onGetQuery(context, request, response, serviceName.str(), methodName.str());
  851. case sub_serv_file_upload:
  852. return onStartUpload(context, request, response, serviceName.str(), methodName.str());
  853. default:
  854. return onGetNotFound(context, request, response, serviceName.str());
  855. }
  856. return 0;
  857. }
  858. int EspHttpBinding::onGetStaticIndex(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv)
  859. {
  860. return onGetNotFound(context, request, response, serv);
  861. }
  862. #if 1
  863. //============================================================
  864. // Filter out XML by schema using Schema parser without indent
  865. static void setStartTag(IPTree* in, IXmlType* type, const char* tag,StringBuffer& out)
  866. {
  867. int nAttrs = type->getAttrCount();
  868. out.appendf("<%s", tag);
  869. for (int attr=0; attr<nAttrs; attr++)
  870. {
  871. IXmlAttribute* att = type->queryAttr(attr);
  872. VStringBuffer prop("@%s", att->queryName());
  873. const char* val = in->queryProp(prop);
  874. if (val && *val)
  875. {
  876. StringBuffer encoded;
  877. encodeXML(val, encoded);
  878. out.appendf(" %s=\"%s\"", att->queryName(), encoded.str());
  879. }
  880. }
  881. out.append('>');
  882. }
  883. static void filterXmlBySchema(IPTree* in, IXmlType* type, const char* tag, StringBuffer& out)
  884. {
  885. assertex(type);
  886. if (type->isComplexType())
  887. {
  888. setStartTag(in,type,tag,out);
  889. int flds = type->getFieldCount();
  890. for (int i=0; i<flds; i++)
  891. {
  892. const char* fldName = type->queryFieldName(i);
  893. IPTree* fld = in ? in->queryBranch(fldName) : NULL;
  894. filterXmlBySchema(fld,type->queryFieldType(i),fldName,out);
  895. }
  896. if (flds==0)
  897. {
  898. if (type->getSubType() == SubType_Complex_SimpleContent) // the first field is baseType
  899. {
  900. const char* val = in->queryProp(".");
  901. if (val && *val) {
  902. StringBuffer encoded;
  903. encodeXML(val, encoded);
  904. out.append(encoded);
  905. }
  906. }
  907. }
  908. out.appendf("</%s>",tag);
  909. }
  910. else if (type->isArray())
  911. {
  912. setStartTag(in,type,tag,out);
  913. //check xml first to decide items, if 0, check schema, generate 1 item by default
  914. const char* itemName = type->queryFieldName(0);
  915. IXmlType* itemType = type->queryFieldType(0);
  916. if (!itemName || !itemType)
  917. {
  918. VStringBuffer s("*** Invalid array definition: tag=%s, itemName=%s", tag, itemName?itemName:"NULL");
  919. out.append(s);
  920. ERRLOG("%s", s.str());
  921. return;
  922. }
  923. bool hasChild = false;
  924. if (in)
  925. {
  926. Owned<IPTreeIterator> it = in->getElements(itemName);
  927. for (it->first(); it->isValid(); it->next())
  928. {
  929. hasChild = true;
  930. filterXmlBySchema(&it->query(), itemType, itemName, out);
  931. }
  932. }
  933. if (!hasChild)
  934. {
  935. filterXmlBySchema(NULL, itemType, itemName, out);
  936. }
  937. out.appendf("</%s>",tag);
  938. }
  939. else // simple type
  940. {
  941. setStartTag(in,type,tag,out);
  942. if (in)
  943. {
  944. const char* value = in->queryProp(NULL);
  945. if (value)
  946. encodeUtf8XML(value,out);
  947. }
  948. out.appendf("</%s>", tag);
  949. }
  950. }
  951. static void filterXmlBySchema(StringBuffer& in, StringBuffer& schema, const char* name, StringBuffer& out)
  952. {
  953. Owned<IXmlSchema> sp = createXmlSchema(schema);
  954. Owned<IPTree> tree = createPTreeFromXMLString(in);
  955. //VStringBuffer name("tns:%s", tree->queryName());
  956. IXmlType* type = sp->queryElementType(name);
  957. if (!type)
  958. {
  959. name = tree->queryName();
  960. type = sp->queryElementType(name);
  961. if (!type)
  962. {
  963. StringBuffer method = name;
  964. if (method.length() > 7)
  965. method.setLength(method.length()-7);
  966. type = sp->queryElementType(method);
  967. }
  968. }
  969. if (type)
  970. filterXmlBySchema(tree,type,name,out);
  971. else
  972. {
  973. const char* value = tree->queryProp(NULL);
  974. DBGLOG("Unknown xml tag ignored: <%s>%s</%s>", name, value?value:"", name);
  975. }
  976. }
  977. void EspHttpBinding::getXMLMessageTag(IEspContext& ctx, bool isRequest, const char *method, StringBuffer& tag)
  978. {
  979. MethodInfoArray info;
  980. getQualifiedNames(ctx, info);
  981. for (unsigned i=0; i<info.length(); i++)
  982. {
  983. CMethodInfo& m = info.item(i);
  984. if (!stricmp(m.m_label, method))
  985. {
  986. tag.set(isRequest ? m.m_requestLabel : m.m_responseLabel);
  987. break;
  988. }
  989. }
  990. if (!tag.length())
  991. tag.append(method).append(isRequest ? "Request" : "Response");
  992. }
  993. // new way to generate soap message
  994. void EspHttpBinding::getSoapMessage(StringBuffer& soapmsg, IEspContext& ctx, CHttpRequest* request, const char *serv, const char *method)
  995. {
  996. StringBuffer reqName(serv);
  997. reqName.append("Request");
  998. Owned<IRpcMessage> msg = new CRpcMessage(reqName.str());
  999. msg->setContext(&ctx);
  1000. Owned<IRpcRequestBinding> rpcreq = createReqBinding(ctx, request, serv, method);
  1001. rpcreq->serialize(*msg);
  1002. StringBuffer req, tag, schema, filtered;
  1003. msg->marshall(req, NULL);
  1004. getSchema(schema,ctx,request,serv,method,false);
  1005. getXMLMessageTag(ctx, true, method, tag);
  1006. filterXmlBySchema(req,schema,tag.str(),filtered);
  1007. StringBuffer ns;
  1008. soapmsg.appendf(
  1009. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
  1010. "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
  1011. " xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\""
  1012. " xmlns=\"%s\">"
  1013. " <soap:Body>%s </soap:Body></soap:Envelope>",
  1014. generateNamespace(ctx, request, serv, method, ns).str(), filtered.str()
  1015. );
  1016. }
  1017. #else
  1018. //=========================================================
  1019. // Filter out XML by schema using Schema parser with indent
  1020. static void filterXmlBySchema(IPTree* in, IXmlType* type, const char* tag, StringBuffer& out,int indent)
  1021. {
  1022. assertex(type);
  1023. if (type->isComplexType())
  1024. {
  1025. out.pad(indent).appendf("<%s>\n", tag);
  1026. int flds = type->getFieldCount();
  1027. for (int i=0; i<flds; i++)
  1028. {
  1029. const char* fldName = type->queryFieldName(i);
  1030. IPTree* fld = in ? in->queryBranch(fldName) : NULL;
  1031. filterXmlBySchema(fld,type->queryFieldType(i),fldName,out,indent+1);
  1032. }
  1033. out.pad(indent).appendf("</%s>\n",tag);
  1034. }
  1035. else if (type->isArray())
  1036. {
  1037. out.pad(indent).appendf("<%s>\n", tag);
  1038. //check xml first to decide items, if 0, check schema, generate 1 item by default
  1039. const char* itemName = type->queryFieldName(0);
  1040. IXmlType* itemType = type->queryFieldType(0);
  1041. if (!itemName || !itemType)
  1042. {
  1043. VStringBuffer s("*** Invalid array definition: tag=%s, indent=%d, itemName=%s", tag, indent,itemName?itemName:"NULL");
  1044. out.append(s);
  1045. ERRLOG(s);
  1046. return;
  1047. }
  1048. bool hasChild = false;
  1049. if (in)
  1050. {
  1051. Owned<IPTreeIterator> it = in->getElements(itemName);
  1052. for (it->first(); it->isValid(); it->next())
  1053. {
  1054. hasChild = true;
  1055. filterXmlBySchema(&it->query(), itemType, itemName, out,indent+1);
  1056. }
  1057. }
  1058. if (!hasChild)
  1059. {
  1060. filterXmlBySchema(NULL, itemType, itemName, out,indent+1);
  1061. }
  1062. out.pad(indent).appendf("</%s>\n",tag);
  1063. }
  1064. else // simple type
  1065. {
  1066. out.pad(indent).appendf("<%s>", tag);
  1067. if (in)
  1068. {
  1069. const char* value = in->queryProp(NULL);
  1070. if (value)
  1071. encodeUtf8XML(value,out);
  1072. }
  1073. out.appendf("</%s>\n", tag);
  1074. }
  1075. }
  1076. static void filterXmlBySchema(StringBuffer& in, StringBuffer& schema, StringBuffer& out,int indent)
  1077. {
  1078. Owned<IXmlSchema> sp = createXmlSchema(schema);
  1079. Owned<IPTree> tree = createPTreeFromXMLString(in);
  1080. //VStringBuffer name("tns:%s", tree->queryName());
  1081. const char* name = tree->queryName();
  1082. IXmlType* type = sp->queryElementType(name);
  1083. if (!type)
  1084. {
  1085. StringBuffer method(strlen(name)-7, name);
  1086. type = sp->queryElementType(method);
  1087. }
  1088. if (type)
  1089. filterXmlBySchema(tree,type,name,out,indent);
  1090. else
  1091. {
  1092. const char* value = tree->queryProp(NULL);
  1093. DBGLOG("Unknown xml tag ignored: <%s>%s</%s>", name, value?value:"", name);
  1094. }
  1095. }
  1096. void EspHttpBinding::getSoapMessage(StringBuffer& soapmsg, IEspContext& ctx, CHttpRequest* request, const char *serv, const char *method)
  1097. {
  1098. StringBuffer reqName(serv);
  1099. reqName.append("Request");
  1100. Owned<IRpcMessage> msg = new CRpcMessage(reqName.str());
  1101. Owned<IRpcRequestBinding> rpcreq = createReqBinding(ctx, request, serv, method);
  1102. rpcreq->serialize(*msg);
  1103. // use schema to filter out fields that are internal, not belong to the version etc
  1104. StringBuffer req,schema,filtered;
  1105. msg->marshall(req, NULL);
  1106. getSchema(schema,ctx,request,serv,method,false);
  1107. //DBGLOG("Schema: %s", schema.str());
  1108. filterXmlBySchema(req,schema,filtered,2);
  1109. soapmsg.appendf(
  1110. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
  1111. "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
  1112. " xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\""
  1113. " xmlns=\"urn:hpccsystems:ws:");
  1114. if (serv && *serv)
  1115. soapmsg.appendLower(strlen(serv), serv);
  1116. soapmsg.appendf("\">\n <soap:Body>\n%s </soap:Body>\n</soap:Envelope>", filtered.str());
  1117. }
  1118. #endif
  1119. int EspHttpBinding::onGetSoapBuilder(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1120. {
  1121. StringBuffer soapmsg, serviceQName, methodQName;
  1122. if (!qualifyServiceName(context, serv, method, serviceQName, &methodQName)
  1123. || methodQName.length()==0)
  1124. throw createEspHttpException(HTTP_STATUS_BAD_REQUEST_CODE, "Bad Request", HTTP_STATUS_BAD_REQUEST);
  1125. getSoapMessage(soapmsg,context,request,serviceQName,methodQName);
  1126. //put all URL parameters into dest
  1127. StringBuffer params;
  1128. const char* excludes[] = {"soap_builder_",NULL};
  1129. getEspUrlParams(context,params,excludes);
  1130. VStringBuffer dest("%s/%s?%s", serviceQName.str(), methodQName.str(), params.str());
  1131. VStringBuffer header("SOAPAction: \"%s\"\n", dest.str());
  1132. header.append("Content-Type: text/xml; charset=UTF-8");
  1133. IXslProcessor* xslp = getXmlLibXslProcessor();
  1134. Owned<IXslTransform> xform = xslp->createXslTransform();
  1135. xform->loadXslFromFile(StringBuffer(getCFD()).append("./xslt/soap_page.xsl").str());
  1136. xform->setXmlSource("<xml/>", 6);
  1137. // params
  1138. xform->setStringParameter("serviceName", serviceQName.str());
  1139. xform->setStringParameter("methodName", methodQName.str());
  1140. xform->setStringParameter("soapbody", soapmsg.str());
  1141. xform->setStringParameter("header", header.str());
  1142. ISecUser* user = context.queryUser();
  1143. bool inhouse = user && (user->getStatus()==SecUserStatus_Inhouse);
  1144. xform->setParameter("inhouseUser", inhouse ? "true()" : "false()");
  1145. VStringBuffer url("%s?%s", methodQName.str(), params.str());
  1146. xform->setStringParameter("destination", url.str());
  1147. const char* authMethod = context.getAuthenticationMethod();
  1148. if (authMethod && !strieq(authMethod, "none") && ((context.getDomainAuthType() == AuthPerSessionOnly) || (context.getDomainAuthType() == AuthTypeMixed)))
  1149. xform->setParameter("showLogout", "1");
  1150. StringBuffer page;
  1151. xform->transform(page);
  1152. response->setContent(page);
  1153. response->setContentType("text/html; charset=UTF-8");
  1154. response->setStatus(HTTP_STATUS_OK);
  1155. response->send();
  1156. return 0;
  1157. }
  1158. int EspHttpBinding::onFeaturesAuthorize(IEspContext &context, MapStringTo<SecAccessFlags> & pmap, const char *serviceName, const char *methodName)
  1159. {
  1160. if (!context.validateFeaturesAccess(pmap, false))
  1161. {
  1162. StringBuffer features;
  1163. HashIterator iter(pmap);
  1164. int index = 0;
  1165. ForEach(iter)
  1166. {
  1167. IMapping &cur = iter.query();
  1168. const char * key = (const char *)cur.getKey();
  1169. SecAccessFlags val = *pmap.getValue(key);
  1170. features.appendf("%s%s:%s", (index++ == 0 ? "" : ", "), key, getSecAccessFlagName(val));
  1171. }
  1172. throw MakeStringException(-1, "%s::%s access denied - Required features: %s.", serviceName, methodName, features.str());
  1173. }
  1174. return 0;
  1175. }
  1176. int EspHttpBinding::onGetInstantQuery(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1177. {
  1178. return onGetNotFound(context, request, response, serv);
  1179. }
  1180. int EspHttpBinding::onGetResult(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method, const char *pathex)
  1181. {
  1182. return onGetNotFound(context, request, response, serv);
  1183. }
  1184. int EspHttpBinding::onGetResultPresentation(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method, StringBuffer &xmlResult)
  1185. {
  1186. return onGetNotFound(context, request, response, serv);
  1187. }
  1188. int EspHttpBinding::onGetFile(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *path)
  1189. {
  1190. return onGetNotFound(context, request, response, NULL);
  1191. }
  1192. int EspHttpBinding::onGetItext(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *path)
  1193. {
  1194. StringBuffer title;
  1195. request->getParameter("text", title);
  1196. StringBuffer content;
  1197. if (checkInitEclIdeResponse(request, response))
  1198. content.append("<!DOCTYPE html>"); //may be safe for all browsers? but better to be safe for now?
  1199. content.append("<html><head>");
  1200. if(title.length() > 0)
  1201. content.appendf("<title>%s</title>", title.str());
  1202. content.appendf("</head><body>"
  1203. "<img src=\"files_/img/topurl.png\" onClick=\"top.location.href=parent.frames['imain'].location.href\" alt=\"No Frames\" width=\"13\" height=\"15\">"
  1204. "&nbsp;&nbsp;<font size=+1><b>%s</b></font></body></html>", title.str());
  1205. response->setContent(content.length(), content.str());
  1206. response->setContentType("text/html");
  1207. response->send();
  1208. return 0;
  1209. }
  1210. int EspHttpBinding::onGetIframe(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *path)
  1211. {
  1212. StringBuffer title;
  1213. request->getParameter("esp_iframe_title", title);
  1214. StringBuffer content;
  1215. if (checkInitEclIdeResponse(request, response))
  1216. content.append("<!DOCTYPE html>"); //may be safe for all browsers? but better to be safe for now?
  1217. content.append("<html><head>");
  1218. if(title.length() > 0)
  1219. content.appendf("<title>%s</title>", title.str());
  1220. content.append("</head>");
  1221. content.append("<frameset rows=\"35,*\" FRAMEPADDING=\"0\" PADDING=\"0\" SPACING=\"0\" FRAMEBORDER=\"0\">");
  1222. content.appendf("<frame src=\"itext?text=%s\" name=\"ititle\" target=\"imain\" scrolling=\"no\"/>", title.str());
  1223. //content.appendf("<frame name=\"imain\" target=\"main\" src=\"../%s?%s\" scrolling=\"auto\" frameborder=\"0\" noresize=\"noresize\"/>", path, request->queryParamStr());
  1224. StringBuffer inner;
  1225. request->getParameter("inner", inner);
  1226. StringBuffer plainText;
  1227. request->getParameter("PlainText", plainText);
  1228. if (plainText.length() > 0)
  1229. inner.appendf("&PlainText=%s", plainText.str());
  1230. ESPLOG(LogNormal,"Inner: %s", inner.str());
  1231. ESPLOG(LogNormal,"Param: %s", request->queryParamStr());
  1232. content.appendf("<frame name=\"imain\" target=\"main\" src=\"%s\" scrolling=\"auto\" frameborder=\"0\" noresize=\"noresize\"/>", inner.str());
  1233. content.append("</frameset>");
  1234. content.append("</body></html>");
  1235. response->setContent(content.length(), content.str());
  1236. response->setContentType("text/html");
  1237. response->send();
  1238. return 0;
  1239. }
  1240. int EspHttpBinding::onGetContent(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1241. {
  1242. return onGetNotFound(context, request, response, serv);
  1243. }
  1244. int EspHttpBinding::onGetConfig(IEspContext &context, CHttpRequest* request, CHttpResponse* response)
  1245. {
  1246. ISecUser* user = context.queryUser();
  1247. if (m_viewConfig || (user && (user->getStatus()==SecUserStatus_Inhouse)))
  1248. {
  1249. ESPLOG(LogNormal, "Get config file: %s", m_configFile.get());
  1250. StringBuffer content;
  1251. xmlContentFromFile(m_configFile, "/esp/xslt/xmlformatter.xsl", content);
  1252. response->setContent(content.str());
  1253. response->setContentType(HTTP_TYPE_APPLICATION_XML_UTF8);
  1254. response->setStatus(HTTP_STATUS_OK);
  1255. response->send();
  1256. return 0;
  1257. }
  1258. DBGLOG("Config access denied");
  1259. return onGetNotFound(context, request, response, NULL);
  1260. }
  1261. int EspHttpBinding::onGetVersion(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service)
  1262. {
  1263. StringBuffer verxml;
  1264. StringBuffer srvQName;
  1265. qualifyServiceName(context, service, NULL, srvQName, NULL);
  1266. verxml.appendf("<VersionInfo><Service>%s</Service><Version>%.3f</Version></VersionInfo>", srvQName.str(), m_wsdlVer);
  1267. response->setContent(verxml.str());
  1268. response->setContentType(HTTP_TYPE_APPLICATION_XML_UTF8);
  1269. response->setStatus(HTTP_STATUS_OK);
  1270. response->send();
  1271. return 0;
  1272. }
  1273. int EspHttpBinding::onGetXsd(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service, const char *method)
  1274. {
  1275. return getWsdlOrXsd(context,request,response,service,method,false);
  1276. }
  1277. int EspHttpBinding::onGetWsdl(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service, const char *method)
  1278. {
  1279. return getWsdlOrXsd(context,request,response,service,method,true);
  1280. }
  1281. bool EspHttpBinding::getSchema(StringBuffer& schema, IEspContext &ctx, CHttpRequest* req, const char *service, const char *method, bool standalone)
  1282. {
  1283. StringBuffer serviceQName;
  1284. StringBuffer methodQName;
  1285. if (!qualifyServiceName(ctx, service, method, serviceQName, &methodQName))
  1286. return false;
  1287. const char *sqName = serviceQName.str();
  1288. const char *mqName = methodQName.str();
  1289. Owned<IPropertyTree> namespaces = createPTree();
  1290. appendSchemaNamespaces(namespaces, ctx, req, service, method);
  1291. Owned<IPropertyTreeIterator> nsiter = namespaces->getElements("namespace");
  1292. StringBuffer nstr;
  1293. generateNamespace(ctx, req, sqName, mqName, nstr);
  1294. schema.appendf("<xsd:schema elementFormDefault=\"qualified\" targetNamespace=\"%s\" ", nstr.str());
  1295. if (standalone)
  1296. schema.appendf(" xmlns:tns=\"%s\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"", nstr.str());
  1297. ForEach(*nsiter)
  1298. {
  1299. IPropertyTree &ns = nsiter->query();
  1300. schema.appendf(" xmlns:%s=\"%s\"", ns.queryProp("@nsvar"), ns.queryProp("@ns"));
  1301. }
  1302. schema.append(">\n");
  1303. ForEach(*nsiter)
  1304. {
  1305. IPropertyTree &ns = nsiter->query();
  1306. if (ns.hasProp("@import"))
  1307. schema.appendf("<xsd:import namespace=\"%s\" schemaLocation=\"%s\"/>", ns.queryProp("@ns"), ns.queryProp("@location"));
  1308. }
  1309. schema.append(
  1310. "<xsd:complexType name=\"EspException\">"
  1311. "<xsd:all>"
  1312. "<xsd:element name=\"Code\" type=\"xsd:string\" minOccurs=\"0\"/>"
  1313. "<xsd:element name=\"Audience\" type=\"xsd:string\" minOccurs=\"0\"/>"
  1314. "<xsd:element name=\"Source\" type=\"xsd:string\" minOccurs=\"0\"/>"
  1315. "<xsd:element name=\"Message\" type=\"xsd:string\" minOccurs=\"0\"/>"
  1316. "</xsd:all>"
  1317. "</xsd:complexType>\n"
  1318. "<xsd:complexType name=\"ArrayOfEspException\">"
  1319. "<xsd:sequence>"
  1320. "<xsd:element name=\"Source\" type=\"xsd:string\" minOccurs=\"0\"/>"
  1321. "<xsd:element name=\"Exception\" type=\"tns:EspException\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>"
  1322. "</xsd:sequence>"
  1323. "</xsd:complexType>\n"
  1324. "<xsd:element name=\"Exceptions\" type=\"tns:ArrayOfEspException\"/>\n"
  1325. );
  1326. if (ctx.queryOptions()&ESPCTX_WSDL_EXT)
  1327. {
  1328. schema.append(
  1329. "<xsd:complexType name=\"EspSecurityInfo\">"
  1330. "<xsd:all>"
  1331. "<xsd:element name=\"UsernameToken\" minOccurs=\"0\">"
  1332. "<xsd:complexType>"
  1333. "<xsd:all>"
  1334. "<xsd:element name=\"Username\" minOccurs=\"0\"/>"
  1335. "<xsd:element name=\"Password\" minOccurs=\"0\"/>"
  1336. "</xsd:all>"
  1337. "</xsd:complexType>"
  1338. "</xsd:element>"
  1339. "<xsd:element name=\"RealmToken\" minOccurs=\"0\">"
  1340. "<xsd:complexType>"
  1341. "<xsd:all>"
  1342. "<xsd:element name=\"Realm\" minOccurs=\"0\"/>"
  1343. "</xsd:all>"
  1344. "</xsd:complexType>"
  1345. "</xsd:element>"
  1346. "</xsd:all>"
  1347. "</xsd:complexType>"
  1348. "<xsd:element name=\"Security\" type=\"tns:EspSecurityInfo\"/>\n"
  1349. );
  1350. }
  1351. bool mda=(req->queryParameters()->getPropInt("mda")!=0);
  1352. getXsdDefinition(ctx, req, schema, sqName, mqName, mda);
  1353. schema.append("<xsd:element name=\"string\" nillable=\"true\" type=\"xsd:string\" />\n");
  1354. schema.append("</xsd:schema>");
  1355. return true;
  1356. }
  1357. int EspHttpBinding::getWsdlOrXsd(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service, const char *method, bool isWsdl)
  1358. {
  1359. bool mda=(request->queryParameters()->getPropInt("mda")!=0);
  1360. try
  1361. {
  1362. StringBuffer serviceQName;
  1363. StringBuffer methodQName;
  1364. if (!qualifyServiceName(context, service, method, serviceQName, &methodQName))
  1365. {
  1366. response->setStatus(HTTP_STATUS_NOT_FOUND);
  1367. }
  1368. else
  1369. {
  1370. const char *sqName = serviceQName.str();
  1371. const char *mqName = methodQName.str();
  1372. StringBuffer ns;
  1373. generateNamespace(context, request, serviceQName.str(), methodQName.str(), ns);
  1374. StringBuffer content("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  1375. if (context.queryRequestParameters()->hasProp("display"))
  1376. content.append("<?xml-stylesheet type=\"text/xsl\" href=\"/esp/xslt/xmlformatter.xsl\"?>");
  1377. else if (isWsdl && context.queryRequestParameters()->hasProp("wsdlviewer"))
  1378. content.append("<?xml-stylesheet type=\"text/xsl\" href=\"/esp/xslt/wsdl-viewer.xsl\"?>");
  1379. if (isWsdl)
  1380. {
  1381. content.appendf("<definitions xmlns=\"http://schemas.xmlsoap.org/wsdl/\" xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\" xmlns:http=\"http://schemas.xmlsoap.org/wsdl/http/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
  1382. " xmlns:mime=\"http://schemas.xmlsoap.org/wsdl/mime/\" xmlns:tns=\"%s\""
  1383. " targetNamespace=\"%s\">", ns.str(), ns.str());
  1384. content.append("<types>");
  1385. }
  1386. getSchema(content,context,request,service,method,!isWsdl);
  1387. if (isWsdl)
  1388. {
  1389. content.append("</types>");
  1390. getWsdlMessages(context, request, content, sqName, mqName, mda);
  1391. getWsdlPorts(context, request, content, sqName, mqName, mda);
  1392. getWsdlBindings(context, request, content, sqName, mqName, mda);
  1393. StringBuffer location(m_wsdlAddress.str());
  1394. if (request->queryParameters()->hasProp("wsdl_destination_path"))
  1395. location.append(request->queryParameters()->queryProp("wsdl_destination_path"));
  1396. else
  1397. location.append('/').append(sqName).appendf("?ver_=%g", context.getClientVersion());
  1398. if (request->queryParameters()->hasProp("encode_results"))
  1399. {
  1400. const char *encval = request->queryParameters()->queryProp("encode_results");
  1401. location.append("&amp;").appendf("encode_=%s", (encval && *encval) ? encval : "1");
  1402. }
  1403. content.appendf("<service name=\"%s\">", sqName);
  1404. content.appendf("<port name=\"%sServiceSoap\" binding=\"tns:%sServiceSoap\">", sqName, sqName);
  1405. content.appendf("<soap:address location=\"%s\"/>", location.str());
  1406. content.append("</port>");
  1407. content.append("</service>");
  1408. content.append("</definitions>");
  1409. }
  1410. response->setContent(content.length(), content.str());
  1411. response->setContentType(HTTP_TYPE_APPLICATION_XML_UTF8);
  1412. response->setStatus(HTTP_STATUS_OK);
  1413. }
  1414. }
  1415. catch (IException *e)
  1416. {
  1417. return onGetException(context, request, response, *e);
  1418. }
  1419. response->send();
  1420. return 0;
  1421. }
  1422. static void genSampleXml(StringStack& parent, IXmlType* type, StringBuffer& out, const char* tag, const char* ns=NULL)
  1423. {
  1424. assertex(type!=NULL);
  1425. const char* typeName = type->queryName();
  1426. if (type->isComplexType())
  1427. {
  1428. if (typeName && std::find(parent.begin(),parent.end(),typeName) != parent.end())
  1429. return; // recursive
  1430. out.appendf("<%s", tag);
  1431. if (ns)
  1432. out.append(' ').append(ns);
  1433. for (unsigned i=0; i<type->getAttrCount(); i++)
  1434. {
  1435. IXmlAttribute* attr = type->queryAttr(i);
  1436. out.appendf(" %s='", attr->queryName());
  1437. attr->getSampleValue(out);
  1438. out.append('\'');
  1439. }
  1440. out.append('>');
  1441. if (typeName)
  1442. parent.push_back(typeName);
  1443. int flds = type->getFieldCount();
  1444. switch (type->getSubType())
  1445. {
  1446. case SubType_Complex_SimpleContent:
  1447. assertex(flds==0);
  1448. type->queryFieldType(0)->getSampleValue(out,tag);
  1449. break;
  1450. default:
  1451. for (int idx=0; idx<flds; idx++)
  1452. genSampleXml(parent,type->queryFieldType(idx),out,type->queryFieldName(idx));
  1453. break;
  1454. }
  1455. if (typeName)
  1456. parent.pop_back();
  1457. out.appendf("</%s>",tag);
  1458. }
  1459. else if (type->isArray())
  1460. {
  1461. if (typeName && std::find(parent.begin(),parent.end(),typeName) != parent.end())
  1462. return; // recursive
  1463. const char* itemName = type->queryFieldName(0);
  1464. IXmlType* itemType = type->queryFieldType(0);
  1465. if (!itemName || !itemType)
  1466. throw MakeStringException(-1,"*** Invalid array definition: tag=%s, itemName=%s", tag, itemName?itemName:"NULL");
  1467. StringBuffer item;
  1468. if (typeName)
  1469. parent.push_back(typeName);
  1470. genSampleXml(parent,itemType,item,itemName);
  1471. if (typeName)
  1472. parent.pop_back();
  1473. // gen two items
  1474. out.appendf("<%s>%s%s</%s>", tag,item.str(),item.str(),tag);
  1475. }
  1476. else // simple type
  1477. {
  1478. out.appendf("<%s>", tag);
  1479. type->getSampleValue(out,NULL);
  1480. out.appendf("</%s>", tag);
  1481. }
  1482. }
  1483. void EspHttpBinding::generateSampleXml(bool isRequest, IEspContext &context, CHttpRequest* request, StringBuffer &content, const char *serv, const char *method)
  1484. {
  1485. StringBuffer schemaXml, element;
  1486. getXMLMessageTag(context, isRequest, method, element);
  1487. getSchema(schemaXml,context,request,serv,method,false);
  1488. Owned<IXmlSchema> schema;
  1489. IXmlType* type = nullptr;
  1490. try
  1491. {
  1492. schema.setown(createXmlSchema(schemaXml));
  1493. if (!schema)
  1494. {
  1495. content.appendf("<Error>generateSampleXml schema error: %s::%s</Error>", serv, method);
  1496. return;
  1497. }
  1498. type = schema->queryElementType(element);
  1499. if (!type)
  1500. {
  1501. content.appendf("<Error>generateSampleXml unknown type: %s in %s::%s</Error>", element.str(), serv, method);
  1502. return;
  1503. }
  1504. }
  1505. catch (IException *E)
  1506. {
  1507. StringBuffer msg;
  1508. content.appendf("<Error>generateSampleXml Exception: %s in %s::%s</Error>", E->errorMessage(msg).str(), serv, method);
  1509. E->Release();
  1510. return;
  1511. }
  1512. StringStack parent;
  1513. StringBuffer nsdecl("xmlns=\"");
  1514. genSampleXml(parent,type, content, element, generateNamespace(context, request, serv, method, nsdecl).append('\"').str());
  1515. }
  1516. void EspHttpBinding::generateSampleXml(bool isRequest, IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1517. {
  1518. StringBuffer serviceName;
  1519. StringBuffer methodName;
  1520. if (!qualifyServiceName(context, serv, method, serviceName, (method) ? &methodName : nullptr))
  1521. return;
  1522. StringBuffer content;
  1523. if (method && *method)
  1524. generateSampleXml(isRequest, context, request, content, serviceName, methodName);
  1525. else
  1526. {
  1527. MethodInfoArray methods;
  1528. getQualifiedNames(context, methods);
  1529. content.appendf("<Examples><%s>\n", isRequest ? "Requests" : "Responses");
  1530. ForEachItemIn(i, methods)
  1531. generateSampleXml(isRequest, context, request, content, serviceName.str(), methods.item(i).m_label.str());
  1532. content.appendf("\n</%s></Examples>", isRequest ? "Requests" : "Responses");
  1533. }
  1534. response->setContent(content.length(), content.str());
  1535. response->setContentType(HTTP_TYPE_TEXT_XML_UTF8);
  1536. response->setStatus(HTTP_STATUS_OK);
  1537. response->send();
  1538. return;
  1539. }
  1540. void EspHttpBinding::generateSampleJson(bool isRequest, IEspContext &context, CHttpRequest* request, StringBuffer &content, const char *serv, const char *method)
  1541. {
  1542. StringBuffer schemaXml, element;
  1543. getXMLMessageTag(context, isRequest, method, element);
  1544. getSchema(schemaXml,context,request,serv,method,false);
  1545. Owned<IXmlSchema> schema;
  1546. IXmlType* type = nullptr;
  1547. try
  1548. {
  1549. schema.setown(createXmlSchema(schemaXml));
  1550. if (!schema)
  1551. {
  1552. content.appendf("{\"Error\": \"generateSampleJson schema error: %s::%s\"}", serv, method);
  1553. return;
  1554. }
  1555. type = schema->queryElementType(element);
  1556. if (!type)
  1557. {
  1558. content.appendf("{\"Error\": \"generateSampleJson unknown type: %s in %s::%s\"}", element.str(), serv, method);
  1559. return;
  1560. }
  1561. }
  1562. catch (IException *E)
  1563. {
  1564. StringBuffer msg;
  1565. content.appendf("{\"Error\": \"generateSampleJson unknown type: %s in %s::%s\"}", E->errorMessage(msg).str(), serv, method);
  1566. E->Release();
  1567. return;
  1568. }
  1569. StringArray parentTypes;
  1570. JsonHelpers::buildJsonMsg(parentTypes, type, content, element, NULL, REQSF_ROOT|REQSF_SAMPLE_DATA|REQSF_FORMAT);
  1571. }
  1572. void EspHttpBinding::generateSampleJson(bool isRequest, IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1573. {
  1574. StringBuffer serviceName;
  1575. StringBuffer methodName;
  1576. if (!qualifyServiceName(context, serv, method, serviceName, (method) ? &methodName : nullptr))
  1577. return;
  1578. StringBuffer content;
  1579. if (method && *method)
  1580. generateSampleJson(isRequest, context, request, content, serviceName, methodName);
  1581. else
  1582. {
  1583. MethodInfoArray methods;
  1584. getQualifiedNames(context, methods);
  1585. content.appendf("{\"Examples\": {\"%s\": [\n", isRequest ? "Request" : "Response");
  1586. ForEachItemIn(i, methods)
  1587. {
  1588. delimitJSON(content, true);
  1589. generateSampleJson(isRequest, context, request, content, serviceName.str(), methods.item(i).m_label.str());
  1590. }
  1591. content.append("]}}\n");
  1592. }
  1593. response->setContent(content.length(), content.str());
  1594. response->setContentType(HTTP_TYPE_TEXT_PLAIN_UTF8);
  1595. response->setStatus(HTTP_STATUS_OK);
  1596. response->send();
  1597. return;
  1598. }
  1599. void EspHttpBinding::generateSampleXmlFromSchema(bool isRequest, IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method, const char * schemaxml)
  1600. {
  1601. StringBuffer serviceQName, methodQName;
  1602. if (!qualifyServiceName(context, serv, method, serviceQName, &methodQName))
  1603. return;
  1604. StringBuffer element, schemaXmlbuff(schemaxml);
  1605. getXMLMessageTag(context, isRequest, methodQName.str(), element);
  1606. Owned<IXmlSchema> schema = createXmlSchema(schemaXmlbuff);
  1607. if (schema.get())
  1608. {
  1609. IXmlType* type = schema->queryElementType(element);
  1610. if (type)
  1611. {
  1612. StringBuffer content;
  1613. StringStack parent;
  1614. StringBuffer nsdecl("xmlns=\"");
  1615. content.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  1616. if (context.queryRequestParameters()->hasProp("display"))
  1617. content.append("<?xml-stylesheet type=\"text/xsl\" href=\"/esp/xslt/xmlformatter.xsl\"?>");
  1618. genSampleXml(parent,type, content, element, generateNamespace(context, request, serviceQName.str(), methodQName.str(), nsdecl).append('\"').str());
  1619. response->setContent(content.length(), content.str());
  1620. response->setContentType(HTTP_TYPE_APPLICATION_XML_UTF8);
  1621. response->setStatus(HTTP_STATUS_OK);
  1622. response->send();
  1623. return;
  1624. }
  1625. }
  1626. throw MakeStringException(-1,"Unknown type: %s", element.str());
  1627. }
  1628. int EspHttpBinding::onGetReqSampleXml(IEspContext &ctx, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1629. {
  1630. generateSampleXml(true, ctx, request, response, serv, method);
  1631. return 0;
  1632. }
  1633. int EspHttpBinding::onGetRespSampleXml(IEspContext &ctx, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1634. {
  1635. generateSampleXml(false, ctx, request, response, serv, method);
  1636. return 0;
  1637. }
  1638. int EspHttpBinding::onGetReqSampleJson(IEspContext &ctx, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1639. {
  1640. generateSampleJson(true, ctx, request, response, serv, method);
  1641. return 0;
  1642. }
  1643. int EspHttpBinding::onGetRespSampleJson(IEspContext &ctx, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1644. {
  1645. generateSampleJson(false, ctx, request, response, serv, method);
  1646. return 0;
  1647. }
  1648. int EspHttpBinding::onStartUpload(IEspContext &ctx, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1649. {
  1650. StringArray fileNames, files;
  1651. Owned<IMultiException> me = MakeMultiException("FileSpray::UploadFile()");
  1652. try
  1653. {
  1654. if (!ctx.validateFeatureAccess(FILE_UPLOAD, SecAccess_Full, false))
  1655. throw MakeStringException(-1, "Permission denied.");
  1656. StringBuffer netAddress, path;
  1657. request->getParameter("NetAddress", netAddress);
  1658. request->getParameter("Path", path);
  1659. if (((netAddress.length() < 1) || (path.length() < 1)))
  1660. request->readUploadFileContent(fileNames, files);
  1661. else
  1662. request->readContentToFiles(netAddress, path, fileNames);
  1663. return onFinishUpload(ctx, request, response, serv, method, fileNames, files, NULL);
  1664. }
  1665. catch (IException* e)
  1666. {
  1667. me->append(*e);
  1668. }
  1669. catch (...)
  1670. {
  1671. me->append(*MakeStringExceptionDirect(-1, "Unknown Exception"));
  1672. }
  1673. return onFinishUpload(ctx, request, response, serv, method, fileNames, files, me);
  1674. }
  1675. int EspHttpBinding::onFinishUpload(IEspContext &ctx, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method,
  1676. StringArray& fileNames, StringArray& files, IMultiException *me)
  1677. {
  1678. response->setContentType("text/html; charset=UTF-8");
  1679. StringBuffer content(
  1680. "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
  1681. "<head>"
  1682. "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>"
  1683. "<title>Enterprise Services Platform</title>"
  1684. "</head>"
  1685. "<body>"
  1686. "<div id=\"DropzoneFileData\">");
  1687. if (!me || (me->ordinality()==0))
  1688. content.append("<br/><b>File has been uploaded.</b>");
  1689. else
  1690. {
  1691. StringBuffer msg;
  1692. WARNLOG("Exception(s) in EspHttpBinding::onFinishUpload - %s", me->errorMessage(msg).append('\n').str());
  1693. content.appendf("<br/><b>%s</b>", msg.str());
  1694. }
  1695. content.append("</div>"
  1696. "</body>"
  1697. "</html>");
  1698. response->setContent(content.str());
  1699. response->send();
  1700. return 0;
  1701. }
  1702. int EspHttpBinding::getWsdlMessages(IEspContext &context, CHttpRequest *request, StringBuffer &content, const char *service, const char *method, bool mda)
  1703. {
  1704. bool allMethods = (method==NULL || !*method);
  1705. MethodInfoArray methods;
  1706. getQualifiedNames(context, methods);
  1707. int count=methods.ordinality();
  1708. for (int indx=0; indx<count; indx++)
  1709. {
  1710. CMethodInfo &info = methods.item(indx);
  1711. {
  1712. if (allMethods || !Utils::strcasecmp(method, info.m_label.str()))
  1713. {
  1714. content.appendf("<message name=\"%sSoapIn\">", info.m_label.str());
  1715. if (context.queryOptions()&ESPCTX_WSDL_EXT)
  1716. content.appendf("<part name=\"security\" element=\"tns:Security\"/>");
  1717. content.appendf("<part name=\"parameters\" element=\"tns:%s\"/>", info.m_requestLabel.str());
  1718. content.append("</message>");
  1719. content.appendf("<message name=\"%sSoapOut\">", info.m_label.str());
  1720. content.appendf("<part name=\"parameters\" element=\"tns:%s\"/>", info.m_responseLabel.str());
  1721. content.append("</message>");
  1722. }
  1723. }
  1724. }
  1725. content.append("<message name=\"EspSoapFault\">"
  1726. "<part name=\"parameters\" element=\"tns:Exceptions\"/>"
  1727. "</message>");
  1728. return 0;
  1729. }
  1730. int EspHttpBinding::getWsdlPorts(IEspContext &context, CHttpRequest *request, StringBuffer &content, const char *service, const char *method, bool mda)
  1731. {
  1732. StringBuffer serviceName;
  1733. StringBuffer methName;
  1734. qualifyServiceName(context, service, method, serviceName, &methName);
  1735. content.appendf("<portType name=\"%sServiceSoap\">", serviceName.str());
  1736. bool allMethods = (method==NULL || !*method);
  1737. MethodInfoArray methods;
  1738. getQualifiedNames(context, methods);
  1739. int count=methods.ordinality();
  1740. for (int indx=0; indx<count; indx++)
  1741. {
  1742. CMethodInfo &info = methods.item(indx);
  1743. {
  1744. if (allMethods || !Utils::strcasecmp(method, info.m_label.str()))
  1745. {
  1746. content.appendf("<operation name=\"%s\">", info.m_label.str());
  1747. content.appendf("<input message=\"tns:%sSoapIn\"/>", info.m_label.str());
  1748. content.appendf("<output message=\"tns:%sSoapOut\"/>", info.m_label.str());
  1749. content.appendf("<fault name=\"excfault\" message=\"tns:EspSoapFault\"/>");
  1750. content.append("</operation>");
  1751. if (!allMethods) // no need to continue
  1752. break;
  1753. }
  1754. }
  1755. }
  1756. content.append("</portType>");
  1757. return 0;
  1758. }
  1759. int EspHttpBinding::getWsdlBindings(IEspContext &context, CHttpRequest *request, StringBuffer &content, const char *service, const char *method, bool mda)
  1760. {
  1761. StringBuffer serviceName;
  1762. StringBuffer methName;
  1763. qualifyServiceName(context, service, method, serviceName, &methName);
  1764. content.appendf("<binding name=\"%sServiceSoap\" type=\"tns:%sServiceSoap\">", serviceName.str(), serviceName.str());
  1765. content.append("<soap:binding transport=\"http://schemas.xmlsoap.org/soap/http\" style=\"document\"/>");
  1766. bool allMethods = (method==NULL || !*method);
  1767. MethodInfoArray methods;
  1768. getQualifiedNames(context, methods);
  1769. int count=methods.ordinality();
  1770. for (int indx=0; indx<count; indx++)
  1771. {
  1772. CMethodInfo &info = methods.item(indx);
  1773. {
  1774. if (allMethods || !Utils::strcasecmp(method, info.m_label.str()))
  1775. {
  1776. content.appendf("<operation name=\"%s\">", info.m_label.str());
  1777. //content.appendf("<soap:operation soapAction=\"%s/%s?ver_=%g\" style=\"document\"/>", serviceName.str(), info.m_label.str(), getWsdlVersion());
  1778. content.appendf("<soap:operation soapAction=\"%s/%s?ver_=%g\" style=\"document\"/>", serviceName.str(), info.m_label.str(), context.getClientVersion());
  1779. content.append("<input>");
  1780. if (context.queryOptions()&ESPCTX_WSDL_EXT)
  1781. content.appendf("<soap:header message=\"tns:%sSoapIn\" part=\"security\" use=\"literal\"/>", info.m_label.str());
  1782. content.append("<soap:body use=\"literal\"/></input>");
  1783. content.append("<output><soap:body use=\"literal\"/></output>");
  1784. content.append("<fault name=\"excfault\"><soap:fault name=\"excfault\" use=\"literal\"/></fault>");
  1785. content.append("</operation>");
  1786. if (!allMethods) // no need to continue
  1787. break;
  1788. }
  1789. }
  1790. }
  1791. content.append("</binding>");
  1792. return 0;
  1793. }
  1794. int EspHttpBinding::onGetNotFound(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service)
  1795. {
  1796. HtmlPage page("Enterprise Services Platform");
  1797. page.appendContent(new CHtmlHeader(H1, "Page Not Found"));
  1798. StringBuffer content;
  1799. page.getHtml(content);
  1800. response->setContent(content.length(), content.str());
  1801. response->setContentType("text/html; charset=UTF-8");
  1802. response->setStatus(HTTP_STATUS_NOT_FOUND);
  1803. response->send();
  1804. return 0;
  1805. }
  1806. int EspHttpBinding::onGetException(IEspContext &context, CHttpRequest* request, CHttpResponse* response, IException &e)
  1807. {
  1808. HtmlPage page("Enterprise Services Platform");
  1809. StringBuffer msg("Exception: ");
  1810. msg.appendf(" %d ", e.errorCode());
  1811. e.errorMessage(msg);
  1812. page.appendContent(new CHtmlHeader(H1, msg.str()));
  1813. StringBuffer content;
  1814. page.getHtml(content);
  1815. response->setContent(content.length(), content.str());
  1816. response->setContentType("text/html; charset=UTF-8");
  1817. response->setStatus(HTTP_STATUS_OK);
  1818. response->send();
  1819. return 0;
  1820. }
  1821. int EspHttpBinding::onGetRoot(IEspContext &context, CHttpRequest* request, CHttpResponse* response)
  1822. {
  1823. StringBuffer serviceName;
  1824. getServiceName(serviceName);
  1825. DBGLOG("CWsADLSoapBindingEx::onGetRoot");
  1826. StringBuffer htmlStr;
  1827. htmlStr.appendf(
  1828. "<html>"
  1829. "<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><title>%s</title></head>"
  1830. "<body><p align=\"center\"><font size=\"55\" color=\"#999999\">%s</font></p></body>"
  1831. "</html>", serviceName.str(), serviceName.str());
  1832. response->setContentType(HTTP_TYPE_TEXT_HTML_UTF8);
  1833. response->setContent(htmlStr.length(), (const char*) htmlStr.str());
  1834. response->send();
  1835. return 0;
  1836. }
  1837. int EspHttpBinding::onGetIndex(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service)
  1838. {
  1839. StringBuffer serviceQName;
  1840. StringBuffer methodQName;
  1841. if (!qualifyServiceName(context, service, "", serviceQName, &methodQName))
  1842. {
  1843. return onGetNotFound(context, request, response, service);
  1844. }
  1845. else
  1846. {
  1847. double ver = context.getClientVersion();
  1848. if (ver<=0)
  1849. ver = getWsdlVersion();
  1850. HtmlPage page("Enterprise Services Platform");
  1851. page.appendContent(new CHtmlHeader(H1, serviceQName));
  1852. const char* build_ver = getBuildVersion();
  1853. VStringBuffer build("Version: %g, Build: %s", ver, build_ver);
  1854. page.appendContent(new CHtmlHeader(H2, build));
  1855. StringBuffer urlParams;
  1856. if (!getUrlParams(context.queryRequestParameters(),urlParams))
  1857. urlParams.appendf("%cver_=%g",(urlParams.length()>0) ? '&' : '?', ver);
  1858. StringBuffer wsLink;
  1859. CHtmlList * list=NULL;
  1860. MethodInfoArray methods;
  1861. getQualifiedNames(context, methods);
  1862. page.appendContent(new CHtmlText("For complete sets of example messages:<br/>"));
  1863. list = (CHtmlList *)page.appendContent(new CHtmlList);
  1864. list->appendContent(new CHtmlLink("XML Requests", wsLink.set(urlParams).append("&reqxml_").str()));
  1865. list->appendContent(new CHtmlLink("XML Responses", wsLink.set(urlParams).append("&respxml_").str()));
  1866. IProperties *parms = request->queryParameters();
  1867. if (parms->hasProp("include_jsonreqs_"))
  1868. list->appendContent(new CHtmlLink("JSON Requests", wsLink.set(urlParams).append("&reqjson_").str()));
  1869. list->appendContent(new CHtmlLink("JSON Responses", wsLink.set(urlParams).append("&respjson_").str()));
  1870. page.appendContent(new CHtmlText("The following operations are supported:<br/>"));
  1871. //links to the form pages
  1872. list = (CHtmlList *)page.appendContent(new CHtmlList);
  1873. StringBuffer urlFormParams(urlParams);
  1874. urlFormParams.append(urlFormParams.length()>0 ? "&form" : "?form");
  1875. for(int i = 0, tot = methods.length(); i < tot; ++i)
  1876. {
  1877. CMethodInfo &method = methods.item(i);
  1878. {
  1879. wsLink.setf("%s%s", method.m_label.str(),urlFormParams.str());
  1880. list->appendContent(new CHtmlLink(method.m_label.str(), wsLink.str()));
  1881. }
  1882. }
  1883. page.appendContent(new CHtmlText("<br/>For a formal definition, please review the "));
  1884. urlParams.append(urlParams.length()>0 ? "&wsdl" : "?wsdl");
  1885. wsLink.clear().appendf("%s", urlParams.str());
  1886. page.appendContent(new CHtmlLink("WSDL Definition", wsLink.str()));
  1887. page.appendContent(new CHtmlText(" for this service, <br/><br/>or the individual WSDL definitions for each operation: "));
  1888. //links to the individual WSDL pages
  1889. list = (CHtmlList *)page.appendContent(new CHtmlList);
  1890. for(int i = 0, tot = methods.length(); i < tot; ++i)
  1891. {
  1892. CMethodInfo &info = methods.item(i);
  1893. {
  1894. wsLink.clear().appendf("%s%s", info.m_label.str(),urlParams.str());
  1895. list->appendContent(new CHtmlLink(info.m_label.str(), wsLink.str()));
  1896. }
  1897. }
  1898. StringBuffer content;
  1899. page.getHtml(content);
  1900. response->setContent(content.length(), content.str());
  1901. response->setContentType("text/html; charset=UTF-8");
  1902. response->setStatus(HTTP_STATUS_OK);
  1903. response->send();
  1904. }
  1905. return 0;
  1906. }
  1907. // Interestingly, only single quote needs to HTML escape.
  1908. // ", <, >, & don't need escape.
  1909. void EspHttpBinding::escapeSingleQuote(StringBuffer& src, StringBuffer& escaped)
  1910. {
  1911. for (const char* p = src.str(); *p!=0; p++)
  1912. {
  1913. if (*p == '\'')
  1914. escaped.append("&apos;");
  1915. else
  1916. escaped.append(*p);
  1917. }
  1918. }
  1919. int EspHttpBinding::onGetXForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1920. {
  1921. StringBuffer serviceQName;
  1922. StringBuffer methodQName;
  1923. if (!qualifyServiceName(context, serv, method, serviceQName, &methodQName))
  1924. {
  1925. return onGetNotFound(context, request, response, serv);
  1926. }
  1927. else
  1928. {
  1929. StringBuffer page;
  1930. IXslProcessor* xslp = getXmlLibXslProcessor();
  1931. // get schema
  1932. StringBuffer schema;
  1933. context.addOptions(ESPCTX_ALL_ANNOTATION);
  1934. getSchema(schema, context, request, serv, method, true);
  1935. Owned<IXslTransform> xform = xslp->createXslTransform();
  1936. xform->loadXslFromFile(StringBuffer(getCFD()).append("./xslt/gen_form.xsl").str());
  1937. xform->setXmlSource(schema.str(), schema.length()+1);
  1938. const char* authMethod = context.getAuthenticationMethod();
  1939. if (authMethod && !strieq(authMethod, "none") && ((context.getDomainAuthType() == AuthPerSessionOnly) || (context.getDomainAuthType() == AuthTypeMixed)))
  1940. xform->setParameter("showLogout", "1");
  1941. // params
  1942. xform->setStringParameter("serviceName", serviceQName);
  1943. StringBuffer version;
  1944. version.appendf("%g",context.getClientVersion());
  1945. xform->setStringParameter("serviceVersion", version);
  1946. StringBuffer methodExt(methodQName);
  1947. if (context.queryRequestParameters()->hasProp("json"))
  1948. methodExt.append(".json");
  1949. xform->setStringParameter("methodName", methodExt);
  1950. // pass params to form (excluding form and __querystring)
  1951. StringBuffer params;
  1952. if (!getUrlParams(context.queryRequestParameters(),params))
  1953. params.appendf("%cver_=%g",(params.length()>0) ? '&' : '?', context.getClientVersion());
  1954. xform->setStringParameter("queryParams", params.str());
  1955. StringBuffer tmp,escaped;
  1956. getMethodHelp(context, serviceQName, methodQName, tmp);
  1957. escapeSingleQuote(tmp,escaped);
  1958. xform->setStringParameter("methodHelp", escaped);
  1959. getMethodDescription(context, serviceQName.str(), methodQName.str(), tmp.clear());
  1960. escapeSingleQuote(tmp,escaped.clear());
  1961. xform->setStringParameter("methodDesc", escaped);
  1962. xform->setParameter("formOptionsAccess", m_formOptions?"1":"0");
  1963. xform->setParameter("includeSoapTest", m_includeSoapTest?"1":"0");
  1964. // set the prop noDefaultValue param
  1965. IProperties* props = context.queryRequestParameters();
  1966. bool formInitialized = false;
  1967. if (props) {
  1968. Owned<IPropertyIterator> it = props->getIterator();
  1969. for (it->first(); it->isValid(); it->next()) {
  1970. const char* key = it->getPropKey();
  1971. if (*key=='.') {
  1972. formInitialized = true;
  1973. break;
  1974. }
  1975. }
  1976. }
  1977. xform->setParameter("noDefaultValue", formInitialized ? "1" : "0");
  1978. MethodInfoArray info;
  1979. getQualifiedNames(context, info);
  1980. for (unsigned i=0; i<info.length(); i++)
  1981. {
  1982. CMethodInfo& m = info.item(i);
  1983. if (stricmp(m.m_label, methodQName)==0)
  1984. {
  1985. xform->setStringParameter("requestLabel", m.m_requestLabel);
  1986. break;
  1987. }
  1988. }
  1989. xform->transform(page);
  1990. response->setContentType("text/html");
  1991. response->setContent(page.str());
  1992. }
  1993. response->send();
  1994. return 0;
  1995. }
  1996. int EspHttpBinding::onGetForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1997. {
  1998. StringBuffer serviceQName;
  1999. StringBuffer methodQName;
  2000. if (!qualifyServiceName(context, serv, method, serviceQName, &methodQName))
  2001. {
  2002. return onGetNotFound(context, request, response, serv);
  2003. }
  2004. else
  2005. {
  2006. StringBuffer page;
  2007. page.append(
  2008. "<html>"
  2009. "<head>"
  2010. "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
  2011. "<title>ESP Service form</title>"
  2012. "<script language=\"JavaScript\" src=\"files_/calendar_xs.js\"></script>"
  2013. "<script language=\"JavaScript\" src=\"files_/hint.js\"></script>"
  2014. "</head>"
  2015. "<body>"
  2016. //"<table border=\"0\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" bgcolor=\"#000000\" height=\"108\">"
  2017. // "<tr>"
  2018. // "<td width=\"24%\" height=\"24\" bgcolor=\"#000000\">"
  2019. // "<img border=\"0\" src=\"/");
  2020. // page.append(serviceQName.str());
  2021. // page.append("/files_/logo.gif\" width=\"258\" height=\"108\" />"
  2022. // "</td>"
  2023. // "</tr>"
  2024. // "<tr>"
  2025. // "<td width=\"24%\" height=\"24\" bgcolor=\"#AA0000\">"
  2026. // "<p align=\"center\" />"
  2027. // "<b>"
  2028. // "<font color=\"#FFFFFF\" size=\"5\">Enterprise Services Platform<sup><font size=\"2\">TM</font></sup></font>"
  2029. // "</b>"
  2030. // "</td>"
  2031. // "</tr>"
  2032. //"</table>"
  2033. //"<br />"
  2034. //"<br />"
  2035. "<p align=\"center\" />"
  2036. "<table cellSpacing=\"0\" cellPadding=\"1\" width=\"90%\" bgColor=\"#666666\" border=\"0\">"
  2037. "<tbody>"
  2038. "<tr align=\"middle\" bgColor=\"#666666\">"
  2039. "<td height=\"23\">"
  2040. "<p align=\"left\">"
  2041. "<font color=\"#efefef\">");
  2042. page.appendf("<b>%s [Version %g]</b>", serviceQName.str(), context.getClientVersion());
  2043. page.append("</font>"
  2044. "</p>"
  2045. "</td>"
  2046. "</tr>"
  2047. "<tr bgColor=\"#ffcc66\">"
  2048. "<td height=\"3\">"
  2049. "<p align=\"left\">");
  2050. page.appendf("<b>&gt; %s</b>", methodQName.str());
  2051. page.append("</p>"
  2052. "</td>"
  2053. "</tr>"
  2054. "<TR bgColor=\"#666666\">"
  2055. "<TABLE cellSpacing=\"0\" width=\"90%\" bgColor=\"#efefef\" border=\"0\">"
  2056. "<TBODY>"
  2057. "<TR>"
  2058. "<TD vAlign=\"center\" align=\"left\">"
  2059. "<p align=\"left\"><br />");
  2060. getMethodDescription(context, serviceQName.str(), methodQName.str(), page);
  2061. page.append("<br/></p>"
  2062. "</TD>"
  2063. "</TR>"
  2064. "</TBODY>"
  2065. "</TABLE>"
  2066. "</TR>"
  2067. "<TR bgColor=\"#666666\">"
  2068. "<TABLE cellSpacing=\"0\" width=\"90%\" bgColor=\"#efefef\" border=\"0\">"
  2069. "<TBODY>"
  2070. "<TR>"
  2071. "<TD vAlign=\"center\" align=\"left\">"
  2072. "<p align=\"left\"><br />");
  2073. getMethodHelp(context, serviceQName.str(), methodQName.str(), page);
  2074. page.append("<br/><br/></p>"
  2075. "</TD>"
  2076. "</TR>"
  2077. "</TBODY>"
  2078. "</TABLE>"
  2079. "</TR>"
  2080. "<TR bgColor=\"#666666\">"
  2081. "<TABLE cellSpacing=\"0\" width=\"90%\" bgColor=\"#efefef\" border=\"0\">"
  2082. "<TBODY>"
  2083. "<TR>"
  2084. "<TD vAlign=\"center\" align=\"left\">");
  2085. getMethodHtmlForm(context, request, serviceQName.str(), methodQName.str(), page, true);
  2086. page.append("</TD>"
  2087. "</TR>"
  2088. "</TBODY>"
  2089. "</TABLE>"
  2090. "</TR>"
  2091. "</tbody>"
  2092. "</table>"
  2093. "<BR />"
  2094. "</body>"
  2095. "</html>");
  2096. response->setContent(page.str());
  2097. response->setContentType("text/html");
  2098. }
  2099. response->send();
  2100. return 0;
  2101. }
  2102. int EspHttpBinding::formatHtmlResultSet(IEspContext &context, const char *serv, const char *method, const char *resultsXml, StringBuffer &html)
  2103. {
  2104. Owned<IPropertyTree> ptree = createPTreeFromXMLString(resultsXml);
  2105. Owned<IPropertyTreeIterator> exceptions = ptree->getElements("//Exception");
  2106. ForEach(*exceptions.get())
  2107. {
  2108. IPropertyTree &xcpt = exceptions->query();
  2109. html.appendf("<br/>Exception: <br/>Reported by: %s <br/>Message: %s <br/>",
  2110. xcpt.queryProp("Source"), xcpt.queryProp("Message"));
  2111. }
  2112. Owned<IPropertyTreeIterator> datasets = ptree->getElements("//Dataset");
  2113. ForEach(*datasets.get())
  2114. {
  2115. html.append("<table bordercolor=\"#808080\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" ><tr><td>");
  2116. html.append("<table border=\"1\" bordercolor=\"#808080\" cellspacing=\"0\" cellpadding=\"0\" >");
  2117. IPropertyTree &dset = datasets->query();
  2118. //column headers
  2119. IPropertyTree *row1 = dset.queryPropTree("Row[1]");
  2120. if (row1)
  2121. {
  2122. html.append("<tr><td bgcolor=\"#808080\"><font face=\"Verdana\" size=\"2\">&nbsp;</font></td>");
  2123. Owned<IPropertyTreeIterator> columns = row1->getElements("*");
  2124. ForEach(*columns.get())
  2125. {
  2126. const char *title = columns->query().queryName();
  2127. html.appendf("<td align=\"center\" bgcolor=\"#808080\"><font face=\"Verdana\" size=\"2\" color=\"#E0E0E0\"><b>%s</b></font></td>", (title!=NULL) ? title : "&nbsp;");
  2128. }
  2129. html.append("</tr>");
  2130. }
  2131. Owned<IPropertyTreeIterator> datarows = dset.getElements("Row");
  2132. if (datarows)
  2133. {
  2134. int count=1;
  2135. ForEach(*datarows.get())
  2136. {
  2137. IPropertyTree &row = datarows->query();
  2138. Owned<IPropertyTreeIterator> columns = row.getElements("*");
  2139. html.appendf("<td align=\"center\" bgcolor=\"#808080\"><font face=\"Verdana\" size=\"2\" color=\"#E0E0E0\"><b>%d</b></font></td>", count++);
  2140. ForEach(*columns.get())
  2141. {
  2142. const char *value = columns->query().queryProp(NULL);
  2143. html.appendf("<td align=\"center\" bgcolor=\"#E0E0E0\"><font face=\"Verdana\" size=\"2\" color=\"#000000\">%s</font></td>", (value!=NULL) ? value : "&nbsp;");
  2144. }
  2145. html.append("</tr>");
  2146. }
  2147. }
  2148. html.append("</td></tr></table></table>");
  2149. }
  2150. return 0;
  2151. }
  2152. int EspHttpBinding::formatResultsPage(IEspContext &context, const char *serv, const char *method, StringBuffer &results, StringBuffer &page)
  2153. {
  2154. StringBuffer serviceQName;
  2155. StringBuffer methodQName;
  2156. qualifyServiceName(context, serv, method, serviceQName, &methodQName);
  2157. page.append(
  2158. "<html>"
  2159. "<head>"
  2160. "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
  2161. "<title>ESP Service form</title>"
  2162. "</head>"
  2163. "<body>"
  2164. //"<table border=\"0\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" bgcolor=\"#000000\" height=\"108\">"
  2165. // "<tr>"
  2166. // "<td width=\"24%\" height=\"24\" bgcolor=\"#000000\">"
  2167. // "<img border=\"0\" src=\"/");
  2168. // page.append(serviceQName.str());
  2169. // page.append("/files_/logo.gif\" width=\"258\" height=\"108\" />"
  2170. // "</td>"
  2171. // "</tr>"
  2172. // "<tr>"
  2173. // "<td width=\"24%\" height=\"24\" bgcolor=\"#AA0000\">"
  2174. // "<p align=\"center\" />"
  2175. // "<b>"
  2176. // "<font color=\"#FFFFFF\" size=\"5\">Enterprise Services Platform<sup><font size=\"2\">TM</font></sup></font>"
  2177. // "</b>"
  2178. // "</td>"
  2179. // "</tr>"
  2180. //"</table>"
  2181. //"<br />"
  2182. //"<br />"
  2183. "<p align=\"center\" />"
  2184. "<table cellSpacing=\"0\" cellPadding=\"1\" width=\"90%\" bgColor=\"#666666\" border=\"0\">"
  2185. "<tbody>"
  2186. "<tr align=\"middle\" bgColor=\"#666666\">"
  2187. "<td height=\"23\">"
  2188. "<p align=\"left\">"
  2189. "<font color=\"#efefef\">");
  2190. page.appendf("<b>%s</b>", serviceQName.str());
  2191. page.append("</font>"
  2192. "</p>"
  2193. "</td>"
  2194. "</tr>"
  2195. "<tr bgColor=\"#ffcc66\">"
  2196. "<td height=\"3\">"
  2197. "<p align=\"left\">");
  2198. page.appendf("<b>&gt; %s Results</b>", methodQName.str());
  2199. page.append("</p>"
  2200. "</td>"
  2201. "</tr>"
  2202. "<TR bgColor=\"#666666\">"
  2203. "<TABLE cellSpacing=\"0\" width=\"90%\" bgColor=\"#efefef\" border=\"0\">"
  2204. "<TBODY>"
  2205. "<TR>"
  2206. "<TD vAlign=\"center\" align=\"left\">");
  2207. page.append(results);
  2208. page.append("</TD>"
  2209. "</TR>"
  2210. "</TBODY>"
  2211. "</TABLE>"
  2212. "</TR>"
  2213. "</tbody>"
  2214. "</table>"
  2215. "<BR />"
  2216. "</body>"
  2217. "</html>");
  2218. return 0;
  2219. }
  2220. bool EspHttpBinding::setContentFromFile(IEspContext &context, CHttpResponse &resp, const char *filepath)
  2221. {
  2222. StringBuffer mimetype, etag, lastModified;
  2223. MemoryBuffer content;
  2224. bool modified = false;
  2225. if (httpContentFromFile(filepath, mimetype, content, modified, lastModified, etag))
  2226. {
  2227. resp.setContent(content.length(), content.toByteArray());
  2228. resp.setContentType(mimetype.str());
  2229. return true;
  2230. }
  2231. return false;
  2232. }
  2233. void EspHttpBinding::onBeforeSendResponse(IEspContext& context, CHttpRequest* request, MemoryBuffer& content,
  2234. const char *serviceName, const char* methodName)
  2235. {
  2236. IProperties* params = request->queryParameters();
  2237. if (params->hasProp("esp_validate"))
  2238. validateResponse(context, request, content, serviceName, methodName);
  2239. else if (params->hasProp("esp_sort_result"))
  2240. sortResponse(context, request, content, serviceName, methodName);
  2241. }
  2242. void EspHttpBinding::validateResponse(IEspContext& context, CHttpRequest* request, MemoryBuffer& content,
  2243. const char *service, const char* method)
  2244. {
  2245. StringBuffer serviceQName;
  2246. StringBuffer methodQName;
  2247. if (!qualifyServiceName(context, service, method, serviceQName, &methodQName))
  2248. return;
  2249. StringBuffer xml,xsd;
  2250. // name space
  2251. StringBuffer ns;
  2252. generateNamespace(context, request, serviceQName.str(), methodQName.str(), ns);
  2253. // XML
  2254. Owned<IPropertyTree> tree;
  2255. try {
  2256. tree.setown(createPTreeFromXMLString(content.length(), content.toByteArray()));
  2257. // format it for better error message
  2258. toXML(tree, xml, 1);
  2259. //Hack: add default name space to XML
  2260. const char* end = strstr(xml, "<?xml");
  2261. if (end)
  2262. {
  2263. end = strstr(end+5, "?>");
  2264. if (end)
  2265. end = strchr(end+2, '>');
  2266. if (!end)
  2267. throw MakeStringException(-1,"Invalid response XML in processing instruction");
  2268. }
  2269. else
  2270. {
  2271. end = strchr(xml, '>');
  2272. if (!end)
  2273. throw MakeStringException(-1,"Can not find the root node");
  2274. if (*(end-1)=='/') // root is like: <xxx />
  2275. end--;
  2276. }
  2277. int offset = end ? end-xml.str() : 0;
  2278. StringBuffer tag(offset, xml.str());
  2279. if (!strstr(tag, " xmlns="))
  2280. {
  2281. StringBuffer defNS;
  2282. defNS.appendf(" xmlns=\"%s\"", ns.str());
  2283. xml.insert(offset, defNS.str());
  2284. }
  2285. } catch (IException* e) {
  2286. StringBuffer msg;
  2287. DBGLOG("Unexpected error: parsing XML: %s", e->errorMessage(msg).str());
  2288. }
  2289. // schema
  2290. getSchema(xsd,context,request,serviceQName,methodQName,true);
  2291. // validation
  2292. if (getEspLogLevel()>LogMax)
  2293. DBGLOG("[VALIDATE] xml: %s\nxsd: %s\nns: %s",xml.str(), xsd.str(), ns.str());
  2294. IXmlValidator* v = getXmlLibXmlValidator();
  2295. v->setXmlSource(xml, strlen(xml));
  2296. v->setSchemaSource(xsd, strlen(xsd));
  2297. v->setTargetNamespace(ns);
  2298. Owned<IPropertyTree> result = createPTree("ModifiedResponse");
  2299. IPropertyTree* e = createPTree();
  2300. try {
  2301. v->validate();
  2302. e->appendProp("Result", "No error found");
  2303. } catch (IMultiException* me) {
  2304. for (unsigned i=0; i<me->ordinality(); i++)
  2305. {
  2306. IException& item = me->item(i);
  2307. StringBuffer s;
  2308. e->addProp("Error", item.errorMessage(s).str());
  2309. }
  2310. me->Release();
  2311. }
  2312. result->addPropTree("SchemaValidation", e);
  2313. result->addPropTree("OriginalResponse", LINK(tree));
  2314. StringBuffer temp;
  2315. toXML(result,temp,1);
  2316. unsigned len = temp.length(); // This has to be done before temp.detach is called!
  2317. content.setBuffer(len, temp.detach(), true);
  2318. }
  2319. void EspHttpBinding::sortResponse(IEspContext& context, CHttpRequest* request, MemoryBuffer& content,
  2320. const char *serviceName, const char* methodName)
  2321. {
  2322. ESPLOG(LogNormal,"Sorting Response XML...");
  2323. try {
  2324. StringBuffer result;
  2325. StringBuffer respXML(content.length(), content.toByteArray());
  2326. //sorting
  2327. IXslProcessor* xslp = getXmlLibXslProcessor();
  2328. Owned<IXslTransform> xform = xslp->createXslTransform();
  2329. xform->loadXslFromFile(StringBuffer(getCFD()).append("./xslt/dict_sort.xsl").str());
  2330. xform->setXmlSource(respXML,respXML.length());
  2331. xform->transform(result);
  2332. if (getEspLogLevel()>LogNormal)
  2333. DBGLOG("XML sorted: %s", result.str());
  2334. unsigned len = result.length();
  2335. content.setBuffer(len, result.detach(), true);
  2336. } catch (IException* e) {
  2337. StringBuffer msg;
  2338. DBGLOG("Unexpected error: parsing XML: %s", e->errorMessage(msg).str());
  2339. }
  2340. }