httpbinding.cpp 104 KB

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