httpservice.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  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. #include "espcontext.hpp"
  22. #include "esphttp.hpp"
  23. //ESP Bindings
  24. #include "http/platform/httpsecurecontext.hpp"
  25. #include "http/platform/httpservice.hpp"
  26. #include "http/platform/httptransport.hpp"
  27. #include "htmlpage.hpp"
  28. /***************************************************************************
  29. * CEspHttpServer Implementation
  30. ***************************************************************************/
  31. CEspHttpServer::CEspHttpServer(ISocket& sock, bool viewConfig, int maxRequestEntityLength):m_socket(sock), m_MaxRequestEntityLength(maxRequestEntityLength)
  32. {
  33. m_request.setown(new CHttpRequest(sock));
  34. IEspContext* ctx = createEspContext(createHttpSecureContext(m_request.get()));
  35. m_request->setMaxRequestEntityLength(maxRequestEntityLength);
  36. m_response.setown(new CHttpResponse(sock));
  37. m_request->setOwnContext(ctx);
  38. m_response->setOwnContext(LINK(ctx));
  39. m_viewConfig=viewConfig;
  40. }
  41. CEspHttpServer::CEspHttpServer(ISocket& sock, CEspApplicationPort* apport, bool viewConfig, int maxRequestEntityLength):m_socket(sock), m_MaxRequestEntityLength(maxRequestEntityLength)
  42. {
  43. m_request.setown(new CHttpRequest(sock));
  44. IEspContext* ctx = createEspContext(createHttpSecureContext(m_request.get()));
  45. m_request->setMaxRequestEntityLength(maxRequestEntityLength);
  46. m_response.setown(new CHttpResponse(sock));
  47. m_request->setOwnContext(ctx);
  48. m_response->setOwnContext(LINK(ctx));
  49. m_apport = apport;
  50. if (apport->getDefaultBinding())
  51. m_defaultBinding.set(apport->getDefaultBinding()->queryBinding());
  52. m_viewConfig=viewConfig;
  53. }
  54. CEspHttpServer::~CEspHttpServer()
  55. {
  56. try
  57. {
  58. IEspContext* ctx = m_request->queryContext();
  59. if (ctx)
  60. {
  61. //Request is logged only when there is an exception or the request time is very long.
  62. //If the flag of 'EspLogRequests' is set or the log level > LogNormal, the Request should
  63. //has been logged and it should not be logged here.
  64. ctx->setProcessingTime();
  65. if ((ctx->queryHasException() || (ctx->queryProcessingTime() > getSlowProcessingTime())) &&
  66. !getEspLogRequests() && (getEspLogLevel() <= LogNormal))
  67. {
  68. StringBuffer logStr;
  69. logStr.appendf("%s %s", m_request->queryMethod(), m_request->queryPath());
  70. const char* paramStr = m_request->queryParamStr();
  71. if (paramStr && *paramStr)
  72. logStr.appendf("?%s", paramStr);
  73. DBGLOG("Request[%s]", logStr.str());
  74. if (m_request->isSoapMessage())
  75. {
  76. StringBuffer requestStr;
  77. m_request->getContent(requestStr);
  78. if (requestStr.length())
  79. m_request->logSOAPMessage(requestStr.str(), NULL);
  80. }
  81. }
  82. }
  83. m_request.clear();
  84. m_response.clear();
  85. }
  86. catch (...)
  87. {
  88. ERRLOG("In CEspHttpServer::~CEspHttpServer() -- Unknown Exception.");
  89. }
  90. }
  91. typedef enum espAuthState_
  92. {
  93. authUnknown,
  94. authRequired,
  95. authProvided,
  96. authSucceeded,
  97. authPending,
  98. authFailed
  99. } EspAuthState;
  100. bool CEspHttpServer::rootAuth(IEspContext* ctx)
  101. {
  102. if (!m_apport->rootAuthRequired())
  103. return true;
  104. bool ret=false;
  105. EspHttpBinding* thebinding=getBinding();
  106. if (thebinding)
  107. {
  108. thebinding->populateRequest(m_request.get());
  109. if(!thebinding->authRequired(m_request.get()) || thebinding->doAuth(ctx))
  110. ret=true;
  111. else
  112. {
  113. ISecUser *user = ctx->queryUser();
  114. if (user && user->getAuthenticateStatus() == AS_PASSWORD_VALID_BUT_EXPIRED)
  115. {
  116. m_response->redirect(*m_request.get(), "/esp/updatepasswordinput");
  117. ret = true;
  118. }
  119. else
  120. {
  121. DBGLOG("User authentication required");
  122. m_response->sendBasicChallenge(thebinding->getChallengeRealm(), true);
  123. }
  124. }
  125. }
  126. return ret;
  127. }
  128. const char* getSubServiceDesc(sub_service stype)
  129. {
  130. #define DEF_CASE(s) case s: return #s;
  131. switch(stype)
  132. {
  133. DEF_CASE(sub_serv_unknown)
  134. DEF_CASE(sub_serv_root)
  135. DEF_CASE(sub_serv_main)
  136. DEF_CASE(sub_serv_service)
  137. DEF_CASE(sub_serv_method)
  138. DEF_CASE(sub_serv_files)
  139. DEF_CASE(sub_serv_itext)
  140. DEF_CASE(sub_serv_iframe)
  141. DEF_CASE(sub_serv_content)
  142. DEF_CASE(sub_serv_result)
  143. DEF_CASE(sub_serv_index)
  144. DEF_CASE(sub_serv_index_redirect)
  145. DEF_CASE(sub_serv_form)
  146. DEF_CASE(sub_serv_xform)
  147. DEF_CASE(sub_serv_query)
  148. DEF_CASE(sub_serv_instant_query)
  149. DEF_CASE(sub_serv_soap_builder)
  150. DEF_CASE(sub_serv_wsdl)
  151. DEF_CASE(sub_serv_xsd)
  152. DEF_CASE(sub_serv_config)
  153. DEF_CASE(sub_serv_getversion)
  154. DEF_CASE(sub_serv_reqsamplexml)
  155. DEF_CASE(sub_serv_respsamplexml)
  156. DEF_CASE(sub_serv_file_upload)
  157. default: return "invalid-type";
  158. }
  159. }
  160. static bool authenticateOptionalFailed(IEspContext& ctx, IEspHttpBinding* binding)
  161. {
  162. #ifdef ENABLE_NEW_SECURITY
  163. if (ctx.queryRequestParameters()->hasProp("internal"))
  164. {
  165. ISecUser* user = ctx.queryUser();
  166. if(!user || user->getStatus()==SecUserStatus_Inhouse || user->getStatus()==SecUserStatus_Unknown)
  167. return false;
  168. ERRLOG("User %s trying to access unauthorized feature: internal", user->getName() ? user->getName() : ctx.queryUserId());
  169. return true;
  170. }
  171. // TODO: handle binding specific optionals
  172. #elif !defined(DISABLE_NEW_SECURITY)
  173. #error Please include esphttp.hpp in this file.
  174. #endif
  175. return false;
  176. }
  177. EspHttpBinding* CEspHttpServer::getBinding()
  178. {
  179. EspHttpBinding* thebinding=NULL;
  180. int ordinality=m_apport->getBindingCount();
  181. if (ordinality==1)
  182. {
  183. CEspBindingEntry *entry = m_apport->queryBindingItem(0);
  184. thebinding = (entry) ? dynamic_cast<EspHttpBinding*>(entry->queryBinding()) : NULL;
  185. }
  186. else if (m_defaultBinding)
  187. thebinding=dynamic_cast<EspHttpBinding*>(m_defaultBinding.get());
  188. return thebinding;
  189. }
  190. //CORS allow headers for interoperability, we do not rely on this for security since
  191. //that only means treating the browser as a trusted entity. We need to be diligent and secure
  192. //for every request whether it comes from a cross domain browser or any other source
  193. void checkSetCORSAllowOrigin(CHttpRequest *req, CHttpResponse *resp)
  194. {
  195. StringBuffer origin;
  196. req->getHeader("Origin", origin);
  197. if (origin.length())
  198. resp->setHeader("Access-Control-Allow-Origin", "*");
  199. }
  200. int CEspHttpServer::processRequest()
  201. {
  202. try
  203. {
  204. if (m_request->receive(NULL)==-1) // MORE - pass in IMultiException if we want to see exceptions (which are not fatal)
  205. return -1;
  206. }
  207. catch(IEspHttpException* e)
  208. {
  209. m_response->sendException(e);
  210. e->Release();
  211. return 0;
  212. }
  213. catch (IException *e)
  214. {
  215. DBGLOG(e);
  216. e->Release();
  217. return 0;
  218. }
  219. catch (...)
  220. {
  221. DBGLOG("Unknown Exception - reading request [CEspHttpServer::processRequest()]");
  222. return 0;
  223. }
  224. try
  225. {
  226. EspHttpBinding* thebinding=NULL;
  227. StringBuffer method;
  228. m_request->getMethod(method);
  229. EspAuthState authState=authUnknown;
  230. sub_service stype=sub_serv_unknown;
  231. StringBuffer pathEx;
  232. StringBuffer serviceName;
  233. StringBuffer methodName;
  234. m_request->getEspPathInfo(stype, &pathEx, &serviceName, &methodName, false);
  235. ESPLOG(LogNormal,"sub service type: %s. parm: %s", getSubServiceDesc(stype), m_request->queryParamStr());
  236. m_request->updateContext();
  237. IEspContext* ctx = m_request->queryContext();
  238. ctx->setServiceName(serviceName.str());
  239. ctx->setHTTPMethod(method.str());
  240. ctx->setServiceMethod(methodName.str());
  241. bool isSoapPost=(stricmp(method.str(), POST_METHOD) == 0 && m_request->isSoapMessage());
  242. if (!isSoapPost)
  243. {
  244. StringBuffer peerStr, pathStr;
  245. const char *userid=ctx->queryUserId();
  246. DBGLOG("%s %s, from %s@%s", method.str(), m_request->getPath(pathStr).str(), (userid) ? userid : "unknown", m_request->getPeer(peerStr).str());
  247. if (m_apport->rootAuthRequired() && (!ctx->queryUserId() || !*ctx->queryUserId()))
  248. {
  249. thebinding = dynamic_cast<EspHttpBinding*>(m_defaultBinding.get());
  250. StringBuffer realmbuf;
  251. if(thebinding)
  252. {
  253. realmbuf.append(thebinding->getChallengeRealm());
  254. }
  255. if(realmbuf.length() == 0)
  256. realmbuf.append("ESP");
  257. DBGLOG("User authentication required");
  258. m_response->sendBasicChallenge(realmbuf.str(), true);
  259. return 0;
  260. }
  261. }
  262. if (!stricmp(method.str(), GET_METHOD))
  263. {
  264. if (stype==sub_serv_root)
  265. {
  266. if (!rootAuth(ctx))
  267. return 0;
  268. if (ctx->queryUser() && (ctx->queryUser()->getAuthenticateStatus() == AS_PASSWORD_VALID_BUT_EXPIRED))
  269. return 0;//allow user to change password
  270. // authenticate optional groups
  271. if (authenticateOptionalFailed(*ctx,NULL))
  272. throw createEspHttpException(401,"Unauthorized Access","Unauthorized Access");
  273. return onGetApplicationFrame(m_request.get(), m_response.get(), ctx);
  274. }
  275. if (!stricmp(serviceName.str(), "esp"))
  276. {
  277. if (!methodName.length())
  278. return 0;
  279. #ifdef _USE_OPENLDAP
  280. if (strieq(methodName.str(), "updatepasswordinput"))//process before authentication check
  281. return onUpdatePasswordInput(m_request.get(), m_response.get());
  282. #endif
  283. if (!rootAuth(ctx) )
  284. return 0;
  285. checkSetCORSAllowOrigin(m_request, m_response);
  286. if (methodName.charAt(methodName.length()-1)=='_')
  287. methodName.setCharAt(methodName.length()-1, 0);
  288. if (!stricmp(methodName.str(), "files"))
  289. {
  290. if (!getTxSummaryResourceReq())
  291. ctx->cancelTxSummary();
  292. checkInitEclIdeResponse(m_request, m_response);
  293. return onGetFile(m_request.get(), m_response.get(), pathEx.str());
  294. }
  295. else if (!stricmp(methodName.str(), "xslt"))
  296. {
  297. if (!getTxSummaryResourceReq())
  298. ctx->cancelTxSummary();
  299. return onGetXslt(m_request.get(), m_response.get(), pathEx.str());
  300. }
  301. else if (!stricmp(methodName.str(), "body"))
  302. return onGetMainWindow(m_request.get(), m_response.get());
  303. else if (!stricmp(methodName.str(), "frame"))
  304. return onGetApplicationFrame(m_request.get(), m_response.get(), ctx);
  305. else if (!stricmp(methodName.str(), "titlebar"))
  306. return onGetTitleBar(m_request.get(), m_response.get());
  307. else if (!stricmp(methodName.str(), "nav"))
  308. return onGetNavWindow(m_request.get(), m_response.get());
  309. else if (!stricmp(methodName.str(), "navdata"))
  310. return onGetDynNavData(m_request.get(), m_response.get());
  311. else if (!stricmp(methodName.str(), "navmenuevent"))
  312. return onGetNavEvent(m_request.get(), m_response.get());
  313. else if (!stricmp(methodName.str(), "soapreq"))
  314. return onGetBuildSoapRequest(m_request.get(), m_response.get());
  315. }
  316. }
  317. #ifdef _USE_OPENLDAP
  318. else if (strieq(method.str(), POST_METHOD) && strieq(serviceName.str(), "esp") && (methodName.length() > 0) && strieq(methodName.str(), "updatepassword"))
  319. {
  320. EspHttpBinding* thebinding = getBinding();
  321. if (thebinding)
  322. thebinding->populateRequest(m_request.get());
  323. return onUpdatePassword(m_request.get(), m_response.get());
  324. }
  325. #endif
  326. if(m_apport != NULL)
  327. {
  328. int ordinality=m_apport->getBindingCount();
  329. bool isSubService = false;
  330. if (ordinality>0)
  331. {
  332. if (ordinality==1)
  333. {
  334. CEspBindingEntry *entry = m_apport->queryBindingItem(0);
  335. thebinding = (entry) ? dynamic_cast<EspHttpBinding*>(entry->queryBinding()) : NULL;
  336. if (thebinding && !isSoapPost && !thebinding->isValidServiceName(*ctx, serviceName.str()))
  337. thebinding=NULL;
  338. }
  339. else
  340. {
  341. EspHttpBinding* lbind=NULL;
  342. for(int index=0; !thebinding && index<ordinality; index++)
  343. {
  344. CEspBindingEntry *entry = m_apport->queryBindingItem(index);
  345. lbind = (entry) ? dynamic_cast<EspHttpBinding*>(entry->queryBinding()) : NULL;
  346. if (lbind)
  347. {
  348. if (!thebinding && lbind->isValidServiceName(*ctx, serviceName.str()))
  349. {
  350. thebinding=lbind;
  351. StringBuffer bindSvcName;
  352. if (stricmp(serviceName, lbind->getServiceName(bindSvcName)))
  353. isSubService = true;
  354. }
  355. }
  356. }
  357. }
  358. if (!thebinding && m_defaultBinding)
  359. thebinding=dynamic_cast<EspHttpBinding*>(m_defaultBinding.get());
  360. if (thebinding)
  361. {
  362. StringBuffer servName(ctx->queryServiceName(NULL));
  363. if (!servName.length())
  364. {
  365. thebinding->getServiceName(servName);
  366. ctx->setServiceName(servName.str());
  367. }
  368. thebinding->populateRequest(m_request.get());
  369. if(thebinding->authRequired(m_request.get()) && !thebinding->doAuth(ctx))
  370. {
  371. authState=authRequired;
  372. if(isSoapPost)
  373. {
  374. authState = authPending;
  375. ctx->setToBeAuthenticated(true);
  376. }
  377. }
  378. else
  379. authState = authSucceeded;
  380. }
  381. }
  382. if (authState==authRequired)
  383. {
  384. ISecUser *user = ctx->queryUser();
  385. if (user && (user->getAuthenticateStatus() == AS_PASSWORD_EXPIRED || user->getAuthenticateStatus() == AS_PASSWORD_VALID_BUT_EXPIRED))
  386. {
  387. DBGLOG("ESP password expired for %s", user->getName());
  388. m_response->setContentType(HTTP_TYPE_TEXT_PLAIN);
  389. m_response->setContent("Your ESP password has expired");
  390. m_response->send();
  391. }
  392. else
  393. {
  394. DBGLOG("User authentication required");
  395. StringBuffer realmbuf;
  396. if(thebinding)
  397. realmbuf.append(thebinding->getChallengeRealm());
  398. if(realmbuf.length() == 0)
  399. realmbuf.append("ESP");
  400. m_response->sendBasicChallenge(realmbuf.str(), !isSoapPost);
  401. }
  402. return 0;
  403. }
  404. // authenticate optional groups
  405. if (authenticateOptionalFailed(*ctx,thebinding))
  406. throw createEspHttpException(401,"Unauthorized Access","Unauthorized Access");
  407. if(strieq(method.str(), OPTIONS_METHOD))
  408. return onOptions();
  409. checkSetCORSAllowOrigin(m_request, m_response);
  410. if (thebinding!=NULL)
  411. {
  412. if(stricmp(method.str(), POST_METHOD)==0)
  413. thebinding->handleHttpPost(m_request.get(), m_response.get());
  414. else if(!stricmp(method.str(), GET_METHOD))
  415. {
  416. if (stype==sub_serv_index_redirect)
  417. {
  418. StringBuffer url;
  419. if (isSubService)
  420. {
  421. StringBuffer qSvcName;
  422. thebinding->qualifySubServiceName(*ctx,serviceName,NULL, qSvcName, NULL);
  423. url.append(qSvcName);
  424. }
  425. else
  426. thebinding->getServiceName(url);
  427. url.append('/');
  428. const char* parms = m_request->queryParamStr();
  429. if (parms && *parms)
  430. url.append('?').append(parms);
  431. m_response->redirect(*m_request.get(),url);
  432. }
  433. else
  434. thebinding->onGet(m_request.get(), m_response.get());
  435. }
  436. else
  437. unsupported();
  438. }
  439. else
  440. {
  441. if(!stricmp(method.str(), POST_METHOD))
  442. onPost();
  443. else if(!stricmp(method.str(), GET_METHOD))
  444. onGet();
  445. else
  446. unsupported();
  447. }
  448. ctx->addTraceSummaryTimeStamp(LogMin, "handleHttp");
  449. }
  450. }
  451. catch(IEspHttpException* e)
  452. {
  453. m_response->sendException(e);
  454. e->Release();
  455. return 0;
  456. }
  457. catch (IException *e)
  458. {
  459. DBGLOG(e);
  460. e->Release();
  461. return 0;
  462. }
  463. catch (...)
  464. {
  465. StringBuffer content_type;
  466. __int64 len = m_request->getContentLength();
  467. DBGLOG("Unknown Exception - processing request");
  468. DBGLOG("METHOD: %s, PATH: %s, TYPE: %s, CONTENT-LENGTH: %" I64F "d", m_request->queryMethod(), m_request->queryPath(), m_request->getContentType(content_type).str(), len);
  469. if (len > 0)
  470. m_request->logMessage(LOGCONTENT, "HTTP request content received:\n");
  471. return 0;
  472. }
  473. return 0;
  474. }
  475. int CEspHttpServer::onGetApplicationFrame(CHttpRequest* request, CHttpResponse* response, IEspContext* ctx)
  476. {
  477. time_t modtime = 0;
  478. IProperties *params = request->queryParameters();
  479. const char *inner=(params)?params->queryProp("inner") : NULL;
  480. StringBuffer ifmodifiedsince;
  481. request->getHeader("If-Modified-Since", ifmodifiedsince);
  482. if (inner&&*inner&&ifmodifiedsince.length())
  483. {
  484. response->setStatus(HTTP_STATUS_NOT_MODIFIED);
  485. response->send();
  486. }
  487. else
  488. {
  489. CEspBindingEntry* entry = m_apport->getDefaultBinding();
  490. if(entry)
  491. {
  492. EspHttpBinding *httpbind = dynamic_cast<EspHttpBinding *>(entry->queryBinding());
  493. if(httpbind)
  494. {
  495. const char *page = httpbind->getRootPage(ctx);
  496. if(page && *page)
  497. return onGetFile(request, response, page);
  498. }
  499. }
  500. StringBuffer html;
  501. m_apport->getAppFrameHtml(modtime, inner, html, ctx);
  502. response->setContent(html.length(), html.str());
  503. response->setContentType("text/html; charset=UTF-8");
  504. response->setStatus(HTTP_STATUS_OK);
  505. const char *timestr=ctime(&modtime);
  506. response->addHeader("Last-Modified", timestr);
  507. response->send();
  508. }
  509. return 0;
  510. }
  511. int CEspHttpServer::onGetTitleBar(CHttpRequest* request, CHttpResponse* response)
  512. {
  513. bool rawXml = request->queryParameters()->hasProp("rawxml_");
  514. StringBuffer m_headerHtml(m_apport->getTitleBarHtml(*request->queryContext(), rawXml));
  515. response->setContent(m_headerHtml.length(), m_headerHtml.str());
  516. response->setContentType(rawXml ? HTTP_TYPE_APPLICATION_XML_UTF8 : "text/html; charset=UTF-8");
  517. response->setStatus(HTTP_STATUS_OK);
  518. response->send();
  519. return 0;
  520. }
  521. int CEspHttpServer::onGetNavWindow(CHttpRequest* request, CHttpResponse* response)
  522. {
  523. StringBuffer navContent;
  524. StringBuffer navContentType;
  525. m_apport->getNavBarContent(*request->queryContext(), navContent, navContentType, request->queryParameters()->hasProp("rawxml_"));
  526. response->setContent(navContent.length(), navContent.str());
  527. response->setContentType(navContentType.str());
  528. response->setStatus(HTTP_STATUS_OK);
  529. response->send();
  530. return 0;
  531. }
  532. int CEspHttpServer::onGetDynNavData(CHttpRequest* request, CHttpResponse* response)
  533. {
  534. StringBuffer navContent;
  535. StringBuffer navContentType;
  536. bool bVolatile;
  537. m_apport->getDynNavData(*request->queryContext(), request->queryParameters(), navContent, navContentType, bVolatile);
  538. if (bVolatile)
  539. response->addHeader("Cache-control", "max-age=0");
  540. response->setContent(navContent.length(), navContent.str());
  541. response->setContentType(navContentType.str());
  542. response->setStatus(HTTP_STATUS_OK);
  543. response->send();
  544. return 0;
  545. }
  546. int CEspHttpServer::onGetNavEvent(CHttpRequest* request, CHttpResponse* response)
  547. {
  548. m_apport->onGetNavEvent(*request->queryContext(), request, response);
  549. return 0;
  550. }
  551. int CEspHttpServer::onGetBuildSoapRequest(CHttpRequest* request, CHttpResponse* response)
  552. {
  553. m_apport->onBuildSoapRequest(*request->queryContext(), request, response);
  554. return 0;
  555. }
  556. #ifdef _USE_OPENLDAP
  557. int CEspHttpServer::onUpdatePasswordInput(CHttpRequest* request, CHttpResponse* response)
  558. {
  559. StringBuffer html;
  560. m_apport->onUpdatePasswordInput(*request->queryContext(), html);
  561. response->setContent(html.length(), html.str());
  562. response->setContentType("text/html; charset=UTF-8");
  563. response->setStatus(HTTP_STATUS_OK);
  564. response->send();
  565. return 0;
  566. }
  567. int CEspHttpServer::onUpdatePassword(CHttpRequest* request, CHttpResponse* response)
  568. {
  569. StringBuffer html;
  570. m_apport->onUpdatePassword(*request->queryContext(), request, html);
  571. response->setContent(html.length(), html.str());
  572. response->setContentType("text/html; charset=UTF-8");
  573. response->setStatus(HTTP_STATUS_OK);
  574. response->send();
  575. return 0;
  576. }
  577. #endif
  578. int CEspHttpServer::onGetMainWindow(CHttpRequest* request, CHttpResponse* response)
  579. {
  580. StringBuffer url("../?main");
  581. double ver = request->queryContext()->getClientVersion();
  582. if (ver>0)
  583. url.appendf("&ver_=%g", ver);
  584. response->redirect(*request, url);
  585. return 0;
  586. }
  587. inline void make_env_var(StringArray &env, StringBuffer &var, const char *name, const char *value)
  588. {
  589. env.append(var.clear().append(name).append('=').append(value).str());
  590. }
  591. inline void make_env_var(StringArray &env, StringBuffer &var, const char *name, const StringBuffer &value)
  592. {
  593. env.append(var.clear().append(name).append('=').append(value).str());
  594. }
  595. inline void make_env_var(StringArray &env, StringBuffer &var, const char *name, __int64 value)
  596. {
  597. env.append(var.clear().append(name).append('=').append(value).str());
  598. }
  599. bool skipHeader(const char *name)
  600. {
  601. if (!stricmp(name, "CONTENT_LENGTH"))
  602. return true;
  603. else if (!strcmp(name, "AUTHORIZATION"))
  604. return true;
  605. else if (!strcmp(name, "CONTENT_TYPE"))
  606. return true;
  607. return false;
  608. }
  609. static void httpGetFile(CHttpRequest* request, CHttpResponse* response, const char *urlpath, const char *filepath)
  610. {
  611. StringBuffer mimetype, etag, lastModified;
  612. MemoryBuffer content;
  613. bool modified = true;
  614. request->getHeader("If-None-Match", etag);
  615. request->getHeader("If-Modified-Since", lastModified);
  616. if (httpContentFromFile(filepath, mimetype, content, modified, lastModified, etag))
  617. {
  618. response->CheckModifiedHTTPContent(modified, lastModified.str(), etag.str(), mimetype.str(), content);
  619. }
  620. else
  621. {
  622. DBGLOG("Get File %s: file not found", filepath);
  623. response->setStatus(HTTP_STATUS_NOT_FOUND);
  624. }
  625. response->send();
  626. }
  627. static void httpGetDirectory(CHttpRequest* request, CHttpResponse* response, const char *urlpath, const char *dirpath, bool top, const StringBuffer &tail)
  628. {
  629. Owned<IPropertyTree> tree = createPTree("directory", ipt_none);
  630. tree->setProp("@path", urlpath);
  631. Owned<IDirectoryIterator> dir = createDirectoryIterator(dirpath, NULL);
  632. ForEach(*dir)
  633. {
  634. IPropertyTree *entry = tree->addPropTree(dir->isDir() ? "directory" : "file", createPTree(ipt_none));
  635. StringBuffer s;
  636. entry->setProp("name", dir->getName(s));
  637. if (!dir->isDir())
  638. entry->setPropInt64("size", dir->getFileSize());
  639. CDateTime cdt;
  640. dir->getModifiedTime(cdt);
  641. entry->setProp("modified", cdt.getString(s.clear(), false));
  642. }
  643. const char *fmt = request->queryParameters()->queryProp("format");
  644. StringBuffer out;
  645. StringBuffer contentType;
  646. if (!fmt || strieq(fmt,"html"))
  647. {
  648. contentType.set("text/html");
  649. out.append("<!DOCTYPE html><html><body>");
  650. if (!top)
  651. out.appendf("<a href='%s'>..</a><br/>", tail.length() ? "." : "..");
  652. Owned<IPropertyTreeIterator> it = tree->getElements("*");
  653. ForEach(*it)
  654. {
  655. IPropertyTree &e = it->query();
  656. const char *href=e.queryProp("name");
  657. if (tail.length())
  658. out.appendf("<a href='%s/%s'>%s</a><br/>", tail.str(), href, href);
  659. else
  660. out.appendf("<a href='%s'>%s</a><br/>", href, href);
  661. }
  662. out.append("</body></html>");
  663. }
  664. else if (strieq(fmt, "json"))
  665. {
  666. contentType.set("application/json");
  667. toJSON(tree, out);
  668. }
  669. else if (strieq(fmt, "xml"))
  670. {
  671. contentType.set("application/xml");
  672. toXML(tree, out);
  673. }
  674. response->setStatus(HTTP_STATUS_OK);
  675. response->setContentType(contentType);
  676. response->setContent(out);
  677. response->send();
  678. }
  679. static bool checkHttpPathStaysWithinBounds(const char *path)
  680. {
  681. if (!path || !*path)
  682. return true;
  683. int depth = 0;
  684. StringArray nodes;
  685. nodes.appendList(path, "/");
  686. ForEachItemIn(i, nodes)
  687. {
  688. const char *node = nodes.item(i);
  689. if (!*node || streq(node, ".")) //empty or "." doesn't advance
  690. continue;
  691. if (!streq(node, ".."))
  692. depth++;
  693. else
  694. {
  695. depth--;
  696. if (depth<0) //only really care that the relative http path doesn't position itself above its own root node
  697. return false;
  698. }
  699. }
  700. return true;
  701. }
  702. int CEspHttpServer::onGetFile(CHttpRequest* request, CHttpResponse* response, const char *urlpath)
  703. {
  704. if (!request || !response || !urlpath)
  705. return -1;
  706. StringBuffer basedir(getCFD());
  707. basedir.append("files/");
  708. if (!checkHttpPathStaysWithinBounds(urlpath))
  709. {
  710. DBGLOG("Get File %s: attempted access outside of %s", urlpath, basedir.str());
  711. response->setStatus(HTTP_STATUS_NOT_FOUND);
  712. response->send();
  713. return 0;
  714. }
  715. StringBuffer ext;
  716. StringBuffer tail;
  717. splitFilename(urlpath, NULL, NULL, &tail, &ext);
  718. bool top = !urlpath || !*urlpath;
  719. StringBuffer httpPath;
  720. request->getPath(httpPath).str();
  721. if (httpPath.charAt(httpPath.length()-1)=='/')
  722. tail.clear();
  723. else if (top)
  724. tail.set("./files");
  725. StringBuffer fullpath;
  726. makeAbsolutePath(urlpath, basedir.str(), fullpath);
  727. if (!checkFileExists(fullpath) && !checkFileExists(fullpath.toUpperCase()) && !checkFileExists(fullpath.toLowerCase()))
  728. {
  729. DBGLOG("Get File %s: file not found", urlpath);
  730. response->setStatus(HTTP_STATUS_NOT_FOUND);
  731. response->send();
  732. return 0;
  733. }
  734. if (isDirectory(fullpath))
  735. httpGetDirectory(request, response, urlpath, fullpath, top, tail);
  736. else
  737. httpGetFile(request, response, urlpath, fullpath);
  738. return 0;
  739. }
  740. int CEspHttpServer::onGetXslt(CHttpRequest* request, CHttpResponse* response, const char *path)
  741. {
  742. if (!request || !response || !path)
  743. return -1;
  744. StringBuffer mimetype, etag, lastModified;
  745. MemoryBuffer content;
  746. bool modified = true;
  747. request->getHeader("If-None-Match", etag);
  748. request->getHeader("If-Modified-Since", lastModified);
  749. VStringBuffer filepath("%ssmc_xslt/%s", getCFD(), path);
  750. if (httpContentFromFile(filepath.str(), mimetype, content, modified, lastModified.clear(), etag) ||
  751. httpContentFromFile(filepath.clear().append(getCFD()).append("xslt/").append(path).str(), mimetype, content, modified, lastModified.clear(), etag))
  752. {
  753. response->CheckModifiedHTTPContent(modified, lastModified.str(), etag.str(), mimetype.str(), content);
  754. }
  755. else
  756. {
  757. DBGLOG("Get XSLT %s: file not found", filepath.str());
  758. response->setStatus(HTTP_STATUS_NOT_FOUND);
  759. }
  760. response->send();
  761. return 0;
  762. }
  763. int CEspHttpServer::unsupported()
  764. {
  765. HtmlPage page("Enterprise Services Platform");
  766. StringBuffer espHeader;
  767. espHeader.append("<table border=\"0\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" bgcolor=\"#000000\" height=\"108\">");
  768. espHeader.append("<tr><td width=\"24%\" height=\"24\" bgcolor=\"#000000\"><img border=\"0\" src=\"esp/files_/logo.gif\" width=\"258\" height=\"108\" /></td></tr>");
  769. espHeader.append("<tr><td width=\"24%\" height=\"24\" bgcolor=\"#AA0000\"><p align=\"center\" /><b><font color=\"#FFFFFF\" size=\"5\">Enterprise Services Platform</font></b></td></tr>");
  770. espHeader.append("</table>");
  771. page.appendContent(new CHtmlText(espHeader.str()));
  772. page.appendContent(new CHtmlHeader(H1, "Unsupported http method"));
  773. StringBuffer content;
  774. page.getHtml(content);
  775. m_response->setVersion(HTTP_VERSION);
  776. m_response->setContent(content.length(), content.str());
  777. m_response->setContentType("text/html; charset=UTF-8");
  778. m_response->setStatus(HTTP_STATUS_OK);
  779. m_response->send();
  780. return 0;
  781. }
  782. int CEspHttpServer::onOptions()
  783. {
  784. m_response->setVersion(HTTP_VERSION);
  785. m_response->setStatus(HTTP_STATUS_OK);
  786. //CORS allow headers for interoperability, we do not rely on this for security since
  787. //that only means treating the browser as a trusted entity. We need to be diligent and secure
  788. //for every request whether it comes from a cross domain browser or any other source
  789. StringBuffer allowHeaders;
  790. m_request->getHeader("Access-Control-Request-Headers", allowHeaders);
  791. if (allowHeaders.length())
  792. m_response->setHeader("Access-Control-Allow-Headers", allowHeaders);
  793. m_response->setHeader("Access-Control-Allow-Origin", "*");
  794. m_response->setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
  795. m_response->setHeader("Access-Control-Max-Age", "86400"); //arbitrary 24 hours
  796. m_response->setContentType("text/plain");
  797. m_response->setContent("");
  798. m_response->send();
  799. return 0;
  800. }
  801. int CEspHttpServer::onPost()
  802. {
  803. HtmlPage page("Enterprise Services Platform");
  804. StringBuffer espHeader;
  805. espHeader.append("<table border=\"0\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" bgcolor=\"#000000\" height=\"108\">");
  806. espHeader.append("<tr><td width=\"24%\" height=\"24\" bgcolor=\"#000000\"><img border=\"0\" src=\"esp/files_/logo.gif\" width=\"258\" height=\"108\" /></td></tr>");
  807. espHeader.append("<tr><td width=\"24%\" height=\"24\" bgcolor=\"#AA0000\"><p align=\"center\" /><b><font color=\"#FFFFFF\" size=\"5\">Enterprise Services Platform</font></b></td></tr>");
  808. espHeader.append("</table>");
  809. page.appendContent(new CHtmlText(espHeader.str()));
  810. page.appendContent(new CHtmlHeader(H1, "Invalid POST"));
  811. StringBuffer content;
  812. page.getHtml(content);
  813. m_response->setVersion(HTTP_VERSION);
  814. m_response->setContent(content.length(), content.str());
  815. m_response->setContentType("text/html; charset=UTF-8");
  816. m_response->setStatus(HTTP_STATUS_OK);
  817. m_response->send();
  818. return 0;
  819. }
  820. int CEspHttpServer::onGet()
  821. {
  822. if (m_request && m_request->queryParameters()->hasProp("config_") && m_viewConfig)
  823. {
  824. StringBuffer mimetype, etag, lastModified;
  825. MemoryBuffer content;
  826. bool modified = true;
  827. m_request->getHeader("If-None-Match", etag);
  828. m_request->getHeader("If-Modified-Since", lastModified);
  829. httpContentFromFile("esp.xml", mimetype, content, modified, lastModified, etag);
  830. m_response->setVersion(HTTP_VERSION);
  831. m_response->CheckModifiedHTTPContent(modified, lastModified.str(), etag.str(), HTTP_TYPE_APPLICATION_XML_UTF8, content);
  832. m_response->send();
  833. }
  834. else
  835. {
  836. HtmlPage page("Enterprise Services Platform");
  837. page.appendContent(new CHtmlHeader(H1, "Available Services:"));
  838. CHtmlList * list = (CHtmlList *)page.appendContent(new CHtmlList);
  839. EspHttpBinding* lbind=NULL;
  840. int ordinality=m_apport->getBindingCount();
  841. double ver = m_request->queryContext()->getClientVersion();
  842. for(int index=0; index<ordinality; index++)
  843. {
  844. CEspBindingEntry *entry = m_apport->queryBindingItem(index);
  845. lbind = (entry) ? dynamic_cast<EspHttpBinding*>(entry->queryBinding()) : NULL;
  846. if (lbind)
  847. {
  848. StringBuffer srv, srvLink;
  849. lbind->getServiceName(srv);
  850. srvLink.appendf("/%s", srv.str());
  851. if (ver)
  852. srvLink.appendf("?ver_=%g", ver);
  853. list->appendContent(new CHtmlLink(srv.str(), srvLink.str()));
  854. }
  855. }
  856. StringBuffer content;
  857. page.getHtml(content);
  858. m_response->setVersion(HTTP_VERSION);
  859. m_response->setContent(content.length(), content.str());
  860. m_response->setContentType("text/html; charset=UTF-8");
  861. m_response->setStatus(HTTP_STATUS_OK);
  862. m_response->send();
  863. }
  864. return 0;
  865. }