esdl_script.cpp 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2020 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. #include "espcontext.hpp"
  14. #include "esdl_script.hpp"
  15. #include "wsexcept.hpp"
  16. #include "httpclient.hpp"
  17. #include "dllserver.hpp"
  18. #include "thorplugin.hpp"
  19. #include "eclrtl.hpp"
  20. #include "rtlformat.hpp"
  21. #include "jsecrets.hpp"
  22. #include "esdl_script.hpp"
  23. #include <xpp/XmlPullParser.h>
  24. using namespace xpp;
  25. interface IEsdlTransformOperation : public IInterface
  26. {
  27. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) = 0;
  28. virtual void toDBGLog() = 0;
  29. };
  30. IEsdlTransformOperation *createEsdlTransformOperation(XmlPullParser &xpp, const StringBuffer &prefix, bool withVariables, bool ignoreCodingErrors);
  31. void createEsdlTransformOperations(IArrayOf<IEsdlTransformOperation> &operations, XmlPullParser &xpp, const StringBuffer &prefix, bool withVariables, bool ignoreCodingErrors);
  32. void createEsdlTransformChooseOperations(IArrayOf<IEsdlTransformOperation> &operations, XmlPullParser &xpp, const StringBuffer &prefix, bool withVariables, bool ignoreCodingErrors);
  33. typedef void (*esdlOperationsFactory_t)(IArrayOf<IEsdlTransformOperation> &operations, XmlPullParser &xpp, const StringBuffer &prefix, bool withVariables, bool ignoreCodingErrors);
  34. bool getStartTagValueBool(StartTag &stag, const char *name, bool defaultValue)
  35. {
  36. if (isEmptyString(name))
  37. return defaultValue;
  38. const char *value = stag.getValue(name);
  39. if (isEmptyString(value))
  40. return defaultValue;
  41. return strToBool(value);
  42. }
  43. inline void esdlOperationError(int code, const char *op, const char *msg, const char *traceName, bool exception)
  44. {
  45. StringBuffer s("ESDL Script: ");
  46. if (!isEmptyString(traceName))
  47. s.append(" '").append(traceName).append("' ");
  48. if (!isEmptyString(op))
  49. s.append(" ").append(op).append(" ");
  50. s.append(msg);
  51. IERRLOG("%s", s.str());
  52. if(exception)
  53. throw MakeStringException(code, "%s", s.str());
  54. }
  55. inline void esdlOperationError(int code, const char *op, const char *msg, bool exception)
  56. {
  57. esdlOperationError(code, op, msg, "", exception);
  58. }
  59. static inline const char *checkSkipOpPrefix(const char *op, const StringBuffer &prefix)
  60. {
  61. if (prefix.length())
  62. {
  63. if (!hasPrefix(op, prefix, true))
  64. {
  65. DBGLOG(1,"Unrecognized script operation: %s", op);
  66. return nullptr;
  67. }
  68. return (op + prefix.length());
  69. }
  70. return op;
  71. }
  72. static inline StringBuffer &makeOperationTagName(StringBuffer &s, const StringBuffer &prefix, const char *op)
  73. {
  74. return s.append(prefix).append(op);
  75. }
  76. class CEsdlTransformOperationBase : public CInterfaceOf<IEsdlTransformOperation>
  77. {
  78. protected:
  79. StringAttr m_tagname;
  80. StringAttr m_traceName;
  81. bool m_ignoreCodingErrors = false; //ideally used only for debugging
  82. public:
  83. CEsdlTransformOperationBase(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix)
  84. {
  85. m_tagname.set(stag.getLocalName());
  86. m_traceName.set(stag.getValue("trace"));
  87. m_ignoreCodingErrors = getStartTagValueBool(stag, "optional", false);
  88. }
  89. };
  90. class CEsdlTransformOperationWithChildren : public CEsdlTransformOperationBase
  91. {
  92. protected:
  93. IArrayOf<IEsdlTransformOperation> m_children;
  94. bool m_withVariables = false;
  95. public:
  96. CEsdlTransformOperationWithChildren(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix, bool withVariables, esdlOperationsFactory_t factory) : CEsdlTransformOperationBase(xpp, stag, prefix), m_withVariables(withVariables)
  97. {
  98. //load children
  99. if (factory)
  100. factory(m_children, xpp, prefix, withVariables, m_ignoreCodingErrors);
  101. else
  102. createEsdlTransformOperations(m_children, xpp, prefix, withVariables, m_ignoreCodingErrors);
  103. }
  104. virtual ~CEsdlTransformOperationWithChildren(){}
  105. virtual bool processChildren(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext)
  106. {
  107. if (!m_children.length())
  108. return false;
  109. Owned<CXpathContextScope> scope = m_withVariables ? new CXpathContextScope(sourceContext, m_tagname) : nullptr;
  110. bool ret = false;
  111. ForEachItemIn(i, m_children)
  112. {
  113. if (m_children.item(i).process(scriptContext, targetContext, sourceContext))
  114. ret = true;
  115. }
  116. return ret;
  117. }
  118. virtual void toDBGLog () override
  119. {
  120. #if defined(_DEBUG)
  121. ForEachItemIn(i, m_children)
  122. m_children.item(i).toDBGLog();
  123. #endif
  124. }
  125. };
  126. class CEsdlTransformOperationWithoutChildren : public CEsdlTransformOperationBase
  127. {
  128. public:
  129. CEsdlTransformOperationWithoutChildren(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationBase(xpp, stag, prefix)
  130. {
  131. if (xpp.skipSubTreeEx())
  132. esdlOperationError(ESDL_SCRIPT_Error, m_tagname, "should not have child tags", m_traceName, !m_ignoreCodingErrors);
  133. }
  134. virtual ~CEsdlTransformOperationWithoutChildren(){}
  135. };
  136. class CEsdlTransformOperationVariable : public CEsdlTransformOperationWithChildren
  137. {
  138. protected:
  139. StringAttr m_name;
  140. Owned<ICompiledXpath> m_select;
  141. public:
  142. CEsdlTransformOperationVariable(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithChildren(xpp, stag, prefix, true, nullptr)
  143. {
  144. if (m_traceName.isEmpty())
  145. m_traceName.set(stag.getValue("name"));
  146. m_name.set(stag.getValue("name"));
  147. if (m_name.isEmpty())
  148. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without name", m_traceName, !m_ignoreCodingErrors);
  149. const char *select = stag.getValue("select");
  150. if (!isEmptyString(select))
  151. m_select.setown(compileXpath(select));
  152. }
  153. virtual ~CEsdlTransformOperationVariable()
  154. {
  155. }
  156. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  157. {
  158. if (m_select)
  159. return sourceContext->addCompiledVariable(m_name, m_select);
  160. else if (m_children.length())
  161. {
  162. VStringBuffer xpath("/esdl_script_context/temporaries/%s", m_name.str());
  163. CXpathContextLocation location(targetContext);
  164. targetContext->ensureLocation(xpath, true);
  165. processChildren(scriptContext, targetContext, sourceContext); //bulid nodeset
  166. sourceContext->addXpathVariable(m_name, xpath);
  167. return false;
  168. }
  169. sourceContext->addVariable(m_name, "");
  170. return false;
  171. }
  172. virtual void toDBGLog() override
  173. {
  174. #if defined(_DEBUG)
  175. DBGLOG(">%s> %s with select(%s)", m_name.str(), m_tagname.str(), m_select.get() ? m_select->getXpath() : "");
  176. #endif
  177. }
  178. };
  179. class CEsdlTransformOperationHttpContentXml : public CEsdlTransformOperationWithChildren
  180. {
  181. public:
  182. CEsdlTransformOperationHttpContentXml(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithChildren(xpp, stag, prefix, true, nullptr)
  183. {
  184. }
  185. virtual ~CEsdlTransformOperationHttpContentXml(){}
  186. bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  187. {
  188. CXpathContextLocation location(targetContext);
  189. targetContext->addElementToLocation("content");
  190. return processChildren(scriptContext, targetContext, sourceContext);
  191. }
  192. virtual void toDBGLog () override
  193. {
  194. #if defined(_DEBUG)
  195. DBGLOG (">>>>>>>>>>> %s >>>>>>>>>>", m_tagname.str());
  196. CEsdlTransformOperationWithChildren::toDBGLog();
  197. DBGLOG (">>>>>>>>>>> %s >>>>>>>>>>", m_tagname.str());
  198. #endif
  199. }
  200. };
  201. interface IEsdlTransformOperationHttpHeader : public IInterface
  202. {
  203. virtual bool processHeader(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext, IProperties *headers) = 0;
  204. };
  205. static Owned<ILoadedDllEntry> mysqlPluginDll;
  206. static Owned<IEmbedContext> mysqlplugin;
  207. IEmbedContext &ensureMysqlEmbed()
  208. {
  209. if (!mysqlplugin)
  210. {
  211. mysqlPluginDll.setown(createDllEntry("mysqlembed", false, NULL, false));
  212. if (!mysqlPluginDll)
  213. throw makeStringException(0, "Failed to load mysqlembed plugin");
  214. GetEmbedContextFunction pf = (GetEmbedContextFunction) mysqlPluginDll->getEntry("getEmbedContextDynamic");
  215. if (!pf)
  216. throw makeStringException(0, "Failed to load mysqlembed plugin");
  217. mysqlplugin.setown(pf());
  218. }
  219. return *mysqlplugin;
  220. }
  221. class CEsdlTransformOperationMySqlBindParmeter : public CEsdlTransformOperationWithoutChildren
  222. {
  223. protected:
  224. StringAttr m_name;
  225. StringAttr m_mysql_type;
  226. Owned<ICompiledXpath> m_value;
  227. bool m_bitfield = false;
  228. public:
  229. IMPLEMENT_IINTERFACE_USING(CEsdlTransformOperationWithoutChildren)
  230. CEsdlTransformOperationMySqlBindParmeter(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithoutChildren(xpp, stag, prefix)
  231. {
  232. m_name.set(stag.getValue("name"));
  233. if (m_name.isEmpty())
  234. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without name or xpath_name", m_traceName, !m_ignoreCodingErrors);
  235. const char *value = stag.getValue("value");
  236. if (isEmptyString(value))
  237. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without value", m_traceName, !m_ignoreCodingErrors);
  238. m_value.setown(compileXpath(value));
  239. //optional, conversions normally work well, ONLY WHEN NEEDED we may need to have special handling for mysql types
  240. m_mysql_type.set(stag.getValue("type"));
  241. if (m_mysql_type.length() && 0==strnicmp(m_mysql_type.str(), "BIT", 3))
  242. m_bitfield = true;
  243. }
  244. virtual ~CEsdlTransformOperationMySqlBindParmeter(){}
  245. bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  246. {
  247. return false;
  248. }
  249. void bindParameter(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext, IEmbedFunctionContext *functionContext)
  250. {
  251. if (!functionContext)
  252. return;
  253. StringBuffer value;
  254. if (m_value)
  255. sourceContext->evaluateAsString(m_value, value);
  256. if (value.isEmpty())
  257. functionContext->bindUTF8Param(m_name, 0, "");
  258. else
  259. {
  260. if (m_bitfield)
  261. functionContext->bindSignedParam(m_name, atoi64(value.str()));
  262. else
  263. functionContext->bindUTF8Param(m_name, rtlUtf8Length(value.length(), value), value);
  264. }
  265. }
  266. virtual void toDBGLog () override
  267. {
  268. #if defined(_DEBUG)
  269. DBGLOG ("> %s (%s, value(%s)) >>>>>>>>>>", m_tagname.str(), m_name.str(), m_value ? m_value->getXpath() : "");
  270. #endif
  271. }
  272. };
  273. static inline void buildMissingMySqlParameterMessage(StringBuffer &msg, const char *name)
  274. {
  275. msg .append(msg.isEmpty() ? "without " : ", ").append(name);
  276. }
  277. static inline void addExceptionsToXpathContext(IXpathContext *targetContext, IMultiException *me)
  278. {
  279. if (!targetContext || !me)
  280. return;
  281. StringBuffer xml;
  282. me->serialize(xml);
  283. CXpathContextLocation content_location(targetContext);
  284. targetContext->ensureSetValue("@status", "error", true);
  285. targetContext->addXmlContent(xml.str());
  286. }
  287. static inline void addExceptionsToXpathContext(IXpathContext *targetContext, IException *E)
  288. {
  289. if (!targetContext || !E)
  290. return;
  291. Owned<IMultiException> me = makeMultiException("ESDLScript");
  292. me->append(*LINK(E));
  293. addExceptionsToXpathContext(targetContext, me);
  294. }
  295. class CEsdlTransformOperationMySqlCall : public CEsdlTransformOperationBase
  296. {
  297. protected:
  298. StringAttr m_name;
  299. Owned<ICompiledXpath> m_vaultName;
  300. Owned<ICompiledXpath> m_secretName;
  301. Owned<ICompiledXpath> m_section;
  302. Owned<ICompiledXpath> m_resultsetTag;
  303. Owned<ICompiledXpath> m_server;
  304. Owned<ICompiledXpath> m_user;
  305. Owned<ICompiledXpath> m_password;
  306. Owned<ICompiledXpath> m_database;
  307. StringArray m_mysqlOptionNames;
  308. IArrayOf<ICompiledXpath> m_mysqlOptionXpaths;
  309. StringBuffer m_sql;
  310. Owned<ICompiledXpath> m_select;
  311. IArrayOf<CEsdlTransformOperationMySqlBindParmeter> m_parameters;
  312. public:
  313. CEsdlTransformOperationMySqlCall(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationBase(xpp, stag, prefix)
  314. {
  315. ensureMysqlEmbed();
  316. m_name.set(stag.getValue("name"));
  317. if (m_traceName.isEmpty())
  318. m_traceName.set(m_name.str());
  319. //select is optional, with select, a mysql call behaves like a for-each, binding and executing each iteration of the selected content
  320. //without select, it executes once in the current context
  321. m_select.setown(compileOptionalXpath(stag.getValue("select")));
  322. m_vaultName.setown(compileOptionalXpath(stag.getValue("vault")));
  323. m_secretName.setown(compileOptionalXpath(stag.getValue("secret")));
  324. m_section.setown(compileOptionalXpath(stag.getValue("section")));
  325. m_resultsetTag.setown(compileOptionalXpath(stag.getValue("resultset-tag")));
  326. m_server.setown(compileOptionalXpath(stag.getValue("server")));
  327. m_user.setown(compileOptionalXpath(stag.getValue("user")));
  328. m_password.setown(compileOptionalXpath(stag.getValue("password")));
  329. m_database.setown(compileOptionalXpath(stag.getValue("database")));
  330. //script can set any MYSQL options using an attribute with the same name as the option enum, for example
  331. // MYSQL_SET_CHARSET_NAME="'latin1'" or MYSQL_SET_CHARSET_NAME="$charset"
  332. //
  333. int attCount = stag.getLength();
  334. for (int i=0; i<attCount; i++)
  335. {
  336. const char *attName = stag.getLocalName(i);
  337. if (attName && hasPrefix(attName, "MYSQL_", false))
  338. {
  339. Owned<ICompiledXpath> attXpath = compileOptionalXpath(stag.getValue(i));
  340. if (attXpath)
  341. {
  342. m_mysqlOptionNames.append(attName);
  343. m_mysqlOptionXpaths.append(*attXpath.getClear());
  344. }
  345. }
  346. }
  347. int type = 0;
  348. while((type = xpp.next()) != XmlPullParser::END_DOCUMENT)
  349. {
  350. switch(type)
  351. {
  352. case XmlPullParser::START_TAG:
  353. {
  354. StartTag stag;
  355. xpp.readStartTag(stag);
  356. const char *op = stag.getLocalName();
  357. if (isEmptyString(op))
  358. esdlOperationError(ESDL_SCRIPT_Error, m_tagname, "unknown error", m_traceName, !m_ignoreCodingErrors);
  359. if (streq(op, "bind"))
  360. m_parameters.append(*new CEsdlTransformOperationMySqlBindParmeter(xpp, stag, prefix));
  361. else if (streq(op, "sql"))
  362. readFullContent(xpp, m_sql);
  363. else
  364. xpp.skipSubTreeEx();
  365. break;
  366. }
  367. case XmlPullParser::END_TAG:
  368. case XmlPullParser::END_DOCUMENT:
  369. return;
  370. }
  371. }
  372. if (!m_section)
  373. m_section.setown(compileXpath("'temporaries'"));
  374. StringBuffer errmsg;
  375. if (m_name.isEmpty())
  376. buildMissingMySqlParameterMessage(errmsg, "name");
  377. if (!m_server)
  378. buildMissingMySqlParameterMessage(errmsg, "server");
  379. if (!m_user)
  380. buildMissingMySqlParameterMessage(errmsg, "user");
  381. if (!m_database)
  382. buildMissingMySqlParameterMessage(errmsg, "database");
  383. if (m_sql.isEmpty())
  384. buildMissingMySqlParameterMessage(errmsg, "sql");
  385. if (errmsg.length())
  386. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, errmsg, m_traceName, !m_ignoreCodingErrors);
  387. }
  388. virtual ~CEsdlTransformOperationMySqlCall()
  389. {
  390. }
  391. void bindParameters(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext, IEmbedFunctionContext *functionContext)
  392. {
  393. if (!m_parameters.length())
  394. return;
  395. CXpathContextLocation location(targetContext);
  396. ForEachItemIn(i, m_parameters)
  397. m_parameters.item(i).bindParameter(scriptContext, targetContext, sourceContext, functionContext);
  398. }
  399. void missingMySqlOptionError(const char *name, bool required)
  400. {
  401. if (required)
  402. {
  403. StringBuffer msg("empty or missing ");
  404. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, msg.append(name), m_traceName, true);
  405. }
  406. }
  407. IPropertyTree *getSecretInfo(IXpathContext * sourceContext)
  408. {
  409. //leaving flexibility for the secret to be configured multiple ways
  410. // the most secure option in my opinion is to at least have the server, name, and password all in the secret
  411. // with the server included the credentials can't be hijacked and sent somewhere else for capture.
  412. //
  413. if (!m_secretName)
  414. return nullptr;
  415. StringBuffer name;
  416. sourceContext->evaluateAsString(m_secretName, name);
  417. if (name.isEmpty())
  418. {
  419. missingMySqlOptionError(name, true);
  420. return nullptr;
  421. }
  422. StringBuffer vault;
  423. if (m_vaultName)
  424. sourceContext->evaluateAsString(m_vaultName, vault);
  425. if (vault.isEmpty())
  426. return getSecret("esp", name);
  427. return getVaultSecret("esp", vault, name);
  428. }
  429. void appendOption(StringBuffer &options, const char *name, const char *value, bool required)
  430. {
  431. if (isEmptyString(value))
  432. {
  433. missingMySqlOptionError(name, required);
  434. return;
  435. }
  436. if (options.length())
  437. options.append(',');
  438. options.append(name).append('=').append(value);
  439. }
  440. void appendOption(StringBuffer &options, const char *name, IXpathContext * sourceContext, ICompiledXpath *cx, IPropertyTree *secret, bool required)
  441. {
  442. if (secret && secret->hasProp(name))
  443. {
  444. StringBuffer value;
  445. getSecretKeyValue(value, secret, name);
  446. appendOption(options, name, value, required);
  447. return;
  448. }
  449. if (!cx)
  450. {
  451. missingMySqlOptionError(name, required);
  452. return;
  453. }
  454. StringBuffer value;
  455. sourceContext->evaluateAsString(cx, value);
  456. if (!value.length())
  457. {
  458. missingMySqlOptionError(name, required);
  459. return;
  460. }
  461. if (options.length())
  462. options.append(',');
  463. options.append(name).append('=').append(value);
  464. }
  465. IEmbedFunctionContext *createFunctionContext(IXpathContext * sourceContext)
  466. {
  467. Owned<IPropertyTree> secret = getSecretInfo(sourceContext);
  468. StringBuffer options;
  469. appendOption(options, "server", sourceContext, m_server, secret, true);
  470. appendOption(options, "user", sourceContext, m_user, secret, true);
  471. appendOption(options, "database", sourceContext, m_database, secret, true);
  472. appendOption(options, "password", sourceContext, m_password, secret, true);
  473. aindex_t count = m_mysqlOptionNames.length();
  474. for (aindex_t i=0; i<count; i++)
  475. appendOption(options, m_mysqlOptionNames.item(i), sourceContext, &m_mysqlOptionXpaths.item(i), nullptr, true);
  476. Owned<IEmbedFunctionContext> fc = ensureMysqlEmbed().createFunctionContext(EFembed, options.str());
  477. fc->compileEmbeddedScript(m_sql.length(), m_sql);
  478. return fc.getClear();
  479. }
  480. void processCurrent(IEmbedFunctionContext *fc, IXmlWriter *writer, const char *tag, IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext)
  481. {
  482. bindParameters(scriptContext, targetContext, sourceContext, fc);
  483. fc->callFunction();
  484. if (!isEmptyString(tag))
  485. writer->outputBeginNested(tag, true);
  486. fc->writeResult(nullptr, nullptr, nullptr, writer);
  487. if (!isEmptyString(tag))
  488. writer->outputEndNested(tag);
  489. }
  490. IXpathContextIterator *select(IXpathContext * xpathContext)
  491. {
  492. IXpathContextIterator *xpathset = nullptr;
  493. try
  494. {
  495. xpathset = xpathContext->evaluateAsNodeSet(m_select);
  496. }
  497. catch (IException* e)
  498. {
  499. int code = e->errorCode();
  500. StringBuffer msg;
  501. e->errorMessage(msg);
  502. e->Release();
  503. esdlOperationError(code, m_tagname, msg, !m_ignoreCodingErrors);
  504. }
  505. catch (...)
  506. {
  507. VStringBuffer msg("unknown exception evaluating select '%s'", m_select.get() ? m_select->getXpath() : "undefined!");
  508. esdlOperationError(ESDL_SCRIPT_Error, m_tagname, msg, !m_ignoreCodingErrors);
  509. }
  510. return xpathset;
  511. }
  512. void getXpathStringValue(StringBuffer &s, IXpathContext * sourceContext, ICompiledXpath *cx, const char *defaultValue)
  513. {
  514. if (cx)
  515. sourceContext->evaluateAsString(cx, s);
  516. if (defaultValue && s.isEmpty())
  517. s.set(defaultValue);
  518. }
  519. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  520. {
  521. StringBuffer section;
  522. getXpathStringValue(section, sourceContext, m_section, "temporaries");
  523. VStringBuffer xpath("/esdl_script_context/%s/%s", section.str(), m_name.str());
  524. CXpathContextLocation location(targetContext);
  525. targetContext->ensureLocation(xpath, true);
  526. Owned<IXpathContextIterator> selected;
  527. if (m_select)
  528. {
  529. selected.setown(select(sourceContext));
  530. if (!selected || !selected->first())
  531. return false;
  532. }
  533. try
  534. {
  535. Owned<IEmbedFunctionContext> fc = createFunctionContext(sourceContext);
  536. Owned<IXmlWriter> writer = targetContext->createXmlWriter();
  537. StringBuffer rstag;
  538. getXpathStringValue(rstag, sourceContext, m_resultsetTag, nullptr);
  539. if (!selected)
  540. processCurrent(fc, writer, rstag, scriptContext, targetContext, sourceContext);
  541. else
  542. {
  543. ForEach(*selected)
  544. processCurrent(fc, writer, rstag, scriptContext, targetContext, &selected->query());
  545. }
  546. }
  547. catch(IMultiException *me)
  548. {
  549. addExceptionsToXpathContext(targetContext, me);
  550. me->Release();
  551. }
  552. catch(IException *E)
  553. {
  554. addExceptionsToXpathContext(targetContext, E);
  555. E->Release();
  556. }
  557. sourceContext->addXpathVariable(m_name, xpath);
  558. return true;
  559. }
  560. virtual void toDBGLog() override
  561. {
  562. #if defined(_DEBUG)
  563. DBGLOG(">%s> %s with name(%s) server(%s) database(%s)", m_name.str(), m_tagname.str(), m_name.str(), m_server->getXpath(), m_database->getXpath());
  564. #endif
  565. }
  566. };
  567. class CEsdlTransformOperationHttpHeader : public CEsdlTransformOperationWithoutChildren, implements IEsdlTransformOperationHttpHeader
  568. {
  569. protected:
  570. StringAttr m_name;
  571. Owned<ICompiledXpath> m_xpath_name;
  572. Owned<ICompiledXpath> m_value;
  573. public:
  574. IMPLEMENT_IINTERFACE_USING(CEsdlTransformOperationWithoutChildren)
  575. CEsdlTransformOperationHttpHeader(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithoutChildren(xpp, stag, prefix)
  576. {
  577. m_name.set(stag.getValue("name"));
  578. const char *xpath_name = stag.getValue("xpath_name");
  579. if (m_name.isEmpty() && isEmptyString(xpath_name))
  580. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without name or xpath_name", m_traceName, !m_ignoreCodingErrors);
  581. if (!isEmptyString(xpath_name))
  582. m_xpath_name.setown(compileXpath(xpath_name));
  583. const char *value = stag.getValue("value");
  584. if (isEmptyString(value))
  585. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without value", m_traceName, !m_ignoreCodingErrors);
  586. m_value.setown(compileXpath(value));
  587. }
  588. virtual ~CEsdlTransformOperationHttpHeader(){}
  589. bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  590. {
  591. return processHeader(scriptContext, targetContext, sourceContext, nullptr);
  592. }
  593. bool processHeader(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext, IProperties *headers) override
  594. {
  595. CXpathContextLocation location(targetContext);
  596. targetContext->addElementToLocation("header");
  597. StringBuffer name;
  598. if (m_xpath_name)
  599. sourceContext->evaluateAsString(m_xpath_name, name);
  600. else
  601. name.set(m_name);
  602. StringBuffer value;
  603. if (m_value)
  604. sourceContext->evaluateAsString(m_value, value);
  605. if (name.length() && value.length())
  606. {
  607. if (headers)
  608. headers->setProp(name, value);
  609. targetContext->ensureSetValue("@name", name, true);
  610. targetContext->ensureSetValue("@value", value, true);
  611. }
  612. return false;
  613. }
  614. virtual void toDBGLog () override
  615. {
  616. #if defined(_DEBUG)
  617. DBGLOG ("> %s (%s, value(%s)) >>>>>>>>>>", m_tagname.str(), m_xpath_name ? m_xpath_name->getXpath() : m_name.str(), m_value ? m_value->getXpath() : "");
  618. #endif
  619. }
  620. };
  621. class CEsdlTransformOperationHttpPostXml : public CEsdlTransformOperationBase
  622. {
  623. protected:
  624. StringAttr m_name;
  625. StringAttr m_section;
  626. Owned<ICompiledXpath> m_url;
  627. IArrayOf<IEsdlTransformOperationHttpHeader> m_headers;
  628. Owned<IEsdlTransformOperation> m_content;
  629. public:
  630. CEsdlTransformOperationHttpPostXml(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationBase(xpp, stag, prefix)
  631. {
  632. m_name.set(stag.getValue("name"));
  633. if (m_traceName.isEmpty())
  634. m_traceName.set(m_name.str());
  635. m_section.set(stag.getValue("section"));
  636. if (m_section.isEmpty())
  637. m_section.set("temporaries");
  638. if (m_name.isEmpty())
  639. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without name", m_traceName, !m_ignoreCodingErrors);
  640. const char *url = stag.getValue("url");
  641. if (isEmptyString(url))
  642. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without url", m_traceName, !m_ignoreCodingErrors);
  643. m_url.setown(compileXpath(url));
  644. int type = 0;
  645. while((type = xpp.next()) != XmlPullParser::END_DOCUMENT)
  646. {
  647. switch(type)
  648. {
  649. case XmlPullParser::START_TAG:
  650. {
  651. StartTag stag;
  652. xpp.readStartTag(stag);
  653. const char *op = stag.getLocalName();
  654. if (isEmptyString(op))
  655. esdlOperationError(ESDL_SCRIPT_Error, m_tagname, "unknown error", m_traceName, !m_ignoreCodingErrors);
  656. if (streq(op, "http-header"))
  657. m_headers.append(*new CEsdlTransformOperationHttpHeader(xpp, stag, prefix));
  658. else if (streq(op, "content"))
  659. m_content.setown(new CEsdlTransformOperationHttpContentXml(xpp, stag, prefix));
  660. else
  661. xpp.skipSubTreeEx();
  662. break;
  663. }
  664. case XmlPullParser::END_TAG:
  665. case XmlPullParser::END_DOCUMENT:
  666. return;
  667. }
  668. }
  669. }
  670. virtual ~CEsdlTransformOperationHttpPostXml()
  671. {
  672. }
  673. void buildHeaders(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext, IProperties *headers)
  674. {
  675. if (!m_headers.length())
  676. return;
  677. CXpathContextLocation location(targetContext);
  678. targetContext->addElementToLocation("headers");
  679. ForEachItemIn(i, m_headers)
  680. m_headers.item(i).processHeader(scriptContext, targetContext, sourceContext, headers);
  681. }
  682. void buildRequest(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext, const char *url, IProperties *headers)
  683. {
  684. CXpathContextLocation location(targetContext);
  685. targetContext->addElementToLocation("request");
  686. targetContext->ensureSetValue("@url", url, true);
  687. buildHeaders(scriptContext, targetContext, sourceContext, headers);
  688. if (m_content)
  689. m_content->process(scriptContext, targetContext, sourceContext);
  690. }
  691. void postRequest(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext, const char *url, IProperties *headers)
  692. {
  693. VStringBuffer xpath("/esdl_script_context/%s/%s/request/content/*[1]", m_section.str(), m_name.str());
  694. StringBuffer content;
  695. sourceContext->toXml(xpath, content);
  696. if (!content)
  697. return;
  698. CXpathContextLocation location(targetContext);
  699. targetContext->addElementToLocation("response");
  700. Owned<IHttpClientContext> httpCtx = getHttpClientContext();
  701. Owned<IHttpClient> httpclient = httpCtx->createHttpClient(NULL, url);
  702. if (!httpclient)
  703. return;
  704. try
  705. {
  706. StringBuffer status;
  707. StringBuffer response;
  708. StringBuffer errmsg;
  709. if (headers && !headers->hasProp("Accept"))
  710. headers->setProp("Accept", "text/html, application/xml");
  711. HttpClientErrCode code = HttpClientErrCode::OK;
  712. Owned<IHttpMessage> resp = httpclient->sendRequestEx("POST", "text/xml", content, code, errmsg, headers);
  713. targetContext->ensureSetValue("@status", status.str(), true);
  714. StringBuffer err;
  715. err.append((int) code);
  716. targetContext->ensureSetValue("@error-code", err.str(), true);
  717. if (code != HttpClientErrCode::OK)
  718. throw MakeStringException(ESDL_SCRIPT_Error, "ESDL Script error sending request in http-post-xml %s url(%s)", m_traceName.str(), url);
  719. resp->getStatus(status);
  720. targetContext->ensureSetValue("@status", status.str(), true);
  721. resp->getContent(response);
  722. if (!response.trim().length())
  723. throw MakeStringException(ESDL_SCRIPT_Error, "ESDL Script empty result calling http-post-xml %s url(%s)", m_traceName.str(), url);
  724. StringBuffer contentType;
  725. resp->getContentType(contentType);
  726. targetContext->ensureSetValue("@content-type", contentType.str(), true);
  727. if (strnicmp("text/xml", contentType.str(), 8)==0 || strnicmp("application/xml", contentType.str(), 15) ==0)
  728. {
  729. CXpathContextLocation content_location(targetContext);
  730. targetContext->addElementToLocation("content");
  731. targetContext->addXmlContent(response.str());
  732. }
  733. else
  734. {
  735. targetContext->ensureSetValue("@status", "error", true);
  736. targetContext->ensureSetValue("text", response.str(), true);
  737. }
  738. }
  739. catch(IMultiException *me)
  740. {
  741. StringBuffer xml;
  742. me->serialize(xml);
  743. CXpathContextLocation content_location(targetContext);
  744. targetContext->ensureSetValue("@status", "error", true);
  745. targetContext->addElementToLocation("content");
  746. targetContext->addXmlContent(xml.str());
  747. me->Release();
  748. }
  749. catch(IException *E)
  750. {
  751. StringBuffer xml;
  752. Owned<IMultiException> me = makeMultiException("ESDLScript");
  753. me->append(*LINK(E));
  754. me->serialize(xml);
  755. CXpathContextLocation content_location(targetContext);
  756. targetContext->ensureSetValue("@status", "error", true);
  757. targetContext->addElementToLocation("content");
  758. targetContext->addXmlContent(xml.str());
  759. E->Release();
  760. }
  761. }
  762. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  763. {
  764. VStringBuffer xpath("/esdl_script_context/%s/%s", m_section.str(), m_name.str());
  765. CXpathContextLocation location(targetContext);
  766. targetContext->ensureLocation(xpath, true);
  767. StringBuffer url;
  768. if (m_url)
  769. sourceContext->evaluateAsString(m_url, url);
  770. Owned<IProperties> headers = createProperties();
  771. buildRequest(scriptContext, targetContext, sourceContext, url, headers);
  772. postRequest(scriptContext, targetContext, sourceContext, url, headers);
  773. sourceContext->addXpathVariable(m_name, xpath);
  774. return false;
  775. }
  776. virtual void toDBGLog() override
  777. {
  778. #if defined(_DEBUG)
  779. DBGLOG(">%s> %s with name(%s) url(%s)", m_name.str(), m_tagname.str(), m_name.str(), m_url ? m_url->getXpath() : "url error");
  780. #endif
  781. }
  782. };
  783. class CEsdlTransformOperationParameter : public CEsdlTransformOperationVariable
  784. {
  785. public:
  786. CEsdlTransformOperationParameter(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationVariable(xpp, stag, prefix)
  787. {
  788. }
  789. virtual ~CEsdlTransformOperationParameter()
  790. {
  791. }
  792. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  793. {
  794. if (m_select)
  795. return sourceContext->declareCompiledParameter(m_name, m_select);
  796. return sourceContext->declareParameter(m_name, "");
  797. }
  798. };
  799. class CEsdlTransformOperationSetSectionAttributeBase : public CEsdlTransformOperationWithoutChildren
  800. {
  801. protected:
  802. StringAttr m_name;
  803. Owned<ICompiledXpath> m_xpath_name;
  804. Owned<ICompiledXpath> m_select;
  805. public:
  806. CEsdlTransformOperationSetSectionAttributeBase(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix, const char *attrName) : CEsdlTransformOperationWithoutChildren(xpp, stag, prefix)
  807. {
  808. if (m_traceName.isEmpty())
  809. m_traceName.set(stag.getValue("name"));
  810. if (!isEmptyString(attrName))
  811. m_name.set(attrName);
  812. else
  813. {
  814. m_name.set(stag.getValue("name"));
  815. const char *xpath_name = stag.getValue("xpath_name");
  816. if (!isEmptyString(xpath_name))
  817. m_xpath_name.setown(compileXpath(xpath_name));
  818. else if (m_name.isEmpty())
  819. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without name", m_traceName, !m_ignoreCodingErrors); //don't mention value, it's deprecated
  820. }
  821. const char *select = stag.getValue("select");
  822. if (isEmptyString(select))
  823. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without select", m_traceName, !m_ignoreCodingErrors); //don't mention value, it's deprecated
  824. m_select.setown(compileXpath(select));
  825. }
  826. virtual void toDBGLog() override
  827. {
  828. #if defined(_DEBUG)
  829. DBGLOG(">%s> %s(name(%s), select('%s'))", m_traceName.str(), m_tagname.str(), (m_xpath_name) ? m_xpath_name->getXpath() : m_name.str(), m_select->getXpath());
  830. #endif
  831. }
  832. virtual ~CEsdlTransformOperationSetSectionAttributeBase(){}
  833. virtual const char *getSectionName() = 0;
  834. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  835. {
  836. if ((!m_name && !m_xpath_name) || !m_select)
  837. return false; //only here if "optional" backward compatible support for now (optional syntax errors aren't actually helpful)
  838. try
  839. {
  840. StringBuffer name;
  841. if (m_xpath_name)
  842. sourceContext->evaluateAsString(m_xpath_name, name);
  843. else
  844. name.set(m_name);
  845. StringBuffer value;
  846. sourceContext->evaluateAsString(m_select, value);
  847. scriptContext->setAttribute(getSectionName(), name, value);
  848. }
  849. catch (IException* e)
  850. {
  851. int code = e->errorCode();
  852. StringBuffer msg;
  853. e->errorMessage(msg);
  854. e->Release();
  855. esdlOperationError(code, m_tagname, msg, m_traceName, !m_ignoreCodingErrors);
  856. }
  857. catch (...)
  858. {
  859. esdlOperationError(ESDL_SCRIPT_Error, m_tagname, "unknown exception processing", m_traceName, !m_ignoreCodingErrors);
  860. }
  861. return false;
  862. }
  863. };
  864. class CEsdlTransformOperationStoreValue : public CEsdlTransformOperationSetSectionAttributeBase
  865. {
  866. public:
  867. CEsdlTransformOperationStoreValue(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationSetSectionAttributeBase(xpp, stag, prefix, nullptr)
  868. {
  869. }
  870. virtual ~CEsdlTransformOperationStoreValue(){}
  871. const char *getSectionName() override {return ESDLScriptCtxSection_Store;}
  872. };
  873. class CEsdlTransformOperationSetLogProfile : public CEsdlTransformOperationSetSectionAttributeBase
  874. {
  875. public:
  876. CEsdlTransformOperationSetLogProfile(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationSetSectionAttributeBase(xpp, stag, prefix, "profile")
  877. {
  878. }
  879. virtual ~CEsdlTransformOperationSetLogProfile(){}
  880. const char *getSectionName() override {return ESDLScriptCtxSection_Logging;}
  881. };
  882. class CEsdlTransformOperationSetLogOption : public CEsdlTransformOperationSetSectionAttributeBase
  883. {
  884. public:
  885. CEsdlTransformOperationSetLogOption(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationSetSectionAttributeBase(xpp, stag, prefix, nullptr)
  886. {
  887. }
  888. virtual ~CEsdlTransformOperationSetLogOption(){}
  889. const char *getSectionName() override {return ESDLScriptCtxSection_Logging;}
  890. };
  891. class CEsdlTransformOperationSetValue : public CEsdlTransformOperationWithoutChildren
  892. {
  893. protected:
  894. Owned<ICompiledXpath> m_select;
  895. Owned<ICompiledXpath> m_xpath_target;
  896. StringAttr m_target;
  897. bool m_required = true;
  898. public:
  899. CEsdlTransformOperationSetValue(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithoutChildren(xpp, stag, prefix)
  900. {
  901. if (m_traceName.isEmpty())
  902. m_traceName.set(stag.getValue("name"));
  903. const char *xpath_target = stag.getValue("xpath_target");
  904. const char *target = stag.getValue("target");
  905. if (isEmptyString(target) && isEmptyString(xpath_target))
  906. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname.str(), "without target", m_traceName.str(), !m_ignoreCodingErrors);
  907. const char *select = stag.getValue("select");
  908. if (isEmptyString(select))
  909. select = stag.getValue("value");
  910. if (isEmptyString(select))
  911. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without select", m_traceName, !m_ignoreCodingErrors); //don't mention value, it's deprecated
  912. m_select.setown(compileXpath(select));
  913. if (!isEmptyString(xpath_target))
  914. m_xpath_target.setown(compileXpath(xpath_target));
  915. else if (!isEmptyString(target))
  916. m_target.set(target);
  917. m_required = getStartTagValueBool(stag, "required", true);
  918. }
  919. virtual void toDBGLog() override
  920. {
  921. #if defined(_DEBUG)
  922. DBGLOG(">%s> %s(%s, select('%s'))", m_traceName.str(), m_tagname.str(), m_target.str(), m_select->getXpath());
  923. #endif
  924. }
  925. virtual ~CEsdlTransformOperationSetValue(){}
  926. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  927. {
  928. if ((!m_xpath_target && m_target.isEmpty()) || !m_select)
  929. return false; //only here if "optional" backward compatible support for now (optional syntax errors aren't actually helpful
  930. try
  931. {
  932. StringBuffer value;
  933. sourceContext->evaluateAsString(m_select, value);
  934. return doSet(sourceContext, targetContext, value);
  935. }
  936. catch (IException* e)
  937. {
  938. int code = e->errorCode();
  939. StringBuffer msg;
  940. e->errorMessage(msg);
  941. e->Release();
  942. esdlOperationError(code, m_tagname, msg, m_traceName, !m_ignoreCodingErrors);
  943. }
  944. catch (...)
  945. {
  946. esdlOperationError(ESDL_SCRIPT_Error, m_tagname, "unknown exception processing", m_traceName, !m_ignoreCodingErrors);
  947. }
  948. return false;
  949. }
  950. const char *getTargetPath(IXpathContext * xpathContext, StringBuffer &s)
  951. {
  952. if (m_xpath_target)
  953. {
  954. xpathContext->evaluateAsString(m_xpath_target, s);
  955. return s;
  956. }
  957. return m_target.str();
  958. }
  959. virtual bool doSet(IXpathContext * sourceContext, IXpathContext *targetContext, const char *value)
  960. {
  961. StringBuffer xpath;
  962. const char *target = getTargetPath(sourceContext, xpath);
  963. targetContext->ensureSetValue(target, value, m_required);
  964. return true;
  965. }
  966. };
  967. class CEsdlTransformOperationNamespace : public CEsdlTransformOperationWithoutChildren
  968. {
  969. protected:
  970. StringAttr m_prefix;
  971. StringAttr m_uri;
  972. bool m_current = false;
  973. public:
  974. CEsdlTransformOperationNamespace(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithoutChildren(xpp, stag, prefix)
  975. {
  976. const char *pfx = stag.getValue("prefix");
  977. const char *uri = stag.getValue("uri");
  978. if (m_traceName.isEmpty())
  979. m_traceName.set(pfx);
  980. if (!pfx && isEmptyString(uri))
  981. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname.str(), "without prefix or uri", m_traceName.str(), !m_ignoreCodingErrors);
  982. m_uri.set(uri);
  983. m_prefix.set(pfx);
  984. m_current = getStartTagValueBool(stag, "current", m_uri.isEmpty());
  985. }
  986. virtual void toDBGLog() override
  987. {
  988. #if defined(_DEBUG)
  989. DBGLOG(">%s> %s(prefix('%s'), uri('%s'), current(%d))", m_traceName.str(), m_tagname.str(), m_prefix.str(), m_uri.str(), m_current);
  990. #endif
  991. }
  992. virtual ~CEsdlTransformOperationNamespace(){}
  993. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  994. {
  995. targetContext->setLocationNamespace(m_prefix, m_uri, m_current);
  996. return false;
  997. }
  998. };
  999. class CEsdlTransformOperationRenameNode : public CEsdlTransformOperationWithoutChildren
  1000. {
  1001. protected:
  1002. StringAttr m_target;
  1003. StringAttr m_new_name;
  1004. Owned<ICompiledXpath> m_xpath_target;
  1005. Owned<ICompiledXpath> m_xpath_new_name;
  1006. bool m_all = false;
  1007. public:
  1008. CEsdlTransformOperationRenameNode(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithoutChildren(xpp, stag, prefix)
  1009. {
  1010. const char *new_name = stag.getValue("new_name");
  1011. const char *xpath_new_name = stag.getValue("xpath_new_name");
  1012. if (isEmptyString(new_name) && isEmptyString(xpath_new_name))
  1013. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname.str(), "without new name", m_traceName.str(), !m_ignoreCodingErrors);
  1014. if (m_traceName.isEmpty())
  1015. m_traceName.set(new_name ? new_name : xpath_new_name);
  1016. const char *target = stag.getValue("target");
  1017. const char *xpath_target = stag.getValue("xpath_target");
  1018. if (isEmptyString(target) && isEmptyString(xpath_target))
  1019. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname.str(), "without target", m_traceName.str(), !m_ignoreCodingErrors);
  1020. if (!isEmptyString(xpath_target))
  1021. m_xpath_target.setown(compileXpath(xpath_target));
  1022. else if (!isEmptyString(target))
  1023. m_target.set(target);
  1024. if (!isEmptyString(xpath_new_name))
  1025. m_xpath_new_name.setown(compileXpath(xpath_new_name));
  1026. else if (!isEmptyString(new_name))
  1027. m_new_name.set(new_name);
  1028. m_all = getStartTagValueBool(stag, "all", false);
  1029. }
  1030. virtual void toDBGLog() override
  1031. {
  1032. #if defined(_DEBUG)
  1033. const char *target = (m_xpath_target) ? m_xpath_target->getXpath() : m_target.str();
  1034. const char *new_name = (m_xpath_new_name) ? m_xpath_new_name->getXpath() : m_new_name.str();
  1035. DBGLOG(">%s> %s(%s, new_name('%s'))", m_traceName.str(), m_tagname.str(), target, new_name);
  1036. #endif
  1037. }
  1038. virtual ~CEsdlTransformOperationRenameNode(){}
  1039. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  1040. {
  1041. if ((!m_xpath_target && m_target.isEmpty()) || (!m_xpath_new_name && m_new_name.isEmpty()))
  1042. return false; //only here if "optional" backward compatible support for now (optional syntax errors aren't actually helpful
  1043. try
  1044. {
  1045. StringBuffer path;
  1046. if (m_xpath_target)
  1047. sourceContext->evaluateAsString(m_xpath_target, path);
  1048. else
  1049. path.set(m_target);
  1050. StringBuffer name;
  1051. if (m_xpath_new_name)
  1052. sourceContext->evaluateAsString(m_xpath_new_name, name);
  1053. else
  1054. name.set(m_new_name);
  1055. targetContext->rename(path, name, m_all);
  1056. }
  1057. catch (IException* e)
  1058. {
  1059. int code = e->errorCode();
  1060. StringBuffer msg;
  1061. e->errorMessage(msg);
  1062. e->Release();
  1063. esdlOperationError(code, m_tagname, msg, m_traceName, !m_ignoreCodingErrors);
  1064. }
  1065. catch (...)
  1066. {
  1067. esdlOperationError(ESDL_SCRIPT_Error, m_tagname, "unknown exception processing", m_traceName, !m_ignoreCodingErrors);
  1068. }
  1069. return false;
  1070. }
  1071. };
  1072. class CEsdlTransformOperationCopyOf : public CEsdlTransformOperationWithoutChildren
  1073. {
  1074. protected:
  1075. Owned<ICompiledXpath> m_select;
  1076. StringAttr m_new_name;
  1077. public:
  1078. CEsdlTransformOperationCopyOf(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithoutChildren(xpp, stag, prefix)
  1079. {
  1080. const char *select = stag.getValue("select");
  1081. if (isEmptyString(select))
  1082. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname.str(), "without select", m_traceName.str(), !m_ignoreCodingErrors);
  1083. m_select.setown(compileXpath(select));
  1084. m_new_name.set(stag.getValue("new_name"));
  1085. }
  1086. virtual void toDBGLog() override
  1087. {
  1088. #if defined(_DEBUG)
  1089. DBGLOG(">%s> %s(%s, new_name('%s'))", m_traceName.str(), m_tagname.str(), m_select->getXpath(), m_new_name.str());
  1090. #endif
  1091. }
  1092. virtual ~CEsdlTransformOperationCopyOf(){}
  1093. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  1094. {
  1095. try
  1096. {
  1097. targetContext->copyFromPrimaryContext(m_select, m_new_name);
  1098. }
  1099. catch (IException* e)
  1100. {
  1101. int code = e->errorCode();
  1102. StringBuffer msg;
  1103. e->errorMessage(msg);
  1104. e->Release();
  1105. esdlOperationError(code, m_tagname, msg, m_traceName, !m_ignoreCodingErrors);
  1106. }
  1107. catch (...)
  1108. {
  1109. esdlOperationError(ESDL_SCRIPT_Error, m_tagname, "unknown exception processing", m_traceName, !m_ignoreCodingErrors);
  1110. }
  1111. return false;
  1112. }
  1113. };
  1114. class CEsdlTransformOperationRemoveNode : public CEsdlTransformOperationWithoutChildren
  1115. {
  1116. protected:
  1117. StringAttr m_target;
  1118. Owned<ICompiledXpath> m_xpath_target;
  1119. bool m_all = false;
  1120. public:
  1121. CEsdlTransformOperationRemoveNode(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithoutChildren(xpp, stag, prefix)
  1122. {
  1123. const char *target = stag.getValue("target");
  1124. const char *xpath_target = stag.getValue("xpath_target");
  1125. if (isEmptyString(target) && isEmptyString(xpath_target))
  1126. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname.str(), "without target", m_traceName.str(), !m_ignoreCodingErrors);
  1127. if (target && isWildString(target))
  1128. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname.str(), "wildcard in target not yet supported", m_traceName.str(), !m_ignoreCodingErrors);
  1129. if (!isEmptyString(xpath_target))
  1130. m_xpath_target.setown(compileXpath(xpath_target));
  1131. else if (!isEmptyString(target))
  1132. m_target.set(target);
  1133. m_all = getStartTagValueBool(stag, "all", false);
  1134. }
  1135. virtual void toDBGLog() override
  1136. {
  1137. #if defined(_DEBUG)
  1138. const char *target = (m_xpath_target) ? m_xpath_target->getXpath() : m_target.str();
  1139. DBGLOG(">%s> %s(%s)", m_traceName.str(), m_tagname.str(), target);
  1140. #endif
  1141. }
  1142. virtual ~CEsdlTransformOperationRemoveNode(){}
  1143. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  1144. {
  1145. if ((!m_xpath_target && m_target.isEmpty()))
  1146. return false; //only here if "optional" backward compatible support for now (optional syntax errors aren't actually helpful
  1147. try
  1148. {
  1149. StringBuffer path;
  1150. if (m_xpath_target)
  1151. sourceContext->evaluateAsString(m_xpath_target, path);
  1152. else
  1153. path.set(m_target);
  1154. targetContext->remove(path, m_all);
  1155. }
  1156. catch (IException* e)
  1157. {
  1158. int code = e->errorCode();
  1159. StringBuffer msg;
  1160. e->errorMessage(msg);
  1161. e->Release();
  1162. esdlOperationError(code, m_tagname, msg, m_traceName, !m_ignoreCodingErrors);
  1163. }
  1164. catch (...)
  1165. {
  1166. esdlOperationError(ESDL_SCRIPT_Error, m_tagname, "unknown exception processing", m_traceName, !m_ignoreCodingErrors);
  1167. }
  1168. return false;
  1169. }
  1170. };
  1171. class CEsdlTransformOperationAppendValue : public CEsdlTransformOperationSetValue
  1172. {
  1173. public:
  1174. CEsdlTransformOperationAppendValue(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationSetValue(xpp, stag, prefix){}
  1175. virtual ~CEsdlTransformOperationAppendValue(){}
  1176. virtual bool doSet(IXpathContext * sourceContext, IXpathContext *targetContext, const char *value) override
  1177. {
  1178. StringBuffer xpath;
  1179. const char *target = getTargetPath(sourceContext, xpath);
  1180. targetContext->ensureAppendToValue(target, value, m_required);
  1181. return true;
  1182. }
  1183. };
  1184. class CEsdlTransformOperationAddValue : public CEsdlTransformOperationSetValue
  1185. {
  1186. public:
  1187. CEsdlTransformOperationAddValue(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationSetValue(xpp, stag, prefix){}
  1188. virtual ~CEsdlTransformOperationAddValue(){}
  1189. virtual bool doSet(IXpathContext * sourceContext, IXpathContext *targetContext, const char *value) override
  1190. {
  1191. StringBuffer xpath;
  1192. const char *target = getTargetPath(sourceContext, xpath);
  1193. targetContext->ensureAddValue(target, value, m_required);
  1194. return true;
  1195. }
  1196. };
  1197. class CEsdlTransformOperationFail : public CEsdlTransformOperationWithoutChildren
  1198. {
  1199. protected:
  1200. Owned<ICompiledXpath> m_message;
  1201. Owned<ICompiledXpath> m_code;
  1202. public:
  1203. CEsdlTransformOperationFail(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithoutChildren(xpp, stag, prefix)
  1204. {
  1205. if (m_traceName.isEmpty())
  1206. m_traceName.set(stag.getValue("name"));
  1207. const char *code = stag.getValue("code");
  1208. const char *message = stag.getValue("message");
  1209. if (isEmptyString(code))
  1210. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without code", m_traceName.str(), true);
  1211. if (isEmptyString(message))
  1212. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without message", m_traceName.str(), true);
  1213. m_code.setown(compileXpath(code));
  1214. m_message.setown(compileXpath(message));
  1215. }
  1216. virtual ~CEsdlTransformOperationFail()
  1217. {
  1218. }
  1219. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  1220. {
  1221. int code = m_code.get() ? (int) sourceContext->evaluateAsNumber(m_code) : ESDL_SCRIPT_Error;
  1222. StringBuffer msg;
  1223. if (m_message.get())
  1224. sourceContext->evaluateAsString(m_message, msg);
  1225. throw makeStringException(code, msg.str());
  1226. return true; //avoid compilation error
  1227. }
  1228. virtual void toDBGLog() override
  1229. {
  1230. #if defined(_DEBUG)
  1231. DBGLOG(">%s> %s with message(%s)", m_traceName.str(), m_tagname.str(), m_message.get() ? m_message->getXpath() : "");
  1232. #endif
  1233. }
  1234. };
  1235. class CEsdlTransformOperationAssert : public CEsdlTransformOperationFail
  1236. {
  1237. private:
  1238. Owned<ICompiledXpath> m_test; //assert is like a conditional fail
  1239. public:
  1240. CEsdlTransformOperationAssert(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationFail(xpp, stag, prefix)
  1241. {
  1242. const char *test = stag.getValue("test");
  1243. if (isEmptyString(test))
  1244. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without test", m_traceName.str(), true);
  1245. m_test.setown(compileXpath(test));
  1246. }
  1247. virtual ~CEsdlTransformOperationAssert()
  1248. {
  1249. }
  1250. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  1251. {
  1252. if (m_test && sourceContext->evaluateAsBoolean(m_test))
  1253. return false;
  1254. return CEsdlTransformOperationFail::process(scriptContext, targetContext, sourceContext);
  1255. }
  1256. virtual void toDBGLog() override
  1257. {
  1258. #if defined(_DEBUG)
  1259. const char *testXpath = m_test.get() ? m_test->getXpath() : "SYNTAX ERROR IN test";
  1260. DBGLOG(">%s> %s if '%s' with message(%s)", m_traceName.str(), m_tagname.str(), testXpath, m_message.get() ? m_message->getXpath() : "");
  1261. #endif
  1262. }
  1263. };
  1264. class CEsdlTransformOperationForEach : public CEsdlTransformOperationWithChildren
  1265. {
  1266. protected:
  1267. Owned<ICompiledXpath> m_select;
  1268. public:
  1269. CEsdlTransformOperationForEach(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithChildren(xpp, stag, prefix, true, nullptr)
  1270. {
  1271. const char *select = stag.getValue("select");
  1272. if (isEmptyString(select))
  1273. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without select", !m_ignoreCodingErrors);
  1274. m_select.setown(compileXpath(select));
  1275. }
  1276. virtual ~CEsdlTransformOperationForEach(){}
  1277. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  1278. {
  1279. Owned<IXpathContextIterator> contexts = evaluate(sourceContext);
  1280. if (!contexts)
  1281. return false;
  1282. if (!contexts->first())
  1283. return false;
  1284. ForEach(*contexts)
  1285. processChildren(scriptContext, targetContext, &contexts->query());
  1286. return true;
  1287. }
  1288. virtual void toDBGLog () override
  1289. {
  1290. #if defined(_DEBUG)
  1291. DBGLOG (">>>>%s %s ", m_tagname.str(), m_select ? m_select->getXpath() : "");
  1292. CEsdlTransformOperationWithChildren::toDBGLog();
  1293. DBGLOG ("<<<<%s<<<<<", m_tagname.str());
  1294. #endif
  1295. }
  1296. private:
  1297. IXpathContextIterator *evaluate(IXpathContext * xpathContext)
  1298. {
  1299. IXpathContextIterator *xpathset = nullptr;
  1300. try
  1301. {
  1302. xpathset = xpathContext->evaluateAsNodeSet(m_select);
  1303. }
  1304. catch (IException* e)
  1305. {
  1306. int code = e->errorCode();
  1307. StringBuffer msg;
  1308. e->errorMessage(msg);
  1309. e->Release();
  1310. esdlOperationError(code, m_tagname, msg, !m_ignoreCodingErrors);
  1311. }
  1312. catch (...)
  1313. {
  1314. VStringBuffer msg("unknown exception evaluating select '%s'", m_select.get() ? m_select->getXpath() : "undefined!");
  1315. esdlOperationError(ESDL_SCRIPT_Error, m_tagname, msg, !m_ignoreCodingErrors);
  1316. }
  1317. return xpathset;
  1318. }
  1319. };
  1320. class CEsdlTransformOperationConditional : public CEsdlTransformOperationWithChildren
  1321. {
  1322. private:
  1323. Owned<ICompiledXpath> m_test;
  1324. char m_op = 'i'; //'i'=if, 'w'=when, 'o'=otherwise
  1325. public:
  1326. CEsdlTransformOperationConditional(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithChildren(xpp, stag, prefix, true, nullptr)
  1327. {
  1328. const char *op = stag.getLocalName();
  1329. if (isEmptyString(op)) //should never get here, we checked already, but
  1330. esdlOperationError(ESDL_SCRIPT_UnknownOperation, m_tagname, "unrecognized conditional missing tag name", !m_ignoreCodingErrors);
  1331. //m_ignoreCodingErrors means op may still be null
  1332. else if (!op || streq(op, "if"))
  1333. m_op = 'i';
  1334. else if (streq(op, "when"))
  1335. m_op = 'w';
  1336. else if (streq(op, "otherwise"))
  1337. m_op = 'o';
  1338. else //should never get here either, but
  1339. esdlOperationError(ESDL_SCRIPT_UnknownOperation, m_tagname, "unrecognized conditional tag name", !m_ignoreCodingErrors);
  1340. if (m_op!='o')
  1341. {
  1342. const char *test = stag.getValue("test");
  1343. if (isEmptyString(test))
  1344. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, m_tagname, "without test", !m_ignoreCodingErrors);
  1345. m_test.setown(compileXpath(test));
  1346. }
  1347. }
  1348. virtual ~CEsdlTransformOperationConditional(){}
  1349. virtual bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  1350. {
  1351. if (!evaluate(sourceContext))
  1352. return false;
  1353. processChildren(scriptContext, targetContext, sourceContext);
  1354. return true; //just means that evaluation succeeded and attempted to process children
  1355. }
  1356. virtual void toDBGLog () override
  1357. {
  1358. #if defined(_DEBUG)
  1359. DBGLOG (">>>>%s %s ", m_tagname.str(), m_test ? m_test->getXpath() : "");
  1360. CEsdlTransformOperationWithChildren::toDBGLog();
  1361. DBGLOG ("<<<<%s<<<<<", m_tagname.str());
  1362. #endif
  1363. }
  1364. private:
  1365. bool evaluate(IXpathContext * xpathContext)
  1366. {
  1367. if (m_op=='o') //'o'/"otherwise" is unconditional
  1368. return true;
  1369. bool match = false;
  1370. try
  1371. {
  1372. match = xpathContext->evaluateAsBoolean(m_test);
  1373. }
  1374. catch (IException* e)
  1375. {
  1376. int code = e->errorCode();
  1377. StringBuffer msg;
  1378. e->errorMessage(msg);
  1379. e->Release();
  1380. esdlOperationError(code, m_tagname, msg, !m_ignoreCodingErrors);
  1381. }
  1382. catch (...)
  1383. {
  1384. VStringBuffer msg("unknown exception evaluating test '%s'", m_test.get() ? m_test->getXpath() : "undefined!");
  1385. esdlOperationError(ESDL_SCRIPT_Error, m_tagname, msg, !m_ignoreCodingErrors);
  1386. }
  1387. return match;
  1388. }
  1389. };
  1390. void loadChooseChildren(IArrayOf<IEsdlTransformOperation> &operations, XmlPullParser &xpp, const StringBuffer &prefix, bool withVariables, bool ignoreCodingErrors)
  1391. {
  1392. Owned<CEsdlTransformOperationConditional> otherwise;
  1393. int type = 0;
  1394. while((type = xpp.next()) != XmlPullParser::END_DOCUMENT)
  1395. {
  1396. switch(type)
  1397. {
  1398. case XmlPullParser::START_TAG:
  1399. {
  1400. StartTag opTag;
  1401. xpp.readStartTag(opTag);
  1402. const char *op = opTag.getLocalName();
  1403. if (streq(op, "when"))
  1404. operations.append(*new CEsdlTransformOperationConditional(xpp, opTag, prefix));
  1405. else if (streq(op, "otherwise"))
  1406. {
  1407. if (otherwise)
  1408. esdlOperationError(ESDL_SCRIPT_Error, op, "only 1 otherwise per choose statement allowed", ignoreCodingErrors);
  1409. otherwise.setown(new CEsdlTransformOperationConditional(xpp, opTag, prefix));
  1410. }
  1411. break;
  1412. }
  1413. case XmlPullParser::END_TAG:
  1414. case XmlPullParser::END_DOCUMENT:
  1415. {
  1416. if (otherwise)
  1417. operations.append(*otherwise.getClear());
  1418. return;
  1419. }
  1420. }
  1421. }
  1422. }
  1423. class CEsdlTransformOperationChoose : public CEsdlTransformOperationWithChildren
  1424. {
  1425. public:
  1426. CEsdlTransformOperationChoose(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithChildren(xpp, stag, prefix, false, loadChooseChildren)
  1427. {
  1428. }
  1429. virtual ~CEsdlTransformOperationChoose(){}
  1430. bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  1431. {
  1432. return processChildren(scriptContext, targetContext, sourceContext);
  1433. }
  1434. virtual bool processChildren(IEsdlScriptContext * scriptContext, IXpathContext *targetContext, IXpathContext * sourceContext) override
  1435. {
  1436. if (m_children.length())
  1437. {
  1438. CXpathContextScope scope(sourceContext, "choose");
  1439. ForEachItemIn(i, m_children)
  1440. {
  1441. if (m_children.item(i).process(scriptContext, targetContext, sourceContext))
  1442. return true;
  1443. }
  1444. }
  1445. return false;
  1446. }
  1447. virtual void toDBGLog () override
  1448. {
  1449. #if defined(_DEBUG)
  1450. DBGLOG (">>>>>>>>>>> %s >>>>>>>>>>", m_tagname.str());
  1451. CEsdlTransformOperationWithChildren::toDBGLog();
  1452. DBGLOG (">>>>>>>>>>> %s >>>>>>>>>>", m_tagname.str());
  1453. #endif
  1454. }
  1455. };
  1456. class CEsdlTransformOperationTarget : public CEsdlTransformOperationWithChildren
  1457. {
  1458. protected:
  1459. Owned<ICompiledXpath> m_xpath;
  1460. bool m_required = true;
  1461. bool m_ensure = false;
  1462. public:
  1463. CEsdlTransformOperationTarget(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithChildren(xpp, stag, prefix, true, nullptr)
  1464. {
  1465. const char *xpath = stag.getValue("xpath");
  1466. if (isEmptyString(xpath))
  1467. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, "target", "without xpath parameter", m_traceName.str(), !m_ignoreCodingErrors);
  1468. m_xpath.setown(compileXpath(xpath));
  1469. m_required = getStartTagValueBool(stag, "required", m_required);
  1470. }
  1471. virtual ~CEsdlTransformOperationTarget(){}
  1472. bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  1473. {
  1474. CXpathContextLocation location(targetContext);
  1475. bool success = false;
  1476. if (m_ensure)
  1477. success = targetContext->ensureLocation(m_xpath->getXpath(), m_required);
  1478. else
  1479. success = targetContext->setLocation(m_xpath, m_required);
  1480. if (success)
  1481. return processChildren(scriptContext, targetContext, sourceContext);
  1482. return false;
  1483. }
  1484. virtual void toDBGLog () override
  1485. {
  1486. #if defined(_DEBUG)
  1487. DBGLOG(">>>%s> %s(%s)>>>>", m_traceName.str(), m_tagname.str(), m_xpath.get() ? m_xpath->getXpath() : "");
  1488. CEsdlTransformOperationWithChildren::toDBGLog();
  1489. DBGLOG (">>>>>>>>>>> %s >>>>>>>>>>", m_tagname.str());
  1490. #endif
  1491. }
  1492. };
  1493. class CEsdlTransformOperationIfTarget : public CEsdlTransformOperationTarget
  1494. {
  1495. public:
  1496. CEsdlTransformOperationIfTarget(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationTarget(xpp, stag, prefix)
  1497. {
  1498. m_required = false;
  1499. }
  1500. virtual ~CEsdlTransformOperationIfTarget(){}
  1501. };
  1502. class CEsdlTransformOperationEnsureTarget : public CEsdlTransformOperationTarget
  1503. {
  1504. public:
  1505. CEsdlTransformOperationEnsureTarget(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationTarget(xpp, stag, prefix)
  1506. {
  1507. m_ensure = true;
  1508. }
  1509. virtual ~CEsdlTransformOperationEnsureTarget(){}
  1510. };
  1511. class CEsdlTransformOperationSource : public CEsdlTransformOperationWithChildren
  1512. {
  1513. protected:
  1514. Owned<ICompiledXpath> m_xpath;
  1515. bool m_required = true;
  1516. public:
  1517. CEsdlTransformOperationSource(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithChildren(xpp, stag, prefix, false, nullptr)
  1518. {
  1519. const char *xpath = stag.getValue("xpath");
  1520. if (isEmptyString(xpath))
  1521. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, "target", "without xpath parameter", m_traceName.str(), !m_ignoreCodingErrors);
  1522. m_xpath.setown(compileXpath(xpath));
  1523. m_required = getStartTagValueBool(stag, "required", m_required);
  1524. }
  1525. virtual ~CEsdlTransformOperationSource(){}
  1526. bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  1527. {
  1528. CXpathContextLocation location(sourceContext);
  1529. if (sourceContext->setLocation(m_xpath, m_required))
  1530. return processChildren(scriptContext, targetContext, sourceContext);
  1531. return false;
  1532. }
  1533. virtual void toDBGLog () override
  1534. {
  1535. #if defined(_DEBUG)
  1536. DBGLOG(">>>%s> %s(%s)>>>>", m_traceName.str(), m_tagname.str(), m_xpath.get() ? m_xpath->getXpath() : "");
  1537. CEsdlTransformOperationWithChildren::toDBGLog();
  1538. DBGLOG (">>>>>>>>>>> %s >>>>>>>>>>", m_tagname.str());
  1539. #endif
  1540. }
  1541. };
  1542. class CEsdlTransformOperationIfSource : public CEsdlTransformOperationSource
  1543. {
  1544. public:
  1545. CEsdlTransformOperationIfSource(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationSource(xpp, stag, prefix)
  1546. {
  1547. m_required = false;
  1548. }
  1549. virtual ~CEsdlTransformOperationIfSource(){}
  1550. };
  1551. class CEsdlTransformOperationElement : public CEsdlTransformOperationWithChildren
  1552. {
  1553. protected:
  1554. StringBuffer m_name;
  1555. StringBuffer m_nsuri;
  1556. public:
  1557. CEsdlTransformOperationElement(XmlPullParser &xpp, StartTag &stag, const StringBuffer &prefix) : CEsdlTransformOperationWithChildren(xpp, stag, prefix, true, nullptr)
  1558. {
  1559. m_name.set(stag.getValue("name"));
  1560. if (m_name.isEmpty())
  1561. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, "element", "without name parameter", m_traceName.str(), !m_ignoreCodingErrors);
  1562. if (m_traceName.isEmpty())
  1563. m_traceName.set(m_name);
  1564. if (!validateXMLTag(m_name))
  1565. {
  1566. VStringBuffer msg("with invalid element name '%s'", m_name.str());
  1567. esdlOperationError(ESDL_SCRIPT_MissingOperationAttr, "element", msg.str(), m_traceName.str(), !m_ignoreCodingErrors);
  1568. }
  1569. m_nsuri.set(stag.getValue("namespace"));
  1570. }
  1571. virtual ~CEsdlTransformOperationElement(){}
  1572. bool process(IEsdlScriptContext * scriptContext, IXpathContext * targetContext, IXpathContext * sourceContext) override
  1573. {
  1574. CXpathContextLocation location(targetContext);
  1575. targetContext->addElementToLocation(m_name);
  1576. return processChildren(scriptContext, targetContext, sourceContext);
  1577. }
  1578. virtual void toDBGLog () override
  1579. {
  1580. #if defined(_DEBUG)
  1581. DBGLOG (">>>>>>>>>>> %s (%s, nsuri(%s)) >>>>>>>>>>", m_tagname.str(), m_name.str(), m_nsuri.str());
  1582. CEsdlTransformOperationWithChildren::toDBGLog();
  1583. DBGLOG (">>>>>>>>>>> %s >>>>>>>>>>", m_tagname.str());
  1584. #endif
  1585. }
  1586. };
  1587. void createEsdlTransformOperations(IArrayOf<IEsdlTransformOperation> &operations, XmlPullParser &xpp, const StringBuffer &prefix, bool withVariables, bool ignoreCodingErrors)
  1588. {
  1589. int type = 0;
  1590. while((type = xpp.next()) != XmlPullParser::END_DOCUMENT)
  1591. {
  1592. switch(type)
  1593. {
  1594. case XmlPullParser::START_TAG:
  1595. {
  1596. Owned<IEsdlTransformOperation> operation = createEsdlTransformOperation(xpp, prefix, withVariables, ignoreCodingErrors);
  1597. if (operation)
  1598. operations.append(*operation.getClear());
  1599. break;
  1600. }
  1601. case XmlPullParser::END_TAG:
  1602. return;
  1603. case XmlPullParser::END_DOCUMENT:
  1604. return;
  1605. }
  1606. }
  1607. }
  1608. IEsdlTransformOperation *createEsdlTransformOperation(XmlPullParser &xpp, const StringBuffer &prefix, bool withVariables, bool ignoreCodingErrors)
  1609. {
  1610. StartTag stag;
  1611. xpp.readStartTag(stag);
  1612. const char *op = stag.getLocalName();
  1613. if (isEmptyString(op))
  1614. return nullptr;
  1615. if (withVariables)
  1616. {
  1617. if (streq(op, "variable"))
  1618. return new CEsdlTransformOperationVariable(xpp, stag, prefix);
  1619. if (streq(op, "param"))
  1620. return new CEsdlTransformOperationParameter(xpp, stag, prefix);
  1621. }
  1622. if (streq(op, "choose"))
  1623. return new CEsdlTransformOperationChoose(xpp, stag, prefix);
  1624. if (streq(op, "for-each"))
  1625. return new CEsdlTransformOperationForEach(xpp, stag, prefix);
  1626. if (streq(op, "if"))
  1627. return new CEsdlTransformOperationConditional(xpp, stag, prefix);
  1628. if (streq(op, "set-value") || streq(op, "SetValue"))
  1629. return new CEsdlTransformOperationSetValue(xpp, stag, prefix);
  1630. if (streq(op, "append-to-value") || streq(op, "AppendValue"))
  1631. return new CEsdlTransformOperationAppendValue(xpp, stag, prefix);
  1632. if (streq(op, "add-value"))
  1633. return new CEsdlTransformOperationAddValue(xpp, stag, prefix);
  1634. if (streq(op, "fail"))
  1635. return new CEsdlTransformOperationFail(xpp, stag, prefix);
  1636. if (streq(op, "assert"))
  1637. return new CEsdlTransformOperationAssert(xpp, stag, prefix);
  1638. if (streq(op, "store-value"))
  1639. return new CEsdlTransformOperationStoreValue(xpp, stag, prefix);
  1640. if (streq(op, "set-log-profile"))
  1641. return new CEsdlTransformOperationSetLogProfile(xpp, stag, prefix);
  1642. if (streq(op, "set-log-option"))
  1643. return new CEsdlTransformOperationSetLogOption(xpp, stag, prefix);
  1644. if (streq(op, "rename-node"))
  1645. return new CEsdlTransformOperationRenameNode(xpp, stag, prefix);
  1646. if (streq(op, "remove-node"))
  1647. return new CEsdlTransformOperationRemoveNode(xpp, stag, prefix);
  1648. if (streq(op, "source"))
  1649. return new CEsdlTransformOperationSource(xpp, stag, prefix);
  1650. if (streq(op, "if-source"))
  1651. return new CEsdlTransformOperationIfSource(xpp, stag, prefix);
  1652. if (streq(op, "target"))
  1653. return new CEsdlTransformOperationTarget(xpp, stag, prefix);
  1654. if (streq(op, "if-target"))
  1655. return new CEsdlTransformOperationIfTarget(xpp, stag, prefix);
  1656. if (streq(op, "ensure-target"))
  1657. return new CEsdlTransformOperationEnsureTarget(xpp, stag, prefix);
  1658. if (streq(op, "element"))
  1659. return new CEsdlTransformOperationElement(xpp, stag, prefix);
  1660. if (streq(op, "copy-of"))
  1661. return new CEsdlTransformOperationCopyOf(xpp, stag, prefix);
  1662. if (streq(op, "namespace"))
  1663. return new CEsdlTransformOperationNamespace(xpp, stag, prefix);
  1664. if (streq(op, "http-post-xml"))
  1665. return new CEsdlTransformOperationHttpPostXml(xpp, stag, prefix);
  1666. if (streq(op, "mysql"))
  1667. return new CEsdlTransformOperationMySqlCall(xpp, stag, prefix);
  1668. return nullptr;
  1669. }
  1670. static inline void replaceVariable(StringBuffer &s, IXpathContext *xpathContext, const char *name)
  1671. {
  1672. StringBuffer temp;
  1673. const char *val = xpathContext->getVariable(name, temp);
  1674. if (val)
  1675. {
  1676. VStringBuffer match("{$%s}", name);
  1677. s.replaceString(match, val);
  1678. }
  1679. }
  1680. class CEsdlCustomTransform : public CInterfaceOf<IEsdlCustomTransform>
  1681. {
  1682. private:
  1683. IArrayOf<IEsdlTransformOperation> m_operations;
  1684. Owned<IProperties> namespaces = createProperties(false);
  1685. StringAttr m_name;
  1686. StringAttr m_target;
  1687. StringAttr m_source;
  1688. StringBuffer m_prefix;
  1689. public:
  1690. CEsdlCustomTransform(){}
  1691. CEsdlCustomTransform(XmlPullParser &xpp, StartTag &stag, const char *ns_prefix) : m_prefix(ns_prefix)
  1692. {
  1693. const char *tag = stag.getLocalName();
  1694. m_name.set(stag.getValue("name"));
  1695. m_target.set(stag.getValue("target"));
  1696. m_source.set(stag.getValue("source"));
  1697. DBGLOG("Compiling ESDL Transform: '%s'", m_name.str());
  1698. map< string, const SXT_CHAR* >::iterator it = xpp.getNsBegin();
  1699. while (it != xpp.getNsEnd())
  1700. {
  1701. if (it->first.compare("xml")!=0)
  1702. namespaces->setProp(it->first.c_str(), it->second);
  1703. it++;
  1704. }
  1705. int type = 0;
  1706. while((type = xpp.next()) != XmlPullParser::END_DOCUMENT)
  1707. {
  1708. switch(type)
  1709. {
  1710. case XmlPullParser::START_TAG:
  1711. {
  1712. Owned<IEsdlTransformOperation> operation = createEsdlTransformOperation(xpp, m_prefix, true, false);
  1713. if (operation)
  1714. m_operations.append(*operation.getClear());
  1715. break;
  1716. }
  1717. case XmlPullParser::END_TAG:
  1718. case XmlPullParser::END_DOCUMENT:
  1719. return;
  1720. }
  1721. }
  1722. }
  1723. virtual void appendPrefixes(StringArray &prefixes) override
  1724. {
  1725. if (m_prefix.length())
  1726. {
  1727. StringAttr copy(m_prefix.str(), m_prefix.length()-1); //remove the colon
  1728. prefixes.appendUniq(copy.str());
  1729. }
  1730. else
  1731. prefixes.appendUniq("");
  1732. }
  1733. virtual void toDBGLog() override
  1734. {
  1735. #if defined(_DEBUG)
  1736. DBGLOG(">>>>>>>>>>>>>>>>transform: '%s'>>>>>>>>>>", m_name.str());
  1737. ForEachItemIn(i, m_operations)
  1738. m_operations.item(i).toDBGLog();
  1739. DBGLOG("<<<<<<<<<<<<<<<<transform<<<<<<<<<<<<");
  1740. #endif
  1741. }
  1742. virtual ~CEsdlCustomTransform(){}
  1743. void processTransformImpl(IEsdlScriptContext * scriptContext, const char *srcSection, const char *tgtSection, IXpathContext *sourceContext, const char *target) override
  1744. {
  1745. if (m_target.length())
  1746. target = m_target.str();
  1747. Owned<IXpathContext> targetXpath = nullptr;
  1748. if (isEmptyString(tgtSection))
  1749. targetXpath.setown(scriptContext->createXpathContext(sourceContext, srcSection, true));
  1750. else
  1751. targetXpath.setown(scriptContext->getCopiedSectionXpathContext(sourceContext, tgtSection, srcSection, true));
  1752. Owned<IProperties> savedNamespaces = createProperties(false);
  1753. Owned<IPropertyIterator> ns = namespaces->getIterator();
  1754. ForEach(*ns)
  1755. {
  1756. const char *prefix = ns->getPropKey();
  1757. const char *existing = sourceContext->queryNamespace(prefix);
  1758. savedNamespaces->setProp(prefix, isEmptyString(existing) ? "" : existing);
  1759. sourceContext->registerNamespace(prefix, namespaces->queryProp(prefix));
  1760. targetXpath->registerNamespace(prefix, namespaces->queryProp(prefix));
  1761. }
  1762. CXpathContextScope scope(sourceContext, "transform", savedNamespaces);
  1763. if (!isEmptyString(target) && !streq(target, "."))
  1764. targetXpath->setLocation(target, true);
  1765. if (!m_source.isEmpty() && !streq(m_source, "."))
  1766. sourceContext->setLocation(m_source, true);
  1767. ForEachItemIn(i, m_operations)
  1768. m_operations.item(i).process(scriptContext, targetXpath, sourceContext);
  1769. scriptContext->cleanupBetweenScripts();
  1770. }
  1771. void processTransform(IEsdlScriptContext * scriptCtx, const char *srcSection, const char *tgtSection) override;
  1772. };
  1773. class CEsdlCustomTransformWrapper : public CInterfaceOf<IEsdlTransformSet>
  1774. {
  1775. Linked<CEsdlCustomTransform> crt;
  1776. public:
  1777. CEsdlCustomTransformWrapper(CEsdlCustomTransform *t) : crt(t) {}
  1778. void processTransformImpl(IEsdlScriptContext * context, const char *srcSection, const char *tgtSection, IXpathContext *sourceContext, const char *target) override
  1779. {
  1780. crt->processTransformImpl(context, srcSection, tgtSection, sourceContext, target);
  1781. }
  1782. void appendPrefixes(StringArray &prefixes) override
  1783. {
  1784. crt->appendPrefixes(prefixes);
  1785. }
  1786. aindex_t length() override
  1787. {
  1788. return crt ? 1 : 0;
  1789. }
  1790. };
  1791. void CEsdlCustomTransform::processTransform(IEsdlScriptContext * scriptCtx, const char *srcSection, const char *tgtSection)
  1792. {
  1793. CEsdlCustomTransformWrapper tfw(this);
  1794. processServiceAndMethodTransforms(scriptCtx, {static_cast<IEsdlTransformSet*>(&tfw)}, srcSection, tgtSection);
  1795. }
  1796. void processServiceAndMethodTransforms(IEsdlScriptContext * scriptCtx, std::initializer_list<IEsdlTransformSet *> const &transforms, const char *srcSection, const char *tgtSection)
  1797. {
  1798. LogLevel level = LogMin;
  1799. if (!scriptCtx)
  1800. return;
  1801. if (!transforms.size())
  1802. return;
  1803. if (isEmptyString(srcSection))
  1804. {
  1805. if (!isEmptyString(tgtSection))
  1806. return;
  1807. }
  1808. level = (LogLevel) scriptCtx->getXPathInt64("target/*/@traceLevel", level);
  1809. const char *method = scriptCtx->queryAttribute(ESDLScriptCtxSection_ESDLInfo, "method");
  1810. if (isEmptyString(method))
  1811. throw MakeStringException(ESDL_SCRIPT_Error, "ESDL script method name not set");
  1812. const char *service = scriptCtx->queryAttribute(ESDLScriptCtxSection_ESDLInfo, "service");
  1813. if (isEmptyString(service))
  1814. throw MakeStringException(ESDL_SCRIPT_Error, "ESDL script service name not set");
  1815. const char *reqtype = scriptCtx->queryAttribute(ESDLScriptCtxSection_ESDLInfo, "request_type");
  1816. if (isEmptyString(reqtype))
  1817. throw MakeStringException(ESDL_SCRIPT_Error, "ESDL script request name not set");
  1818. IEspContext *context = reinterpret_cast<IEspContext*>(scriptCtx->queryEspContext());
  1819. if (level >= LogMax)
  1820. {
  1821. StringBuffer logtxt;
  1822. scriptCtx->toXML(logtxt, srcSection, false);
  1823. DBGLOG("ORIGINAL content: %s", logtxt.str());
  1824. scriptCtx->toXML(logtxt.clear(), ESDLScriptCtxSection_BindingConfig);
  1825. DBGLOG("BINDING CONFIG: %s", logtxt.str());
  1826. scriptCtx->toXML(logtxt.clear(), ESDLScriptCtxSection_TargetConfig);
  1827. DBGLOG("TARGET CONFIG: %s", logtxt.str());
  1828. }
  1829. bool strictParams = scriptCtx->getXPathBool("config/*/@strictParams", false);
  1830. Owned<IXpathContext> sourceContext = scriptCtx->createXpathContext(nullptr, srcSection, strictParams);
  1831. StringArray prefixes;
  1832. for ( IEsdlTransformSet * const & item : transforms)
  1833. {
  1834. if (item)
  1835. item->appendPrefixes(prefixes);
  1836. }
  1837. registerEsdlXPathExtensions(sourceContext, scriptCtx, prefixes);
  1838. VStringBuffer ver("%g", context->getClientVersion());
  1839. if(!sourceContext->addVariable("clientversion", ver.str()))
  1840. OERRLOG("Could not set ESDL Script variable: clientversion:'%s'", ver.str());
  1841. //in case transform wants to make use of these values:
  1842. //make them few well known values variables rather than inputs so they are automatically available
  1843. StringBuffer temp;
  1844. sourceContext->addVariable("query", scriptCtx->getXPathString("target/*/@queryname", temp));
  1845. ISecUser *user = context->queryUser();
  1846. if (user)
  1847. {
  1848. static const std::map<SecUserStatus, const char*> statusLabels =
  1849. {
  1850. #define STATUS_LABEL_NODE(s) { s, #s }
  1851. STATUS_LABEL_NODE(SecUserStatus_Inhouse),
  1852. STATUS_LABEL_NODE(SecUserStatus_Active),
  1853. STATUS_LABEL_NODE(SecUserStatus_Exempt),
  1854. STATUS_LABEL_NODE(SecUserStatus_FreeTrial),
  1855. STATUS_LABEL_NODE(SecUserStatus_csdemo),
  1856. STATUS_LABEL_NODE(SecUserStatus_Rollover),
  1857. STATUS_LABEL_NODE(SecUserStatus_Suspended),
  1858. STATUS_LABEL_NODE(SecUserStatus_Terminated),
  1859. STATUS_LABEL_NODE(SecUserStatus_TrialExpired),
  1860. STATUS_LABEL_NODE(SecUserStatus_Status_Hold),
  1861. STATUS_LABEL_NODE(SecUserStatus_Unknown),
  1862. #undef STATUS_LABEL_NODE
  1863. };
  1864. Owned<IPropertyIterator> userPropIt = user->getPropertyIterator();
  1865. ForEach(*userPropIt)
  1866. {
  1867. const char *name = userPropIt->getPropKey();
  1868. if (name && *name)
  1869. sourceContext->addInputValue(name, user->getProperty(name));
  1870. }
  1871. auto it = statusLabels.find(user->getStatus());
  1872. sourceContext->addInputValue("espUserName", user->getName());
  1873. sourceContext->addInputValue("espUserRealm", user->getRealm() ? user->getRealm() : "");
  1874. sourceContext->addInputValue("espUserPeer", user->getPeer() ? user->getPeer() : "");
  1875. sourceContext->addInputValue("espUserStatus", VStringBuffer("%d", int(user->getStatus())));
  1876. if (it != statusLabels.end())
  1877. sourceContext->addInputValue("espUserStatusString", it->second);
  1878. else
  1879. throw MakeStringException(ESDL_SCRIPT_Error, "encountered unexpected secure user status (%d) while processing transform", int(user->getStatus()));
  1880. }
  1881. else
  1882. {
  1883. // enable transforms to distinguish secure versus insecure requests
  1884. sourceContext->addInputValue("espUserName", "");
  1885. sourceContext->addInputValue("espUserRealm", "");
  1886. sourceContext->addInputValue("espUserPeer", "");
  1887. sourceContext->addInputValue("espUserStatus", "");
  1888. sourceContext->addInputValue("espUserStatusString", "");
  1889. }
  1890. StringBuffer defaultTarget; //This default gives us backward compatibility with only being able to write to the actual request
  1891. StringBuffer queryName;
  1892. const char *tgtQueryName = scriptCtx->getXPathString("target/*/@queryname", queryName);
  1893. if (!isEmptyString(srcSection) && streq(srcSection, ESDLScriptCtxSection_ESDLRequest))
  1894. defaultTarget.setf("soap:Body/%s/%s", tgtQueryName ? tgtQueryName : method, reqtype);
  1895. for ( auto&& item : transforms)
  1896. {
  1897. if (item)
  1898. {
  1899. item->processTransformImpl(scriptCtx, srcSection, tgtSection, sourceContext, defaultTarget);
  1900. }
  1901. }
  1902. if (level >= LogMax)
  1903. {
  1904. StringBuffer content;
  1905. scriptCtx->toXML(content);
  1906. DBGLOG(1,"Entire script context after transforms: %s", content.str());
  1907. }
  1908. }
  1909. IEsdlCustomTransform *createEsdlCustomTransform(const char *scriptXml, const char *ns_prefix)
  1910. {
  1911. if (isEmptyString(scriptXml))
  1912. return nullptr;
  1913. std::unique_ptr<XmlPullParser> xpp(new XmlPullParser());
  1914. int bufSize = strlen(scriptXml);
  1915. xpp->setSupportNamespaces(true);
  1916. xpp->setInput(scriptXml, bufSize);
  1917. int type;
  1918. StartTag stag;
  1919. EndTag etag;
  1920. while((type = xpp->next()) != XmlPullParser::END_DOCUMENT)
  1921. {
  1922. if(type == XmlPullParser::START_TAG)
  1923. {
  1924. StartTag stag;
  1925. xpp->readStartTag(stag);
  1926. if (strieq(stag.getLocalName(), "Transforms")) //allow common mistake,.. starting with the outer tag, not the script
  1927. continue;
  1928. return new CEsdlCustomTransform(*xpp, stag, ns_prefix);
  1929. }
  1930. }
  1931. return nullptr;
  1932. }
  1933. class CEsdlTransformSet : public CInterfaceOf<IEsdlTransformSet>
  1934. {
  1935. IArrayOf<CEsdlCustomTransform> transforms;
  1936. public:
  1937. CEsdlTransformSet()
  1938. {
  1939. }
  1940. virtual void appendPrefixes(StringArray &prefixes) override
  1941. {
  1942. ForEachItemIn(i, transforms)
  1943. transforms.item(i).appendPrefixes(prefixes);
  1944. }
  1945. virtual void processTransformImpl(IEsdlScriptContext * scriptContext, const char *srcSection, const char *tgtSection, IXpathContext *sourceContext, const char *target) override
  1946. {
  1947. ForEachItemIn(i, transforms)
  1948. transforms.item(i).processTransformImpl(scriptContext, srcSection, tgtSection, sourceContext, target);
  1949. }
  1950. virtual void add(XmlPullParser &xpp, StartTag &stag)
  1951. {
  1952. transforms.append(*new CEsdlCustomTransform(xpp, stag, nullptr));
  1953. }
  1954. virtual aindex_t length() override
  1955. {
  1956. return transforms.length();
  1957. }
  1958. };
  1959. class CEsdlTransformEntryPointMap : public CInterfaceOf<IEsdlTransformEntryPointMap>
  1960. {
  1961. MapStringToMyClass<CEsdlTransformSet> map;
  1962. public:
  1963. CEsdlTransformEntryPointMap()
  1964. {
  1965. }
  1966. virtual void addChild(XmlPullParser &xpp, StartTag &childTag, bool &foundNonLegacyTransforms)
  1967. {
  1968. const char *tagname = childTag.getLocalName();
  1969. if (streq("Scripts", tagname) || streq("Transforms", tagname)) //allow nesting of root structure
  1970. add(xpp, childTag, foundNonLegacyTransforms);
  1971. else
  1972. {
  1973. if (streq(tagname, ESDLScriptEntryPoint_Legacy))
  1974. tagname = ESDLScriptEntryPoint_BackendRequest;
  1975. else
  1976. foundNonLegacyTransforms = true;
  1977. CEsdlTransformSet *set = map.getValue(tagname);
  1978. if (set)
  1979. set->add(xpp, childTag);
  1980. else
  1981. {
  1982. Owned<CEsdlTransformSet> set = new CEsdlTransformSet();
  1983. map.setValue(tagname, set);
  1984. set->add(xpp, childTag);
  1985. }
  1986. }
  1987. }
  1988. virtual void add(XmlPullParser &xpp, StartTag &stag, bool &foundNonLegacyTransforms)
  1989. {
  1990. int type;
  1991. StartTag childTag;
  1992. while((type = xpp.next()) != XmlPullParser::END_DOCUMENT)
  1993. {
  1994. switch (type)
  1995. {
  1996. case XmlPullParser::START_TAG:
  1997. {
  1998. xpp.readStartTag(childTag);
  1999. const char *tagname = childTag.getLocalName();
  2000. if (streq("Scripts", tagname) || streq("Transforms", tagname)) //allow nesting of container structures for maximum compatability
  2001. add(xpp, childTag, foundNonLegacyTransforms);
  2002. else
  2003. addChild(xpp, childTag,foundNonLegacyTransforms);
  2004. break;
  2005. }
  2006. case XmlPullParser::END_TAG:
  2007. return;
  2008. }
  2009. }
  2010. }
  2011. void add(const char *scriptXml, bool &foundNonLegacyTransforms)
  2012. {
  2013. if (isEmptyString(scriptXml))
  2014. return;
  2015. std::unique_ptr<XmlPullParser> xpp(new XmlPullParser());
  2016. int bufSize = strlen(scriptXml);
  2017. xpp->setSupportNamespaces(true);
  2018. xpp->setInput(scriptXml, bufSize);
  2019. int type;
  2020. StartTag stag;
  2021. while((type = xpp->next()) != XmlPullParser::END_DOCUMENT)
  2022. {
  2023. switch (type)
  2024. {
  2025. case XmlPullParser::START_TAG:
  2026. {
  2027. xpp->readStartTag(stag);
  2028. addChild(*xpp, stag, foundNonLegacyTransforms);
  2029. break;
  2030. }
  2031. }
  2032. }
  2033. }
  2034. virtual IEsdlTransformSet *queryEntryPoint(const char *name) override
  2035. {
  2036. return map.getValue(name);
  2037. }
  2038. virtual void removeEntryPoint(const char *name) override
  2039. {
  2040. map.remove(name);
  2041. }
  2042. };
  2043. class CEsdlTransformMethodMap : public CInterfaceOf<IEsdlTransformMethodMap>
  2044. {
  2045. MapStringToMyClass<CEsdlTransformEntryPointMap> map;
  2046. public:
  2047. CEsdlTransformMethodMap()
  2048. {
  2049. }
  2050. virtual IEsdlTransformEntryPointMap *queryMethod(const char *name) override
  2051. {
  2052. return map.getValue(name);
  2053. }
  2054. virtual IEsdlTransformSet *queryMethodEntryPoint(const char *method, const char *name) override
  2055. {
  2056. IEsdlTransformEntryPointMap *epm = queryMethod(method);
  2057. if (epm)
  2058. return epm->queryEntryPoint(name);
  2059. return nullptr;
  2060. }
  2061. virtual void removeMethod(const char *name) override
  2062. {
  2063. map.remove(name);
  2064. }
  2065. virtual void addMethodTransforms(const char *method, const char *scriptXml, bool &foundNonLegacyTransforms) override
  2066. {
  2067. try
  2068. {
  2069. CEsdlTransformEntryPointMap *entry = map.getValue(method ? method : "");
  2070. if (entry)
  2071. entry->add(scriptXml, foundNonLegacyTransforms);
  2072. else
  2073. {
  2074. Owned<CEsdlTransformEntryPointMap> epm = new CEsdlTransformEntryPointMap();
  2075. epm->add(scriptXml, foundNonLegacyTransforms);
  2076. map.setValue(method, epm.get());
  2077. }
  2078. }
  2079. catch (XmlPullParserException& xppe)
  2080. {
  2081. VStringBuffer msg("Error parsing ESDL transform script (method '%s', line %d, col %d) %s", method ? method : "", xppe.getLineNumber(), xppe.getColumnNumber(), xppe.what());
  2082. IERRLOG("%s", msg.str());
  2083. throw MakeStringException(ESDL_SCRIPT_Error, "%s", msg.str());
  2084. }
  2085. }
  2086. };
  2087. esdl_decl IEsdlTransformMethodMap *createEsdlTransformMethodMap()
  2088. {
  2089. return new CEsdlTransformMethodMap();
  2090. }