httpbinding.cpp 82 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219
  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 "espcontext.hpp"
  29. #include "httpbinding.hpp"
  30. #include "htmlpage.hpp"
  31. #include "seclib.hpp"
  32. #include "../../../system/security/shared/secloader.hpp"
  33. #include "../../SOAP/Platform/soapmessage.hpp"
  34. #include "xmlvalidator.hpp"
  35. #include "xsdparser.hpp"
  36. #include "espsecurecontext.hpp"
  37. #define FILE_UPLOAD "FileUploadAccess"
  38. static HINSTANCE getXmlLib()
  39. {
  40. const char* name = SharedObjectPrefix "xmllib" SharedObjectExtension;
  41. HINSTANCE xmllib = LoadSharedObject(name,true,false);
  42. if (!LoadSucceeded(xmllib))
  43. throw MakeStringException(-1,"load %s failed with code %d", name, GetSharedObjectError());
  44. return xmllib;
  45. }
  46. static IXslProcessor* getXmlLibXslProcessor()
  47. {
  48. static Owned<IXslProcessor> processor;
  49. if (!processor)
  50. {
  51. typedef IXslProcessor* (*getXslProcessor_t)();
  52. getXslProcessor_t f = (getXslProcessor_t)GetSharedProcedure(getXmlLib(), "getXslProcessor");
  53. processor.setown(f());
  54. }
  55. return processor.get();
  56. }
  57. static IXmlValidator* getXmlLibXmlValidator()
  58. {
  59. static Owned<IXmlValidator> v;
  60. if (!v)
  61. {
  62. typedef IXmlDomParser* (*getDomParser_t)();
  63. getDomParser_t f = (getDomParser_t)GetSharedProcedure(getXmlLib(), "getXmlDomParser");
  64. Owned<IXmlDomParser> d = f();
  65. v.setown(d->createXmlValidator());
  66. }
  67. return v.get();
  68. }
  69. static IXmlSchema* createXmlSchema(const char* schema)
  70. {
  71. const char* name = SharedObjectPrefix "xmllib" SharedObjectExtension;
  72. HINSTANCE xmllib = LoadSharedObject(name,true,false);
  73. if (!LoadSucceeded(xmllib))
  74. throw MakeStringException(-1,"load %s failed with code %d", name, GetSharedObjectError());
  75. typedef IXmlSchema* (*XmlSchemaCreator)(const char*);
  76. XmlSchemaCreator creator = (XmlSchemaCreator)GetSharedProcedure(xmllib, "createXmlSchemaFromString");
  77. if (!creator)
  78. throw MakeStringException(-1,"load XmlSchema factory failed: createXmlSchemaFromString()");
  79. return creator(schema);
  80. }
  81. EspHttpBinding::EspHttpBinding(IPropertyTree* tree, const char *bindname, const char *procname)
  82. {
  83. Owned<IPropertyTree> proc_cfg = getProcessConfig(tree, procname);
  84. m_viewConfig = proc_cfg ? proc_cfg->getPropBool("@httpConfigAccess") : false;
  85. m_formOptions = proc_cfg ? proc_cfg->getPropBool("@formOptionsAccess") : false;
  86. m_includeSoapTest = true;
  87. m_configFile.set(tree ? tree->queryProp("@config") : "esp.xml");
  88. Owned<IPropertyTree> bnd_cfg = getBindingConfig(tree, bindname, procname);
  89. m_wsdlVer=0.0;
  90. // get the config default version
  91. const char* defVersion = bnd_cfg->queryProp("@defaultServiceVersion");
  92. if (defVersion && *defVersion)
  93. m_defaultSvcVersion.set(defVersion);
  94. if(!bnd_cfg)
  95. {
  96. m_filespath.append(getCFD()).append("files/");
  97. }
  98. else
  99. {
  100. if (!bnd_cfg->getProp("@baseFilesPath", m_filespath))
  101. m_filespath.append(getCFD()).append("./files/");
  102. m_host.set(bnd_cfg->queryProp("@netAddress"));
  103. m_port = bnd_cfg->getPropInt("@port");
  104. const char *realm = bnd_cfg->queryProp("@realm");
  105. if (realm)
  106. m_realm.set(realm);
  107. else
  108. {
  109. StringBuffer xpath;
  110. xpath.appendf("EspBinding[@port='%d']", m_port);
  111. Owned<IPropertyTreeIterator> bindings = proc_cfg->getElements(xpath.str());
  112. for (bindings->first(); bindings->isValid(); bindings->next())
  113. {
  114. const char *type = bindings->query().queryProp("@type");
  115. if (type && !stricmp(type, "ws_smcSoapBinding"))
  116. {
  117. m_realm.set("EclWatch");
  118. break;
  119. }
  120. }
  121. if (!m_realm.length())
  122. {
  123. const char *srvName = bnd_cfg->queryProp("@service");
  124. if (srvName)
  125. {
  126. Owned<IPropertyTree> srv_cfg = getServiceConfig(tree, srvName, procname);
  127. if (srv_cfg)
  128. {
  129. realm = srv_cfg->queryProp("@type");
  130. const char *ssfile=srv_cfg->queryProp("@subservices");
  131. if (ssfile && checkFileExists(ssfile))
  132. m_subservices.setown(createPTreeFromXMLFile(ssfile, ipt_caseInsensitive));
  133. }
  134. }
  135. m_realm.set((realm) ? realm : "EspServices");
  136. }
  137. }
  138. m_wsdlAddress.append(bnd_cfg->queryProp("@wsdlServiceAddress"));
  139. if (!m_wsdlAddress.length())
  140. {
  141. const char *host=getHost();
  142. if (!host || !(*host) || !strcmp(host, ".") || !strcmp(host, "0.0.0.0"))
  143. queryHostIP().getIpText(m_wsdlAddress);
  144. else
  145. m_wsdlAddress.append(host);
  146. if (!strchr(m_wsdlAddress.str(), ':') && m_port!=80 && m_port!=443)
  147. m_wsdlAddress.append(':').append(m_port);
  148. }
  149. if (strnicmp(m_wsdlAddress.str(), "http", 4))
  150. m_wsdlAddress.insert(0, (m_port!=443) ? "http://" : "https://");
  151. Owned<IPropertyTree> authcfg = bnd_cfg->getPropTree("Authenticate");
  152. if(authcfg != NULL)
  153. {
  154. //Instantiate a Security Manager
  155. m_authtype.set(authcfg->queryProp("@type"));
  156. m_authmethod.set(authcfg->queryProp("@method"));
  157. if (!m_authmethod.isEmpty())
  158. {
  159. Owned<IPropertyTree> secMgrCfg;
  160. if(proc_cfg.get() != NULL)
  161. {
  162. VStringBuffer sb("SecurityManagers/SecurityManager[@name='%s']", m_authmethod.str());
  163. Owned<IPropertyTree> smTree;
  164. smTree.setown(proc_cfg->getPropTree(sb.str()));
  165. if (smTree && smTree->hasProp("@type"))
  166. secMgrCfg.setown(smTree->getPropTree(smTree->queryProp("@type")));
  167. }
  168. if (secMgrCfg)
  169. {
  170. //This is a Pluggable Security Manager
  171. m_secmgr.setown(SecLoader::loadPluggableSecManager(bindname, bnd_cfg, secMgrCfg));
  172. m_authmap.setown(m_secmgr->createAuthMap(authcfg));
  173. }
  174. else
  175. {
  176. //Legacy Security Manager
  177. if(stricmp(m_authmethod.str(), "LdapSecurity") == 0)
  178. {
  179. StringBuffer lsname;
  180. authcfg->getProp("@config", lsname);
  181. Owned<IPropertyTree> lscfg = bnd_cfg->getPropTree(StringBuffer(".//ldapSecurity[@name=").appendf("\"%s\"]", lsname.str()).str());
  182. if(lscfg == NULL)
  183. {
  184. if(proc_cfg.get() != NULL)
  185. lscfg.setown(proc_cfg->getPropTree(StringBuffer("ldapSecurity[@name=").appendf("\"%s\"]", lsname.str()).str()));
  186. if(lscfg == NULL)
  187. {
  188. ERRLOG("can't find bnd_cfg for LdapSecurity %s", lsname.str());
  189. throw MakeStringException(-1, "can't find bnd_cfg for LdapSecurity %s", lsname.str());
  190. }
  191. }
  192. m_secmgr.setown(SecLoader::loadSecManager("LdapSecurity", "EspHttpBinding", LINK(lscfg)));
  193. if(m_secmgr.get() == NULL)
  194. {
  195. throw MakeStringException(-1, "error generating SecManager");
  196. }
  197. StringBuffer basednbuf;
  198. authcfg->getProp("@resourcesBasedn", basednbuf);
  199. m_secmgr->setExtraParam("resourcesBasedn", basednbuf.str());
  200. basednbuf.clear();
  201. authcfg->getProp("@workunitsBasedn", basednbuf);
  202. m_secmgr->setExtraParam("workunitsBasedn", basednbuf.str());
  203. m_authmap.setown(m_secmgr->createAuthMap(authcfg));
  204. m_feature_authmap.setown(m_secmgr->createFeatureMap(authcfg));
  205. }
  206. else if(stricmp(m_authmethod.str(), "Local") == 0)
  207. {
  208. m_secmgr.setown(SecLoader::loadSecManager("Local", "EspHttpBinding", NULL));
  209. m_authmap.setown(m_secmgr->createAuthMap(authcfg));
  210. }
  211. IRestartManager* restartManager = dynamic_cast<IRestartManager*>(m_secmgr.get());
  212. if(restartManager!=NULL)
  213. {
  214. IRestartHandler* pHandler = dynamic_cast<IRestartHandler*>(getESPContainer());
  215. if(pHandler!=NULL)
  216. restartManager->setRestartHandler(pHandler);
  217. }
  218. }
  219. }
  220. }
  221. }
  222. if(m_secmgr.get())
  223. {
  224. const char* desc = m_secmgr->getDescription();
  225. if(desc && *desc)
  226. m_challenge_realm.appendf("ESP (Authentication: %s)", desc);
  227. }
  228. if(m_challenge_realm.length() == 0)
  229. m_challenge_realm.append("ESP");
  230. }
  231. StringBuffer &EspHttpBinding::generateNamespace(IEspContext &context, CHttpRequest* request, const char *serv, const char *method, StringBuffer &ns)
  232. {
  233. ns.append("urn:hpccsystems:ws:");
  234. if (serv && *serv)
  235. ns.appendLower(strlen(serv), serv);
  236. return ns;
  237. }
  238. void EspHttpBinding::getSchemaLocation( IEspContext &context, CHttpRequest* request, StringBuffer &schemaLocation )
  239. {
  240. const char* svcName = request->queryServiceName();
  241. const char* method = request->queryServiceMethod();
  242. if ( !svcName || !(*svcName) )
  243. return;
  244. StringBuffer host;
  245. const char* wsdlAddr = request->queryParameters()->queryProp("__wsdl_address");
  246. if (wsdlAddr && *wsdlAddr)
  247. host.append(wsdlAddr);
  248. else
  249. {
  250. host.append(request->queryHost());
  251. if (request->getPort()>0)
  252. host.append(":").append(request->getPort());
  253. }
  254. schemaLocation.appendf("%s/%s/%s?xsd&amp;ver_=%g", host.str(), svcName, method ? method : "", context.getClientVersion());
  255. }
  256. int EspHttpBinding::getMethodDescription(IEspContext &context, const char *serv, const char *method, StringBuffer &page)
  257. {
  258. StringBuffer key(method);
  259. StringAttr *descrStr=desc_map.getValue(key.toUpperCase().str());
  260. page.append(descrStr ? descrStr->get() : "No description available");
  261. return 0;
  262. }
  263. int EspHttpBinding::getMethodHelp(IEspContext &context, const char *serv, const char *method, StringBuffer &page)
  264. {
  265. StringBuffer key(method);
  266. StringAttr *helpStr=help_map.getValue(key.toUpperCase().str());
  267. page.append(helpStr ? helpStr->get() : "No Help available");
  268. return 0;
  269. }
  270. bool EspHttpBinding::qualifySubServiceName(IEspContext &context, const char *servname, const char *methname, StringBuffer &servQName, StringBuffer *methQName)
  271. {
  272. if (m_subservices)
  273. {
  274. StringBuffer xpath;
  275. xpath.appendf("SubService[@name='%s']/@name", servname);
  276. const char *qname=m_subservices->queryProp(xpath.str());
  277. if (qname)
  278. {
  279. servQName.clear().append(qname);
  280. return qualifyMethodName(context, methname, methQName);
  281. }
  282. }
  283. return false;
  284. }
  285. bool EspHttpBinding::hasSubService(IEspContext &context, const char *name)
  286. {
  287. if (m_subservices)
  288. {
  289. StringBuffer xpath;
  290. xpath.appendf("SubService[@name='%s']", name);
  291. return m_subservices->hasProp(xpath.str());
  292. }
  293. return false;
  294. }
  295. void EspHttpBinding::setRequestPath(const char *path)
  296. {
  297. m_reqPath.clear();
  298. m_reqPath.append(path);
  299. }
  300. bool EspHttpBinding::rootAuthRequired()
  301. {
  302. if(!m_authmap.get())
  303. return false;
  304. if(!stricmp(m_authmethod.str(), "UserDefined") && m_authmap->shouldAuth("/"))
  305. return true;
  306. Owned<ISecResourceList> rlist = m_authmap->getResourceList("/");
  307. if (rlist.get() == NULL)
  308. return false;
  309. return true;
  310. }
  311. bool EspHttpBinding::authRequired(CHttpRequest *request)
  312. {
  313. StringBuffer path;
  314. request->getPath(path);
  315. if(path.length() == 0)
  316. {
  317. throw MakeStringException(-1, "Path is empty for http request");
  318. }
  319. if(m_authmap.get() == NULL)
  320. return false;
  321. if(stricmp(m_authmethod.str(), "UserDefined") == 0 && m_authmap->shouldAuth(path.str()))
  322. return true;
  323. ISecResourceList* rlist = m_authmap->getResourceList(path.str());
  324. if(rlist == NULL)
  325. return false;
  326. request->queryContext()->setResources(rlist);
  327. return true;
  328. }
  329. void EspHttpBinding::populateRequest(CHttpRequest *request)
  330. {
  331. IEspContext* ctx = request->queryContext();
  332. ctx->setSecManger(m_secmgr.getLink());
  333. ctx->setFeatureAuthMap(m_feature_authmap.getLink());
  334. StringBuffer userid, password,realm,peer;
  335. ctx->getUserID(userid);
  336. if(m_secmgr.get() == NULL)
  337. {
  338. return;
  339. }
  340. ISecUser *user = m_secmgr->createUser(userid.str());
  341. if(user == NULL)
  342. {
  343. WARNLOG("Couldn't create ISecUser object for %s", userid.str());
  344. return;
  345. }
  346. ctx->getPassword(password);
  347. user->credentials().setPassword(password.str());
  348. ctx->getRealm(realm);
  349. user->setRealm(realm.str());
  350. ctx->getPeer(peer);
  351. user->setPeer(peer.str());
  352. ctx->setUser(user);
  353. if(m_setting_authmap.get() != NULL)
  354. {
  355. ISecResourceList* settinglist = m_setting_authmap->getResourceList("*");
  356. if(settinglist == NULL)
  357. return ;
  358. if (getEspLogLevel()>=LogMax)
  359. {
  360. StringBuffer s;
  361. DBGLOG("Set security settings: %s", settinglist->toString(s).str());
  362. }
  363. ctx->setSecuritySettings(settinglist);
  364. }
  365. return ;
  366. }
  367. bool EspHttpBinding::doAuth(IEspContext* ctx)
  368. {
  369. if(m_authtype.length() == 0 || stricmp(m_authtype.str(), "Basic") == 0)
  370. {
  371. return basicAuth(ctx);
  372. }
  373. return false;
  374. }
  375. bool EspHttpBinding::basicAuth(IEspContext* ctx)
  376. {
  377. StringBuffer userid;
  378. ctx->getUserID(userid);
  379. if(userid.length() == 0)
  380. {
  381. return false;
  382. }
  383. if(stricmp(m_authmethod.str(), "UserDefined") == 0)
  384. return true;
  385. ISecUser *user = ctx->queryUser();
  386. if(user == NULL)
  387. {
  388. WARNLOG("Can't find user in context");
  389. ctx->AuditMessage(AUDIT_TYPE_ACCESS_FAILURE, "Authentication", "Access Denied: No username provided");
  390. return false;
  391. }
  392. if(m_secmgr.get() == NULL)
  393. {
  394. WARNLOG("No mechanism established for authentication");
  395. return false;
  396. }
  397. ISecResourceList* rlist = ctx->queryResources();
  398. if(rlist == NULL)
  399. return false;
  400. bool authenticated = m_secmgr->authorize(*user, rlist, ctx->querySecureContext());
  401. if(!authenticated)
  402. {
  403. if (user->getAuthenticateStatus() == AS_PASSWORD_EXPIRED || user->getAuthenticateStatus() == AS_PASSWORD_VALID_BUT_EXPIRED)
  404. ctx->AuditMessage(AUDIT_TYPE_ACCESS_FAILURE, "Authentication", "ESP password is expired");
  405. else
  406. ctx->AuditMessage(AUDIT_TYPE_ACCESS_FAILURE, "Authentication", "Access Denied: User or password invalid");
  407. return false;
  408. }
  409. bool authorized = true;
  410. for(int i = 0; i < rlist->count(); i++)
  411. {
  412. ISecResource* curres = rlist->queryResource(i);
  413. if(curres != NULL)
  414. {
  415. int access = (int)curres->getAccessFlags();
  416. int required = (int)curres->getRequiredAccessFlags();
  417. if(access < required)
  418. {
  419. const char *desc=curres->getDescription();
  420. ESPLOG(LogMin, "Access for user '%s' denied to: %s. Access=%d, Required=%d", user->getName(), desc?desc:"<no-desc>", access, required);
  421. ctx->AuditMessage(AUDIT_TYPE_ACCESS_FAILURE, "Authorization", "Access Denied: Not Authorized", "Resource: %s [%s]", curres->getName(), (desc) ? desc : "");
  422. authorized = false;
  423. break;
  424. }
  425. }
  426. }
  427. if(authorized==false)
  428. return false;
  429. ISecPropertyList* securitySettings = ctx->querySecuritySettings();
  430. if(securitySettings == NULL)
  431. return authorized;
  432. m_secmgr->updateSettings(*user,securitySettings, ctx->querySecureContext());
  433. ctx->addTraceSummaryTimeStamp(LogMin, "basicAuth");
  434. return authorized;
  435. }
  436. void EspHttpBinding::handleHttpPost(CHttpRequest *request, CHttpResponse *response)
  437. {
  438. if(request->isSoapMessage())
  439. {
  440. request->queryParameters()->setProp("__wsdl_address", m_wsdlAddress.str());
  441. onSoapRequest(request, response);
  442. }
  443. else if(request->isFormSubmission())
  444. onPostForm(request, response);
  445. else
  446. onPost(request, response);
  447. }
  448. int EspHttpBinding::onGet(CHttpRequest* request, CHttpResponse* response)
  449. {
  450. IEspContext& context = *request->queryContext();
  451. // At this time, the request is already received and fully passed, and
  452. // the user authenticated
  453. LogLevel level = getEspLogLevel(&context);
  454. if (level >= LogNormal)
  455. DBGLOG("EspHttpBinding::onGet");
  456. response->setVersion(HTTP_VERSION);
  457. response->addHeader("Expires", "0");
  458. response->setStatus(HTTP_STATUS_OK);
  459. sub_service sstype = sub_serv_unknown;
  460. StringBuffer pathEx;
  461. StringBuffer serviceName;
  462. StringBuffer methodName;
  463. StringBuffer paramStr;
  464. request->getEspPathInfo(sstype, &pathEx, &serviceName, &methodName, false);
  465. // adjust version if necessary
  466. if (m_defaultSvcVersion.get() && !context.queryRequestParameters()->queryProp("ver_"))
  467. {
  468. switch(sstype)
  469. {
  470. case sub_serv_root:
  471. case sub_serv_main:
  472. case sub_serv_index:
  473. case sub_serv_xform:
  474. case sub_serv_xsd:
  475. case sub_serv_wsdl:
  476. case sub_serv_soap_builder:
  477. case sub_serv_reqsamplexml:
  478. case sub_serv_respsamplexml:
  479. context.setClientVersion(atof(m_defaultSvcVersion));
  480. default:
  481. break;
  482. }
  483. }
  484. switch (sstype)
  485. {
  486. case sub_serv_root:
  487. case sub_serv_main:
  488. return onGetRoot(context, request, response);
  489. case sub_serv_config:
  490. return onGetConfig(context, request, response);
  491. case sub_serv_getversion:
  492. return onGetVersion(context, request, response, serviceName.str());
  493. case sub_serv_index:
  494. return onGetIndex(context, request, response, serviceName.str());
  495. case sub_serv_files:
  496. checkInitEclIdeResponse(request, response);
  497. return onGetFile(context, request, response, pathEx.str());
  498. case sub_serv_itext:
  499. return onGetItext(context, request, response, pathEx.str());
  500. case sub_serv_iframe:
  501. return onGetIframe(context, request, response, pathEx.str());
  502. case sub_serv_content:
  503. return onGetContent(context, request, response, serviceName.str(), methodName.str());
  504. case sub_serv_method:
  505. return onGetService(context, request, response, serviceName.str(), methodName.str(), pathEx.str());
  506. case sub_serv_form:
  507. return onGetForm(context, request, response, serviceName.str(), methodName.str());
  508. case sub_serv_xform:
  509. return onGetXForm(context, request, response, serviceName.str(), methodName.str());
  510. case sub_serv_result:
  511. return onGetResult(context, request, response, serviceName.str(), methodName.str(), pathEx.str());
  512. case sub_serv_wsdl:
  513. return onGetWsdl(context, request, response, serviceName.str(), methodName.str());
  514. case sub_serv_xsd:
  515. return onGetXsd(context, request, response, serviceName.str(), methodName.str());
  516. case sub_serv_instant_query:
  517. return onGetInstantQuery(context, request, response, serviceName.str(), methodName.str());
  518. case sub_serv_soap_builder:
  519. return onGetSoapBuilder(context, request, response, serviceName.str(), methodName.str());
  520. case sub_serv_reqsamplexml:
  521. return onGetReqSampleXml(context, request, response, serviceName.str(), methodName.str());
  522. case sub_serv_respsamplexml:
  523. return onGetRespSampleXml(context, request, response, serviceName.str(), methodName.str());
  524. case sub_serv_query:
  525. return onGetQuery(context, request, response, serviceName.str(), methodName.str());
  526. case sub_serv_file_upload:
  527. return onStartUpload(context, request, response, serviceName.str(), methodName.str());
  528. default:
  529. return onGetNotFound(context, request, response, serviceName.str());
  530. }
  531. return 0;
  532. }
  533. int EspHttpBinding::onGetStaticIndex(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv)
  534. {
  535. return onGetNotFound(context, request, response, serv);
  536. }
  537. #if 1
  538. //============================================================
  539. // Filter out XML by schema using Schema parser without indent
  540. static void setStartTag(IPTree* in, IXmlType* type, const char* tag,StringBuffer& out)
  541. {
  542. int nAttrs = type->getAttrCount();
  543. out.appendf("<%s", tag);
  544. for (int attr=0; attr<nAttrs; attr++)
  545. {
  546. IXmlAttribute* att = type->queryAttr(attr);
  547. VStringBuffer prop("@%s", att->queryName());
  548. const char* val = in->queryProp(prop);
  549. if (val && *val)
  550. {
  551. StringBuffer encoded;
  552. encodeXML(val, encoded);
  553. out.appendf(" %s=\"%s\"", att->queryName(), encoded.str());
  554. }
  555. }
  556. out.append('>');
  557. }
  558. static void filterXmlBySchema(IPTree* in, IXmlType* type, const char* tag, StringBuffer& out)
  559. {
  560. assertex(type);
  561. if (type->isComplexType())
  562. {
  563. setStartTag(in,type,tag,out);
  564. int flds = type->getFieldCount();
  565. for (int i=0; i<flds; i++)
  566. {
  567. const char* fldName = type->queryFieldName(i);
  568. IPTree* fld = in ? in->queryBranch(fldName) : NULL;
  569. filterXmlBySchema(fld,type->queryFieldType(i),fldName,out);
  570. }
  571. if (flds==0)
  572. {
  573. if (type->getSubType() == SubType_Complex_SimpleContent) // the first field is baseType
  574. {
  575. const char* val = in->queryProp(".");
  576. if (val && *val) {
  577. StringBuffer encoded;
  578. encodeXML(val, encoded);
  579. out.append(encoded);
  580. }
  581. }
  582. }
  583. out.appendf("</%s>",tag);
  584. }
  585. else if (type->isArray())
  586. {
  587. setStartTag(in,type,tag,out);
  588. //check xml first to decide items, if 0, check schema, generate 1 item by default
  589. const char* itemName = type->queryFieldName(0);
  590. IXmlType* itemType = type->queryFieldType(0);
  591. if (!itemName || !itemType)
  592. {
  593. VStringBuffer s("*** Invalid array definition: tag=%s, itemName=%s", tag, itemName?itemName:"NULL");
  594. out.append(s);
  595. ERRLOG("%s", s.str());
  596. return;
  597. }
  598. bool hasChild = false;
  599. if (in)
  600. {
  601. Owned<IPTreeIterator> it = in->getElements(itemName);
  602. for (it->first(); it->isValid(); it->next())
  603. {
  604. hasChild = true;
  605. filterXmlBySchema(&it->query(), itemType, itemName, out);
  606. }
  607. }
  608. if (!hasChild)
  609. {
  610. filterXmlBySchema(NULL, itemType, itemName, out);
  611. }
  612. out.appendf("</%s>",tag);
  613. }
  614. else // simple type
  615. {
  616. setStartTag(in,type,tag,out);
  617. if (in)
  618. {
  619. const char* value = in->queryProp(NULL);
  620. if (value)
  621. encodeUtf8XML(value,out);
  622. }
  623. out.appendf("</%s>", tag);
  624. }
  625. }
  626. static void filterXmlBySchema(StringBuffer& in, StringBuffer& schema, const char* name, StringBuffer& out)
  627. {
  628. Owned<IXmlSchema> sp = createXmlSchema(schema);
  629. Owned<IPTree> tree = createPTreeFromXMLString(in);
  630. //VStringBuffer name("tns:%s", tree->queryName());
  631. IXmlType* type = sp->queryElementType(name);
  632. if (!type)
  633. {
  634. name = tree->queryName();
  635. type = sp->queryElementType(name);
  636. if (!type)
  637. {
  638. StringBuffer method = name;
  639. if (method.length() > 7)
  640. method.setLength(method.length()-7);
  641. type = sp->queryElementType(method);
  642. }
  643. }
  644. if (type)
  645. filterXmlBySchema(tree,type,name,out);
  646. else
  647. {
  648. const char* value = tree->queryProp(NULL);
  649. DBGLOG("Unknown xml tag ignored: <%s>%s</%s>", name, value?value:"", name);
  650. }
  651. }
  652. void EspHttpBinding::getXMLMessageTag(IEspContext& ctx, bool isRequest, const char *method, StringBuffer& tag)
  653. {
  654. MethodInfoArray info;
  655. getQualifiedNames(ctx, info);
  656. for (unsigned i=0; i<info.length(); i++)
  657. {
  658. CMethodInfo& m = info.item(i);
  659. if (!stricmp(m.m_label, method))
  660. {
  661. tag.set(isRequest ? m.m_requestLabel : m.m_responseLabel);
  662. break;
  663. }
  664. }
  665. if (!tag.length())
  666. tag.append(method).append(isRequest ? "Request" : "Response");
  667. }
  668. // new way to generate soap message
  669. void EspHttpBinding::getSoapMessage(StringBuffer& soapmsg, IEspContext& ctx, CHttpRequest* request, const char *serv, const char *method)
  670. {
  671. StringBuffer reqName(serv);
  672. reqName.append("Request");
  673. Owned<IRpcMessage> msg = new CRpcMessage(reqName.str());
  674. msg->setContext(&ctx);
  675. Owned<IRpcRequestBinding> rpcreq = createReqBinding(ctx, request, serv, method);
  676. rpcreq->serialize(*msg);
  677. StringBuffer req, tag, schema, filtered;
  678. msg->marshall(req, NULL);
  679. getSchema(schema,ctx,request,serv,method,false);
  680. getXMLMessageTag(ctx, true, method, tag);
  681. filterXmlBySchema(req,schema,tag.str(),filtered);
  682. StringBuffer ns;
  683. soapmsg.appendf(
  684. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
  685. "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
  686. " xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\""
  687. " xmlns=\"%s\">"
  688. " <soap:Body>%s </soap:Body></soap:Envelope>",
  689. generateNamespace(ctx, request, serv, method, ns).str(), filtered.str()
  690. );
  691. }
  692. #else
  693. //=========================================================
  694. // Filter out XML by schema using Schema parser with indent
  695. static void filterXmlBySchema(IPTree* in, IXmlType* type, const char* tag, StringBuffer& out,int indent)
  696. {
  697. assertex(type);
  698. if (type->isComplexType())
  699. {
  700. out.pad(indent).appendf("<%s>\n", tag);
  701. int flds = type->getFieldCount();
  702. for (int i=0; i<flds; i++)
  703. {
  704. const char* fldName = type->queryFieldName(i);
  705. IPTree* fld = in ? in->queryBranch(fldName) : NULL;
  706. filterXmlBySchema(fld,type->queryFieldType(i),fldName,out,indent+1);
  707. }
  708. out.pad(indent).appendf("</%s>\n",tag);
  709. }
  710. else if (type->isArray())
  711. {
  712. out.pad(indent).appendf("<%s>\n", tag);
  713. //check xml first to decide items, if 0, check schema, generate 1 item by default
  714. const char* itemName = type->queryFieldName(0);
  715. IXmlType* itemType = type->queryFieldType(0);
  716. if (!itemName || !itemType)
  717. {
  718. VStringBuffer s("*** Invalid array definition: tag=%s, indent=%d, itemName=%s", tag, indent,itemName?itemName:"NULL");
  719. out.append(s);
  720. ERRLOG(s);
  721. return;
  722. }
  723. bool hasChild = false;
  724. if (in)
  725. {
  726. Owned<IPTreeIterator> it = in->getElements(itemName);
  727. for (it->first(); it->isValid(); it->next())
  728. {
  729. hasChild = true;
  730. filterXmlBySchema(&it->query(), itemType, itemName, out,indent+1);
  731. }
  732. }
  733. if (!hasChild)
  734. {
  735. filterXmlBySchema(NULL, itemType, itemName, out,indent+1);
  736. }
  737. out.pad(indent).appendf("</%s>\n",tag);
  738. }
  739. else // simple type
  740. {
  741. out.pad(indent).appendf("<%s>", tag);
  742. if (in)
  743. {
  744. const char* value = in->queryProp(NULL);
  745. if (value)
  746. encodeUtf8XML(value,out);
  747. }
  748. out.appendf("</%s>\n", tag);
  749. }
  750. }
  751. static void filterXmlBySchema(StringBuffer& in, StringBuffer& schema, StringBuffer& out,int indent)
  752. {
  753. Owned<IXmlSchema> sp = createXmlSchema(schema);
  754. Owned<IPTree> tree = createPTreeFromXMLString(in);
  755. //VStringBuffer name("tns:%s", tree->queryName());
  756. const char* name = tree->queryName();
  757. IXmlType* type = sp->queryElementType(name);
  758. if (!type)
  759. {
  760. StringBuffer method(strlen(name)-7, name);
  761. type = sp->queryElementType(method);
  762. }
  763. if (type)
  764. filterXmlBySchema(tree,type,name,out,indent);
  765. else
  766. {
  767. const char* value = tree->queryProp(NULL);
  768. DBGLOG("Unknown xml tag ignored: <%s>%s</%s>", name, value?value:"", name);
  769. }
  770. }
  771. void EspHttpBinding::getSoapMessage(StringBuffer& soapmsg, IEspContext& ctx, CHttpRequest* request, const char *serv, const char *method)
  772. {
  773. StringBuffer reqName(serv);
  774. reqName.append("Request");
  775. Owned<IRpcMessage> msg = new CRpcMessage(reqName.str());
  776. Owned<IRpcRequestBinding> rpcreq = createReqBinding(ctx, request, serv, method);
  777. rpcreq->serialize(*msg);
  778. // use schema to filter out fields that are internal, not belong to the version etc
  779. StringBuffer req,schema,filtered;
  780. msg->marshall(req, NULL);
  781. getSchema(schema,ctx,request,serv,method,false);
  782. //DBGLOG("Schema: %s", schema.str());
  783. filterXmlBySchema(req,schema,filtered,2);
  784. soapmsg.appendf(
  785. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
  786. "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
  787. " xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\""
  788. " xmlns=\"urn:hpccsystems:ws:");
  789. if (serv && *serv)
  790. soapmsg.appendLower(strlen(serv), serv);
  791. soapmsg.appendf("\">\n <soap:Body>\n%s </soap:Body>\n</soap:Envelope>", filtered.str());
  792. }
  793. #endif
  794. int EspHttpBinding::onGetSoapBuilder(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  795. {
  796. StringBuffer soapmsg, serviceQName, methodQName;
  797. if (!qualifyServiceName(context, serv, method, serviceQName, &methodQName)
  798. || methodQName.length()==0)
  799. throw createEspHttpException(HTTP_STATUS_BAD_REQUEST_CODE, "Bad Request", HTTP_STATUS_BAD_REQUEST);
  800. getSoapMessage(soapmsg,context,request,serviceQName,methodQName);
  801. //put all URL parameters into dest
  802. StringBuffer params;
  803. const char* excludes[] = {"soap_builder_",NULL};
  804. getEspUrlParams(context,params,excludes);
  805. VStringBuffer dest("%s/%s?%s", serviceQName.str(), methodQName.str(), params.str());
  806. VStringBuffer header("SOAPAction: \"%s\"\n", dest.str());
  807. header.append("Content-Type: text/xml; charset=UTF-8");
  808. IXslProcessor* xslp = getXmlLibXslProcessor();
  809. Owned<IXslTransform> xform = xslp->createXslTransform();
  810. xform->loadXslFromFile(StringBuffer(getCFD()).append("./xslt/soap_page.xsl").str());
  811. xform->setXmlSource("<xml/>", 6);
  812. // params
  813. xform->setStringParameter("serviceName", serviceQName.str());
  814. xform->setStringParameter("methodName", methodQName.str());
  815. xform->setStringParameter("soapbody", soapmsg.str());
  816. xform->setStringParameter("header", header.str());
  817. ISecUser* user = context.queryUser();
  818. bool inhouse = user && (user->getStatus()==SecUserStatus_Inhouse);
  819. xform->setParameter("inhouseUser", inhouse ? "true()" : "false()");
  820. VStringBuffer url("%s?%s", methodQName.str(), params.str());
  821. xform->setStringParameter("destination", url.str());
  822. StringBuffer page;
  823. xform->transform(page);
  824. response->setContent(page);
  825. response->setContentType("text/html; charset=UTF-8");
  826. response->setStatus(HTTP_STATUS_OK);
  827. response->send();
  828. return 0;
  829. }
  830. int EspHttpBinding::onFeaturesAuthorize(IEspContext &context, MapStringTo<SecAccessFlags> & pmap, const char *serviceName, const char *methodName)
  831. {
  832. if (!context.validateFeaturesAccess(pmap, false))
  833. {
  834. StringBuffer features;
  835. HashIterator iter(pmap);
  836. int index = 0;
  837. ForEach(iter)
  838. {
  839. IMapping &cur = iter.query();
  840. const char * key = (const char *)cur.getKey();
  841. SecAccessFlags val = *pmap.getValue(key);
  842. features.appendf("%s%s:%s", (index++ == 0 ? "" : ", "), key, getSecAccessFlagName(val));
  843. }
  844. throw MakeStringException(-1, "%s::%s access denied - Required features: %s.", serviceName, methodName, features.str());
  845. }
  846. return 0;
  847. }
  848. int EspHttpBinding::onGetInstantQuery(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  849. {
  850. return onGetNotFound(context, request, response, serv);
  851. }
  852. int EspHttpBinding::onGetResult(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method, const char *pathex)
  853. {
  854. return onGetNotFound(context, request, response, serv);
  855. }
  856. int EspHttpBinding::onGetResultPresentation(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method, StringBuffer &xmlResult)
  857. {
  858. return onGetNotFound(context, request, response, serv);
  859. }
  860. int EspHttpBinding::onGetFile(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *path)
  861. {
  862. return onGetNotFound(context, request, response, NULL);
  863. }
  864. int EspHttpBinding::onGetItext(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *path)
  865. {
  866. StringBuffer title;
  867. request->getParameter("text", title);
  868. StringBuffer content;
  869. if (checkInitEclIdeResponse(request, response))
  870. content.append("<!DOCTYPE html>"); //may be safe for all browsers? but better to be safe for now?
  871. content.append("<html><head>");
  872. if(title.length() > 0)
  873. content.appendf("<title>%s</title>", title.str());
  874. content.appendf("</head><body>"
  875. "<img src=\"files_/img/topurl.png\" onClick=\"top.location.href=parent.frames['imain'].location.href\" alt=\"No Frames\" width=\"13\" height=\"15\">"
  876. "&nbsp;&nbsp;<font size=+1><b>%s</b></font></body></html>", title.str());
  877. response->setContent(content.length(), content.str());
  878. response->setContentType("text/html");
  879. response->send();
  880. return 0;
  881. }
  882. int EspHttpBinding::onGetIframe(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *path)
  883. {
  884. StringBuffer title;
  885. request->getParameter("esp_iframe_title", title);
  886. StringBuffer content;
  887. if (checkInitEclIdeResponse(request, response))
  888. content.append("<!DOCTYPE html>"); //may be safe for all browsers? but better to be safe for now?
  889. content.append("<html><head>");
  890. if(title.length() > 0)
  891. content.appendf("<title>%s</title>", title.str());
  892. content.append("</head>");
  893. content.append("<frameset rows=\"35,*\" FRAMEPADDING=\"0\" PADDING=\"0\" SPACING=\"0\" FRAMEBORDER=\"0\">");
  894. content.appendf("<frame src=\"itext?text=%s\" name=\"ititle\" target=\"imain\" scrolling=\"no\"/>", title.str());
  895. //content.appendf("<frame name=\"imain\" target=\"main\" src=\"../%s?%s\" scrolling=\"auto\" frameborder=\"0\" noresize=\"noresize\"/>", path, request->queryParamStr());
  896. StringBuffer inner;
  897. request->getParameter("inner", inner);
  898. StringBuffer plainText;
  899. request->getParameter("PlainText", plainText);
  900. if (plainText.length() > 0)
  901. inner.appendf("&PlainText=%s", plainText.str());
  902. ESPLOG(LogNormal,"Inner: %s", inner.str());
  903. ESPLOG(LogNormal,"Param: %s", request->queryParamStr());
  904. content.appendf("<frame name=\"imain\" target=\"main\" src=\"%s\" scrolling=\"auto\" frameborder=\"0\" noresize=\"noresize\"/>", inner.str());
  905. content.append("</frameset>");
  906. content.append("</body></html>");
  907. response->setContent(content.length(), content.str());
  908. response->setContentType("text/html");
  909. response->send();
  910. return 0;
  911. }
  912. int EspHttpBinding::onGetContent(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  913. {
  914. return onGetNotFound(context, request, response, serv);
  915. }
  916. int EspHttpBinding::onGetConfig(IEspContext &context, CHttpRequest* request, CHttpResponse* response)
  917. {
  918. ISecUser* user = context.queryUser();
  919. if (m_viewConfig || (user && (user->getStatus()==SecUserStatus_Inhouse)))
  920. {
  921. ESPLOG(LogNormal, "Get config file: %s", m_configFile.get());
  922. StringBuffer content;
  923. xmlContentFromFile(m_configFile, "/esp/xslt/xmlformatter.xsl", content);
  924. response->setContent(content.str());
  925. response->setContentType(HTTP_TYPE_APPLICATION_XML_UTF8);
  926. response->setStatus(HTTP_STATUS_OK);
  927. response->send();
  928. return 0;
  929. }
  930. DBGLOG("Config access denied");
  931. return onGetNotFound(context, request, response, NULL);
  932. }
  933. int EspHttpBinding::onGetVersion(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service)
  934. {
  935. StringBuffer verxml;
  936. StringBuffer srvQName;
  937. qualifyServiceName(context, service, NULL, srvQName, NULL);
  938. verxml.appendf("<VersionInfo><Service>%s</Service><Version>%.3f</Version></VersionInfo>", srvQName.str(), m_wsdlVer);
  939. response->setContent(verxml.str());
  940. response->setContentType(HTTP_TYPE_APPLICATION_XML_UTF8);
  941. response->setStatus(HTTP_STATUS_OK);
  942. response->send();
  943. return 0;
  944. }
  945. int EspHttpBinding::onGetXsd(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service, const char *method)
  946. {
  947. return getWsdlOrXsd(context,request,response,service,method,false);
  948. }
  949. int EspHttpBinding::onGetWsdl(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service, const char *method)
  950. {
  951. return getWsdlOrXsd(context,request,response,service,method,true);
  952. }
  953. bool EspHttpBinding::getSchema(StringBuffer& schema, IEspContext &ctx, CHttpRequest* req, const char *service, const char *method, bool standalone)
  954. {
  955. StringBuffer serviceQName;
  956. StringBuffer methodQName;
  957. if (!qualifyServiceName(ctx, service, method, serviceQName, &methodQName))
  958. return false;
  959. const char *sqName = serviceQName.str();
  960. const char *mqName = methodQName.str();
  961. Owned<IPropertyTree> namespaces = createPTree();
  962. appendSchemaNamespaces(namespaces, ctx, req, service, method);
  963. Owned<IPropertyTreeIterator> nsiter = namespaces->getElements("namespace");
  964. StringBuffer nstr;
  965. generateNamespace(ctx, req, sqName, mqName, nstr);
  966. schema.appendf("<xsd:schema elementFormDefault=\"qualified\" targetNamespace=\"%s\" ", nstr.str());
  967. if (standalone)
  968. schema.appendf(" xmlns:tns=\"%s\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"", nstr.str());
  969. ForEach(*nsiter)
  970. {
  971. IPropertyTree &ns = nsiter->query();
  972. schema.appendf(" xmlns:%s=\"%s\"", ns.queryProp("@nsvar"), ns.queryProp("@ns"));
  973. }
  974. schema.append(">\n");
  975. ForEach(*nsiter)
  976. {
  977. IPropertyTree &ns = nsiter->query();
  978. if (ns.hasProp("@import"))
  979. schema.appendf("<xsd:import namespace=\"%s\" schemaLocation=\"%s\"/>", ns.queryProp("@ns"), ns.queryProp("@location"));
  980. }
  981. schema.append(
  982. "<xsd:complexType name=\"EspException\">"
  983. "<xsd:all>"
  984. "<xsd:element name=\"Code\" type=\"xsd:string\" minOccurs=\"0\"/>"
  985. "<xsd:element name=\"Audience\" type=\"xsd:string\" minOccurs=\"0\"/>"
  986. "<xsd:element name=\"Source\" type=\"xsd:string\" minOccurs=\"0\"/>"
  987. "<xsd:element name=\"Message\" type=\"xsd:string\" minOccurs=\"0\"/>"
  988. "</xsd:all>"
  989. "</xsd:complexType>\n"
  990. "<xsd:complexType name=\"ArrayOfEspException\">"
  991. "<xsd:sequence>"
  992. "<xsd:element name=\"Source\" type=\"xsd:string\" minOccurs=\"0\"/>"
  993. "<xsd:element name=\"Exception\" type=\"tns:EspException\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>"
  994. "</xsd:sequence>"
  995. "</xsd:complexType>\n"
  996. "<xsd:element name=\"Exceptions\" type=\"tns:ArrayOfEspException\"/>\n"
  997. );
  998. if (ctx.queryOptions()&ESPCTX_WSDL_EXT)
  999. {
  1000. schema.append(
  1001. "<xsd:complexType name=\"EspSecurityInfo\">"
  1002. "<xsd:all>"
  1003. "<xsd:element name=\"UsernameToken\" minOccurs=\"0\">"
  1004. "<xsd:complexType>"
  1005. "<xsd:all>"
  1006. "<xsd:element name=\"Username\" minOccurs=\"0\"/>"
  1007. "<xsd:element name=\"Password\" minOccurs=\"0\"/>"
  1008. "</xsd:all>"
  1009. "</xsd:complexType>"
  1010. "</xsd:element>"
  1011. "<xsd:element name=\"RealmToken\" minOccurs=\"0\">"
  1012. "<xsd:complexType>"
  1013. "<xsd:all>"
  1014. "<xsd:element name=\"Realm\" minOccurs=\"0\"/>"
  1015. "</xsd:all>"
  1016. "</xsd:complexType>"
  1017. "</xsd:element>"
  1018. "</xsd:all>"
  1019. "</xsd:complexType>"
  1020. "<xsd:element name=\"Security\" type=\"tns:EspSecurityInfo\"/>\n"
  1021. );
  1022. }
  1023. bool mda=(req->queryParameters()->getPropInt("mda")!=0);
  1024. getXsdDefinition(ctx, req, schema, sqName, mqName, mda);
  1025. schema.append("<xsd:element name=\"string\" nillable=\"true\" type=\"xsd:string\" />\n");
  1026. schema.append("</xsd:schema>");
  1027. return true;
  1028. }
  1029. int EspHttpBinding::getWsdlOrXsd(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service, const char *method, bool isWsdl)
  1030. {
  1031. bool mda=(request->queryParameters()->getPropInt("mda")!=0);
  1032. try
  1033. {
  1034. StringBuffer serviceQName;
  1035. StringBuffer methodQName;
  1036. if (!qualifyServiceName(context, service, method, serviceQName, &methodQName))
  1037. {
  1038. response->setStatus(HTTP_STATUS_NOT_FOUND);
  1039. }
  1040. else
  1041. {
  1042. const char *sqName = serviceQName.str();
  1043. const char *mqName = methodQName.str();
  1044. StringBuffer ns;
  1045. generateNamespace(context, request, serviceQName.str(), methodQName.str(), ns);
  1046. StringBuffer content("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  1047. if (context.queryRequestParameters()->hasProp("display"))
  1048. content.append("<?xml-stylesheet type=\"text/xsl\" href=\"/esp/xslt/xmlformatter.xsl\"?>");
  1049. else if (isWsdl && context.queryRequestParameters()->hasProp("wsdlviewer"))
  1050. content.append("<?xml-stylesheet type=\"text/xsl\" href=\"/esp/xslt/wsdl-viewer.xsl\"?>");
  1051. if (isWsdl)
  1052. {
  1053. 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\""
  1054. " xmlns:mime=\"http://schemas.xmlsoap.org/wsdl/mime/\" xmlns:tns=\"%s\""
  1055. " targetNamespace=\"%s\">", ns.str(), ns.str());
  1056. content.append("<types>");
  1057. }
  1058. getSchema(content,context,request,service,method,!isWsdl);
  1059. if (isWsdl)
  1060. {
  1061. content.append("</types>");
  1062. getWsdlMessages(context, request, content, sqName, mqName, mda);
  1063. getWsdlPorts(context, request, content, sqName, mqName, mda);
  1064. getWsdlBindings(context, request, content, sqName, mqName, mda);
  1065. StringBuffer location(m_wsdlAddress.str());
  1066. if (request->queryParameters()->hasProp("wsdl_destination_path"))
  1067. location.append(request->queryParameters()->queryProp("wsdl_destination_path"));
  1068. else
  1069. location.append('/').append(sqName).appendf("?ver_=%g", context.getClientVersion());
  1070. if (request->queryParameters()->hasProp("encode_results"))
  1071. {
  1072. const char *encval = request->queryParameters()->queryProp("encode_results");
  1073. location.append("&amp;").appendf("encode_=%s", (encval && *encval) ? encval : "1");
  1074. }
  1075. content.appendf("<service name=\"%s\">", sqName);
  1076. content.appendf("<port name=\"%sServiceSoap\" binding=\"tns:%sServiceSoap\">", sqName, sqName);
  1077. content.appendf("<soap:address location=\"%s\"/>", location.str());
  1078. content.append("</port>");
  1079. content.append("</service>");
  1080. content.append("</definitions>");
  1081. }
  1082. response->setContent(content.length(), content.str());
  1083. response->setContentType(HTTP_TYPE_APPLICATION_XML_UTF8);
  1084. response->setStatus(HTTP_STATUS_OK);
  1085. }
  1086. }
  1087. catch (IException *e)
  1088. {
  1089. return onGetException(context, request, response, *e);
  1090. }
  1091. response->send();
  1092. return 0;
  1093. }
  1094. static void genSampleXml(StringStack& parent, IXmlType* type, StringBuffer& out, const char* tag, const char* ns=NULL)
  1095. {
  1096. assertex(type!=NULL);
  1097. const char* typeName = type->queryName();
  1098. if (type->isComplexType())
  1099. {
  1100. if (typeName && std::find(parent.begin(),parent.end(),typeName) != parent.end())
  1101. return; // recursive
  1102. out.appendf("<%s", tag);
  1103. if (ns)
  1104. out.append(' ').append(ns);
  1105. for (unsigned i=0; i<type->getAttrCount(); i++)
  1106. {
  1107. IXmlAttribute* attr = type->queryAttr(i);
  1108. out.appendf(" %s='", attr->queryName());
  1109. attr->getSampleValue(out);
  1110. out.append('\'');
  1111. }
  1112. out.append('>');
  1113. if (typeName)
  1114. parent.push_back(typeName);
  1115. int flds = type->getFieldCount();
  1116. switch (type->getSubType())
  1117. {
  1118. case SubType_Complex_SimpleContent:
  1119. assertex(flds==0);
  1120. type->queryFieldType(0)->getSampleValue(out,tag);
  1121. break;
  1122. default:
  1123. for (int idx=0; idx<flds; idx++)
  1124. genSampleXml(parent,type->queryFieldType(idx),out,type->queryFieldName(idx));
  1125. break;
  1126. }
  1127. if (typeName)
  1128. parent.pop_back();
  1129. out.appendf("</%s>",tag);
  1130. }
  1131. else if (type->isArray())
  1132. {
  1133. if (typeName && std::find(parent.begin(),parent.end(),typeName) != parent.end())
  1134. return; // recursive
  1135. const char* itemName = type->queryFieldName(0);
  1136. IXmlType* itemType = type->queryFieldType(0);
  1137. if (!itemName || !itemType)
  1138. throw MakeStringException(-1,"*** Invalid array definition: tag=%s, itemName=%s", tag, itemName?itemName:"NULL");
  1139. StringBuffer item;
  1140. if (typeName)
  1141. parent.push_back(typeName);
  1142. genSampleXml(parent,itemType,item,itemName);
  1143. if (typeName)
  1144. parent.pop_back();
  1145. // gen two items
  1146. out.appendf("<%s>%s%s</%s>", tag,item.str(),item.str(),tag);
  1147. }
  1148. else // simple type
  1149. {
  1150. out.appendf("<%s>", tag);
  1151. type->getSampleValue(out,NULL);
  1152. out.appendf("</%s>", tag);
  1153. }
  1154. }
  1155. void EspHttpBinding::generateSampleXml(bool isRequest, IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1156. {
  1157. StringBuffer serviceQName, methodQName;
  1158. if (!qualifyServiceName(context, serv, method, serviceQName, &methodQName))
  1159. return;
  1160. StringBuffer schemaXml, element;
  1161. getXMLMessageTag(context, isRequest, methodQName.str(), element);
  1162. getSchema(schemaXml,context,request,serv,method,false);
  1163. Owned<IXmlSchema> schema = createXmlSchema(schemaXml);
  1164. if (schema.get())
  1165. {
  1166. IXmlType* type = schema->queryElementType(element);
  1167. if (type)
  1168. {
  1169. StringBuffer content;
  1170. StringStack parent;
  1171. StringBuffer nsdecl("xmlns=\"");
  1172. content.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  1173. if (context.queryRequestParameters()->hasProp("display"))
  1174. content.append("<?xml-stylesheet type=\"text/xsl\" href=\"/esp/xslt/xmlformatter.xsl\"?>");
  1175. genSampleXml(parent,type, content, element, generateNamespace(context, request, serviceQName.str(), methodQName.str(), nsdecl).append('\"').str());
  1176. response->setContent(content.length(), content.str());
  1177. response->setContentType(HTTP_TYPE_APPLICATION_XML_UTF8);
  1178. response->setStatus(HTTP_STATUS_OK);
  1179. response->send();
  1180. return;
  1181. }
  1182. }
  1183. throw MakeStringException(-1,"Unknown type: %s", element.str());
  1184. }
  1185. void EspHttpBinding::generateSampleXmlFromSchema(bool isRequest, IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method, const char * schemaxml)
  1186. {
  1187. StringBuffer serviceQName, methodQName;
  1188. if (!qualifyServiceName(context, serv, method, serviceQName, &methodQName))
  1189. return;
  1190. StringBuffer element, schemaXmlbuff(schemaxml);
  1191. getXMLMessageTag(context, isRequest, methodQName.str(), element);
  1192. Owned<IXmlSchema> schema = createXmlSchema(schemaXmlbuff);
  1193. if (schema.get())
  1194. {
  1195. IXmlType* type = schema->queryElementType(element);
  1196. if (type)
  1197. {
  1198. StringBuffer content;
  1199. StringStack parent;
  1200. StringBuffer nsdecl("xmlns=\"");
  1201. content.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  1202. if (context.queryRequestParameters()->hasProp("display"))
  1203. content.append("<?xml-stylesheet type=\"text/xsl\" href=\"/esp/xslt/xmlformatter.xsl\"?>");
  1204. genSampleXml(parent,type, content, element, generateNamespace(context, request, serviceQName.str(), methodQName.str(), nsdecl).append('\"').str());
  1205. response->setContent(content.length(), content.str());
  1206. response->setContentType(HTTP_TYPE_APPLICATION_XML_UTF8);
  1207. response->setStatus(HTTP_STATUS_OK);
  1208. response->send();
  1209. return;
  1210. }
  1211. }
  1212. throw MakeStringException(-1,"Unknown type: %s", element.str());
  1213. }
  1214. int EspHttpBinding::onGetReqSampleXml(IEspContext &ctx, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1215. {
  1216. generateSampleXml(true, ctx, request, response, serv, method);
  1217. return 0;
  1218. }
  1219. int EspHttpBinding::onGetRespSampleXml(IEspContext &ctx, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1220. {
  1221. generateSampleXml(false, ctx, request, response, serv, method);
  1222. return 0;
  1223. }
  1224. int EspHttpBinding::onStartUpload(IEspContext &ctx, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1225. {
  1226. StringArray fileNames, files;
  1227. Owned<IMultiException> me = MakeMultiException("FileSpray::UploadFile()");
  1228. try
  1229. {
  1230. if (!ctx.validateFeatureAccess(FILE_UPLOAD, SecAccess_Full, false))
  1231. throw MakeStringException(-1, "Permission denied.");
  1232. StringBuffer netAddress, path;
  1233. request->getParameter("NetAddress", netAddress);
  1234. request->getParameter("Path", path);
  1235. if (((netAddress.length() < 1) || (path.length() < 1)))
  1236. request->readUploadFileContent(fileNames, files);
  1237. else
  1238. request->readContentToFiles(netAddress, path, fileNames);
  1239. return onFinishUpload(ctx, request, response, serv, method, fileNames, files, NULL);
  1240. }
  1241. catch (IException* e)
  1242. {
  1243. me->append(*e);
  1244. }
  1245. catch (...)
  1246. {
  1247. me->append(*MakeStringExceptionDirect(-1, "Unknown Exception"));
  1248. }
  1249. return onFinishUpload(ctx, request, response, serv, method, fileNames, files, me);
  1250. }
  1251. int EspHttpBinding::onFinishUpload(IEspContext &ctx, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method,
  1252. StringArray& fileNames, StringArray& files, IMultiException *me)
  1253. {
  1254. response->setContentType("text/html; charset=UTF-8");
  1255. StringBuffer content(
  1256. "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
  1257. "<head>"
  1258. "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>"
  1259. "<title>Enterprise Services Platform</title>"
  1260. "</head>"
  1261. "<body>"
  1262. "<div id=\"DropzoneFileData\">");
  1263. if (!me || (me->ordinality()==0))
  1264. content.append("<br/><b>File has been uploaded.</b>");
  1265. else
  1266. {
  1267. StringBuffer msg;
  1268. WARNLOG("Exception(s) in EspHttpBinding::onFinishUpload - %s", me->errorMessage(msg).append('\n').str());
  1269. content.appendf("<br/><b>%s</b>", msg.str());
  1270. }
  1271. content.append("</div>"
  1272. "</body>"
  1273. "</html>");
  1274. response->setContent(content.str());
  1275. response->send();
  1276. return 0;
  1277. }
  1278. int EspHttpBinding::getWsdlMessages(IEspContext &context, CHttpRequest *request, StringBuffer &content, const char *service, const char *method, bool mda)
  1279. {
  1280. bool allMethods = (method==NULL || !*method);
  1281. MethodInfoArray methods;
  1282. getQualifiedNames(context, methods);
  1283. int count=methods.ordinality();
  1284. for (int indx=0; indx<count; indx++)
  1285. {
  1286. CMethodInfo &info = methods.item(indx);
  1287. {
  1288. if (allMethods || !Utils::strcasecmp(method, info.m_label.str()))
  1289. {
  1290. content.appendf("<message name=\"%sSoapIn\">", info.m_label.str());
  1291. if (context.queryOptions()&ESPCTX_WSDL_EXT)
  1292. content.appendf("<part name=\"security\" element=\"tns:Security\"/>");
  1293. content.appendf("<part name=\"parameters\" element=\"tns:%s\"/>", info.m_requestLabel.str());
  1294. content.append("</message>");
  1295. content.appendf("<message name=\"%sSoapOut\">", info.m_label.str());
  1296. content.appendf("<part name=\"parameters\" element=\"tns:%s\"/>", info.m_responseLabel.str());
  1297. content.append("</message>");
  1298. }
  1299. }
  1300. }
  1301. content.append("<message name=\"EspSoapFault\">"
  1302. "<part name=\"parameters\" element=\"tns:Exceptions\"/>"
  1303. "</message>");
  1304. return 0;
  1305. }
  1306. int EspHttpBinding::getWsdlPorts(IEspContext &context, CHttpRequest *request, StringBuffer &content, const char *service, const char *method, bool mda)
  1307. {
  1308. StringBuffer serviceName;
  1309. StringBuffer methName;
  1310. qualifyServiceName(context, service, method, serviceName, &methName);
  1311. content.appendf("<portType name=\"%sServiceSoap\">", serviceName.str());
  1312. bool allMethods = (method==NULL || !*method);
  1313. MethodInfoArray methods;
  1314. getQualifiedNames(context, methods);
  1315. int count=methods.ordinality();
  1316. for (int indx=0; indx<count; indx++)
  1317. {
  1318. CMethodInfo &info = methods.item(indx);
  1319. {
  1320. if (allMethods || !Utils::strcasecmp(method, info.m_label.str()))
  1321. {
  1322. content.appendf("<operation name=\"%s\">", info.m_label.str());
  1323. content.appendf("<input message=\"tns:%sSoapIn\"/>", info.m_label.str());
  1324. content.appendf("<output message=\"tns:%sSoapOut\"/>", info.m_label.str());
  1325. content.appendf("<fault name=\"excfault\" message=\"tns:EspSoapFault\"/>");
  1326. content.append("</operation>");
  1327. if (!allMethods) // no need to continue
  1328. break;
  1329. }
  1330. }
  1331. }
  1332. content.append("</portType>");
  1333. return 0;
  1334. }
  1335. int EspHttpBinding::getWsdlBindings(IEspContext &context, CHttpRequest *request, StringBuffer &content, const char *service, const char *method, bool mda)
  1336. {
  1337. StringBuffer serviceName;
  1338. StringBuffer methName;
  1339. qualifyServiceName(context, service, method, serviceName, &methName);
  1340. content.appendf("<binding name=\"%sServiceSoap\" type=\"tns:%sServiceSoap\">", serviceName.str(), serviceName.str());
  1341. content.append("<soap:binding transport=\"http://schemas.xmlsoap.org/soap/http\" style=\"document\"/>");
  1342. bool allMethods = (method==NULL || !*method);
  1343. MethodInfoArray methods;
  1344. getQualifiedNames(context, methods);
  1345. int count=methods.ordinality();
  1346. for (int indx=0; indx<count; indx++)
  1347. {
  1348. CMethodInfo &info = methods.item(indx);
  1349. {
  1350. if (allMethods || !Utils::strcasecmp(method, info.m_label.str()))
  1351. {
  1352. content.appendf("<operation name=\"%s\">", info.m_label.str());
  1353. //content.appendf("<soap:operation soapAction=\"%s/%s?ver_=%g\" style=\"document\"/>", serviceName.str(), info.m_label.str(), getWsdlVersion());
  1354. content.appendf("<soap:operation soapAction=\"%s/%s?ver_=%g\" style=\"document\"/>", serviceName.str(), info.m_label.str(), context.getClientVersion());
  1355. content.append("<input>");
  1356. if (context.queryOptions()&ESPCTX_WSDL_EXT)
  1357. content.appendf("<soap:header message=\"tns:%sSoapIn\" part=\"security\" use=\"literal\"/>", info.m_label.str());
  1358. content.append("<soap:body use=\"literal\"/></input>");
  1359. content.append("<output><soap:body use=\"literal\"/></output>");
  1360. content.append("<fault name=\"excfault\"><soap:fault name=\"excfault\" use=\"literal\"/></fault>");
  1361. content.append("</operation>");
  1362. if (!allMethods) // no need to continue
  1363. break;
  1364. }
  1365. }
  1366. }
  1367. content.append("</binding>");
  1368. return 0;
  1369. }
  1370. int EspHttpBinding::onGetNotFound(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service)
  1371. {
  1372. HtmlPage page("Enterprise Services Platform");
  1373. page.appendContent(new CHtmlHeader(H1, "Page Not Found"));
  1374. StringBuffer content;
  1375. page.getHtml(content);
  1376. response->setContent(content.length(), content.str());
  1377. response->setContentType("text/html; charset=UTF-8");
  1378. response->setStatus(HTTP_STATUS_NOT_FOUND);
  1379. response->send();
  1380. return 0;
  1381. }
  1382. int EspHttpBinding::onGetException(IEspContext &context, CHttpRequest* request, CHttpResponse* response, IException &e)
  1383. {
  1384. HtmlPage page("Enterprise Services Platform");
  1385. StringBuffer msg("Exception: ");
  1386. msg.appendf(" %d ", e.errorCode());
  1387. e.errorMessage(msg);
  1388. page.appendContent(new CHtmlHeader(H1, msg.str()));
  1389. StringBuffer content;
  1390. page.getHtml(content);
  1391. response->setContent(content.length(), content.str());
  1392. response->setContentType("text/html; charset=UTF-8");
  1393. response->setStatus(HTTP_STATUS_OK);
  1394. response->send();
  1395. return 0;
  1396. }
  1397. int EspHttpBinding::onGetRoot(IEspContext &context, CHttpRequest* request, CHttpResponse* response)
  1398. {
  1399. StringBuffer serviceName;
  1400. getServiceName(serviceName);
  1401. DBGLOG("CWsADLSoapBindingEx::onGetRoot");
  1402. StringBuffer htmlStr;
  1403. htmlStr.appendf(
  1404. "<html>"
  1405. "<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><title>%s</title></head>"
  1406. "<body><p align=\"center\"><font size=\"55\" color=\"#999999\">%s</font></p></body>"
  1407. "</html>", serviceName.str(), serviceName.str());
  1408. response->setContentType(HTTP_TYPE_TEXT_HTML_UTF8);
  1409. response->setContent(htmlStr.length(), (const char*) htmlStr.str());
  1410. response->send();
  1411. return 0;
  1412. }
  1413. int EspHttpBinding::onGetIndex(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service)
  1414. {
  1415. StringBuffer serviceQName;
  1416. StringBuffer methodQName;
  1417. if (!qualifyServiceName(context, service, "", serviceQName, &methodQName))
  1418. {
  1419. return onGetNotFound(context, request, response, service);
  1420. }
  1421. else
  1422. {
  1423. double ver = context.getClientVersion();
  1424. if (ver<=0)
  1425. ver = getWsdlVersion();
  1426. HtmlPage page("Enterprise Services Platform");
  1427. page.appendContent(new CHtmlHeader(H1, serviceQName));
  1428. const char* build_ver = getBuildVersion();
  1429. VStringBuffer build("Version: %g, Build: %s", ver, build_ver);
  1430. page.appendContent(new CHtmlHeader(H2, build));
  1431. StringBuffer urlParams;
  1432. if (!getUrlParams(context.queryRequestParameters(),urlParams))
  1433. urlParams.appendf("%cver_=%g",(urlParams.length()>0) ? '&' : '?', ver);
  1434. StringBuffer wsLink;
  1435. CHtmlList * list=NULL;
  1436. MethodInfoArray methods;
  1437. getQualifiedNames(context, methods);
  1438. if (supportGeneratedForms() || request->queryParameters()->hasProp("list_forms"))
  1439. {
  1440. page.appendContent(new CHtmlText("The following operations are supported:<br/>"));
  1441. //links to the form pages
  1442. list = (CHtmlList *)page.appendContent(new CHtmlList);
  1443. StringBuffer urlFormParams(urlParams);
  1444. urlFormParams.append(urlFormParams.length()>0 ? "&form" : "?form");
  1445. for(int i = 0, tot = methods.length(); i < tot; ++i)
  1446. {
  1447. CMethodInfo &info = methods.item(i);
  1448. {
  1449. wsLink.clear().appendf("%s%s", methods.item(i).m_label.str(),urlFormParams.str());
  1450. list->appendContent(new CHtmlLink(methods.item(i).m_label.str(), wsLink.str()));
  1451. }
  1452. }
  1453. }
  1454. page.appendContent(new CHtmlText("<br/>For a formal definition, please review the "));
  1455. urlParams.append(urlParams.length()>0 ? "&wsdl" : "?wsdl");
  1456. wsLink.clear().appendf("%s", urlParams.str());
  1457. page.appendContent(new CHtmlLink("WSDL Definition", wsLink.str()));
  1458. page.appendContent(new CHtmlText(" for this service, <br/><br/>or the individual WSDL definitions for each operation: "));
  1459. //links to the individual WSDL pages
  1460. list = (CHtmlList *)page.appendContent(new CHtmlList);
  1461. for(int i = 0, tot = methods.length(); i < tot; ++i)
  1462. {
  1463. CMethodInfo &info = methods.item(i);
  1464. {
  1465. wsLink.clear().appendf("%s%s", info.m_label.str(),urlParams.str());
  1466. list->appendContent(new CHtmlLink(info.m_label.str(), wsLink.str()));
  1467. }
  1468. }
  1469. StringBuffer content;
  1470. page.getHtml(content);
  1471. response->setContent(content.length(), content.str());
  1472. response->setContentType("text/html; charset=UTF-8");
  1473. response->setStatus(HTTP_STATUS_OK);
  1474. response->send();
  1475. }
  1476. return 0;
  1477. }
  1478. // Interestingly, only single quote needs to HTML escape.
  1479. // ", <, >, & don't need escape.
  1480. void EspHttpBinding::escapeSingleQuote(StringBuffer& src, StringBuffer& escaped)
  1481. {
  1482. for (const char* p = src.str(); *p!=0; p++)
  1483. {
  1484. if (*p == '\'')
  1485. escaped.append("&apos;");
  1486. else
  1487. escaped.append(*p);
  1488. }
  1489. }
  1490. int EspHttpBinding::onGetXForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1491. {
  1492. StringBuffer serviceQName;
  1493. StringBuffer methodQName;
  1494. if (!qualifyServiceName(context, serv, method, serviceQName, &methodQName))
  1495. {
  1496. return onGetNotFound(context, request, response, serv);
  1497. }
  1498. else
  1499. {
  1500. StringBuffer page;
  1501. IXslProcessor* xslp = getXmlLibXslProcessor();
  1502. // get schema
  1503. StringBuffer schema;
  1504. context.addOptions(ESPCTX_ALL_ANNOTATION);
  1505. getSchema(schema, context, request, serv, method, true);
  1506. //DBGLOG("Schema: %s", schema.str());
  1507. Owned<IXslTransform> xform = xslp->createXslTransform();
  1508. xform->loadXslFromFile(StringBuffer(getCFD()).append("./xslt/gen_form.xsl").str());
  1509. xform->setXmlSource(schema.str(), schema.length()+1);
  1510. // params
  1511. xform->setStringParameter("serviceName", serviceQName);
  1512. StringBuffer version;
  1513. version.appendf("%g",context.getClientVersion());
  1514. xform->setStringParameter("serviceVersion", version);
  1515. StringBuffer methodExt(methodQName);
  1516. if (context.queryRequestParameters()->hasProp("json"))
  1517. methodExt.append(".json");
  1518. xform->setStringParameter("methodName", methodExt);
  1519. // pass params to form (excluding form and __querystring)
  1520. StringBuffer params;
  1521. if (!getUrlParams(context.queryRequestParameters(),params))
  1522. params.appendf("%cver_=%g",(params.length()>0) ? '&' : '?', context.getClientVersion());
  1523. xform->setStringParameter("queryParams", params.str());
  1524. StringBuffer tmp,escaped;
  1525. getMethodHelp(context, serviceQName, methodQName, tmp);
  1526. escapeSingleQuote(tmp,escaped);
  1527. xform->setStringParameter("methodHelp", escaped);
  1528. getMethodDescription(context, serviceQName.str(), methodQName.str(), tmp.clear());
  1529. escapeSingleQuote(tmp,escaped.clear());
  1530. xform->setStringParameter("methodDesc", escaped);
  1531. xform->setParameter("formOptionsAccess", m_formOptions?"1":"0");
  1532. xform->setParameter("includeSoapTest", m_includeSoapTest?"1":"0");
  1533. // set the prop noDefaultValue param
  1534. IProperties* props = context.queryRequestParameters();
  1535. bool formInitialized = false;
  1536. if (props) {
  1537. Owned<IPropertyIterator> it = props->getIterator();
  1538. for (it->first(); it->isValid(); it->next()) {
  1539. const char* key = it->getPropKey();
  1540. if (*key=='.') {
  1541. formInitialized = true;
  1542. break;
  1543. }
  1544. }
  1545. }
  1546. xform->setParameter("noDefaultValue", formInitialized ? "1" : "0");
  1547. MethodInfoArray info;
  1548. getQualifiedNames(context, info);
  1549. for (unsigned i=0; i<info.length(); i++)
  1550. {
  1551. CMethodInfo& m = info.item(i);
  1552. if (stricmp(m.m_label, methodQName)==0)
  1553. {
  1554. xform->setStringParameter("requestLabel", m.m_requestLabel);
  1555. break;
  1556. }
  1557. }
  1558. xform->transform(page);
  1559. response->setContentType("text/html");
  1560. response->setContent(page.str());
  1561. }
  1562. response->send();
  1563. return 0;
  1564. }
  1565. int EspHttpBinding::onGetForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *serv, const char *method)
  1566. {
  1567. StringBuffer serviceQName;
  1568. StringBuffer methodQName;
  1569. if (!qualifyServiceName(context, serv, method, serviceQName, &methodQName))
  1570. {
  1571. return onGetNotFound(context, request, response, serv);
  1572. }
  1573. else
  1574. {
  1575. StringBuffer page;
  1576. page.append(
  1577. "<html>"
  1578. "<head>"
  1579. "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
  1580. "<title>ESP Service form</title>"
  1581. "<script language=\"JavaScript\" src=\"files_/calendar_xs.js\"></script>"
  1582. "<script language=\"JavaScript\" src=\"files_/hint.js\"></script>"
  1583. "</head>"
  1584. "<body>"
  1585. //"<table border=\"0\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" bgcolor=\"#000000\" height=\"108\">"
  1586. // "<tr>"
  1587. // "<td width=\"24%\" height=\"24\" bgcolor=\"#000000\">"
  1588. // "<img border=\"0\" src=\"/");
  1589. // page.append(serviceQName.str());
  1590. // page.append("/files_/logo.gif\" width=\"258\" height=\"108\" />"
  1591. // "</td>"
  1592. // "</tr>"
  1593. // "<tr>"
  1594. // "<td width=\"24%\" height=\"24\" bgcolor=\"#AA0000\">"
  1595. // "<p align=\"center\" />"
  1596. // "<b>"
  1597. // "<font color=\"#FFFFFF\" size=\"5\">Enterprise Services Platform<sup><font size=\"2\">TM</font></sup></font>"
  1598. // "</b>"
  1599. // "</td>"
  1600. // "</tr>"
  1601. //"</table>"
  1602. //"<br />"
  1603. //"<br />"
  1604. "<p align=\"center\" />"
  1605. "<table cellSpacing=\"0\" cellPadding=\"1\" width=\"90%\" bgColor=\"#666666\" border=\"0\">"
  1606. "<tbody>"
  1607. "<tr align=\"middle\" bgColor=\"#666666\">"
  1608. "<td height=\"23\">"
  1609. "<p align=\"left\">"
  1610. "<font color=\"#efefef\">");
  1611. page.appendf("<b>%s [Version %g]</b>", serviceQName.str(), context.getClientVersion());
  1612. page.append("</font>"
  1613. "</p>"
  1614. "</td>"
  1615. "</tr>"
  1616. "<tr bgColor=\"#ffcc66\">"
  1617. "<td height=\"3\">"
  1618. "<p align=\"left\">");
  1619. page.appendf("<b>&gt; %s</b>", methodQName.str());
  1620. page.append("</p>"
  1621. "</td>"
  1622. "</tr>"
  1623. "<TR bgColor=\"#666666\">"
  1624. "<TABLE cellSpacing=\"0\" width=\"90%\" bgColor=\"#efefef\" border=\"0\">"
  1625. "<TBODY>"
  1626. "<TR>"
  1627. "<TD vAlign=\"center\" align=\"left\">"
  1628. "<p align=\"left\"><br />");
  1629. getMethodDescription(context, serviceQName.str(), methodQName.str(), page);
  1630. page.append("<br/></p>"
  1631. "</TD>"
  1632. "</TR>"
  1633. "</TBODY>"
  1634. "</TABLE>"
  1635. "</TR>"
  1636. "<TR bgColor=\"#666666\">"
  1637. "<TABLE cellSpacing=\"0\" width=\"90%\" bgColor=\"#efefef\" border=\"0\">"
  1638. "<TBODY>"
  1639. "<TR>"
  1640. "<TD vAlign=\"center\" align=\"left\">"
  1641. "<p align=\"left\"><br />");
  1642. getMethodHelp(context, serviceQName.str(), methodQName.str(), page);
  1643. page.append("<br/><br/></p>"
  1644. "</TD>"
  1645. "</TR>"
  1646. "</TBODY>"
  1647. "</TABLE>"
  1648. "</TR>"
  1649. "<TR bgColor=\"#666666\">"
  1650. "<TABLE cellSpacing=\"0\" width=\"90%\" bgColor=\"#efefef\" border=\"0\">"
  1651. "<TBODY>"
  1652. "<TR>"
  1653. "<TD vAlign=\"center\" align=\"left\">");
  1654. getMethodHtmlForm(context, request, serviceQName.str(), methodQName.str(), page, true);
  1655. page.append("</TD>"
  1656. "</TR>"
  1657. "</TBODY>"
  1658. "</TABLE>"
  1659. "</TR>"
  1660. "</tbody>"
  1661. "</table>"
  1662. "<BR />"
  1663. "</body>"
  1664. "</html>");
  1665. response->setContent(page.str());
  1666. response->setContentType("text/html");
  1667. }
  1668. response->send();
  1669. return 0;
  1670. }
  1671. int EspHttpBinding::formatHtmlResultSet(IEspContext &context, const char *serv, const char *method, const char *resultsXml, StringBuffer &html)
  1672. {
  1673. Owned<IPropertyTree> ptree = createPTreeFromXMLString(resultsXml);
  1674. Owned<IPropertyTreeIterator> exceptions = ptree->getElements("//Exception");
  1675. ForEach(*exceptions.get())
  1676. {
  1677. IPropertyTree &xcpt = exceptions->query();
  1678. html.appendf("<br/>Exception: <br/>Reported by: %s <br/>Message: %s <br/>",
  1679. xcpt.queryProp("Source"), xcpt.queryProp("Message"));
  1680. }
  1681. Owned<IPropertyTreeIterator> datasets = ptree->getElements("//Dataset");
  1682. ForEach(*datasets.get())
  1683. {
  1684. html.append("<table bordercolor=\"#808080\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" ><tr><td>");
  1685. html.append("<table border=\"1\" bordercolor=\"#808080\" cellspacing=\"0\" cellpadding=\"0\" >");
  1686. IPropertyTree &dset = datasets->query();
  1687. //column headers
  1688. IPropertyTree *row1 = dset.queryPropTree("Row[1]");
  1689. if (row1)
  1690. {
  1691. html.append("<tr><td bgcolor=\"#808080\"><font face=\"Verdana\" size=\"2\">&nbsp;</font></td>");
  1692. Owned<IPropertyTreeIterator> columns = row1->getElements("*");
  1693. ForEach(*columns.get())
  1694. {
  1695. const char *title = columns->query().queryName();
  1696. html.appendf("<td align=\"center\" bgcolor=\"#808080\"><font face=\"Verdana\" size=\"2\" color=\"#E0E0E0\"><b>%s</b></font></td>", (title!=NULL) ? title : "&nbsp;");
  1697. }
  1698. html.append("</tr>");
  1699. }
  1700. Owned<IPropertyTreeIterator> datarows = dset.getElements("Row");
  1701. if (datarows)
  1702. {
  1703. int count=1;
  1704. ForEach(*datarows.get())
  1705. {
  1706. IPropertyTree &row = datarows->query();
  1707. Owned<IPropertyTreeIterator> columns = row.getElements("*");
  1708. html.appendf("<td align=\"center\" bgcolor=\"#808080\"><font face=\"Verdana\" size=\"2\" color=\"#E0E0E0\"><b>%d</b></font></td>", count++);
  1709. ForEach(*columns.get())
  1710. {
  1711. const char *value = columns->query().queryProp(NULL);
  1712. html.appendf("<td align=\"center\" bgcolor=\"#E0E0E0\"><font face=\"Verdana\" size=\"2\" color=\"#000000\">%s</font></td>", (value!=NULL) ? value : "&nbsp;");
  1713. }
  1714. html.append("</tr>");
  1715. }
  1716. }
  1717. html.append("</td></tr></table></table>");
  1718. }
  1719. return 0;
  1720. }
  1721. int EspHttpBinding::formatResultsPage(IEspContext &context, const char *serv, const char *method, StringBuffer &results, StringBuffer &page)
  1722. {
  1723. StringBuffer serviceQName;
  1724. StringBuffer methodQName;
  1725. qualifyServiceName(context, serv, method, serviceQName, &methodQName);
  1726. page.append(
  1727. "<html>"
  1728. "<head>"
  1729. "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
  1730. "<title>ESP Service form</title>"
  1731. "</head>"
  1732. "<body>"
  1733. //"<table border=\"0\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" bgcolor=\"#000000\" height=\"108\">"
  1734. // "<tr>"
  1735. // "<td width=\"24%\" height=\"24\" bgcolor=\"#000000\">"
  1736. // "<img border=\"0\" src=\"/");
  1737. // page.append(serviceQName.str());
  1738. // page.append("/files_/logo.gif\" width=\"258\" height=\"108\" />"
  1739. // "</td>"
  1740. // "</tr>"
  1741. // "<tr>"
  1742. // "<td width=\"24%\" height=\"24\" bgcolor=\"#AA0000\">"
  1743. // "<p align=\"center\" />"
  1744. // "<b>"
  1745. // "<font color=\"#FFFFFF\" size=\"5\">Enterprise Services Platform<sup><font size=\"2\">TM</font></sup></font>"
  1746. // "</b>"
  1747. // "</td>"
  1748. // "</tr>"
  1749. //"</table>"
  1750. //"<br />"
  1751. //"<br />"
  1752. "<p align=\"center\" />"
  1753. "<table cellSpacing=\"0\" cellPadding=\"1\" width=\"90%\" bgColor=\"#666666\" border=\"0\">"
  1754. "<tbody>"
  1755. "<tr align=\"middle\" bgColor=\"#666666\">"
  1756. "<td height=\"23\">"
  1757. "<p align=\"left\">"
  1758. "<font color=\"#efefef\">");
  1759. page.appendf("<b>%s</b>", serviceQName.str());
  1760. page.append("</font>"
  1761. "</p>"
  1762. "</td>"
  1763. "</tr>"
  1764. "<tr bgColor=\"#ffcc66\">"
  1765. "<td height=\"3\">"
  1766. "<p align=\"left\">");
  1767. page.appendf("<b>&gt; %s Results</b>", methodQName.str());
  1768. page.append("</p>"
  1769. "</td>"
  1770. "</tr>"
  1771. "<TR bgColor=\"#666666\">"
  1772. "<TABLE cellSpacing=\"0\" width=\"90%\" bgColor=\"#efefef\" border=\"0\">"
  1773. "<TBODY>"
  1774. "<TR>"
  1775. "<TD vAlign=\"center\" align=\"left\">");
  1776. page.append(results);
  1777. page.append("</TD>"
  1778. "</TR>"
  1779. "</TBODY>"
  1780. "</TABLE>"
  1781. "</TR>"
  1782. "</tbody>"
  1783. "</table>"
  1784. "<BR />"
  1785. "</body>"
  1786. "</html>");
  1787. return 0;
  1788. }
  1789. bool EspHttpBinding::setContentFromFile(IEspContext &context, CHttpResponse &resp, const char *filepath)
  1790. {
  1791. StringBuffer mimetype, etag, lastModified;
  1792. MemoryBuffer content;
  1793. bool modified = false;
  1794. if (httpContentFromFile(filepath, mimetype, content, modified, lastModified, etag))
  1795. {
  1796. resp.setContent(content.length(), content.toByteArray());
  1797. resp.setContentType(mimetype.str());
  1798. return true;
  1799. }
  1800. return false;
  1801. }
  1802. void EspHttpBinding::onBeforeSendResponse(IEspContext& context, CHttpRequest* request, MemoryBuffer& content,
  1803. const char *serviceName, const char* methodName)
  1804. {
  1805. IProperties* params = request->queryParameters();
  1806. if (params->hasProp("esp_validate"))
  1807. validateResponse(context, request, content, serviceName, methodName);
  1808. else if (params->hasProp("esp_sort_result"))
  1809. sortResponse(context, request, content, serviceName, methodName);
  1810. }
  1811. void EspHttpBinding::validateResponse(IEspContext& context, CHttpRequest* request, MemoryBuffer& content,
  1812. const char *service, const char* method)
  1813. {
  1814. StringBuffer serviceQName;
  1815. StringBuffer methodQName;
  1816. if (!qualifyServiceName(context, service, method, serviceQName, &methodQName))
  1817. return;
  1818. StringBuffer xml,xsd;
  1819. // name space
  1820. StringBuffer ns;
  1821. generateNamespace(context, request, serviceQName.str(), methodQName.str(), ns);
  1822. // XML
  1823. Owned<IPropertyTree> tree;
  1824. try {
  1825. tree.setown(createPTreeFromXMLString(content.length(), content.toByteArray()));
  1826. // format it for better error message
  1827. toXML(tree, xml, 1);
  1828. //Hack: add default name space to XML
  1829. const char* end = strstr(xml, "<?xml");
  1830. if (end)
  1831. {
  1832. end = strstr(end+5, "?>");
  1833. if (end)
  1834. end = strchr(end+2, '>');
  1835. if (!end)
  1836. throw MakeStringException(-1,"Invalid response XML in processing instruction");
  1837. }
  1838. else
  1839. {
  1840. end = strchr(xml, '>');
  1841. if (!end)
  1842. throw MakeStringException(-1,"Can not find the root node");
  1843. if (*(end-1)=='/') // root is like: <xxx />
  1844. end--;
  1845. }
  1846. int offset = end ? end-xml.str() : 0;
  1847. StringBuffer tag(offset, xml.str());
  1848. if (!strstr(tag, " xmlns="))
  1849. {
  1850. StringBuffer defNS;
  1851. defNS.appendf(" xmlns=\"%s\"", ns.str());
  1852. xml.insert(offset, defNS.str());
  1853. }
  1854. } catch (IException* e) {
  1855. StringBuffer msg;
  1856. DBGLOG("Unexpected error: parsing XML: %s", e->errorMessage(msg).str());
  1857. }
  1858. // schema
  1859. getSchema(xsd,context,request,serviceQName,methodQName,true);
  1860. // validation
  1861. if (getEspLogLevel()>LogMax)
  1862. DBGLOG("[VALIDATE] xml: %s\nxsd: %s\nns: %s",xml.str(), xsd.str(), ns.str());
  1863. IXmlValidator* v = getXmlLibXmlValidator();
  1864. v->setXmlSource(xml, strlen(xml));
  1865. v->setSchemaSource(xsd, strlen(xsd));
  1866. v->setTargetNamespace(ns);
  1867. Owned<IPropertyTree> result = createPTree("ModifiedResponse");
  1868. IPropertyTree* e = createPTree();
  1869. try {
  1870. v->validate();
  1871. e->appendProp("Result", "No error found");
  1872. } catch (IMultiException* me) {
  1873. for (unsigned i=0; i<me->ordinality(); i++)
  1874. {
  1875. IException& item = me->item(i);
  1876. StringBuffer s;
  1877. e->addProp("Error", item.errorMessage(s).str());
  1878. }
  1879. me->Release();
  1880. }
  1881. result->addPropTree("SchemaValidation", e);
  1882. result->addPropTree("OriginalResponse", LINK(tree));
  1883. StringBuffer temp;
  1884. toXML(result,temp,1);
  1885. unsigned len = temp.length(); // This has to be done before temp.detach is called!
  1886. content.setBuffer(len, temp.detach(), true);
  1887. }
  1888. void EspHttpBinding::sortResponse(IEspContext& context, CHttpRequest* request, MemoryBuffer& content,
  1889. const char *serviceName, const char* methodName)
  1890. {
  1891. ESPLOG(LogNormal,"Sorting Response XML...");
  1892. try {
  1893. StringBuffer result;
  1894. StringBuffer respXML(content.length(), content.toByteArray());
  1895. //sorting
  1896. IXslProcessor* xslp = getXmlLibXslProcessor();
  1897. Owned<IXslTransform> xform = xslp->createXslTransform();
  1898. xform->loadXslFromFile(StringBuffer(getCFD()).append("./xslt/dict_sort.xsl").str());
  1899. xform->setXmlSource(respXML,respXML.length());
  1900. xform->transform(result);
  1901. if (getEspLogLevel()>LogNormal)
  1902. DBGLOG("XML sorted: %s", result.str());
  1903. unsigned len = result.length();
  1904. content.setBuffer(len, result.detach(), true);
  1905. } catch (IException* e) {
  1906. StringBuffer msg;
  1907. DBGLOG("Unexpected error: parsing XML: %s", e->errorMessage(msg).str());
  1908. }
  1909. }