You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lua_url.c 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "lua_common.h"
  17. #include "lua_url.h"
  18. /***
  19. * @module rspamd_url
  20. * This module provides routines to handle URL's and extract URL's from the text.
  21. * Objects of this class are returned, for example, by `task:get_urls()` or `task:get_emails()`.
  22. * You can also create `rspamd_url` from any text.
  23. * @example
  24. local url = require "rspamd_url"
  25. local mpool = require "rspamd_mempool"
  26. url.init("/usr/share/rspamd/effective_tld_names.dat")
  27. local pool = mpool.create()
  28. local res = url.create(pool, 'Look at: http://user@test.example.com/test?query")
  29. local t = res:to_table()
  30. -- Content of t:
  31. -- url = ['http://test.example.com/test?query']
  32. -- host = ['test.example.com']
  33. -- user = ['user']
  34. -- path = ['test']
  35. -- tld = ['example.com']
  36. pool:destroy() -- res is destroyed here, so you should not use it afterwards
  37. local mistake = res:to_table() -- INVALID! as pool is destroyed
  38. */
  39. /* URL methods */
  40. LUA_FUNCTION_DEF (url, get_length);
  41. LUA_FUNCTION_DEF (url, get_host);
  42. LUA_FUNCTION_DEF (url, get_port);
  43. LUA_FUNCTION_DEF (url, get_user);
  44. LUA_FUNCTION_DEF (url, get_path);
  45. LUA_FUNCTION_DEF (url, get_query);
  46. LUA_FUNCTION_DEF (url, get_fragment);
  47. LUA_FUNCTION_DEF (url, get_text);
  48. LUA_FUNCTION_DEF (url, tostring);
  49. LUA_FUNCTION_DEF (url, get_raw);
  50. LUA_FUNCTION_DEF (url, get_tld);
  51. LUA_FUNCTION_DEF (url, get_flags);
  52. LUA_FUNCTION_DEF (url, get_flags_num);
  53. LUA_FUNCTION_DEF (url, get_protocol);
  54. LUA_FUNCTION_DEF (url, to_table);
  55. LUA_FUNCTION_DEF (url, is_phished);
  56. LUA_FUNCTION_DEF (url, is_redirected);
  57. LUA_FUNCTION_DEF (url, is_obscured);
  58. LUA_FUNCTION_DEF (url, is_html_displayed);
  59. LUA_FUNCTION_DEF (url, is_subject);
  60. LUA_FUNCTION_DEF (url, get_phished);
  61. LUA_FUNCTION_DEF (url, set_redirected);
  62. LUA_FUNCTION_DEF (url, get_count);
  63. LUA_FUNCTION_DEF (url, get_visible);
  64. LUA_FUNCTION_DEF (url, create);
  65. LUA_FUNCTION_DEF (url, init);
  66. LUA_FUNCTION_DEF (url, all);
  67. LUA_FUNCTION_DEF (url, lt);
  68. LUA_FUNCTION_DEF (url, eq);
  69. static const struct luaL_reg urllib_m[] = {
  70. LUA_INTERFACE_DEF (url, get_length),
  71. LUA_INTERFACE_DEF (url, get_host),
  72. LUA_INTERFACE_DEF (url, get_port),
  73. LUA_INTERFACE_DEF (url, get_user),
  74. LUA_INTERFACE_DEF (url, get_path),
  75. LUA_INTERFACE_DEF (url, get_query),
  76. LUA_INTERFACE_DEF (url, get_fragment),
  77. LUA_INTERFACE_DEF (url, get_text),
  78. LUA_INTERFACE_DEF (url, get_tld),
  79. LUA_INTERFACE_DEF (url, get_raw),
  80. LUA_INTERFACE_DEF (url, get_protocol),
  81. LUA_INTERFACE_DEF (url, to_table),
  82. LUA_INTERFACE_DEF (url, is_phished),
  83. LUA_INTERFACE_DEF (url, is_redirected),
  84. LUA_INTERFACE_DEF (url, is_obscured),
  85. LUA_INTERFACE_DEF (url, is_html_displayed),
  86. LUA_INTERFACE_DEF (url, is_subject),
  87. LUA_INTERFACE_DEF (url, get_phished),
  88. LUA_INTERFACE_DEF (url, get_visible),
  89. LUA_INTERFACE_DEF (url, get_count),
  90. LUA_INTERFACE_DEF (url, get_flags),
  91. LUA_INTERFACE_DEF (url, get_flags_num),
  92. {"get_redirected", lua_url_get_phished},
  93. LUA_INTERFACE_DEF (url, set_redirected),
  94. {"__tostring", lua_url_tostring},
  95. {"__eq", lua_url_eq},
  96. {"__lt", lua_url_lt},
  97. {NULL, NULL}
  98. };
  99. static const struct luaL_reg urllib_f[] = {
  100. LUA_INTERFACE_DEF (url, init),
  101. LUA_INTERFACE_DEF (url, create),
  102. LUA_INTERFACE_DEF (url, all),
  103. {NULL, NULL}
  104. };
  105. struct rspamd_lua_url *
  106. lua_check_url (lua_State * L, gint pos)
  107. {
  108. void *ud = rspamd_lua_check_udata (L, pos, "rspamd{url}");
  109. luaL_argcheck (L, ud != NULL, pos, "'url' expected");
  110. return ud ? ((struct rspamd_lua_url *)ud) : NULL;
  111. }
  112. static gboolean
  113. lua_url_single_inserter (struct rspamd_url *url, gsize start_offset,
  114. gsize end_offset, gpointer ud)
  115. {
  116. lua_State *L = ud;
  117. struct rspamd_lua_url *lua_url;
  118. lua_url = lua_newuserdata (L, sizeof (struct rspamd_lua_url));
  119. rspamd_lua_setclass (L, "rspamd{url}", -1);
  120. lua_url->url = url;
  121. return TRUE;
  122. }
  123. /***
  124. * @method url:get_length()
  125. * Get length of the url
  126. * @return {number} length of url in bytes
  127. */
  128. static gint
  129. lua_url_get_length (lua_State *L)
  130. {
  131. LUA_TRACE_POINT;
  132. struct rspamd_lua_url *url = lua_check_url (L, 1);
  133. if (url != NULL) {
  134. lua_pushinteger (L, url->url->urllen);
  135. }
  136. else {
  137. lua_pushnil (L);
  138. }
  139. return 1;
  140. }
  141. /***
  142. * @method url:get_host()
  143. * Get domain part of the url
  144. * @return {string} domain part of URL
  145. */
  146. static gint
  147. lua_url_get_host (lua_State *L)
  148. {
  149. LUA_TRACE_POINT;
  150. struct rspamd_lua_url *url = lua_check_url (L, 1);
  151. if (url != NULL && url->url && url->url->hostlen > 0) {
  152. lua_pushlstring (L, rspamd_url_host (url->url), url->url->hostlen);
  153. }
  154. else {
  155. lua_pushnil (L);
  156. }
  157. return 1;
  158. }
  159. /***
  160. * @method url:get_port()
  161. * Get port of the url
  162. * @return {number} url port
  163. */
  164. static gint
  165. lua_url_get_port (lua_State *L)
  166. {
  167. LUA_TRACE_POINT;
  168. struct rspamd_lua_url *url = lua_check_url (L, 1);
  169. if (url != NULL) {
  170. lua_pushinteger (L, url->url->port);
  171. }
  172. else {
  173. lua_pushnil (L);
  174. }
  175. return 1;
  176. }
  177. /***
  178. * @method url:get_user()
  179. * Get user part of the url (e.g. username in email)
  180. * @return {string} user part of URL
  181. */
  182. static gint
  183. lua_url_get_user (lua_State *L)
  184. {
  185. LUA_TRACE_POINT;
  186. struct rspamd_lua_url *url = lua_check_url (L, 1);
  187. if (url != NULL && rspamd_url_user (url->url) != NULL) {
  188. lua_pushlstring (L, rspamd_url_user (url->url), url->url->userlen);
  189. }
  190. else {
  191. lua_pushnil (L);
  192. }
  193. return 1;
  194. }
  195. /***
  196. * @method url:get_path()
  197. * Get path of the url
  198. * @return {string} path part of URL
  199. */
  200. static gint
  201. lua_url_get_path (lua_State *L)
  202. {
  203. LUA_TRACE_POINT;
  204. struct rspamd_lua_url *url = lua_check_url (L, 1);
  205. if (url != NULL && url->url->datalen > 0) {
  206. lua_pushlstring (L, rspamd_url_data_unsafe (url->url), url->url->datalen);
  207. }
  208. else {
  209. lua_pushnil (L);
  210. }
  211. return 1;
  212. }
  213. /***
  214. * @method url:get_query()
  215. * Get query of the url
  216. * @return {string} query part of URL
  217. */
  218. static gint
  219. lua_url_get_query (lua_State *L)
  220. {
  221. LUA_TRACE_POINT;
  222. struct rspamd_lua_url *url = lua_check_url (L, 1);
  223. if (url != NULL && url->url->querylen > 0) {
  224. lua_pushlstring (L, rspamd_url_query_unsafe (url->url), url->url->querylen);
  225. }
  226. else {
  227. lua_pushnil (L);
  228. }
  229. return 1;
  230. }
  231. /***
  232. * @method url:get_fragment()
  233. * Get fragment of the url
  234. * @return {string} fragment part of URL
  235. */
  236. static gint
  237. lua_url_get_fragment (lua_State *L)
  238. {
  239. LUA_TRACE_POINT;
  240. struct rspamd_lua_url *url = lua_check_url (L, 1);
  241. if (url != NULL && url->url->fragmentlen > 0) {
  242. lua_pushlstring (L, rspamd_url_fragment_unsafe (url->url), url->url->fragmentlen);
  243. }
  244. else {
  245. lua_pushnil (L);
  246. }
  247. return 1;
  248. }
  249. /***
  250. * @method url:get_text()
  251. * Get full content of the url
  252. * @return {string} url string
  253. */
  254. static gint
  255. lua_url_get_text (lua_State *L)
  256. {
  257. LUA_TRACE_POINT;
  258. struct rspamd_lua_url *url = lua_check_url (L, 1);
  259. if (url != NULL) {
  260. lua_pushlstring (L, url->url->string, url->url->urllen);
  261. }
  262. else {
  263. lua_pushnil (L);
  264. }
  265. return 1;
  266. }
  267. /***
  268. * @method url:tostring()
  269. * Get full content of the url or user@domain in case of email
  270. * @return {string} url as a string
  271. */
  272. static gint
  273. lua_url_tostring (lua_State *L)
  274. {
  275. LUA_TRACE_POINT;
  276. struct rspamd_lua_url *url = lua_check_url (L, 1);
  277. if (url != NULL && url->url != NULL) {
  278. if (url->url->protocol == PROTOCOL_MAILTO) {
  279. gchar *tmp = g_malloc (url->url->userlen + 1 +
  280. url->url->hostlen);
  281. if (url->url->userlen) {
  282. memcpy (tmp, url->url->string + url->url->usershift, url->url->userlen);
  283. }
  284. tmp[url->url->userlen] = '@';
  285. memcpy (tmp + url->url->userlen + 1, rspamd_url_host_unsafe (url->url),
  286. url->url->hostlen);
  287. lua_pushlstring (L, tmp, url->url->userlen + 1 + url->url->hostlen);
  288. g_free (tmp);
  289. }
  290. else {
  291. lua_pushlstring (L, url->url->string, url->url->urllen);
  292. }
  293. }
  294. else {
  295. lua_pushnil (L);
  296. }
  297. return 1;
  298. }
  299. /***
  300. * @method url:get_raw()
  301. * Get full content of the url as it was parsed (e.g. with urldecode)
  302. * @return {string} url string
  303. */
  304. static gint
  305. lua_url_get_raw (lua_State *L)
  306. {
  307. LUA_TRACE_POINT;
  308. struct rspamd_lua_url *url = lua_check_url (L, 1);
  309. if (url != NULL) {
  310. lua_pushlstring (L, url->url->raw, url->url->rawlen);
  311. }
  312. else {
  313. lua_pushnil (L);
  314. }
  315. return 1;
  316. }
  317. /***
  318. * @method url:is_phished()
  319. * Check whether URL is treated as phished
  320. * @return {boolean} `true` if URL is phished
  321. */
  322. static gint
  323. lua_url_is_phished (lua_State *L)
  324. {
  325. LUA_TRACE_POINT;
  326. struct rspamd_lua_url *url = lua_check_url (L, 1);
  327. if (url != NULL) {
  328. lua_pushboolean (L, url->url->flags & RSPAMD_URL_FLAG_PHISHED);
  329. }
  330. else {
  331. lua_pushnil (L);
  332. }
  333. return 1;
  334. }
  335. /***
  336. * @method url:is_redirected()
  337. * Check whether URL was redirected
  338. * @return {boolean} `true` if URL is redirected
  339. */
  340. static gint
  341. lua_url_is_redirected (lua_State *L)
  342. {
  343. LUA_TRACE_POINT;
  344. struct rspamd_lua_url *url = lua_check_url (L, 1);
  345. if (url != NULL) {
  346. lua_pushboolean (L, url->url->flags & RSPAMD_URL_FLAG_REDIRECTED);
  347. }
  348. else {
  349. lua_pushnil (L);
  350. }
  351. return 1;
  352. }
  353. /***
  354. * @method url:is_obscured()
  355. * Check whether URL is treated as obscured or obfuscated (e.g. numbers in IP address or other hacks)
  356. * @return {boolean} `true` if URL is obscured
  357. */
  358. static gint
  359. lua_url_is_obscured (lua_State *L)
  360. {
  361. LUA_TRACE_POINT;
  362. struct rspamd_lua_url *url = lua_check_url (L, 1);
  363. if (url != NULL) {
  364. lua_pushboolean (L, url->url->flags & RSPAMD_URL_FLAG_OBSCURED);
  365. }
  366. else {
  367. lua_pushnil (L);
  368. }
  369. return 1;
  370. }
  371. /***
  372. * @method url:is_html_displayed()
  373. * Check whether URL is just displayed in HTML (e.g. NOT a real href)
  374. * @return {boolean} `true` if URL is displayed only
  375. */
  376. static gint
  377. lua_url_is_html_displayed (lua_State *L)
  378. {
  379. LUA_TRACE_POINT;
  380. struct rspamd_lua_url *url = lua_check_url (L, 1);
  381. if (url != NULL) {
  382. lua_pushboolean (L, url->url->flags & RSPAMD_URL_FLAG_HTML_DISPLAYED);
  383. }
  384. else {
  385. lua_pushnil (L);
  386. }
  387. return 1;
  388. }
  389. /***
  390. * @method url:is_subject()
  391. * Check whether URL is found in subject
  392. * @return {boolean} `true` if URL is found in subject
  393. */
  394. static gint
  395. lua_url_is_subject (lua_State *L)
  396. {
  397. LUA_TRACE_POINT;
  398. struct rspamd_lua_url *url = lua_check_url (L, 1);
  399. if (url != NULL) {
  400. lua_pushboolean (L, url->url->flags & RSPAMD_URL_FLAG_SUBJECT);
  401. }
  402. else {
  403. lua_pushnil (L);
  404. }
  405. return 1;
  406. }
  407. /***
  408. * @method url:get_phished()
  409. * Get another URL that pretends to be this URL (e.g. used in phishing)
  410. * @return {url} phished URL
  411. */
  412. static gint
  413. lua_url_get_phished (lua_State *L)
  414. {
  415. LUA_TRACE_POINT;
  416. struct rspamd_lua_url *purl, *url = lua_check_url (L, 1);
  417. if (url) {
  418. if (url->url->linked_url != NULL) {
  419. if (url->url->flags &
  420. (RSPAMD_URL_FLAG_PHISHED|RSPAMD_URL_FLAG_REDIRECTED)) {
  421. purl = lua_newuserdata (L, sizeof (struct rspamd_lua_url));
  422. rspamd_lua_setclass (L, "rspamd{url}", -1);
  423. purl->url = url->url->linked_url;
  424. return 1;
  425. }
  426. }
  427. }
  428. lua_pushnil (L);
  429. return 1;
  430. }
  431. /***
  432. * @method url:set_redirected(url,[ pool])
  433. * Set url as redirected to another url
  434. * @param {string|url} url new url that is redirecting an old one
  435. * @param {pool} pool if url is a string this is required for parsing
  436. * @return {url} parsed redirected url (if needed)
  437. */
  438. static gint
  439. lua_url_set_redirected (lua_State *L)
  440. {
  441. LUA_TRACE_POINT;
  442. struct rspamd_lua_url *url = lua_check_url (L, 1), *redir;
  443. rspamd_mempool_t *pool = NULL;
  444. if (url == NULL) {
  445. return luaL_error (L, "url is required as the first argument");
  446. }
  447. if (lua_type (L, 2) == LUA_TSTRING) {
  448. /* Parse url */
  449. if (lua_type (L, 3) != LUA_TUSERDATA) {
  450. return luaL_error (L, "mempool is required as the third argument");
  451. }
  452. pool = rspamd_lua_check_mempool (L, 3);
  453. if (pool == NULL) {
  454. return luaL_error (L, "mempool is required as the third argument");
  455. }
  456. gsize len;
  457. const gchar *urlstr = lua_tolstring (L, 2, &len);
  458. rspamd_url_find_single (pool, urlstr, len, RSPAMD_URL_FIND_ALL,
  459. lua_url_single_inserter, L);
  460. if (lua_type (L, -1) != LUA_TUSERDATA) {
  461. /* URL is actually not found */
  462. lua_pushnil (L);
  463. }
  464. else {
  465. redir = lua_check_url (L, -1);
  466. url->url->flags |= RSPAMD_URL_FLAG_REDIRECTED;
  467. url->url->linked_url = redir->url;
  468. }
  469. }
  470. else {
  471. redir = lua_check_url (L, 2);
  472. if (redir == NULL) {
  473. return luaL_error (L, "url is required as the second argument");
  474. }
  475. url->url->flags |= RSPAMD_URL_FLAG_REDIRECTED;
  476. url->url->linked_url = redir->url;
  477. /* Push back on stack */
  478. lua_pushvalue (L, 2);
  479. }
  480. return 1;
  481. }
  482. /***
  483. * @method url:get_tld()
  484. * Get effective second level domain part (eSLD) of the url host
  485. * @return {string} effective second level domain part (eSLD) of the url host
  486. */
  487. static gint
  488. lua_url_get_tld (lua_State *L)
  489. {
  490. LUA_TRACE_POINT;
  491. struct rspamd_lua_url *url = lua_check_url (L, 1);
  492. if (url != NULL && url->url->tldlen > 0) {
  493. lua_pushlstring (L, rspamd_url_tld_unsafe (url->url), url->url->tldlen);
  494. }
  495. else {
  496. lua_pushnil (L);
  497. }
  498. return 1;
  499. }
  500. /***
  501. * @method url:get_protocol()
  502. * Get protocol name
  503. * @return {string} protocol as a string
  504. */
  505. static gint
  506. lua_url_get_protocol (lua_State *L)
  507. {
  508. LUA_TRACE_POINT;
  509. struct rspamd_lua_url *url = lua_check_url (L, 1);
  510. if (url != NULL && url->url->protocol != PROTOCOL_UNKNOWN) {
  511. lua_pushstring (L, rspamd_url_protocol_name (url->url->protocol));
  512. }
  513. else {
  514. lua_pushnil (L);
  515. }
  516. return 1;
  517. }
  518. /***
  519. * @method url:get_count()
  520. * Return number of occurrencies for this particular URL
  521. * @return {number} number of occurrencies
  522. */
  523. static gint
  524. lua_url_get_count (lua_State *L)
  525. {
  526. LUA_TRACE_POINT;
  527. struct rspamd_lua_url *url = lua_check_url (L, 1);
  528. if (url != NULL && url->url != NULL) {
  529. lua_pushinteger (L, url->url->count);
  530. }
  531. else {
  532. lua_pushnil (L);
  533. }
  534. return 1;
  535. }
  536. /***
  537. * @method url:get_visible()
  538. * Get visible part of the url with html tags stripped
  539. * @return {string} url string
  540. */
  541. static gint
  542. lua_url_get_visible (lua_State *L)
  543. {
  544. LUA_TRACE_POINT;
  545. struct rspamd_lua_url *url = lua_check_url (L, 1);
  546. if (url != NULL && url->url->visible_part) {
  547. lua_pushstring (L, url->url->visible_part);
  548. }
  549. else {
  550. lua_pushnil (L);
  551. }
  552. return 1;
  553. }
  554. /***
  555. * @method url:to_table()
  556. * Return url as a table with the following fields:
  557. *
  558. * - `url`: full content
  559. * - `host`: hostname part
  560. * - `user`: user part
  561. * - `path`: path part
  562. * - `tld`: top level domain
  563. * - `protocol`: url protocol
  564. * @return {table} URL as a table
  565. */
  566. static gint
  567. lua_url_to_table (lua_State *L)
  568. {
  569. LUA_TRACE_POINT;
  570. struct rspamd_lua_url *url = lua_check_url (L, 1);
  571. struct rspamd_url *u;
  572. if (url != NULL) {
  573. u = url->url;
  574. lua_createtable (L, 0, 12);
  575. lua_pushstring (L, "url");
  576. lua_pushlstring (L, u->string, u->urllen);
  577. lua_settable (L, -3);
  578. if (u->hostlen > 0) {
  579. lua_pushstring (L, "host");
  580. lua_pushlstring (L, rspamd_url_host_unsafe (u), u->hostlen);
  581. lua_settable (L, -3);
  582. }
  583. if (u->port != 0) {
  584. lua_pushstring (L, "port");
  585. lua_pushinteger (L, u->port);
  586. lua_settable (L, -3);
  587. }
  588. if (u->tldlen > 0) {
  589. lua_pushstring (L, "tld");
  590. lua_pushlstring (L, rspamd_url_tld_unsafe (u), u->tldlen);
  591. lua_settable (L, -3);
  592. }
  593. if (u->userlen > 0) {
  594. lua_pushstring (L, "user");
  595. lua_pushlstring (L, rspamd_url_user (u), u->userlen);
  596. lua_settable (L, -3);
  597. }
  598. if (u->datalen > 0) {
  599. lua_pushstring (L, "path");
  600. lua_pushlstring (L, rspamd_url_data_unsafe (u), u->datalen);
  601. lua_settable (L, -3);
  602. }
  603. if (u->querylen > 0) {
  604. lua_pushstring (L, "query");
  605. lua_pushlstring (L, rspamd_url_query_unsafe (u), u->querylen);
  606. lua_settable (L, -3);
  607. }
  608. if (u->fragmentlen > 0) {
  609. lua_pushstring (L, "fragment");
  610. lua_pushlstring (L, rspamd_url_fragment_unsafe (u), u->fragmentlen);
  611. lua_settable (L, -3);
  612. }
  613. lua_pushstring (L, "protocol");
  614. lua_pushstring (L, rspamd_url_protocol_name (u->protocol));
  615. lua_settable (L, -3);
  616. }
  617. else {
  618. lua_pushnil (L);
  619. }
  620. return 1;
  621. }
  622. static rspamd_mempool_t *static_lua_url_pool;
  623. RSPAMD_CONSTRUCTOR(rspamd_urls_static_pool_ctor) {
  624. static_lua_url_pool = rspamd_mempool_new (rspamd_mempool_suggest_size (),
  625. "static_lua_url", 0);
  626. }
  627. RSPAMD_DESTRUCTOR(rspamd_urls_static_pool_dtor) {
  628. rspamd_mempool_delete (static_lua_url_pool);
  629. }
  630. /***
  631. * @function url.create([mempool,] str, [{flags_table}])
  632. * @param {rspamd_mempool} memory pool for URL, e.g. `task:get_mempool()`
  633. * @param {string} text that contains URL (can also contain other stuff)
  634. * @return {url} new url object that exists as long as the corresponding mempool exists
  635. */
  636. static gint
  637. lua_url_create (lua_State *L)
  638. {
  639. LUA_TRACE_POINT;
  640. rspamd_mempool_t *pool;
  641. struct rspamd_lua_text *t;
  642. gboolean own_pool = FALSE;
  643. struct rspamd_lua_url *u;
  644. if (lua_type (L, 1) == LUA_TUSERDATA) {
  645. pool = rspamd_lua_check_mempool (L, 1);
  646. t = lua_check_text_or_string (L, 2);
  647. }
  648. else {
  649. own_pool = TRUE;
  650. pool = static_lua_url_pool;
  651. t = lua_check_text_or_string (L, 2);
  652. }
  653. if (pool == NULL || t == NULL) {
  654. return luaL_error (L, "invalid arguments");
  655. }
  656. else {
  657. rspamd_url_find_single (pool, t->start, t->len, RSPAMD_URL_FIND_ALL,
  658. lua_url_single_inserter, L);
  659. if (lua_type (L, -1) != LUA_TUSERDATA) {
  660. /* URL is actually not found */
  661. lua_pushnil (L);
  662. return 1;
  663. }
  664. u = (struct rspamd_lua_url *)lua_touserdata (L, -1);
  665. if (lua_type (L, 3) == LUA_TTABLE) {
  666. /* Add flags */
  667. for (lua_pushnil (L); lua_next (L, 3); lua_pop (L, 1)) {
  668. int nmask = 0;
  669. const gchar *fname = lua_tostring (L, -1);
  670. if (rspamd_url_flag_from_string (fname, &nmask)) {
  671. u->url->flags |= nmask;
  672. }
  673. else {
  674. lua_pop (L, 1);
  675. return luaL_error (L, "invalid flag: %s", fname);
  676. }
  677. }
  678. }
  679. }
  680. return 1;
  681. }
  682. /***
  683. * @function url.init(tld_file)
  684. * Initialize url library if not initialized yet by Rspamd
  685. * @param {string} tld_file path to effective_tld_names.dat file (public suffix list)
  686. * @return nothing
  687. */
  688. static gint
  689. lua_url_init (lua_State *L)
  690. {
  691. const gchar *tld_path;
  692. tld_path = luaL_checkstring (L, 1);
  693. rspamd_url_init (tld_path);
  694. return 0;
  695. }
  696. static gboolean
  697. lua_url_table_inserter (struct rspamd_url *url, gsize start_offset,
  698. gsize end_offset, gpointer ud)
  699. {
  700. lua_State *L = ud;
  701. struct rspamd_lua_url *lua_url;
  702. gint n;
  703. n = rspamd_lua_table_size (L, -1);
  704. lua_url = lua_newuserdata (L, sizeof (struct rspamd_lua_url));
  705. rspamd_lua_setclass (L, "rspamd{url}", -1);
  706. lua_url->url = url;
  707. lua_rawseti (L, -2, n + 1);
  708. return TRUE;
  709. }
  710. static gint
  711. lua_url_all (lua_State *L)
  712. {
  713. LUA_TRACE_POINT;
  714. rspamd_mempool_t *pool = rspamd_lua_check_mempool (L, 1);
  715. const gchar *text;
  716. size_t length;
  717. if (pool == NULL) {
  718. lua_pushnil (L);
  719. }
  720. else {
  721. text = luaL_checklstring (L, 2, &length);
  722. if (text != NULL) {
  723. lua_newtable (L);
  724. rspamd_url_find_multiple (pool, text, length,
  725. RSPAMD_URL_FIND_ALL, NULL,
  726. lua_url_table_inserter, L);
  727. }
  728. else {
  729. lua_pushnil (L);
  730. }
  731. }
  732. return 1;
  733. }
  734. /***
  735. * @method url:get_flags()
  736. * Return flags for a specified URL as map 'flag'->true for all flags set,
  737. * possible flags are:
  738. *
  739. * - `phished`: URL is likely phished
  740. * - `numeric`: URL is numeric (e.g. IP address)
  741. * - `obscured`: URL was obscured
  742. * - `redirected`: URL comes from redirector
  743. * - `html_displayed`: URL is used just for displaying purposes
  744. * - `text`: URL comes from the text
  745. * - `subject`: URL comes from the subject
  746. * - `host_encoded`: URL host part is encoded
  747. * - `schema_encoded`: URL schema part is encoded
  748. * - `query_encoded`: URL query part is encoded
  749. * - `missing_slahes`: URL has some slashes missing
  750. * - `idn`: URL has international characters
  751. * - `has_port`: URL has port
  752. * - `has_user`: URL has user part
  753. * - `schemaless`: URL has no schema
  754. * - `unnormalised`: URL has some unicode unnormalities
  755. * - `zw_spaces`: URL has some zero width spaces
  756. * - `url_displayed`: URL has some other url-like string in visible part
  757. * - `image`: URL is from src attribute of img HTML tag
  758. * @return {table} URL flags
  759. */
  760. #define PUSH_FLAG(fl) do { \
  761. if (flags & (fl)) { \
  762. lua_pushstring (L, rspamd_url_flag_to_string (fl)); \
  763. lua_pushboolean (L, true); \
  764. lua_settable (L, -3); \
  765. } \
  766. } while (0)
  767. static gint
  768. lua_url_get_flags (lua_State *L)
  769. {
  770. LUA_TRACE_POINT;
  771. struct rspamd_lua_url *url = lua_check_url (L, 1);
  772. enum rspamd_url_flags flags;
  773. if (url != NULL) {
  774. flags = url->url->flags;
  775. lua_createtable (L, 0, 4);
  776. for (gint i = 0; i < RSPAMD_URL_MAX_FLAG_SHIFT; i ++) {
  777. PUSH_FLAG (1u << i);
  778. }
  779. }
  780. else {
  781. return luaL_error (L, "invalid arguments");
  782. }
  783. return 1;
  784. }
  785. #undef PUSH_FLAG
  786. static gint
  787. lua_url_get_flags_num (lua_State *L)
  788. {
  789. LUA_TRACE_POINT;
  790. struct rspamd_lua_url *url = lua_check_url (L, 1);
  791. if (url) {
  792. lua_pushinteger (L, url->url->flags);
  793. }
  794. else {
  795. return luaL_error (L, "invalid arguments");
  796. }
  797. return 1;
  798. }
  799. void
  800. lua_tree_url_callback (gpointer key, gpointer value, gpointer ud)
  801. {
  802. struct rspamd_lua_url *lua_url;
  803. struct rspamd_url *url = (struct rspamd_url *)value;
  804. struct lua_tree_cb_data *cb = ud;
  805. if ((url->protocol & cb->protocols_mask) == url->protocol) {
  806. /* Handle different flags application logic */
  807. switch (cb->flags_mode) {
  808. case url_flags_mode_include_any:
  809. if (url->flags != (url->flags & cb->flags_mask)) {
  810. return;
  811. }
  812. break;
  813. case url_flags_mode_include_explicit:
  814. if ((url->flags & cb->flags_mask) != cb->flags_mask) {
  815. return;
  816. }
  817. break;
  818. case url_flags_mode_exclude_include:
  819. if ((url->flags & cb->flags_exclude_mask) != 0) {
  820. return;
  821. }
  822. if ((url->flags & cb->flags_mask) == 0) {
  823. return;
  824. }
  825. break;
  826. }
  827. if (cb->skip_prob > 0) {
  828. gdouble coin = rspamd_random_double_fast_seed (cb->xoroshiro_state);
  829. if (coin < cb->skip_prob) {
  830. return;
  831. }
  832. }
  833. lua_url = lua_newuserdata (cb->L, sizeof (struct rspamd_lua_url));
  834. lua_pushvalue (cb->L, cb->metatable_pos);
  835. lua_setmetatable (cb->L, -2);
  836. lua_url->url = url;
  837. lua_rawseti (cb->L, -2, cb->i++);
  838. }
  839. }
  840. gboolean
  841. lua_url_cbdata_fill (lua_State *L,
  842. gint pos,
  843. struct lua_tree_cb_data *cbd,
  844. guint default_protocols,
  845. guint default_flags,
  846. gsize max_urls)
  847. {
  848. gint protocols_mask = 0;
  849. gint pos_arg_type = lua_type (L, pos);
  850. guint flags_mask = default_flags;
  851. gboolean seen_flags = FALSE, seen_protocols = FALSE;
  852. memset (cbd, 0, sizeof (*cbd));
  853. cbd->flags_mode = url_flags_mode_include_any;
  854. if (pos_arg_type == LUA_TBOOLEAN) {
  855. protocols_mask = default_protocols;
  856. if (lua_toboolean (L, 2)) {
  857. protocols_mask |= PROTOCOL_MAILTO;
  858. }
  859. }
  860. else if (pos_arg_type == LUA_TTABLE) {
  861. if (rspamd_lua_geti (L, 1, pos) == LUA_TNIL) {
  862. /* New method: indexed table */
  863. lua_getfield (L, pos, "flags");
  864. if (lua_istable (L, -1)) {
  865. gint top = lua_gettop (L);
  866. lua_getfield (L, pos, "flags_mode");
  867. if (lua_isstring (L, -1)) {
  868. const gchar *mode_str = lua_tostring (L, -1);
  869. if (strcmp (mode_str, "explicit") == 0) {
  870. cbd->flags_mode = url_flags_mode_include_explicit;
  871. /*
  872. * Ignore default flags in this mode and include
  873. * merely flags specified by a caller
  874. */
  875. flags_mask = 0;
  876. }
  877. }
  878. lua_pop (L, 1);
  879. for (lua_pushnil (L); lua_next (L, top); lua_pop (L, 1)) {
  880. int nmask = 0;
  881. if (lua_type (L, -1) == LUA_TSTRING) {
  882. const gchar *fname = lua_tostring (L, -1);
  883. if (rspamd_url_flag_from_string (fname, &nmask)) {
  884. flags_mask |= nmask;
  885. }
  886. else {
  887. msg_info ("bad url flag: %s", fname);
  888. return FALSE;
  889. }
  890. }
  891. else {
  892. flags_mask |= lua_tointeger (L, -1);
  893. }
  894. }
  895. seen_flags = TRUE;
  896. }
  897. else {
  898. flags_mask |= default_flags;
  899. }
  900. lua_pop (L, 1);
  901. lua_getfield (L, pos, "protocols");
  902. if (lua_istable (L, -1)) {
  903. gint top = lua_gettop (L);
  904. for (lua_pushnil (L); lua_next (L, top); lua_pop (L, 1)) {
  905. int nmask;
  906. const gchar *pname = lua_tostring (L, -1);
  907. nmask = rspamd_url_protocol_from_string (pname);
  908. if (nmask != PROTOCOL_UNKNOWN) {
  909. protocols_mask |= nmask;
  910. }
  911. else {
  912. msg_info ("bad url protocol: %s", pname);
  913. return FALSE;
  914. }
  915. }
  916. seen_protocols = TRUE;
  917. }
  918. else {
  919. protocols_mask = default_protocols;
  920. }
  921. lua_pop (L, 1);
  922. if (!seen_protocols) {
  923. lua_getfield (L, pos, "emails");
  924. if (lua_isboolean (L, -1)) {
  925. if (lua_toboolean (L, -1)) {
  926. protocols_mask |= PROTOCOL_MAILTO;
  927. }
  928. }
  929. lua_pop (L, 1);
  930. }
  931. if (!seen_flags) {
  932. lua_getfield (L, pos, "images");
  933. if (lua_isboolean (L, -1)) {
  934. if (lua_toboolean (L, -1)) {
  935. flags_mask |= RSPAMD_URL_FLAG_IMAGE;
  936. }
  937. else {
  938. flags_mask &= ~RSPAMD_URL_FLAG_IMAGE;
  939. }
  940. }
  941. else {
  942. flags_mask &= ~RSPAMD_URL_FLAG_IMAGE;
  943. }
  944. lua_pop (L, 1);
  945. }
  946. if (!seen_flags) {
  947. lua_getfield (L, pos, "content");
  948. if (lua_isboolean (L, -1)) {
  949. if (lua_toboolean (L, -1)) {
  950. flags_mask |= RSPAMD_URL_FLAG_CONTENT;
  951. }
  952. else {
  953. flags_mask &= ~RSPAMD_URL_FLAG_CONTENT;
  954. }
  955. }
  956. else {
  957. flags_mask &= ~RSPAMD_URL_FLAG_CONTENT;
  958. }
  959. lua_pop (L, 1);
  960. }
  961. lua_getfield (L, pos, "max_urls");
  962. if (lua_isnumber (L, -1)) {
  963. max_urls = lua_tonumber (L, -1);
  964. }
  965. lua_pop (L, 1);
  966. lua_getfield (L, pos, "sort");
  967. if (lua_isboolean (L, -1)) {
  968. cbd->sort = TRUE;
  969. }
  970. lua_pop (L, 1);
  971. }
  972. else {
  973. /* Plain table of the protocols */
  974. for (lua_pushnil (L); lua_next (L, pos); lua_pop (L, 1)) {
  975. int nmask;
  976. const gchar *pname = lua_tostring (L, -1);
  977. nmask = rspamd_url_protocol_from_string (pname);
  978. if (nmask != PROTOCOL_UNKNOWN) {
  979. protocols_mask |= nmask;
  980. }
  981. else {
  982. msg_info ("bad url protocol: %s", pname);
  983. return FALSE;
  984. }
  985. }
  986. }
  987. lua_pop (L, 1); /* After rspamd_lua_geti */
  988. }
  989. else if (pos_arg_type == LUA_TSTRING) {
  990. const gchar *plist = lua_tostring (L, pos);
  991. gchar **strvec;
  992. gchar * const *cvec;
  993. strvec = g_strsplit_set (plist, ",;", -1);
  994. cvec = strvec;
  995. while (*cvec) {
  996. int nmask;
  997. nmask = rspamd_url_protocol_from_string (*cvec);
  998. if (nmask != PROTOCOL_UNKNOWN) {
  999. protocols_mask |= nmask;
  1000. }
  1001. else {
  1002. msg_info ("bad url protocol: %s", *cvec);
  1003. g_strfreev (strvec);
  1004. return FALSE;
  1005. }
  1006. cvec ++;
  1007. }
  1008. g_strfreev (strvec);
  1009. }
  1010. else if (pos_arg_type == LUA_TNONE || pos_arg_type == LUA_TNIL) {
  1011. protocols_mask = default_protocols;
  1012. flags_mask = default_flags;
  1013. }
  1014. else {
  1015. return FALSE;
  1016. }
  1017. if (lua_type (L, pos + 1) == LUA_TBOOLEAN) {
  1018. if (lua_toboolean (L, pos + 1)) {
  1019. flags_mask |= RSPAMD_URL_FLAG_IMAGE;
  1020. }
  1021. else {
  1022. flags_mask &= ~RSPAMD_URL_FLAG_IMAGE;
  1023. }
  1024. }
  1025. cbd->i = 1;
  1026. cbd->L = L;
  1027. cbd->max_urls = max_urls;
  1028. cbd->protocols_mask = protocols_mask;
  1029. cbd->flags_mask = flags_mask;
  1030. /* This needs to be removed from the stack */
  1031. rspamd_lua_class_metatable (L, "rspamd{url}");
  1032. cbd->metatable_pos = lua_gettop (L);
  1033. (void)lua_checkstack (L, cbd->metatable_pos + 4);
  1034. return TRUE;
  1035. }
  1036. gboolean
  1037. lua_url_cbdata_fill_exclude_include (lua_State *L,
  1038. gint pos,
  1039. struct lua_tree_cb_data *cbd,
  1040. guint default_protocols,
  1041. gsize max_urls)
  1042. {
  1043. guint protocols_mask = default_protocols;
  1044. guint include_flags_mask, exclude_flags_mask;
  1045. gint pos_arg_type = lua_type (L, pos);
  1046. memset (cbd, 0, sizeof (*cbd));
  1047. cbd->flags_mode = url_flags_mode_exclude_include;
  1048. /* Include flags */
  1049. if (pos_arg_type == LUA_TTABLE) {
  1050. include_flags_mask = 0; /* Reset to no flags */
  1051. for (lua_pushnil(L); lua_next(L, pos); lua_pop (L, 1)) {
  1052. int nmask = 0;
  1053. if (lua_type (L, -1) == LUA_TSTRING) {
  1054. const gchar *fname = lua_tostring (L, -1);
  1055. if (rspamd_url_flag_from_string(fname, &nmask)) {
  1056. include_flags_mask |= nmask;
  1057. }
  1058. else {
  1059. msg_info ("bad url include flag: %s", fname);
  1060. return FALSE;
  1061. }
  1062. }
  1063. else {
  1064. include_flags_mask |= lua_tointeger (L, -1);
  1065. }
  1066. }
  1067. }
  1068. else if (pos_arg_type == LUA_TNIL || pos_arg_type == LUA_TNONE) {
  1069. /* Include all flags */
  1070. include_flags_mask = ~0U;
  1071. }
  1072. else {
  1073. msg_info ("bad arguments: wrong include mask");
  1074. return FALSE;
  1075. }
  1076. /* Exclude flags */
  1077. pos_arg_type = lua_type (L, pos + 1);
  1078. if (pos_arg_type == LUA_TTABLE) {
  1079. exclude_flags_mask = 0; /* Reset to no flags */
  1080. for (lua_pushnil(L); lua_next(L, pos); lua_pop (L, 1)) {
  1081. int nmask = 0;
  1082. if (lua_type (L, -1) == LUA_TSTRING) {
  1083. const gchar *fname = lua_tostring (L, -1);
  1084. if (rspamd_url_flag_from_string(fname, &nmask)) {
  1085. exclude_flags_mask |= nmask;
  1086. }
  1087. else {
  1088. msg_info ("bad url exclude flag: %s", fname);
  1089. return FALSE;
  1090. }
  1091. }
  1092. else {
  1093. exclude_flags_mask |= lua_tointeger (L, -1);
  1094. }
  1095. }
  1096. }
  1097. else if (pos_arg_type == LUA_TNIL || pos_arg_type == LUA_TNONE) {
  1098. /* Empty all exclude flags */
  1099. exclude_flags_mask = 0U;
  1100. }
  1101. else {
  1102. msg_info ("bad arguments: wrong exclude mask");
  1103. return FALSE;
  1104. }
  1105. if (lua_type (L, pos + 2) == LUA_TTABLE) {
  1106. protocols_mask = 0U; /* Reset all protocols */
  1107. for (lua_pushnil (L); lua_next (L, pos + 2); lua_pop (L, 1)) {
  1108. int nmask;
  1109. const gchar *pname = lua_tostring (L, -1);
  1110. nmask = rspamd_url_protocol_from_string (pname);
  1111. if (nmask != PROTOCOL_UNKNOWN) {
  1112. protocols_mask |= nmask;
  1113. }
  1114. else {
  1115. msg_info ("bad url protocol: %s", pname);
  1116. return FALSE;
  1117. }
  1118. }
  1119. }
  1120. else {
  1121. protocols_mask = default_protocols;
  1122. }
  1123. cbd->i = 1;
  1124. cbd->L = L;
  1125. cbd->max_urls = max_urls;
  1126. cbd->protocols_mask = protocols_mask;
  1127. cbd->flags_mask = include_flags_mask;
  1128. cbd->flags_exclude_mask = exclude_flags_mask;
  1129. /* This needs to be removed from the stack */
  1130. rspamd_lua_class_metatable (L, "rspamd{url}");
  1131. cbd->metatable_pos = lua_gettop (L);
  1132. (void)lua_checkstack (L, cbd->metatable_pos + 4);
  1133. return TRUE;
  1134. }
  1135. void
  1136. lua_url_cbdata_dtor (struct lua_tree_cb_data *cbd)
  1137. {
  1138. if (cbd->metatable_pos != -1) {
  1139. lua_remove (cbd->L, cbd->metatable_pos);
  1140. }
  1141. }
  1142. gsize
  1143. lua_url_adjust_skip_prob (gdouble timestamp,
  1144. guchar digest[16],
  1145. struct lua_tree_cb_data *cb,
  1146. gsize sz)
  1147. {
  1148. if (cb->max_urls > 0 && sz > cb->max_urls) {
  1149. cb->skip_prob = 1.0 - ((gdouble)cb->max_urls) / (gdouble)sz;
  1150. /*
  1151. * Use task dependent probabilistic seed to ensure that
  1152. * consequent task:get_urls return the same list of urls
  1153. */
  1154. memset (cb->xoroshiro_state, 0, sizeof (cb->xoroshiro_state));
  1155. memcpy (&cb->xoroshiro_state[0], &timestamp,
  1156. MIN (sizeof (cb->xoroshiro_state[0]), sizeof (timestamp)));
  1157. memcpy (&cb->xoroshiro_state[1], digest, 16);
  1158. sz = cb->max_urls;
  1159. }
  1160. return sz;
  1161. }
  1162. static gint
  1163. lua_url_eq (lua_State *L)
  1164. {
  1165. LUA_TRACE_POINT;
  1166. struct rspamd_lua_url *u1 = lua_check_url (L, 1),
  1167. *u2 = lua_check_url (L, 2);
  1168. if (u1 && u2) {
  1169. lua_pushboolean (L, (rspamd_url_cmp (u1->url, u2->url) == 0));
  1170. }
  1171. else {
  1172. lua_pushboolean (L, false);
  1173. }
  1174. return 1;
  1175. }
  1176. static gint
  1177. lua_url_lt (lua_State *L)
  1178. {
  1179. LUA_TRACE_POINT;
  1180. struct rspamd_lua_url *u1 = lua_check_url (L, 1),
  1181. *u2 = lua_check_url (L, 2);
  1182. if (u1 && u2) {
  1183. lua_pushinteger (L, rspamd_url_cmp (u1->url, u2->url));
  1184. }
  1185. else {
  1186. return luaL_error (L, "invalid arguments");
  1187. }
  1188. return 1;
  1189. }
  1190. static gint
  1191. lua_load_url (lua_State * L)
  1192. {
  1193. lua_newtable (L);
  1194. luaL_register (L, NULL, urllib_f);
  1195. /* Push flags */
  1196. lua_createtable (L, 0, RSPAMD_URL_MAX_FLAG_SHIFT);
  1197. for (int i = 0; i < RSPAMD_URL_MAX_FLAG_SHIFT; i ++) {
  1198. guint flag = 1u << i;
  1199. lua_pushinteger (L, flag);
  1200. lua_setfield (L, -2, rspamd_url_flag_to_string (flag));
  1201. }
  1202. lua_setfield (L, -2, "flags");
  1203. return 1;
  1204. }
  1205. void
  1206. luaopen_url (lua_State * L)
  1207. {
  1208. rspamd_lua_new_class (L, "rspamd{url}", urllib_m);
  1209. lua_pop (L, 1);
  1210. rspamd_lua_add_preload (L, "rspamd_url", lua_load_url);
  1211. }