sq-server-plugin.nut 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074
  1. /*
  2. * Copyright (C) 2013 by Domingo Alvarez Duarte <[email protected]>
  3. *
  4. * Licensed under GPLv3, see http://www.gnu.org/licenses/gpl.html.
  5. */
  6. local globals = getroottable();
  7. if(!table_rawget(globals, "APP_CODE_FOLDER", false)) ::APP_CODE_FOLDER <- ".";
  8. WIN32 <- os.getenv("WINDIR") != null;
  9. function debugLog(msg)
  10. {
  11. local fd = file(APP_CODE_FOLDER + "/debug.log", "a");
  12. fd.write(msg);
  13. fd.write("\n");
  14. fd.close();
  15. }
  16. //debugLog("starting sq-server_plugin.nut");
  17. math.srand(os.time());
  18. //local AT_DEV_DBG=true;
  19. //local APP_CODE_FOLDER = sqfs.currentdir();
  20. //local EDIT_MD5_PASSWORD = md5("edit_user:r.dadbiz.es:okdoedit");
  21. //local VIEW_MD5_PASSWORD = md5("view_user:r.dadbiz.es:okdoview");
  22. local _sq_profile_calls_at, _sq_profile_calls, _sq_profile_total, _sq_profile_this,
  23. _sq_profile_start_time, _sq_profile_end_time;
  24. local function profileReset()
  25. {
  26. _sq_profile_calls_at = {};
  27. _sq_profile_calls = {};
  28. _sq_profile_total = {};
  29. _sq_profile_this = {};
  30. _sq_profile_start_time = 0;
  31. _sq_profile_end_time = 0;
  32. }
  33. profileReset();
  34. local function profileDebughook(event_type,sourcefile,line,funcname)
  35. {
  36. //local fname = format("%s:%d", funcname ? funcname : "unknown", line);
  37. local fname = funcname ? funcname : "unknown";
  38. local srcfile=sourcefile ? sourcefile : "unknown";
  39. local fname_at = format("%s:%d:%s", fname, line, srcfile);
  40. //local fname_at = fname + ":" + line + ":" + srcfile;
  41. switch (event_type) {
  42. //case 'l': //called every line(that contains some code)
  43. //::print("LINE line [" + line + "] func [" + fname + "]");
  44. //::print("file [" + srcfile + "]\n");
  45. //break;
  46. case 'c': //called when a function has been called
  47. //::print("LINE line [" + line + "] func [" + fname + "]");
  48. //::print("file [" + srcfile + "]\n");
  49. table_rawset(_sq_profile_calls_at, fname_at, table_rawget(_sq_profile_calls_at, fname_at, 0) + 1);
  50. table_rawset(_sq_profile_thisfname, os.clock());
  51. break;
  52. case 'r': //called when a function returns
  53. //::print("LINE line [" + line + "] func [" + fname + "]");
  54. //::print("file [" + srcfile + "]\n");
  55. local time = os.clock() - table_rawget(_sq_profile_this, fname, 0);
  56. table_rawset(_sq_profile_total, fname, table_rawget(_sq_profile_total, fname, 0) + time);
  57. table_rawset(_sq_profile_calls, fname, table_rawget(_sq_profile_calls, fname, 0) + 1);
  58. break;
  59. }
  60. }
  61. local function profileStart()
  62. {
  63. profileReset();
  64. _sq_profile_start_time = os.clock();
  65. setdebughook(profileDebughook);
  66. }
  67. local function profileEnd()
  68. {
  69. setdebughook(null);
  70. _sq_profile_end_time = os.clock();
  71. }
  72. local function profileDump()
  73. {
  74. // print the results
  75. local total_time = _sq_profile_end_time - _sq_profile_start_time;
  76. print(format("Profile info: %.3f seconds", total_time));
  77. local info_ary = [];
  78. foreach( fname, time in _sq_profile_total )
  79. {
  80. if(fname == "profileStart" || fname == "profileEnd") continue;
  81. local relative_time = time / (total_time / 100.0);
  82. local rt_int = relative_time.tointeger();
  83. local rt_frac = ((relative_time - rt_int) * 100).tointeger();
  84. info_ary.append(format("%02d.%02d %% in %.3f seconds after %d calls to %s", rt_int, rt_frac, time, table_rawget(_sq_profile_calls, fname, 0), fname));
  85. }
  86. info_ary.sort(@(a,b) a<b ? 1 : (a>b ? -1 : 0));
  87. foreach(line in info_ary)
  88. {
  89. print(line);
  90. }
  91. info_ary.clear();
  92. foreach( fname, count in _sq_profile_calls_at )
  93. {
  94. if(fname.startswith("profileStart") || fname.startswith("profileEnd")) continue;
  95. info_ary.append(format("%6d\tcalls to %s", count, fname));
  96. }
  97. info_ary.sort(@(a,b) a<b ? 1 : (a>b ? -1 : 0));
  98. foreach(line in info_ary)
  99. {
  100. print(line);
  101. }
  102. }
  103. local _sq_time_start = 0;
  104. local function sqStartTimer()
  105. {
  106. _sq_time_start = os.clock();
  107. }
  108. local function sqGetElapsedTimer()
  109. {
  110. return os.clock() - _sq_time_start;
  111. }
  112. local function sqPrintElapsedTimer()
  113. {
  114. print(format("Elapsed time %.3f seconds", sqGetElapsedTimer()));
  115. }
  116. class MySMTP {
  117. boundary = null;
  118. smtp_server = null;
  119. smtp_port = null;
  120. smtp_user_name = null;
  121. smtp_user_passw = null;
  122. smtp_from = null;
  123. smtp_to = null;
  124. smtp_subject = null;
  125. smtp_message = null;
  126. attachments = null;
  127. _ssl = null;
  128. _quite = null;
  129. _fout = null;
  130. constructor(smtp, port){
  131. server(smtp, port);
  132. attachments = [];
  133. _quite = false;
  134. }
  135. function server(smtp, port) {
  136. smtp_server = smtp;
  137. smtp_port = port;
  138. boundary = "_=_+-mixed-+19JK4720AB04PX483";
  139. _fout = file("MySMPT.log", "wb");
  140. }
  141. function login(user, passw) {
  142. smtp_user_name = user;
  143. smtp_user_passw = passw;
  144. }
  145. function from(pfrom) {smtp_from = pfrom;}
  146. function to(pto) {smtp_to = pto; }
  147. function subject(psubject) {smtp_subject = psubject; }
  148. function message(pmessage) {smtp_message = pmessage; }
  149. function attach(fn, mime) {
  150. attachments.push([fn, mime]);
  151. }
  152. function _write(str){
  153. _ssl.write(str);
  154. _fout.write(str);
  155. }
  156. function _read_line(expected_code){
  157. local result;
  158. while (true){
  159. result = _ssl.read();
  160. if(type(result) == "integer"){
  161. if (result < 0){
  162. throw(axtls.get_error(result));
  163. }
  164. }
  165. if(type(result) == "string"){
  166. //print(result);
  167. local response_code;
  168. result.gmatch("^(%d+)", function(m){
  169. response_code = m.tointeger();
  170. return false;
  171. });
  172. //print("Code check", expected_code, response_code);
  173. if(!response_code || (expected_code != response_code)){
  174. throw(format("Response code '%d' not equal to expected '%d'",
  175. response_code, expected_code));
  176. }
  177. return true;
  178. }
  179. os.sleep(10);
  180. }
  181. }
  182. function _write_line(line){
  183. local end_line = "\r\n";
  184. local result = _write(line);
  185. _write(end_line);
  186. return result;
  187. }
  188. function _write_message(msg){
  189. local end_msg = "\r\n.\r\n";
  190. local result = _write(msg);
  191. _write(end_msg);
  192. return result;
  193. }
  194. function _getAttachemt(fn){
  195. local fd = file(fn, "rb");
  196. local fc = fd.read(fd.len());
  197. fd.close();
  198. return base64.encode(fc);
  199. }
  200. function send(){
  201. local client_sock = socket.tcp();
  202. client_sock.connect(smtp_server, smtp_port);
  203. local options = axtls.SSL_SERVER_VERIFY_LATER;
  204. local ssl_ctx = axtls.ssl_ctx(options, axtls.SSL_DEFAULT_CLNT_SESS);
  205. _ssl = ssl_ctx.client_new(client_sock.getfd());
  206. // check the return status
  207. local res = _ssl.handshake_status();
  208. if (res != axtls.SSL_OK) throw( axtls.get_error(res));
  209. _read_line(220);
  210. _write_line("ehlo " + smtp_user_name);
  211. _read_line(250);
  212. local credentials = base64.encode(format("\x00%s\x00%s",
  213. smtp_user_name, smtp_user_passw));
  214. //print("credentials", credentials);
  215. _write_line(format("AUTH PLAIN %s", credentials));
  216. _read_line(235);
  217. _write_line(format("mail from: <%s>", smtp_from || smtp_user_name));
  218. _read_line(250);
  219. _write_line(format("rcpt to: <%s>", smtp_to));
  220. _read_line(250);
  221. _write_line("data");
  222. _read_line(354);
  223. local buf = format([==[
  224. From: Tais Alvarez <%s>
  225. To: Domingo <%s>
  226. Subject: %s
  227. ]==], smtp_from || smtp_user_name, smtp_to, smtp_subject);
  228. _write(buf);
  229. local hasAttachment = attachments.len() > 0;
  230. if (hasAttachment){
  231. buf = format([==[
  232. Content-Type: multipart/mixed; boundary="%s"
  233. --%s
  234. Content-Type: text/plain; charset="utf-8"
  235. Content-Transfer-Encoding: 8bit
  236. ]==], boundary, boundary);
  237. _write(buf);
  238. }
  239. buf = format([==[
  240. %s
  241. %s
  242. ]==], os.date(), smtp_message);
  243. _write(buf);
  244. if (hasAttachment){
  245. foreach( k,v in attachments) {
  246. buf = format([==[
  247. --%s
  248. Content-Type: %s; name="%s"
  249. Content-Transfer-Encoding: base64
  250. Content-Disposition: attachment
  251. %s
  252. ]==], boundary, v[1], v[0], _getAttachemt(v[0]));
  253. _write(buf);
  254. }
  255. buf = format("--%s--", boundary);
  256. _write(buf);
  257. }
  258. _write_message("");
  259. //print("Done !");
  260. _read_line(250);
  261. _write_line("quit");
  262. _read_line(221);
  263. _ssl.free();
  264. ssl_ctx.free();
  265. client_sock.close();
  266. }
  267. }
  268. function IntToDottedIP( intip )
  269. {
  270. local octet = [0,0,0,0];
  271. for(local i=3; i >= 0; --i)
  272. {
  273. octet[i] = (intip & 0xFF).tostring();
  274. intip = intip >> 8;
  275. }
  276. return octet.concat(".");
  277. }
  278. if(!table_rawget(globals, "gmFile", false)) ::gmFile <- blob();
  279. if(!table_rawget(globals, "__tplCache", false)) ::__tplCache <- {};
  280. function getTemplate(fname, nocache){
  281. local mixBase = ::table_rawget(__tplCache, fname, false);
  282. if (!mixBase || nocache){
  283. local rfn = format("%s/%s", APP_CODE_FOLDER, fname);
  284. //debug_print("\n", rfn);
  285. try {
  286. mixBase = sqmix.loadfile(rfn);
  287. } catch(e){
  288. debug_print("\n", e);
  289. }
  290. ::__tplCache[fname] <- mixBase;
  291. }
  292. return mixBase;
  293. }
  294. /*
  295. if(!table_rawget(globals, "__stmtCache", false)) ::__stmtCache <- {};
  296. function getCachedStmt(db, stmt_key, sql_or_func){
  297. local stmt = ::table_rawget(__stmtCache, stmt_key, false);
  298. if (!stmt){
  299. //local db =checkCompaniesUkDB()
  300. local sql;
  301. if (type(sql_or_func) == "function") sql = sql_or_func();
  302. else sql = sql_or_func;
  303. //debug_print("\n", sql);
  304. stmt = ::db.prepare(sql);
  305. ::__stmtCache.stmt_key <- stmt;
  306. }
  307. return stmt;
  308. }
  309. */
  310. function glob2Sql(v){
  311. if( v ) {
  312. v = v.gsub("*", "%%");
  313. v = v.gsub("%?", "_");
  314. }
  315. return v;
  316. }
  317. function unescapeHtml ( str ){
  318. if (str){
  319. return str.gsub("(&[^;]-;)", function(m){
  320. if (m == "&lt;") return "<";
  321. else if (m == "&gt;") return ">";
  322. else if (m == "&amp;") return "&";
  323. else if (m == "&quot;") return "\"";
  324. else if (m == "&#x27;") return "'";
  325. else if (m == "&#x2F;") return "/";
  326. return "??";
  327. });
  328. }
  329. }
  330. function escapeHtml ( str ){
  331. if (str){
  332. return str.gsub("([<>&'\"/])", function(m){
  333. if (m == "<") return "&lt;";
  334. else if (m == ">") return "&gt;";
  335. else if (m == "&") return "&amp;";
  336. else if (m == "\"") return "&quot;";
  337. else if (m == "'") return "&#x27;";
  338. else if (m == "/") return "&#x2F;";
  339. return "??";
  340. });
  341. }
  342. }
  343. function var2json(v){
  344. switch(type(v)){
  345. case "string":
  346. return format("%q", v);
  347. break;
  348. case "table":
  349. local result = [];
  350. foreach(k2, v2 in v) {
  351. result.push( format([==["%s":%s]==], k2, var2json(v2)));
  352. }
  353. return "{" + result.concat(",") + "}";
  354. break;
  355. case "array":
  356. local result = [];
  357. for(local i=0, len=v.len(); i<len; ++i) {
  358. result.push(var2json(v[i]));
  359. }
  360. return "[" + result.concat(",") + "]";
  361. break;
  362. case "integer":
  363. case "float":
  364. return v.tostring();
  365. break;
  366. case "bool":
  367. return v ? "true" : "false";
  368. break;
  369. case "null":
  370. return "null";
  371. break;
  372. default:
  373. //return "\"" + v.tostring().replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t").replace("\b", "\\b").replace("\f", "\\f") + "\"";
  374. //debug_print("\n", __LINE__, ":", type(v));
  375. return format("%q", try_tostring(v));
  376. }
  377. return "";
  378. }
  379. function json2var(json) {
  380. local vm = SlaveVM();
  381. local slave_func = "getTable";
  382. //debug_print(json, "\n");
  383. //convert new data from json to squilu table for merge
  384. vm.compilestring(slave_func, "return " + json);
  385. local tbl = vm.call(true, slave_func);
  386. return tbl;
  387. }
  388. function time_stamp(){
  389. return os.date("!%Y-%m-%d %H:%M:%S");
  390. }
  391. class ObjecIdMaker
  392. {
  393. _machine_id = null;
  394. _pid = null;
  395. _index = null;
  396. constructor()
  397. {
  398. _machine_id = (math.random() * 0xFFFFFF).tointeger();
  399. _index = (math.random() * 0xFFFFFF).tointeger();
  400. _pid = (math.random() * 100000).tointeger() % 0xFFFF;
  401. }
  402. function next(tm=null) {
  403. _index = (_index+1) % 0xFFFFFF;
  404. return format("%.8x%.6x%.4x%.6x",
  405. (tm == null) ? os.time() : tm, _machine_id, _pid, _index);
  406. }
  407. function createFromTime(tm=null) {
  408. return format("%.8x0000000000000000", (tm == null) ? os.time() : tm);
  409. }
  410. function getTime(objectId) {
  411. local p1 = objectId.slice(0, 8);
  412. return p1.tointeger(16);
  413. }
  414. function getDate(objectId) {
  415. local p1 = objectId.slice(0, 8);
  416. return os.date("!%Y-%m-%dT%H:%M:%S.000Z", p1.tointeger(16));
  417. }
  418. function getParts(oid) {
  419. local tm = oid.slice(0, 8).tointeger(16);
  420. local m_id = oid.slice(8, 14).tointeger(16);
  421. local p_id = oid.slice(14, 18).tointeger(16);
  422. local idx_id = oid.slice(18, 24).tointeger(16);
  423. return [tm, m_id, p_id, idx_id];
  424. }
  425. function createFromParts(oid) {
  426. return format("%.8x%.6x%.4x%.6x",
  427. oid[0], oid[1], oid[2], oid[3]);
  428. }
  429. }
  430. ObjectId <- ObjecIdMaker();
  431. function doSaveTableArrayToFD(ta, fd){
  432. local function dumpValue(val){
  433. local vtype = type(val);
  434. if(vtype == "string") fd.write(format("%q,\n", val));
  435. else if(vtype == "integer") fd.write(format("%d,\n", val));
  436. else if(vtype == "float") fd.write(format("%f,\n", val));
  437. else if(vtype == "bool") fd.write(format("%s,\n", val ? "true" : "false"));
  438. else if(vtype == "null") fd.write("null,\n");
  439. else throw format("Only string, integer, float, bool are supported ! (%s)", vtype);
  440. }
  441. local tatype = type(ta);
  442. if(tatype == "table"){
  443. fd.write(format("{\n"));
  444. foreach(k,v in ta){
  445. fd.write(format("[%q] = ", k));
  446. local vtype = type(v);
  447. if(vtype == "table" || vtype == "array"){
  448. doSaveTableArrayToFD(v, fd);
  449. }
  450. else
  451. {
  452. dumpValue(v);
  453. }
  454. }
  455. fd.write(format("},\n"));
  456. }
  457. else if(tatype == "array"){
  458. fd.write(format("[\n"));
  459. foreach(k,v in ta){
  460. local vtype = type(v);
  461. if(vtype == "table" || vtype == "array"){
  462. doSaveTableArrayToFD(v, fd);
  463. }
  464. else
  465. {
  466. dumpValue(v);
  467. }
  468. }
  469. fd.write(format("],\n"));
  470. }
  471. else throw "Only table/array suported";
  472. }
  473. function doSaveTableArrayToFileName(tbl, fname){
  474. local fd = file(fname, "w");
  475. fd.write("return [\n");
  476. doSaveTableArrayToFD(tbl, fd);
  477. fd.write("];");
  478. fd.close();
  479. }
  480. function doLoadTableArrayFromFileName(fname){
  481. local func = loadfile(fname);
  482. return func()[0];
  483. }
  484. function fillTemplate(template, data, nocache){
  485. data.escapeHtml <- escapeHtml;
  486. local mixFunc =getTemplate(template, nocache);
  487. mixFunc.call(data);
  488. }
  489. function getFfileName(full_path) {
  490. return full_path.match("([^/]+)$");
  491. }
  492. //
  493. // Post
  494. //
  495. function split_filename(path){
  496. local result;
  497. path.gmatch("[/\\]?([^/\\]+)$", function(m){
  498. result = m;
  499. return false;
  500. });
  501. return result;
  502. }
  503. function form_url_insert_field (dest, key, value){
  504. local fld = table_rawget(dest, key, null);
  505. if (!fld) dest[key] <- value;
  506. else
  507. {
  508. if (type (fld) == "array") fld.push(value);
  509. else dest[key] <- [fld, value];
  510. }
  511. }
  512. function multipart_data_get_field_names(headers, name_value){
  513. //foreach(k,v in headers) debug_print(k, "::", v, "\n");
  514. local disp_header = headers["content-disposition"] || "";
  515. local attrs = {};
  516. disp_header.gmatch(";%s*([^%s=]+)=\"(.-)\"", function(attr, val) {
  517. attrs[attr] <- val;
  518. //debug_print(attr, "::", val, "\n");
  519. return true;
  520. });
  521. name_value.push(attrs.name);
  522. name_value.push(table_rawget(attrs, "filename", false) ? split_filename(attrs.filename) : null);
  523. }
  524. function multipart_data_break_headers(header_data){
  525. local headers = {};
  526. header_data.gmatch("([^%c%s:]+):%s+([^\n]+)", function(type, val){
  527. headers[type.tolower()] <- val;
  528. return true;
  529. });
  530. return headers;
  531. }
  532. function multipart_data_read_field_headers(input, state){
  533. local s, e, pos = state.pos;
  534. input.find_lua("\r\n\r\n", function(start, end){s=start; e=end; return false;}, pos, true);
  535. if( s ) {
  536. state.pos <- e;
  537. return multipart_data_break_headers(input.slice(pos, s));
  538. }
  539. else return null;
  540. }
  541. function multipart_data_read_field_contents(input, state){
  542. local boundaryline = "\r\n" + state.boundary;
  543. local s, e, pos = state.pos;
  544. input.find_lua(boundaryline, function(start, end){ s=start; e=end; return false;}, pos, true)
  545. if (s) {
  546. state.pos <- e;
  547. state.size <- s-pos;
  548. return input.slice(pos, s);
  549. }
  550. else {
  551. state.size <- 0;
  552. return null;
  553. }
  554. }
  555. function multipart_data_file_value(file_contents, file_name, file_size, headers){
  556. local value = { contents = file_contents, name = file_name, size = file_size };
  557. foreach( h, v in headers) {
  558. if (h != "content-disposition") value[h] <- v;
  559. }
  560. return value;
  561. }
  562. function multipart_data_parse_field(input, state){
  563. local headers, value;
  564. headers = multipart_data_read_field_headers(input, state);
  565. if (headers) {
  566. local name_value=[];
  567. multipart_data_get_field_names(headers, name_value);
  568. if (name_value[1]) { //file_name
  569. value = multipart_data_read_field_contents(input, state);
  570. value = multipart_data_file_value(value, name_value[1], state.size, headers);
  571. name_value[1] = value;
  572. }
  573. else name_value[1] = multipart_data_read_field_contents(input, state)
  574. return name_value;
  575. }
  576. return null;
  577. }
  578. function multipart_data_get_boundary(content_type){
  579. local boundary;
  580. content_type.gmatch("boundary%=(.-)$", function(m){
  581. boundary = m;
  582. return false;
  583. });
  584. return "--" + boundary;
  585. }
  586. function parse_multipart_data(input, input_type, tab=null){
  587. if(!tab) tab = {};
  588. local state = {};
  589. state.boundary <- multipart_data_get_boundary(input_type);
  590. input.find_lua(state.boundary, function(start, end){state.pos <- end+1;return false;}, 0, true);
  591. while(true){
  592. local name_value = multipart_data_parse_field(input, state);
  593. //debug_print("\nparse_multipart_data: ", name_value);
  594. if(!name_value) break;
  595. form_url_insert_field(tab, name_value[0], name_value[1]);
  596. }
  597. return tab;
  598. }
  599. function parse_qs(qs, tab=null){
  600. if(!tab) tab = {};
  601. if (type(qs) == "string") {
  602. //debug_print(qs)
  603. qs.gmatch("([^&=]+)=([^&=]*)&?", function(key,val){
  604. //debug_print(key, "->", val)
  605. form_url_insert_field(tab, url_decode(key), url_decode(val));
  606. return true;
  607. });
  608. }
  609. else if (qs) throw("Request error: invalid query string");
  610. return tab;
  611. }
  612. function parse_qs_to_table(qs, tab=null){
  613. if(!tab) tab = {};
  614. if (type(qs) == "string") {
  615. //debug_print(qs)
  616. qs.gmatch("([^&=]+)=([^&=]*)&?", function(key,val){
  617. //debug_print(key, "->", val)
  618. key = url_decode(key);
  619. tab[key] <- url_decode(val);
  620. return true;
  621. });
  622. }
  623. else if (qs) throw("Request error: invalid query string");
  624. return tab;
  625. }
  626. function parse_qs_to_table_k(qs, tab=null, tabk=null){
  627. if(!tab) tab = {};
  628. if(!tabk) tabk = [];
  629. if (type(qs) == "string") {
  630. qs.gmatch("([^&=]+)=([^&=]*)&?", function(key, val){
  631. //debug_print(key, "->", val)
  632. key = url_decode(key);
  633. tabk.push(key);
  634. tab[key] <- url_decode(val);
  635. return true;
  636. });
  637. }
  638. else if (qs) throw("Request error: invalid query string");
  639. return tab;
  640. }
  641. function parse_post_data(input_type, data, tab = null){
  642. if(!tab) tab = {};
  643. local length = data.len();
  644. if (input_type.find("x-www-form-urlencoded") >= 0) parse_qs(data, tab);
  645. else if (input_type.find("multipart/form-data") >= 0) parse_multipart_data(data, input_type, tab);
  646. else if (input_type.find("SLE") >= 0) {
  647. local vv = [];
  648. sle2vecOfvec(data, vv);
  649. if (vv.len() > 0) {
  650. local names = vv[0];
  651. local values = vv[1];
  652. for (local i=0, len = names.len(); i < len; ++i){
  653. tab[names[i]] <- values[i];
  654. }
  655. }
  656. }
  657. return tab;
  658. }
  659. function get_post_fields(request, max_len=1024*1000, post_fields=false){
  660. local data_len = (request.get_header("Content-Length") || "0").tointeger();
  661. if(!post_fields) post_fields = {};
  662. //debug_print("\nget_post_fields: ", __LINE__, ":", data_len, ":", max_len);
  663. if (data_len > 0 && data_len <= max_len) {
  664. local content_type = request.get_header("Content-Type") || "x-www-form-urlencoded";
  665. local data = request.read(data_len);
  666. /*
  667. local fd = file(::APP_CODE_FOLDER + "/post.txt", "wb");
  668. fd.write(data, data.len());
  669. fd.close();
  670. debug_print(request.get_header("Content-Type"), "\n");
  671. */
  672. if(content_type.find("application/json") >= 0){
  673. return data;
  674. }
  675. parse_post_data(content_type, data, post_fields);
  676. }
  677. return post_fields;
  678. }
  679. local allowedUploadFileExtensions = {
  680. [".png"] = "image/png",
  681. [".jpg"] = "image/jpeg",
  682. [".gif"] = "image/gif",
  683. [".svg"] = "image/svg+xml",
  684. }
  685. function getMimeType(fname){
  686. local ext;
  687. fname.gmatch("(%.?[^%.\\/]*)$", @(m) ext=m);
  688. if( ext ) return table_rawget(allowedUploadFileExtensions, ext, "unknown");
  689. return "unknown";
  690. }
  691. function sanitizePath(path){
  692. //reorient separators
  693. path=path.gsub("\\", "/");
  694. //remove relativeness
  695. local relatpattern = "%.%.+";
  696. while (path.find_lua(relatpattern) > 0){
  697. path=path.gsub(relatpattern, "") //something like /Repositories/swycartographer/res/msys/ + /mod
  698. } //gets converted to /Repositories/swycartographer/res/msys/mod
  699. //remove possible doubles
  700. relatpattern = "//";
  701. while(path.find(relatpattern) >= 0){
  702. path=path.gsub(relatpattern, "/");
  703. }
  704. //remove trailing slash
  705. if(path.endswith("/")) path=path.slice(0,-2);
  706. //remove slash at the begining
  707. if(path.startswith("/")) path = path.slice(1);
  708. path = path.gsub("[^A-Za-z0-9_%-%./]", "");
  709. return path
  710. }
  711. local allowedEditFileExtensions = {
  712. [".nut"] = true,
  713. [".tpl"] = true,
  714. [".html"] = true,
  715. [".css"] = true,
  716. [".js"] = true,
  717. }
  718. function isExtensionAllowed(fname){
  719. local ext;
  720. fname.gmatch("(%.?[^%.\\/]*)$", @(m) ext=m);
  721. if( ext ) return table_rawget(allowedEditFileExtensions, ext, false);
  722. return false;
  723. }
  724. function getFilesInPath(path, files=null, prefix=""){
  725. if(!files) files = [];
  726. local prefix_len = prefix.len();
  727. foreach( file in sqfs.dir(path) ){
  728. if(file != "." && file != ".." ){
  729. local f = path + "/" + file;
  730. local pf
  731. if (prefix_len > 0) pf = prefix + "/" + file;
  732. else pf = file;
  733. try {
  734. local attr = sqfs.attributes (f);
  735. if(attr.mode == "directory") getFilesInPath (f, files, pf);
  736. else
  737. {
  738. if( isExtensionAllowed(pf) ) files.push(pf);
  739. //foreach(name, value in attr) print (name, value);
  740. }
  741. } catch(e) {}
  742. }
  743. }
  744. files.sort();
  745. return files;
  746. }
  747. function getDbListFromStmt(stmt, maxSeconds=0){
  748. local error_code = 0;
  749. local rows = [];
  750. local db = stmt.get_db();
  751. if (maxSeconds){
  752. //local x = 0
  753. db.progress_handler(25, function(info) {
  754. //x = x +1
  755. //debug_print(x, "\n")
  756. //debug_print(x, ":", os.difftime(os.time(), info[0]), ":", info[1], "\n")
  757. if (os.difftime(os.time(), info[1]) > info[0]) return 1;
  758. return 0;
  759. }, [maxSeconds, os.time()]);
  760. }
  761. else db.progress_handler(null);
  762. try {
  763. while (stmt.next_row()){
  764. rows.push(stmt.asArray());
  765. }
  766. } catch(e){
  767. error_code = db.error_code();
  768. }
  769. if (maxSeconds) db.progress_handler(null);
  770. stmt.reset();
  771. //debug_print("\n", rows.len(), "\n");
  772. return [rows, error_code];
  773. }
  774. function strHasContent(v){
  775. if(v && v.len() > 0) return true;
  776. return false;
  777. }
  778. function send_http_error_500(request, err_msg){
  779. if(AT_DEV_DBG) {
  780. foreach(k,v in get_last_stackinfo()) debug_print("\n", k, ":", v);
  781. debug_print("\n", err_msg, "\n")
  782. }
  783. local response = format("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: %d\r\n\r\n%s",
  784. err_msg.len(), err_msg);
  785. request.write(response, response.len());
  786. return true;
  787. }
  788. local uri_handlers = {
  789. ["/SQ/hello-world"] = function(request){
  790. local hello = "Hello World !\n";
  791. local resp = format("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8;\r\nContent-Length: %d\r\n\r\n%s", hello.len(), hello)
  792. request.print(resp)
  793. return true;
  794. },
  795. ["/SQ/testParams"] = function(request){
  796. local mFile = gmFile;
  797. mFile.clear(); //allways reset global vars
  798. mFile.write("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n")
  799. mFile.write("<html><body><h1>Request Info</h1><ul>")
  800. foreach(k, v in request.info) {
  801. if ("table" == type(v) ){
  802. mFile.write(format("<li><b>%s</b>:</li><ul>", k));
  803. foreach( k2, v2 in v){
  804. mFile.write(format("<li><b>%s</b>: %s</li>", k2, v2));
  805. }
  806. mFile.write("</ul>");
  807. }
  808. else mFile.write(format("<li><b>%s</b>: %s</li>", k, (v == NULL ? "" : v).tostring()));
  809. }
  810. mFile.write("</ul></body></html>");
  811. request.write_blob(mFile);
  812. return true;
  813. },
  814. ["/SQ/logout"] = function(request){
  815. request.close_session();
  816. request.print(format("HTTP/1.1 302 Found\r\nLocation: http%s://%s\r\n\r\n",
  817. request.info.is_ssl ? "s" : "", request.info.http_headers.Host))
  818. return true;
  819. },
  820. ["/SQ/edit"] = function(request){
  821. //password protected
  822. bool_t canEdit = false;
  823. //print("EDIT_MD5_PASSWORD=", EDIT_MD5_PASSWORD, "\n")
  824. bool_t isViewOnly = table_rawget(globals, "VIEW_MD5_PASSWORD", false) && request.check_password(VIEW_MD5_PASSWORD);
  825. if (!isViewOnly) canEdit = table_rawget(globals, "EDIT_MD5_PASSWORD", false) && request.check_password(EDIT_MD5_PASSWORD);
  826. if(!(canEdit || isViewOnly) ) {
  827. request.send_authorization_request("r.dadbiz.es");
  828. return true;
  829. }
  830. table_t data = {
  831. file_name=null,
  832. content=null,
  833. };
  834. data.files <- getFilesInPath(APP_CODE_FOLDER);
  835. local query_string = request.info.query_string;
  836. bool_t isPost = request.info.request_method == "POST";
  837. if (isPost && canEdit) {
  838. local post_fields = get_post_fields(request);
  839. if (table_rawget(post_fields, "save", false)) {
  840. local content = table_rawget(post_fields, "content", null);
  841. if (content){
  842. data.file_name <- sanitizePath(post_fields.file_name);
  843. if (!isExtensionAllowed(data.file_name)) data.file_name = NULL;
  844. else
  845. {
  846. local fd = file(APP_CODE_FOLDER + "/" + data.file_name, "w");
  847. content = content.gsub("\r", "");
  848. fd.write(content);
  849. fd.close();
  850. }
  851. }
  852. }
  853. }
  854. else if( query_string ) data.file_name <- request.get_var(query_string, "file");
  855. //debug_print(data.search_id, "\n")
  856. if( data.file_name ){
  857. data.file_name = sanitizePath(data.file_name);
  858. if(! isExtensionAllowed(data.file_name) ) data.file_name = NULL;
  859. else
  860. {
  861. local fd = file(APP_CODE_FOLDER + "/" + data.file_name, "r");
  862. if( fd ){
  863. data.content <- fd.read(fd.len());
  864. fd.close();
  865. }
  866. }
  867. }
  868. //debug_tprint(data.company)
  869. local mFile = gmFile;
  870. mFile.clear(); //allways reset global vars
  871. data.mix_write <- function(str){ mFile.write(str || "")}
  872. fillTemplate("webappedit.tpl", data, AT_DEV_DBG);
  873. request.print(format("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: %d\r\n\r\n", mFile.len()));
  874. request.write_blob(mFile);
  875. return true;
  876. },
  877. }
  878. function add_uri_hanlders(tbl){
  879. foreach(k,v in tbl) uri_handlers[k] <- v;
  880. }
  881. local uri_filters = [];
  882. function add_uri_filters(func){
  883. uri_filters.push(func);
  884. }
  885. function remove_uri_filters(func){
  886. for(local i=0, len=uri_filters.len(); i < len; ++i){
  887. if(uri_filters[i] == func) {
  888. uri_filters.remove(i);
  889. break;
  890. }
  891. }
  892. }
  893. function apply_uri_filters(request){
  894. for(local i=0, len=uri_filters.len(); i < len; ++i){
  895. if(uri_filters[i](request)) {
  896. return true;
  897. }
  898. }
  899. return false;
  900. }
  901. function sendJson(request, response, extra_headers=""){
  902. request.print(format("HTTP/1.1 200 OK\r\nServer: SquiluAppServer\r\nContent-Type: text/json; charset=utf-8\r\nCache-Control: no-cache\r\nContent-Length: %d\r\n%s\r\n%s", response.len(), extra_headers, response));
  903. return true;
  904. }
  905. function sendJs(request, response, extra_headers=""){
  906. request.print(format("HTTP/1.1 200 OK\r\nServer: SquiluAppServer\r\nContent-Type: application/x-javascript; charset=utf-8\r\nCache-Control: no-cache\r\nContent-Length: %d\r\n%s\r\n%s", response.len(), extra_headers, response));
  907. return true;
  908. }
  909. if(AT_DEV_DBG || !table_rawget(globals, "MyCompaniesUkLoaded", false)) {
  910. dofile(APP_CODE_FOLDER + "/companies-uk.nut");
  911. }
  912. if(AT_DEV_DBG || !table_rawget(globals, "MyOurBizLoaded", false)) {
  913. dofile(APP_CODE_FOLDER + "/ourbiz.nut");
  914. }
  915. if(AT_DEV_DBG || !table_rawget(globals, "MyOurShoppingCartLoaded", false)) {
  916. //dofile(APP_CODE_FOLDER + "/ourbiz-shopping-cart.nut");
  917. }
  918. local ourbiz_password = md5("mingote:ourbiz.dadbiz.es:tr14pink");
  919. function handle_request(request){
  920. //static content served by mongoose directly
  921. local request_uri = request.info.uri;
  922. //debug_print(request.get_option("document_root"), "::", request_uri, "\n")
  923. /*
  924. local ext = request_uri.match("%.%w+$");
  925. switch(ext)
  926. {
  927. case ".jpg":
  928. case ".png":
  929. case ".svg":
  930. case ".js":
  931. case ".css":
  932. case ".mp4":
  933. case ".webm":
  934. case ".ico":
  935. case ".php":
  936. case ".html":
  937. return false;
  938. }
  939. */
  940. if(apply_uri_filters(request)) {
  941. return true;
  942. }
  943. if(request_uri.startswith("/DB/")){
  944. if(!request.check_password(ourbiz_password)) {
  945. //request.send_authorization_request("ourbiz.dadbiz.es");
  946. //return true;
  947. }
  948. }
  949. if (request_uri.endswith(".js") || request_uri.endswith(".css") ) return false;
  950. if (request_uri == "/index.html" || request_uri == "/" ) return uri_handlers["/search"](request);
  951. if( table_rawget(uri_handlers, request_uri, false) ) return uri_handlers[request_uri](request);
  952. return false;
  953. }
  954. if(table_rawin(globals, "addExtraWebAppCode"))
  955. {
  956. //debugLog("call addExtraWebAppCode");
  957. //addExtraWebAppCode(this);
  958. }
  959. //debugLog("end of sq-server_plugin.nut");