PageRenderTime 70ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/src/message.c

https://bitbucket.org/vstakhov/rspamd/
C | 1681 lines | 1642 code | 15 blank | 24 comment | 38 complexity | bec15ba0968e6014de4ddf7a19de329e MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /*
  2. * Copyright (c) 2009, Rambler media
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY Rambler media ''AS IS'' AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  15. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. * DISCLAIMED. IN NO EVENT SHALL Rambler BE LIABLE FOR ANY
  17. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  18. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  19. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  20. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  22. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. */
  24. #include "config.h"
  25. #include "util.h"
  26. #include "main.h"
  27. #include "message.h"
  28. #include "cfg_file.h"
  29. #include "html.h"
  30. #include "images.h"
  31. #define RECURSION_LIMIT 30
  32. #define UTF8_CHARSET "UTF-8"
  33. GByteArray *
  34. strip_html_tags (struct worker_task *task, memory_pool_t * pool, struct mime_text_part *part, GByteArray * src, gint *stateptr)
  35. {
  36. uint8_t *p, *rp, *tbegin = NULL, *end, c, lc;
  37. gint br, i = 0, depth = 0, in_q = 0;
  38. gint state = 0;
  39. GByteArray *buf;
  40. GNode *level_ptr = NULL;
  41. gboolean erase = FALSE;
  42. if (stateptr)
  43. state = *stateptr;
  44. buf = g_byte_array_sized_new (src->len);
  45. g_byte_array_append (buf, src->data, src->len);
  46. c = *src->data;
  47. lc = '\0';
  48. p = src->data;
  49. rp = buf->data;
  50. end = src->data + src->len;
  51. br = 0;
  52. while (i < (gint)src->len) {
  53. switch (c) {
  54. case '\0':
  55. break;
  56. case '<':
  57. if (g_ascii_isspace (*(p + 1))) {
  58. goto reg_char;
  59. }
  60. if (state == 0) {
  61. lc = '<';
  62. tbegin = p + 1;
  63. state = 1;
  64. }
  65. else if (state == 1) {
  66. /* Opening bracket without closing one */
  67. p --;
  68. while (g_ascii_isspace (*p) && p > src->data) {
  69. p --;
  70. }
  71. p ++;
  72. goto unbreak_tag;
  73. }
  74. break;
  75. case '(':
  76. if (state == 2) {
  77. if (lc != '"' && lc != '\'') {
  78. lc = '(';
  79. br++;
  80. }
  81. }
  82. else if (state == 0 && !erase) {
  83. *(rp++) = c;
  84. }
  85. break;
  86. case ')':
  87. if (state == 2) {
  88. if (lc != '"' && lc != '\'') {
  89. lc = ')';
  90. br--;
  91. }
  92. }
  93. else if (state == 0 && !erase) {
  94. *(rp++) = c;
  95. }
  96. break;
  97. case '>':
  98. if (depth) {
  99. depth--;
  100. break;
  101. }
  102. if (in_q) {
  103. break;
  104. }
  105. unbreak_tag:
  106. switch (state) {
  107. case 1: /* HTML/XML */
  108. lc = '>';
  109. in_q = state = 0;
  110. erase = !add_html_node (task, pool, part, tbegin, p - tbegin, end - tbegin, &level_ptr);
  111. break;
  112. case 2: /* PHP */
  113. if (!br && lc != '\"' && *(p - 1) == '?') {
  114. in_q = state = 0;
  115. }
  116. break;
  117. case 3:
  118. in_q = state = 0;
  119. break;
  120. case 4: /* JavaScript/CSS/etc... */
  121. if (p >= src->data + 2 && *(p - 1) == '-' && *(p - 2) == '-') {
  122. in_q = state = 0;
  123. }
  124. break;
  125. default:
  126. if (!erase) {
  127. *(rp++) = c;
  128. }
  129. break;
  130. }
  131. break;
  132. case '"':
  133. case '\'':
  134. if (state == 2 && *(p - 1) != '\\') {
  135. if (lc == c) {
  136. lc = '\0';
  137. }
  138. else if (lc != '\\') {
  139. lc = c;
  140. }
  141. }
  142. else if (state == 0 && !erase) {
  143. *(rp++) = c;
  144. }
  145. if (state && p != src->data && *(p - 1) != '\\' && (!in_q || *p == in_q)) {
  146. if (in_q) {
  147. in_q = 0;
  148. }
  149. else {
  150. in_q = *p;
  151. }
  152. }
  153. break;
  154. case '!':
  155. /* JavaScript & Other HTML scripting languages */
  156. if (state == 1 && *(p - 1) == '<') {
  157. state = 3;
  158. lc = c;
  159. }
  160. else {
  161. if (state == 0 && !erase) {
  162. *(rp++) = c;
  163. }
  164. }
  165. break;
  166. case '-':
  167. if (state == 3 && p >= src->data + 2 && *(p - 1) == '-' && *(p - 2) == '!') {
  168. state = 4;
  169. }
  170. else {
  171. goto reg_char;
  172. }
  173. break;
  174. case '?':
  175. if (state == 1 && *(p - 1) == '<') {
  176. br = 0;
  177. state = 2;
  178. break;
  179. }
  180. case 'E':
  181. case 'e':
  182. /* !DOCTYPE exception */
  183. if (state == 3 && p > src->data + 6
  184. && g_ascii_tolower (*(p - 1)) == 'p'
  185. && g_ascii_tolower (*(p - 2)) == 'y'
  186. && g_ascii_tolower (*(p - 3)) == 't' && g_ascii_tolower (*(p - 4)) == 'c' && g_ascii_tolower (*(p - 5)) == 'o' && g_ascii_tolower (*(p - 6)) == 'd') {
  187. state = 1;
  188. break;
  189. }
  190. /* fall-through */
  191. case 'l':
  192. /* swm: If we encounter '<?xml' then we shouldn't be in
  193. * state == 2 (PHP). Switch back to HTML.
  194. */
  195. if (state == 2 && p > src->data + 2 && *(p - 1) == 'm' && *(p - 2) == 'x') {
  196. state = 1;
  197. break;
  198. }
  199. /* fall-through */
  200. default:
  201. reg_char:
  202. if (state == 0 && !erase) {
  203. *(rp++) = c;
  204. }
  205. break;
  206. }
  207. i++;
  208. if (i < (gint)src->len) {
  209. c = *(++p);
  210. }
  211. }
  212. if (rp < buf->data + src->len) {
  213. *rp = '\0';
  214. g_byte_array_set_size (buf, rp - buf->data);
  215. }
  216. /* Check tag balancing */
  217. if (level_ptr && level_ptr->data != NULL) {
  218. part->is_balanced = FALSE;
  219. }
  220. if (stateptr) {
  221. *stateptr = state;
  222. }
  223. return buf;
  224. }
  225. static void
  226. parse_qmail_recv (memory_pool_t * pool, gchar *line, struct received_header *r)
  227. {
  228. gchar *s, *p, t;
  229. /* We are interested only with received from network headers */
  230. if ((p = strstr (line, "from network")) == NULL) {
  231. r->is_error = 2;
  232. return;
  233. }
  234. p += sizeof ("from network") - 1;
  235. while (g_ascii_isspace (*p) || *p == '[') {
  236. p++;
  237. }
  238. /* format is ip/host */
  239. s = p;
  240. if (*p) {
  241. while (g_ascii_isdigit (*++p) || *p == '.');
  242. if (*p != '/') {
  243. r->is_error = 1;
  244. return;
  245. }
  246. else {
  247. *p = '\0';
  248. r->real_ip = memory_pool_strdup (pool, s);
  249. *p = '/';
  250. /* Now try to parse hostname */
  251. s = ++p;
  252. while (g_ascii_isalnum (*p) || *p == '.' || *p == '-' || *p == '_') {
  253. p++;
  254. }
  255. t = *p;
  256. *p = '\0';
  257. r->real_hostname = memory_pool_strdup (pool, s);
  258. *p = t;
  259. }
  260. }
  261. }
  262. static void
  263. parse_recv_header (memory_pool_t * pool, gchar *line, struct received_header *r)
  264. {
  265. gchar *p, *s, t, **res = NULL;
  266. gint state = 0, next_state = 0;
  267. g_strstrip (line);
  268. p = line;
  269. s = line;
  270. while (*p) {
  271. switch (state) {
  272. /* Initial state, search for from */
  273. case 0:
  274. if (*p == 'f' || *p == 'F') {
  275. if (g_ascii_tolower (*++p) == 'r' && g_ascii_tolower (*++p) == 'o' && g_ascii_tolower (*++p) == 'm') {
  276. p++;
  277. state = 99;
  278. next_state = 1;
  279. }
  280. }
  281. else if (g_ascii_tolower (*p) == 'b' && g_ascii_tolower (*(p + 1)) == 'y') {
  282. state = 3;
  283. }
  284. else {
  285. /* This can be qmail header, parse it separately */
  286. parse_qmail_recv (pool, line, r);
  287. return;
  288. }
  289. break;
  290. /* Read hostname */
  291. case 1:
  292. if (*p == '[') {
  293. /* This should be IP address */
  294. res = &r->from_ip;
  295. state = 98;
  296. next_state = 3;
  297. s = ++p;
  298. }
  299. else if (g_ascii_isalnum (*p) || *p == '.' || *p == '-' || *p == '_') {
  300. p++;
  301. }
  302. else {
  303. t = *p;
  304. *p = '\0';
  305. r->from_hostname = memory_pool_strdup (pool, s);
  306. *p = t;
  307. state = 99;
  308. next_state = 3;
  309. }
  310. break;
  311. /* Try to extract additional info */
  312. case 3:
  313. /* Try to extract ip or () info or by */
  314. if (g_ascii_tolower (*p) == 'b' && g_ascii_tolower (*(p + 1)) == 'y') {
  315. p += 2;
  316. /* Skip spaces after by */
  317. state = 99;
  318. next_state = 5;
  319. }
  320. else if (*p == '(') {
  321. state = 99;
  322. next_state = 4;
  323. p++;
  324. }
  325. else if (*p == '[') {
  326. /* Got ip before '(' so extract it */
  327. s = ++p;
  328. res = &r->from_ip;
  329. state = 98;
  330. next_state = 3;
  331. }
  332. else {
  333. p++;
  334. }
  335. break;
  336. /* We are in () block. Here can be found real hostname and real ip, this is written by some MTA */
  337. case 4:
  338. /* End of block */
  339. if (*p == ')') {
  340. p++;
  341. state = 3;
  342. }
  343. else if (g_ascii_isalnum (*p) || *p == '.' || *p == '-' || *p == '_') {
  344. p++;
  345. }
  346. else if (*p == '[') {
  347. s = ++p;
  348. state = 98;
  349. res = &r->real_ip;
  350. next_state = 3;
  351. }
  352. else {
  353. if (s != p) {
  354. /* Got some real hostname */
  355. /* check whether it is helo or p is not space symbol */
  356. if (!g_ascii_isspace (*p) || *(p + 1) != '[') {
  357. /* skip all */
  358. while (*p++ != ')' && *p != '\0');
  359. state = 3;
  360. }
  361. else {
  362. t = *p;
  363. *p = '\0';
  364. r->real_hostname = memory_pool_strdup (pool, s);
  365. *p = t;
  366. /* Now parse ip */
  367. p += 2;
  368. s = p;
  369. res = &r->real_ip;
  370. state = 98;
  371. next_state = 4;
  372. }
  373. }
  374. else {
  375. r->is_error = 1;
  376. return;
  377. }
  378. }
  379. break;
  380. /* Got by word */
  381. case 5:
  382. /* Here can be only hostname */
  383. if (g_ascii_isalnum (*p) || *p == '.' || *p == '-' || *p == '_') {
  384. p++;
  385. }
  386. else {
  387. /* We got something like hostname */
  388. t = *p;
  389. *p = '\0';
  390. r->by_hostname = memory_pool_strdup (pool, s);
  391. *p = t;
  392. /* Now end of parsing */
  393. return;
  394. }
  395. break;
  396. /* Extract ip */
  397. case 98:
  398. while (g_ascii_isdigit (*++p) || *p == '.');
  399. if (*p != ']') {
  400. /* Not an ip in fact */
  401. state = next_state;
  402. p++;
  403. }
  404. else {
  405. *p = '\0';
  406. *res = memory_pool_strdup (pool, s);
  407. *p = ']';
  408. p++;
  409. state = next_state;
  410. }
  411. break;
  412. /* Skip spaces */
  413. case 99:
  414. if (!g_ascii_isspace (*p)) {
  415. state = next_state;
  416. s = p;
  417. }
  418. else {
  419. p++;
  420. }
  421. break;
  422. case 100:
  423. r->is_error = 1;
  424. return;
  425. break;
  426. }
  427. }
  428. r->is_error = 1;
  429. return;
  430. }
  431. /* Convert raw headers to a list of struct raw_header * */
  432. static void
  433. process_raw_headers (struct worker_task *task)
  434. {
  435. struct raw_header *new = NULL, *lp;
  436. gchar *p, *c, *tmp, *tp;
  437. gint state = 0, l, next_state = 100, err_state = 100, t_state;
  438. gboolean valid_folding = FALSE;
  439. p = task->raw_headers_str;
  440. c = p;
  441. while (*p) {
  442. /* FSM for processing headers */
  443. switch (state) {
  444. case 0:
  445. /* Begin processing headers */
  446. if (!g_ascii_isalpha (*p)) {
  447. /* We have some garbage at the beginning of headers, skip this line */
  448. state = 100;
  449. next_state = 0;
  450. }
  451. else {
  452. state = 1;
  453. c = p;
  454. }
  455. break;
  456. case 1:
  457. /* We got something like header's name */
  458. if (*p == ':') {
  459. new = memory_pool_alloc0 (task->task_pool, sizeof (struct raw_header));
  460. l = p - c;
  461. tmp = memory_pool_alloc (task->task_pool, l + 1);
  462. rspamd_strlcpy (tmp, c, l + 1);
  463. new->name = tmp;
  464. new->empty_separator = TRUE;
  465. p ++;
  466. state = 2;
  467. c = p;
  468. }
  469. else if (g_ascii_isspace (*p)) {
  470. /* Not header but some garbage */
  471. state = 100;
  472. next_state = 0;
  473. }
  474. else {
  475. p ++;
  476. }
  477. break;
  478. case 2:
  479. /* We got header's name, so skip any \t or spaces */
  480. if (*p == '\t') {
  481. new->tab_separated = TRUE;
  482. new->empty_separator = FALSE;
  483. p ++;
  484. }
  485. else if (*p == ' ') {
  486. new->empty_separator = FALSE;
  487. p ++;
  488. }
  489. else if (*p == '\n' || *p == '\r') {
  490. /* Process folding */
  491. state = 99;
  492. l = p - c;
  493. if (l > 0) {
  494. tmp = memory_pool_alloc (task->task_pool, l + 1);
  495. rspamd_strlcpy (tmp, c, l + 1);
  496. new->separator = tmp;
  497. }
  498. next_state = 3;
  499. err_state = 5;
  500. c = p;
  501. }
  502. else {
  503. /* Process value */
  504. l = p - c;
  505. if (l > 0) {
  506. tmp = memory_pool_alloc (task->task_pool, l + 1);
  507. rspamd_strlcpy (tmp, c, l + 1);
  508. new->separator = tmp;
  509. }
  510. c = p;
  511. state = 3;
  512. }
  513. break;
  514. case 3:
  515. if (*p == '\r' || *p == '\n') {
  516. /* Hold folding */
  517. state = 99;
  518. next_state = 3;
  519. err_state = 4;
  520. }
  521. else if (*(p + 1) == '\0') {
  522. state = 4;
  523. }
  524. else {
  525. p ++;
  526. }
  527. break;
  528. case 4:
  529. /* Copy header's value */
  530. l = p - c;
  531. tmp = memory_pool_alloc (task->task_pool, l + 1);
  532. tp = tmp;
  533. t_state = 0;
  534. while (l --) {
  535. if (t_state == 0) {
  536. /* Before folding */
  537. if (*c == '\n' || *c == '\r') {
  538. t_state = 1;
  539. c ++;
  540. *tp ++ = ' ';
  541. }
  542. else {
  543. *tp ++ = *c ++;
  544. }
  545. }
  546. else if (t_state == 1) {
  547. /* Inside folding */
  548. if (g_ascii_isspace (*c)) {
  549. c++;
  550. }
  551. else {
  552. t_state = 0;
  553. *tp ++ = *c ++;
  554. }
  555. }
  556. }
  557. /* Strip last space that can be added by \r\n parsing */
  558. if (*(tp - 1) == ' ') {
  559. tp --;
  560. }
  561. *tp = '\0';
  562. new->value = tmp;
  563. new->next = NULL;
  564. if ((lp = g_hash_table_lookup (task->raw_headers, new->name)) != NULL) {
  565. while (lp->next != NULL) {
  566. lp = lp->next;
  567. }
  568. lp->next = new;
  569. }
  570. else {
  571. g_hash_table_insert (task->raw_headers, new->name, new);
  572. }
  573. debug_task ("add raw header %s: %s", new->name, new->value);
  574. state = 0;
  575. break;
  576. case 5:
  577. /* Header has only name, no value */
  578. new->next = NULL;
  579. if ((lp = g_hash_table_lookup (task->raw_headers, new->name)) != NULL) {
  580. while (lp->next != NULL) {
  581. lp = lp->next;
  582. }
  583. lp->next = new;
  584. }
  585. else {
  586. g_hash_table_insert (task->raw_headers, new->name, new);
  587. }
  588. state = 0;
  589. debug_task ("add raw header %s: %s", new->name, new->value);
  590. break;
  591. case 99:
  592. /* Folding state */
  593. if (*(p + 1) == '\0') {
  594. state = err_state;
  595. }
  596. else {
  597. if (*p == '\r' || *p == '\n') {
  598. p ++;
  599. valid_folding = FALSE;
  600. }
  601. else if (*p == '\t' || *p == ' ') {
  602. /* Valid folding */
  603. p ++;
  604. valid_folding = TRUE;
  605. }
  606. else {
  607. if (valid_folding) {
  608. debug_task ("go to state: %d->%d", state, next_state);
  609. state = next_state;
  610. }
  611. else {
  612. /* Fall back */
  613. debug_task ("go to state: %d->%d", state, err_state);
  614. state = err_state;
  615. }
  616. }
  617. }
  618. break;
  619. case 100:
  620. /* Fail state, skip line */
  621. if (*p == '\r') {
  622. if (*(p + 1) == '\n') {
  623. p ++;
  624. }
  625. p ++;
  626. state = next_state;
  627. }
  628. else if (*p == '\n') {
  629. if (*(p + 1) == '\r') {
  630. p ++;
  631. }
  632. p ++;
  633. state = next_state;
  634. }
  635. else if (*(p + 1) == '\0') {
  636. state = next_state;
  637. p ++;
  638. }
  639. else {
  640. p ++;
  641. }
  642. break;
  643. }
  644. }
  645. }
  646. static void
  647. free_byte_array_callback (void *pointer)
  648. {
  649. GByteArray *arr = (GByteArray *) pointer;
  650. g_byte_array_free (arr, TRUE);
  651. }
  652. static GByteArray *
  653. convert_text_to_utf (struct worker_task *task, GByteArray * part_content, GMimeContentType * type, struct mime_text_part *text_part)
  654. {
  655. GError *err = NULL;
  656. gsize read_bytes, write_bytes;
  657. const gchar *charset;
  658. gchar *res_str;
  659. GByteArray *result_array;
  660. if (task->cfg->raw_mode) {
  661. text_part->is_raw = TRUE;
  662. return part_content;
  663. }
  664. if ((charset = g_mime_content_type_get_parameter (type, "charset")) == NULL) {
  665. text_part->is_raw = TRUE;
  666. return part_content;
  667. }
  668. if (g_ascii_strcasecmp (charset, "utf-8") == 0 || g_ascii_strcasecmp (charset, "utf8") == 0) {
  669. if (g_utf8_validate (part_content->data, part_content->len, NULL)) {
  670. text_part->is_raw = FALSE;
  671. text_part->is_utf = TRUE;
  672. return part_content;
  673. }
  674. else {
  675. msg_info ("<%s>: contains invalid utf8 characters, assume it as raw", task->message_id);
  676. text_part->is_raw = TRUE;
  677. return part_content;
  678. }
  679. }
  680. res_str = g_convert_with_fallback (part_content->data, part_content->len, UTF8_CHARSET, charset, NULL, &read_bytes, &write_bytes, &err);
  681. if (res_str == NULL) {
  682. msg_warn ("<%s>: cannot convert from %s to utf8: %s", task->message_id, charset, err ? err->message : "unknown problem");
  683. text_part->is_raw = TRUE;
  684. return part_content;
  685. }
  686. result_array = memory_pool_alloc (task->task_pool, sizeof (GByteArray));
  687. result_array->data = res_str;
  688. result_array->len = write_bytes;
  689. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_free, res_str);
  690. text_part->is_raw = FALSE;
  691. text_part->is_utf = TRUE;
  692. return result_array;
  693. }
  694. static void
  695. process_text_part (struct worker_task *task, GByteArray *part_content, GMimeContentType *type,
  696. GMimeObject *part, GMimeObject *parent, gboolean is_empty)
  697. {
  698. struct mime_text_part *text_part;
  699. const gchar *cd;
  700. /* Skip attachements */
  701. #ifndef GMIME24
  702. cd = g_mime_part_get_content_disposition (GMIME_PART (part));
  703. if (cd && g_ascii_strcasecmp (cd, "attachment") == 0 && !task->cfg->check_text_attachements) {
  704. debug_task ("skip attachments for checking as text parts");
  705. return;
  706. }
  707. #else
  708. cd = g_mime_object_get_disposition (GMIME_OBJECT (part));
  709. if (cd && g_ascii_strcasecmp (cd, GMIME_DISPOSITION_ATTACHMENT) == 0 && !task->cfg->check_text_attachements) {
  710. debug_task ("skip attachments for checking as text parts");
  711. return;
  712. }
  713. #endif
  714. if (g_mime_content_type_is_type (type, "text", "html") || g_mime_content_type_is_type (type, "text", "xhtml")) {
  715. debug_task ("got urls from text/html part");
  716. text_part = memory_pool_alloc0 (task->task_pool, sizeof (struct mime_text_part));
  717. text_part->is_html = TRUE;
  718. if (is_empty) {
  719. text_part->is_empty = TRUE;
  720. text_part->orig = NULL;
  721. text_part->content = NULL;
  722. task->text_parts = g_list_prepend (task->text_parts, text_part);
  723. return;
  724. }
  725. text_part->orig = convert_text_to_utf (task, part_content, type, text_part);
  726. text_part->is_balanced = TRUE;
  727. text_part->html_nodes = NULL;
  728. text_part->parent = parent;
  729. text_part->content = strip_html_tags (task, task->task_pool, text_part, text_part->orig, NULL);
  730. if (text_part->html_nodes == NULL) {
  731. url_parse_text (task->task_pool, task, text_part, FALSE);
  732. }
  733. else {
  734. decode_entitles (text_part->content->data, &text_part->content->len);
  735. url_parse_text (task->task_pool, task, text_part, FALSE);
  736. #if 0
  737. url_parse_text (task->task_pool, task, text_part, TRUE);
  738. #endif
  739. }
  740. fuzzy_init_part (text_part, task->task_pool, task->cfg->max_diff);
  741. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) free_byte_array_callback, text_part->content);
  742. task->text_parts = g_list_prepend (task->text_parts, text_part);
  743. }
  744. else if (g_mime_content_type_is_type (type, "text", "*")) {
  745. debug_task ("got urls from text/plain part");
  746. text_part = memory_pool_alloc0 (task->task_pool, sizeof (struct mime_text_part));
  747. text_part->is_html = FALSE;
  748. text_part->parent = parent;
  749. if (is_empty) {
  750. text_part->is_empty = TRUE;
  751. text_part->orig = NULL;
  752. text_part->content = NULL;
  753. task->text_parts = g_list_prepend (task->text_parts, text_part);
  754. return;
  755. }
  756. text_part->orig = convert_text_to_utf (task, part_content, type, text_part);
  757. text_part->content = text_part->orig;
  758. url_parse_text (task->task_pool, task, text_part, FALSE);
  759. fuzzy_init_part (text_part, task->task_pool, task->cfg->max_diff);
  760. task->text_parts = g_list_prepend (task->text_parts, text_part);
  761. }
  762. }
  763. #ifdef GMIME24
  764. static void
  765. mime_foreach_callback (GMimeObject * parent, GMimeObject * part, gpointer user_data)
  766. #else
  767. static void
  768. mime_foreach_callback (GMimeObject * part, gpointer user_data)
  769. #endif
  770. {
  771. struct worker_task *task = (struct worker_task *)user_data;
  772. struct mime_part *mime_part;
  773. GMimeContentType *type;
  774. GMimeDataWrapper *wrapper;
  775. GMimeStream *part_stream;
  776. GByteArray *part_content;
  777. task->parts_count++;
  778. /* 'part' points to the current part node that g_mime_message_foreach_part() is iterating over */
  779. /* find out what class 'part' is... */
  780. if (GMIME_IS_MESSAGE_PART (part)) {
  781. /* message/rfc822 or message/news */
  782. GMimeMessage *message;
  783. /* g_mime_message_foreach_part() won't descend into
  784. child message parts, so if we want to count any
  785. subparts of this child message, we'll have to call
  786. g_mime_message_foreach_part() again here. */
  787. message = g_mime_message_part_get_message ((GMimeMessagePart *) part);
  788. if (task->parser_recursion++ < RECURSION_LIMIT) {
  789. #ifdef GMIME24
  790. g_mime_message_foreach (message, mime_foreach_callback, task);
  791. #else
  792. g_mime_message_foreach_part (message, mime_foreach_callback, task);
  793. #endif
  794. }
  795. else {
  796. msg_err ("endless recursion detected: %d", task->parser_recursion);
  797. return;
  798. }
  799. #ifndef GMIME24
  800. g_object_unref (message);
  801. #endif
  802. }
  803. else if (GMIME_IS_MESSAGE_PARTIAL (part)) {
  804. /* message/partial */
  805. /* this is an incomplete message part, probably a
  806. large message that the sender has broken into
  807. smaller parts and is sending us bit by bit. we
  808. could save some info about it so that we could
  809. piece this back together again once we get all the
  810. parts? */
  811. }
  812. else if (GMIME_IS_MULTIPART (part)) {
  813. /* multipart/mixed, multipart/alternative, multipart/related, multipart/signed, multipart/encrypted, etc... */
  814. task->parser_parent_part = part;
  815. #ifndef GMIME24
  816. debug_task ("detected multipart part");
  817. /* we'll get to finding out if this is a signed/encrypted multipart later... */
  818. if (task->parser_recursion++ < RECURSION_LIMIT) {
  819. g_mime_multipart_foreach ((GMimeMultipart *) part, mime_foreach_callback, task);
  820. }
  821. else {
  822. msg_err ("endless recursion detected: %d", task->parser_recursion);
  823. return;
  824. }
  825. #endif
  826. }
  827. else if (GMIME_IS_PART (part)) {
  828. /* a normal leaf part, could be text/plain or image/jpeg etc */
  829. #ifdef GMIME24
  830. type = (GMimeContentType *) g_mime_object_get_content_type (GMIME_OBJECT (part));
  831. #else
  832. type = (GMimeContentType *) g_mime_part_get_content_type (GMIME_PART (part));
  833. #endif
  834. if (type == NULL) {
  835. msg_warn ("type of part is unknown, assume text/plain");
  836. type = g_mime_content_type_new ("text", "plain");
  837. #ifdef GMIME24
  838. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_object_unref, type);
  839. #else
  840. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_mime_content_type_destroy, type);
  841. #endif
  842. }
  843. wrapper = g_mime_part_get_content_object (GMIME_PART (part));
  844. #ifdef GMIME24
  845. if (wrapper != NULL && GMIME_IS_DATA_WRAPPER (wrapper)) {
  846. #else
  847. if (wrapper != NULL) {
  848. #endif
  849. part_stream = g_mime_stream_mem_new ();
  850. if (g_mime_data_wrapper_write_to_stream (wrapper, part_stream) != -1) {
  851. g_mime_stream_mem_set_owner (GMIME_STREAM_MEM (part_stream), FALSE);
  852. part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (part_stream));
  853. g_object_unref (part_stream);
  854. mime_part = memory_pool_alloc (task->task_pool, sizeof (struct mime_part));
  855. mime_part->type = type;
  856. mime_part->content = part_content;
  857. mime_part->parent = task->parser_parent_part;
  858. mime_part->filename = g_mime_part_get_filename (GMIME_PART (part));
  859. debug_task ("found part with content-type: %s/%s", type->type, type->subtype);
  860. task->parts = g_list_prepend (task->parts, mime_part);
  861. /* Skip empty parts */
  862. process_text_part (task, part_content, type, part, task->parser_parent_part, (part_content->len <= 0));
  863. }
  864. else {
  865. msg_warn ("write to stream failed: %d, %s", errno, strerror (errno));
  866. }
  867. #ifndef GMIME24
  868. g_object_unref (wrapper);
  869. #endif
  870. }
  871. else {
  872. msg_warn ("cannot get wrapper for mime part, type of part: %s/%s", type->type, type->subtype);
  873. }
  874. }
  875. else {
  876. g_assert_not_reached ();
  877. }
  878. }
  879. static void
  880. destroy_message (void *pointer)
  881. {
  882. GMimeMessage *msg = pointer;
  883. msg_debug ("freeing pointer %p", msg);
  884. g_object_unref (msg);
  885. }
  886. gint
  887. process_message (struct worker_task *task)
  888. {
  889. GMimeMessage *message;
  890. GMimeParser *parser;
  891. GMimeStream *stream;
  892. GByteArray *tmp;
  893. GList *first, *cur;
  894. GMimePart *part;
  895. GMimeDataWrapper *wrapper;
  896. struct received_header *recv;
  897. gchar *mid, *url_str, *p, *end, *url_end;
  898. struct uri *subject_url;
  899. gsize len;
  900. gint rc;
  901. tmp = memory_pool_alloc (task->task_pool, sizeof (GByteArray));
  902. tmp->data = task->msg->begin;
  903. tmp->len = task->msg->len;
  904. stream = g_mime_stream_mem_new_with_byte_array (tmp);
  905. /*
  906. * This causes g_mime_stream not to free memory by itself as it is memory allocated by
  907. * pool allocator
  908. */
  909. g_mime_stream_mem_set_owner (GMIME_STREAM_MEM (stream), FALSE);
  910. if (task->is_mime) {
  911. debug_task ("construct mime parser from string length %d", (gint)task->msg->len);
  912. /* create a new parser object to parse the stream */
  913. parser = g_mime_parser_new_with_stream (stream);
  914. g_object_unref (stream);
  915. /* parse the message from the stream */
  916. message = g_mime_parser_construct_message (parser);
  917. if (message == NULL) {
  918. msg_warn ("cannot construct mime from stream");
  919. return -1;
  920. }
  921. task->message = message;
  922. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) destroy_message, task->message);
  923. /* Save message id for future use */
  924. task->message_id = g_mime_message_get_message_id (task->message);
  925. if (task->message_id == NULL) {
  926. task->message_id = "undef";
  927. }
  928. task->parser_recursion = 0;
  929. #ifdef GMIME24
  930. g_mime_message_foreach (message, mime_foreach_callback, task);
  931. #else
  932. /*
  933. * This is rather strange, but gmime 2.2 do NOT pass top-level part to foreach callback
  934. * so we need to set up parent part by hands
  935. */
  936. task->parser_parent_part = g_mime_message_get_mime_part (message);
  937. g_object_unref (task->parser_parent_part);
  938. g_mime_message_foreach_part (message, mime_foreach_callback, task);
  939. #endif
  940. debug_task ("found %d parts in message", task->parts_count);
  941. if (task->queue_id == NULL) {
  942. task->queue_id = "undef";
  943. }
  944. #ifdef GMIME24
  945. task->raw_headers_str = g_mime_object_get_headers (GMIME_OBJECT (task->message));
  946. #else
  947. task->raw_headers_str = g_mime_message_get_headers (task->message);
  948. #endif
  949. process_images (task);
  950. /* Parse received headers */
  951. first = message_get_header (task->task_pool, message, "Received", FALSE);
  952. cur = first;
  953. while (cur) {
  954. recv = memory_pool_alloc0 (task->task_pool, sizeof (struct received_header));
  955. parse_recv_header (task->task_pool, cur->data, recv);
  956. task->received = g_list_prepend (task->received, recv);
  957. cur = g_list_next (cur);
  958. }
  959. if (first) {
  960. g_list_free (first);
  961. }
  962. if (task->raw_headers_str) {
  963. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_free, task->raw_headers_str);
  964. process_raw_headers (task);
  965. }
  966. task->rcpts = g_mime_message_get_all_recipients (message);
  967. if (task->rcpts) {
  968. #ifdef GMIME24
  969. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_object_unref, task->rcpts);
  970. #else
  971. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) internet_address_list_destroy, task->rcpts);
  972. #endif
  973. }
  974. /* free the parser (and the stream) */
  975. g_object_unref (parser);
  976. }
  977. else {
  978. /* We got only message, no mime headers or anything like this */
  979. /* Construct fake message for it */
  980. task->message = g_mime_message_new (TRUE);
  981. if (task->from) {
  982. g_mime_message_set_sender (task->message, task->from);
  983. }
  984. /* Construct part for it */
  985. part = g_mime_part_new_with_type ("text", "html");
  986. #ifdef GMIME24
  987. wrapper = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_8BIT);
  988. #else
  989. wrapper = g_mime_data_wrapper_new_with_stream (stream, GMIME_PART_ENCODING_8BIT);
  990. #endif
  991. g_mime_part_set_content_object (part, wrapper);
  992. g_mime_message_set_mime_part (task->message, GMIME_OBJECT (part));
  993. /* Register destructors */
  994. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_object_unref, wrapper);
  995. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_object_unref, part);
  996. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) destroy_message, task->message);
  997. /* Now parse in a normal way */
  998. task->parser_recursion = 0;
  999. #ifdef GMIME24
  1000. g_mime_message_foreach (task->message, mime_foreach_callback, task);
  1001. #else
  1002. g_mime_message_foreach_part (task->message, mime_foreach_callback, task);
  1003. #endif
  1004. /* Generate message ID */
  1005. mid = g_mime_utils_generate_message_id ("localhost.localdomain");
  1006. memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_free, mid);
  1007. g_mime_message_set_message_id (task->message, mid);
  1008. task->message_id = mid;
  1009. task->queue_id = mid;
  1010. /* Set headers for message */
  1011. if (task->subject) {
  1012. g_mime_message_set_subject (task->message, task->subject);
  1013. }
  1014. /* Add recipients */
  1015. #ifndef GMIME24
  1016. if (task->rcpt) {
  1017. cur = task->rcpt;
  1018. while (cur) {
  1019. g_mime_message_add_recipient (task->message, GMIME_RECIPIENT_TYPE_TO, NULL, (gchar *)cur->data);
  1020. cur = g_list_next (cur);
  1021. }
  1022. }
  1023. #endif
  1024. }
  1025. /* Parse urls inside Subject header */
  1026. cur = message_get_header (task->task_pool, task->message, "Subject", FALSE);
  1027. if (cur) {
  1028. p = cur->data;
  1029. len = strlen (p);
  1030. end = p + len;
  1031. while (p < end) {
  1032. /* Search to the end of url */
  1033. if (url_try_text (task->task_pool, p, end - p, NULL, &url_end, &url_str)) {
  1034. if (url_str != NULL) {
  1035. subject_url = memory_pool_alloc0 (task->task_pool, sizeof (struct uri));
  1036. if (subject_url != NULL) {
  1037. /* Try to parse url */
  1038. rc = parse_uri (subject_url, url_str, task->task_pool);
  1039. if ((rc == URI_ERRNO_OK || rc == URI_ERRNO_NO_SLASHES || rc == URI_ERRNO_NO_HOST_SLASH) &&
  1040. subject_url->hostlen > 0) {
  1041. if (subject_url->protocol != PROTOCOL_MAILTO) {
  1042. if (!g_tree_lookup (task->urls, subject_url)) {
  1043. g_tree_insert (task->urls, subject_url, subject_url);
  1044. }
  1045. }
  1046. }
  1047. else if (rc != URI_ERRNO_OK) {
  1048. msg_info ("extract of url '%s' failed: %s", url_str, url_strerror (rc));
  1049. }
  1050. }
  1051. }
  1052. }
  1053. else {
  1054. break;
  1055. }
  1056. p = url_end + 1;
  1057. }
  1058. /* Free header's list */
  1059. g_list_free (cur);
  1060. }
  1061. return 0;
  1062. }
  1063. struct gmime_raw_header {
  1064. struct raw_header *next;
  1065. gchar *name;
  1066. gchar *value;
  1067. };
  1068. typedef struct _GMimeHeader {
  1069. GHashTable *hash;
  1070. GHashTable *writers;
  1071. struct raw_header *headers;
  1072. } local_GMimeHeader;
  1073. /* known header field types */
  1074. enum {
  1075. HEADER_FROM = 0,
  1076. HEADER_REPLY_TO,
  1077. HEADER_TO,
  1078. HEADER_CC,
  1079. HEADER_BCC,
  1080. HEADER_SUBJECT,
  1081. HEADER_DATE,
  1082. HEADER_MESSAGE_ID,
  1083. HEADER_UNKNOWN
  1084. };
  1085. /*
  1086. * Iterate throught all headers and make a list
  1087. */
  1088. #ifndef GMIME24
  1089. static void
  1090. header_iterate (memory_pool_t * pool, struct gmime_raw_header *h, GList ** ret, const gchar *field, gboolean strong)
  1091. {
  1092. while (h) {
  1093. if (G_LIKELY (!strong)) {
  1094. if (h->value && !g_ascii_strncasecmp (field, h->name, strlen (field))) {
  1095. if (pool != NULL) {
  1096. *ret = g_list_prepend (*ret, memory_pool_strdup (pool, h->value));
  1097. }
  1098. else {
  1099. *ret = g_list_prepend (*ret, g_strdup (h->value));
  1100. }
  1101. }
  1102. }
  1103. else {
  1104. if (h->value && !strncmp (field, h->name, strlen (field))) {
  1105. if (pool != NULL) {
  1106. *ret = g_list_prepend (*ret, memory_pool_strdup (pool, h->value));
  1107. }
  1108. else {
  1109. *ret = g_list_prepend (*ret, g_strdup (h->value));
  1110. }
  1111. }
  1112. }
  1113. h = (struct gmime_raw_header *)h->next;
  1114. }
  1115. }
  1116. #else
  1117. static void
  1118. header_iterate (memory_pool_t * pool, GMimeHeaderList * ls, GList ** ret, const gchar *field, gboolean strong)
  1119. {
  1120. /* Use iterator in case of gmime 2.4 */
  1121. GMimeHeaderIter *iter;
  1122. const gchar *name;
  1123. if (ls == NULL) {
  1124. *ret = NULL;
  1125. return;
  1126. }
  1127. iter = g_mime_header_iter_new ();
  1128. if (g_mime_header_list_get_iter (ls, iter) && g_mime_header_iter_first (iter)) {
  1129. /* Iterate throught headers */
  1130. while (g_mime_header_iter_is_valid (iter)) {
  1131. name = g_mime_header_iter_get_name (iter);
  1132. if (G_LIKELY (!strong)) {
  1133. if (!g_ascii_strncasecmp (field, name, strlen (name))) {
  1134. if (pool != NULL) {
  1135. *ret = g_list_prepend (*ret, memory_pool_strdup (pool, g_mime_header_iter_get_value (iter)));
  1136. }
  1137. else {
  1138. *ret = g_list_prepend (*ret, g_strdup (g_mime_header_iter_get_value (iter)));
  1139. }
  1140. }
  1141. }
  1142. else {
  1143. if (!strncmp (field, name, strlen (name))) {
  1144. if (pool != NULL) {
  1145. *ret = g_list_prepend (*ret, memory_pool_strdup (pool, g_mime_header_iter_get_value (iter)));
  1146. }
  1147. else {
  1148. *ret = g_list_prepend (*ret, g_strdup (g_mime_header_iter_get_value (iter)));
  1149. }
  1150. }
  1151. }
  1152. if (!g_mime_header_iter_next (iter)) {
  1153. break;
  1154. }
  1155. }
  1156. }
  1157. g_mime_header_iter_free (iter);
  1158. }
  1159. #endif
  1160. struct multipart_cb_data {
  1161. GList *ret;
  1162. memory_pool_t *pool;
  1163. const gchar *field;
  1164. gboolean try_search;
  1165. gboolean strong;
  1166. gint rec;
  1167. };
  1168. #define MAX_REC 10
  1169. static void
  1170. #ifdef GMIME24
  1171. multipart_iterate (GMimeObject * parent, GMimeObject * part, gpointer user_data)
  1172. #else
  1173. multipart_iterate (GMimeObject * part, gpointer user_data)
  1174. #endif
  1175. {
  1176. struct multipart_cb_data *data = user_data;
  1177. #ifndef GMIME24
  1178. struct gmime_raw_header *h;
  1179. #endif
  1180. GList *l = NULL;
  1181. if (data->try_search && part != NULL && GMIME_IS_PART (part)) {
  1182. #ifdef GMIME24
  1183. GMimeHeaderList *ls;
  1184. ls = g_mime_object_get_header_list (GMIME_OBJECT (part));
  1185. header_iterate (data->pool, ls, &l, data->field, data->strong);
  1186. #else
  1187. h = (struct gmime_raw_header *)part->headers->headers;
  1188. header_iterate (data->pool, h, &l, data->field, data->strong);
  1189. #endif
  1190. if (l == NULL) {
  1191. /* Header not found, abandon search results */
  1192. data->try_search = FALSE;
  1193. g_list_free (data->ret);
  1194. data->ret = NULL;
  1195. }
  1196. else {
  1197. data->ret = g_list_concat (l, data->ret);
  1198. }
  1199. }
  1200. else if (data->try_search && GMIME_IS_MULTIPART (part)) {
  1201. /* Maybe endless recursion here ? */
  1202. if (data->rec++ < MAX_REC) {
  1203. g_mime_multipart_foreach (GMIME_MULTIPART (part), multipart_iterate, data);
  1204. }
  1205. else {
  1206. msg_info ("maximum recurse limit is over, stop recursing, %d", data->rec);
  1207. data->try_search = FALSE;
  1208. }
  1209. }
  1210. }
  1211. static GList *
  1212. local_message_get_header (memory_pool_t * pool, GMimeMessage * message, const gchar *field, gboolean strong)
  1213. {
  1214. GList *gret = NULL;
  1215. GMimeObject *part;
  1216. struct multipart_cb_data cb = {
  1217. .try_search = TRUE,
  1218. .rec = 0,
  1219. .ret = NULL,
  1220. };
  1221. cb.pool = pool;
  1222. cb.field = field;
  1223. cb.strong = strong;
  1224. #ifndef GMIME24
  1225. struct gmime_raw_header *h;
  1226. if (field == NULL) {
  1227. return NULL;
  1228. }
  1229. msg_debug ("iterate over headers to find header %s", field);
  1230. h = (struct gmime_raw_header *) (GMIME_OBJECT (message)->headers->headers);
  1231. header_iterate (pool, h, &gret, field, strong);
  1232. if (gret == NULL) {
  1233. /* Try to iterate with mime part headers */
  1234. msg_debug ("iterate over headers of mime part to find header %s", field);
  1235. part = g_mime_message_get_mime_part (message);
  1236. if (part) {
  1237. h = (struct gmime_raw_header *)part->headers->headers;
  1238. header_iterate (pool, h, &gret, field, strong);
  1239. if (gret == NULL && GMIME_IS_MULTIPART (part)) {
  1240. msg_debug ("iterate over headers of each multipart's subparts %s", field);
  1241. g_mime_multipart_foreach (GMIME_MULTIPART (part), multipart_iterate, &cb);
  1242. if (cb.ret != NULL) {
  1243. gret = cb.ret;
  1244. }
  1245. }
  1246. #ifndef GMIME24
  1247. g_object_unref (part);
  1248. #endif
  1249. }
  1250. }
  1251. return gret;
  1252. #else
  1253. GMimeHeaderList *ls;
  1254. ls = g_mime_object_get_header_list (GMIME_OBJECT (message));
  1255. header_iterate (pool, ls, &gret, field, strong);
  1256. if (gret == NULL) {
  1257. /* Try to iterate with mime part headers */
  1258. part = g_mime_message_get_mime_part (message);
  1259. if (part) {
  1260. ls = g_mime_object_get_header_list (GMIME_OBJECT (part));
  1261. header_iterate (pool, ls, &gret, field, strong);
  1262. if (gret == NULL && GMIME_IS_MULTIPART (part)) {
  1263. g_mime_multipart_foreach (GMIME_MULTIPART (part), multipart_iterate, &cb);
  1264. if (cb.ret != NULL) {
  1265. gret = cb.ret;
  1266. }
  1267. }
  1268. #ifndef GMIME24
  1269. g_object_unref (part);
  1270. #endif
  1271. }
  1272. }
  1273. return gret;
  1274. #endif
  1275. }
  1276. /**
  1277. * g_mime_message_set_date_from_string: Set the message sent-date
  1278. * @message: MIME Message
  1279. * @string: A string of date
  1280. *
  1281. * Set the sent-date on a MIME Message.
  1282. **/
  1283. void
  1284. local_mime_message_set_date_from_string (GMimeMessage * message, const gchar * string)
  1285. {
  1286. time_t date;
  1287. gint offset = 0;
  1288. date = g_mime_utils_header_decode_date (string, &offset);
  1289. g_mime_message_set_date (message, date, offset);
  1290. }
  1291. /*
  1292. * Replacements for standart gmime functions but converting adresses to IA
  1293. */
  1294. static const gchar *
  1295. local_message_get_sender (GMimeMessage * message)
  1296. {
  1297. gchar *res;
  1298. const gchar *from = g_mime_message_get_sender (message);
  1299. InternetAddressList *ia;
  1300. #ifndef GMIME24
  1301. ia = internet_address_parse_string (from);
  1302. #else
  1303. ia = internet_address_list_parse_string (from);
  1304. #endif
  1305. if (!ia) {
  1306. return NULL;
  1307. }
  1308. res = internet_address_list_to_string (ia, FALSE);
  1309. #ifndef GMIME24
  1310. internet_address_list_destroy (ia);
  1311. #else
  1312. g_object_unref (ia);
  1313. #endif
  1314. return res;
  1315. }
  1316. static const gchar *
  1317. local_message_get_reply_to (GMimeMessage * message)
  1318. {
  1319. gchar *res;
  1320. const gchar *from = g_mime_message_get_reply_to (message);
  1321. InternetAddressList *ia;
  1322. #ifndef GMIME24
  1323. ia = internet_address_parse_string (from);
  1324. #else
  1325. ia = internet_address_list_parse_string (from);
  1326. #endif
  1327. if (!ia) {
  1328. return NULL;
  1329. }
  1330. res = internet_address_list_to_string (ia, FALSE);
  1331. #ifndef GMIME24
  1332. internet_address_list_destroy (ia);
  1333. #else
  1334. g_object_unref (ia);
  1335. #endif
  1336. return res;
  1337. }
  1338. #ifdef GMIME24
  1339. # define ADD_RECIPIENT_TEMPLATE(type,def) \
  1340. static void \
  1341. local_message_add_recipients_from_string_##type (GMimeMessage *message, const gchar *string, const gchar *value) \
  1342. { \
  1343. InternetAddressList *il, *new; \
  1344. \
  1345. il = g_mime_message_get_recipients (message, (def)); \
  1346. new = internet_address_list_parse_string (string); \
  1347. internet_address_list_append (il, new); \
  1348. } \
  1349. ADD_RECIPIENT_TEMPLATE (to, GMIME_RECIPIENT_TYPE_TO)
  1350. ADD_RECIPIENT_TEMPLATE (cc, GMIME_RECIPIENT_TYPE_CC)
  1351. ADD_RECIPIENT_TEMPLATE (bcc, GMIME_RECIPIENT_TYPE_BCC)
  1352. # define GET_RECIPIENT_TEMPLATE(type,def) \
  1353. static InternetAddressList* \
  1354. local_message_get_recipients_##type (GMimeMessage *message, const gchar *unused) \
  1355. { \
  1356. return g_mime_message_get_recipients (message, (def)); \
  1357. }
  1358. GET_RECIPIENT_TEMPLATE (to, GMIME_RECIPIENT_TYPE_TO)
  1359. GET_RECIPIENT_TEMPLATE (cc, GMIME_RECIPIENT_TYPE_CC)
  1360. GET_RECIPIENT_TEMPLATE (bcc, GMIME_RECIPIENT_TYPE_BCC)
  1361. #endif
  1362. /* different declarations for different types of set and get functions */
  1363. typedef const gchar *(*GetFunc) (GMimeMessage * message);
  1364. typedef InternetAddressList *(*GetRcptFunc) (GMimeMessage * message, const gchar *type);
  1365. typedef GList *(*GetListFunc) (memory_pool_t * pool, GMimeMessage * message, const gchar *type, gboolean strong);
  1366. typedef void (*SetFunc) (GMimeMessage * message, const gchar *value);
  1367. typedef void (*SetListFunc) (GMimeMessage * message, const gchar *field, const gchar *value);
  1368. /** different types of functions
  1369. *
  1370. * FUNC_CHARPTR
  1371. * - function with no arguments
  1372. * - get returns gchar*
  1373. *
  1374. * FUNC_IA (from Internet Address)
  1375. * - function with additional "field" argument from the fieldfunc table,
  1376. * - get returns Glist*
  1377. *
  1378. * FUNC_LIST
  1379. * - function with additional "field" argument (given arbitrary header field name)
  1380. * - get returns Glist*
  1381. **/
  1382. enum {
  1383. FUNC_CHARPTR = 0,
  1384. FUNC_CHARFREEPTR,
  1385. FUNC_IA,
  1386. FUNC_LIST
  1387. };
  1388. /**
  1389. * fieldfunc struct: structure of MIME fields and corresponding get and set
  1390. * functions.
  1391. **/
  1392. static struct {
  1393. gchar *name;
  1394. GetFunc func;
  1395. GetRcptFunc rcptfunc;
  1396. GetListFunc getlistfunc;
  1397. SetFunc setfunc;
  1398. SetListFunc setlfunc;
  1399. gint functype;
  1400. } fieldfunc[] =
  1401. {
  1402. {
  1403. "From", local_message_get_sender, NULL, NULL, g_mime_message_set_sender, NULL, FUNC_CHARFREEPTR}, {
  1404. "Reply-To", local_message_get_reply_to, NULL, NULL, g_mime_message_set_reply_to, NULL, FUNC_CHARFREEPTR},
  1405. #ifndef GMIME24
  1406. {
  1407. "To", NULL, (GetRcptFunc) g_mime_message_get_recipients, NULL, NULL, (SetListFunc) g_mime_message_add_recipients_from_string, FUNC_IA}, {
  1408. "Cc", NULL, (GetRcptFunc) g_mime_message_get_recipients, NULL, NULL, (SetListFunc) g_mime_message_add_recipients_from_string, FUNC_IA}, {
  1409. "Bcc", NULL, (GetRcptFunc) g_mime_message_get_recipients, NULL, NULL, (SetListFunc) g_mime_message_add_recipients_from_string, FUNC_IA}, {
  1410. "Date", (GetFunc) g_mime_message_get_date_string, NULL, NULL, local_mime_message_set_date_from_string, NULL, FUNC_CHARFREEPTR},
  1411. #else
  1412. {
  1413. "To", NULL, local_message_get_recipients_to, NULL, NULL, local_message_add_recipients_from_string_to, FUNC_IA}, {
  1414. "Cc", NULL, local_message_get_recipients_cc, NULL, NULL, local_message_add_recipients_from_string_cc, FUNC_IA}, {
  1415. "Bcc", NULL, local_message_get_recipients_bcc, NULL, NULL, local_message_add_recipients_from_string_bcc, FUNC_IA}, {
  1416. "Date", (GetFunc)g_mime_message_get_date_as_string, NULL, NULL, local_mime_message_set_date_from_string, NULL, FUNC_CHARFREEPTR},
  1417. #endif
  1418. {
  1419. "Subject", g_mime_message_get_subject, NULL, NULL, g_mime_message_set_subject, NULL, FUNC_CHARPTR}, {
  1420. "Message-Id", g_mime_message_get_message_id, NULL, NULL, g_mime_message_set_message_id, NULL, FUNC_CHARPTR},
  1421. #ifndef GMIME24
  1422. {
  1423. NULL, NULL, NULL, local_message_get_header, NULL, g_mime_message_add_header, FUNC_LIST}
  1424. #else
  1425. {
  1426. NULL, NULL, NULL, local_message_get_header, NULL, (SetListFunc)g_mime_object_append_header, FUNC_LIST}
  1427. #endif
  1428. };
  1429. /**
  1430. * message_set_header: set header of any type excluding special (Content- and MIME-Version:)
  1431. **/
  1432. void
  1433. message_set_header (GMimeMessage * message, const gchar *field, const gchar *value)
  1434. {
  1435. gint i;
  1436. if (!g_ascii_strcasecmp (field, "MIME-Version:") || !g_ascii_strncasecmp (field, "Content-", 8)) {
  1437. return;
  1438. }
  1439. for (i = 0; i <= HEADER_UNKNOWN; ++i) {
  1440. if (!fieldfunc[i].name || !g_ascii_strncasecmp (field, fieldfunc[i].name, strlen (fieldfunc[i].name))) {
  1441. switch (fieldfunc[i].functype) {
  1442. case FUNC_CHARPTR:
  1443. (*(fieldfunc[i].setfunc)) (message, value);
  1444. break;
  1445. case FUNC_IA:
  1446. (*(fieldfunc[i].setlfunc)) (message, fieldfunc[i].name, value);
  1447. break;
  1448. case FUNC_LIST:
  1449. (*(fieldfunc[i].setlfunc)) (message, field, value);
  1450. break;
  1451. }
  1452. break;
  1453. }
  1454. }
  1455. }
  1456. /**
  1457. * message_get_header: returns the list of 'any header' values
  1458. * (except of unsupported yet Content- and MIME-Version special headers)
  1459. *
  1460. * You should free the GList list by yourself.
  1461. **/
  1462. GList *
  1463. message_get_header (memory_pool_t * pool, GMimeMessage * message, const gchar *field, gboolean strong)
  1464. {
  1465. gint i;
  1466. gchar *ret = NULL, *ia_string;
  1467. GList *gret = NULL;
  1468. InternetAddressList *ia_list = NULL, *ia;
  1469. for (i = 0; i <= HEADER_UNKNOWN; ++i) {
  1470. if (!fieldfunc[i].name || !g_ascii_strncasecmp (field, fieldfunc[i].name, strlen (fieldfunc[i].name))) {
  1471. switch (fieldfunc[i].functype) {
  1472. case FUNC_CHARFREEPTR:
  1473. ret = (gchar *)(*(fieldfunc[i].func)) (message);
  1474. break;
  1475. case FUNC_CHARPTR:
  1476. ret = (gchar *)(*(fieldfunc[i].func)) (message);
  1477. break;
  1478. case FUNC_IA:
  1479. ia_list = (*(fieldfunc[i].rcptfunc)) (message, field);
  1480. ia = ia_list;
  1481. #ifndef GMIME24
  1482. while (ia && ia->address) {
  1483. ia_string = internet_address_to_string ((InternetAddress *) ia->address, FALSE);
  1484. if (pool != NULL) {
  1485. memory_pool_add_destructor (pool, (pool_destruct_func) g_free, ia_string);
  1486. }
  1487. gret = g_list_prepend (gret, ia_string);
  1488. ia = ia->next;
  1489. }
  1490. #else
  1491. i = internet_address_list_length (ia);
  1492. while (--i >= 0) {
  1493. ia_string = internet_address_to_string (internet_address_list_get_address (ia, i), FALSE);
  1494. if (pool != NULL) {
  1495. memory_pool_add_destructor (pool, (pool_destruct_func) g_free, ia_string);
  1496. }
  1497. gret = g_list_prepend (gret, ia_string);
  1498. }
  1499. #endif
  1500. break;
  1501. case FUNC_LIST:
  1502. gret = (*(fieldfunc[i].getlistfunc)) (pool, message, field, strong);
  1503. break;
  1504. }
  1505. break;
  1506. }
  1507. }
  1508. if (gret == NULL && ret != NULL) {
  1509. if (pool != NULL) {
  1510. gret = g_list_prepend (gret, memory_pool_strdup (pool, ret));
  1511. }
  1512. else {
  1513. gret = g_list_prepend (gret, g_strdup (ret));
  1514. }
  1515. }
  1516. if (fieldfunc[i].functype == FUNC_CHARFREEPTR && ret) {
  1517. g_free (ret);
  1518. }
  1519. return gret;
  1520. }
  1521. GList*
  1522. message_get_raw_header (struct worker_task *task, const gchar *field, gboolean strong)
  1523. {
  1524. GList *gret = NULL;
  1525. struct raw_header *rh;
  1526. rh = g_hash_table_lookup (task->raw_headers, field);
  1527. if (rh == NULL) {
  1528. return NULL;
  1529. }
  1530. while (rh) {
  1531. if (strong) {
  1532. if (strcmp (rh->name, field) == 0) {
  1533. gret = g_list_prepend (gret, rh);
  1534. }
  1535. }
  1536. else {
  1537. if (g_ascii_strcasecmp (rh->name, field) == 0) {
  1538. gret = g_list_prepend (gret, rh);
  1539. }
  1540. }
  1541. rh = rh->next;
  1542. }
  1543. if (gret != NULL) {
  1544. memory_pool_add_destructor (task->task_pool, (pool_destruct_func)g_list_free, gret);
  1545. }
  1546. return gret;
  1547. }