1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681 |
- /*
- * Copyright (c) 2009, Rambler media
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY Rambler media ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL Rambler BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- #include "config.h"
- #include "util.h"
- #include "main.h"
- #include "message.h"
- #include "cfg_file.h"
- #include "html.h"
- #include "images.h"
-
- #define RECURSION_LIMIT 30
- #define UTF8_CHARSET "UTF-8"
-
- GByteArray *
- strip_html_tags (struct worker_task *task, memory_pool_t * pool, struct mime_text_part *part, GByteArray * src, gint *stateptr)
- {
- uint8_t *p, *rp, *tbegin = NULL, *end, c, lc;
- gint br, i = 0, depth = 0, in_q = 0;
- gint state = 0;
- GByteArray *buf;
- GNode *level_ptr = NULL;
- gboolean erase = FALSE;
-
- if (stateptr)
- state = *stateptr;
-
- buf = g_byte_array_sized_new (src->len);
- g_byte_array_append (buf, src->data, src->len);
-
- c = *src->data;
- lc = '\0';
- p = src->data;
- rp = buf->data;
- end = src->data + src->len;
- br = 0;
-
- while (i < (gint)src->len) {
- switch (c) {
- case '\0':
- break;
- case '<':
- if (g_ascii_isspace (*(p + 1))) {
- goto reg_char;
- }
- if (state == 0) {
- lc = '<';
- tbegin = p + 1;
- state = 1;
- }
- else if (state == 1) {
- /* Opening bracket without closing one */
- p --;
- while (g_ascii_isspace (*p) && p > src->data) {
- p --;
- }
- p ++;
- goto unbreak_tag;
- }
- break;
-
- case '(':
- if (state == 2) {
- if (lc != '"' && lc != '\'') {
- lc = '(';
- br++;
- }
- }
- else if (state == 0 && !erase) {
- *(rp++) = c;
- }
- break;
-
- case ')':
- if (state == 2) {
- if (lc != '"' && lc != '\'') {
- lc = ')';
- br--;
- }
- }
- else if (state == 0 && !erase) {
- *(rp++) = c;
- }
- break;
-
- case '>':
- if (depth) {
- depth--;
- break;
- }
-
- if (in_q) {
- break;
- }
- unbreak_tag:
- switch (state) {
- case 1: /* HTML/XML */
- lc = '>';
- in_q = state = 0;
- erase = !add_html_node (task, pool, part, tbegin, p - tbegin, end - tbegin, &level_ptr);
- break;
-
- case 2: /* PHP */
- if (!br && lc != '\"' && *(p - 1) == '?') {
- in_q = state = 0;
- }
- break;
-
- case 3:
- in_q = state = 0;
- break;
-
- case 4: /* JavaScript/CSS/etc... */
- if (p >= src->data + 2 && *(p - 1) == '-' && *(p - 2) == '-') {
- in_q = state = 0;
- }
- break;
-
- default:
- if (!erase) {
- *(rp++) = c;
- }
- break;
- }
- break;
-
- case '"':
- case '\'':
- if (state == 2 && *(p - 1) != '\\') {
- if (lc == c) {
- lc = '\0';
- }
- else if (lc != '\\') {
- lc = c;
- }
- }
- else if (state == 0 && !erase) {
- *(rp++) = c;
- }
- if (state && p != src->data && *(p - 1) != '\\' && (!in_q || *p == in_q)) {
- if (in_q) {
- in_q = 0;
- }
- else {
- in_q = *p;
- }
- }
- break;
-
- case '!':
- /* JavaScript & Other HTML scripting languages */
- if (state == 1 && *(p - 1) == '<') {
- state = 3;
- lc = c;
- }
- else {
- if (state == 0 && !erase) {
- *(rp++) = c;
- }
- }
- break;
-
- case '-':
- if (state == 3 && p >= src->data + 2 && *(p - 1) == '-' && *(p - 2) == '!') {
- state = 4;
- }
- else {
- goto reg_char;
- }
- break;
-
- case '?':
-
- if (state == 1 && *(p - 1) == '<') {
- br = 0;
- state = 2;
- break;
- }
-
- case 'E':
- case 'e':
- /* !DOCTYPE exception */
- if (state == 3 && p > src->data + 6
- && g_ascii_tolower (*(p - 1)) == 'p'
- && g_ascii_tolower (*(p - 2)) == 'y'
- && g_ascii_tolower (*(p - 3)) == 't' && g_ascii_tolower (*(p - 4)) == 'c' && g_ascii_tolower (*(p - 5)) == 'o' && g_ascii_tolower (*(p - 6)) == 'd') {
- state = 1;
- break;
- }
- /* fall-through */
-
- case 'l':
-
- /* swm: If we encounter '<?xml' then we shouldn't be in
- * state == 2 (PHP). Switch back to HTML.
- */
-
- if (state == 2 && p > src->data + 2 && *(p - 1) == 'm' && *(p - 2) == 'x') {
- state = 1;
- break;
- }
-
- /* fall-through */
- default:
- reg_char:
- if (state == 0 && !erase) {
- *(rp++) = c;
- }
- break;
- }
- i++;
- if (i < (gint)src->len) {
- c = *(++p);
- }
- }
- if (rp < buf->data + src->len) {
- *rp = '\0';
- g_byte_array_set_size (buf, rp - buf->data);
- }
-
- /* Check tag balancing */
- if (level_ptr && level_ptr->data != NULL) {
- part->is_balanced = FALSE;
- }
-
- if (stateptr) {
- *stateptr = state;
- }
-
- return buf;
- }
-
- static void
- parse_qmail_recv (memory_pool_t * pool, gchar *line, struct received_header *r)
- {
- gchar *s, *p, t;
-
- /* We are interested only with received from network headers */
- if ((p = strstr (line, "from network")) == NULL) {
- r->is_error = 2;
- return;
- }
-
- p += sizeof ("from network") - 1;
- while (g_ascii_isspace (*p) || *p == '[') {
- p++;
- }
- /* format is ip/host */
- s = p;
- if (*p) {
- while (g_ascii_isdigit (*++p) || *p == '.');
- if (*p != '/') {
- r->is_error = 1;
- return;
- }
- else {
- *p = '\0';
- r->real_ip = memory_pool_strdup (pool, s);
- *p = '/';
- /* Now try to parse hostname */
- s = ++p;
- while (g_ascii_isalnum (*p) || *p == '.' || *p == '-' || *p == '_') {
- p++;
- }
- t = *p;
- *p = '\0';
- r->real_hostname = memory_pool_strdup (pool, s);
- *p = t;
- }
- }
- }
-
- static void
- parse_recv_header (memory_pool_t * pool, gchar *line, struct received_header *r)
- {
- gchar *p, *s, t, **res = NULL;
- gint state = 0, next_state = 0;
-
- g_strstrip (line);
- p = line;
- s = line;
-
- while (*p) {
- switch (state) {
- /* Initial state, search for from */
- case 0:
- if (*p == 'f' || *p == 'F') {
- if (g_ascii_tolower (*++p) == 'r' && g_ascii_tolower (*++p) == 'o' && g_ascii_tolower (*++p) == 'm') {
- p++;
- state = 99;
- next_state = 1;
- }
- }
- else if (g_ascii_tolower (*p) == 'b' && g_ascii_tolower (*(p + 1)) == 'y') {
- state = 3;
- }
- else {
- /* This can be qmail header, parse it separately */
- parse_qmail_recv (pool, line, r);
- return;
- }
- break;
- /* Read hostname */
- case 1:
- if (*p == '[') {
- /* This should be IP address */
- res = &r->from_ip;
- state = 98;
- next_state = 3;
- s = ++p;
- }
- else if (g_ascii_isalnum (*p) || *p == '.' || *p == '-' || *p == '_') {
- p++;
- }
- else {
- t = *p;
- *p = '\0';
- r->from_hostname = memory_pool_strdup (pool, s);
- *p = t;
- state = 99;
- next_state = 3;
- }
- break;
- /* Try to extract additional info */
- case 3:
- /* Try to extract ip or () info or by */
- if (g_ascii_tolower (*p) == 'b' && g_ascii_tolower (*(p + 1)) == 'y') {
- p += 2;
- /* Skip spaces after by */
- state = 99;
- next_state = 5;
- }
- else if (*p == '(') {
- state = 99;
- next_state = 4;
- p++;
- }
- else if (*p == '[') {
- /* Got ip before '(' so extract it */
- s = ++p;
- res = &r->from_ip;
- state = 98;
- next_state = 3;
- }
- else {
- p++;
- }
- break;
- /* We are in () block. Here can be found real hostname and real ip, this is written by some MTA */
- case 4:
- /* End of block */
- if (*p == ')') {
- p++;
- state = 3;
- }
- else if (g_ascii_isalnum (*p) || *p == '.' || *p == '-' || *p == '_') {
- p++;
- }
- else if (*p == '[') {
- s = ++p;
- state = 98;
- res = &r->real_ip;
- next_state = 3;
- }
- else {
- if (s != p) {
- /* Got some real hostname */
- /* check whether it is helo or p is not space symbol */
- if (!g_ascii_isspace (*p) || *(p + 1) != '[') {
- /* skip all */
- while (*p++ != ')' && *p != '\0');
- state = 3;
- }
- else {
- t = *p;
- *p = '\0';
- r->real_hostname = memory_pool_strdup (pool, s);
- *p = t;
- /* Now parse ip */
- p += 2;
- s = p;
- res = &r->real_ip;
- state = 98;
- next_state = 4;
- }
- }
- else {
- r->is_error = 1;
- return;
- }
- }
- break;
- /* Got by word */
- case 5:
- /* Here can be only hostname */
- if (g_ascii_isalnum (*p) || *p == '.' || *p == '-' || *p == '_') {
- p++;
- }
- else {
- /* We got something like hostname */
- t = *p;
- *p = '\0';
- r->by_hostname = memory_pool_strdup (pool, s);
- *p = t;
- /* Now end of parsing */
- return;
- }
- break;
-
- /* Extract ip */
- case 98:
- while (g_ascii_isdigit (*++p) || *p == '.');
- if (*p != ']') {
- /* Not an ip in fact */
- state = next_state;
- p++;
- }
- else {
- *p = '\0';
- *res = memory_pool_strdup (pool, s);
- *p = ']';
- p++;
- state = next_state;
- }
- break;
-
- /* Skip spaces */
- case 99:
- if (!g_ascii_isspace (*p)) {
- state = next_state;
- s = p;
- }
- else {
- p++;
- }
- break;
- case 100:
- r->is_error = 1;
- return;
- break;
- }
- }
-
- r->is_error = 1;
- return;
- }
-
- /* Convert raw headers to a list of struct raw_header * */
- static void
- process_raw_headers (struct worker_task *task)
- {
- struct raw_header *new = NULL, *lp;
- gchar *p, *c, *tmp, *tp;
- gint state = 0, l, next_state = 100, err_state = 100, t_state;
- gboolean valid_folding = FALSE;
-
- p = task->raw_headers_str;
- c = p;
- while (*p) {
- /* FSM for processing headers */
- switch (state) {
- case 0:
- /* Begin processing headers */
- if (!g_ascii_isalpha (*p)) {
- /* We have some garbage at the beginning of headers, skip this line */
- state = 100;
- next_state = 0;
- }
- else {
- state = 1;
- c = p;
- }
- break;
- case 1:
- /* We got something like header's name */
- if (*p == ':') {
- new = memory_pool_alloc0 (task->task_pool, sizeof (struct raw_header));
- l = p - c;
- tmp = memory_pool_alloc (task->task_pool, l + 1);
- rspamd_strlcpy (tmp, c, l + 1);
- new->name = tmp;
- new->empty_separator = TRUE;
- p ++;
- state = 2;
- c = p;
- }
- else if (g_ascii_isspace (*p)) {
- /* Not header but some garbage */
- state = 100;
- next_state = 0;
- }
- else {
- p ++;
- }
- break;
- case 2:
- /* We got header's name, so skip any \t or spaces */
- if (*p == '\t') {
- new->tab_separated = TRUE;
- new->empty_separator = FALSE;
- p ++;
- }
- else if (*p == ' ') {
- new->empty_separator = FALSE;
- p ++;
- }
- else if (*p == '\n' || *p == '\r') {
- /* Process folding */
- state = 99;
- l = p - c;
- if (l > 0) {
- tmp = memory_pool_alloc (task->task_pool, l + 1);
- rspamd_strlcpy (tmp, c, l + 1);
- new->separator = tmp;
- }
- next_state = 3;
- err_state = 5;
- c = p;
- }
- else {
- /* Process value */
- l = p - c;
- if (l > 0) {
- tmp = memory_pool_alloc (task->task_pool, l + 1);
- rspamd_strlcpy (tmp, c, l + 1);
- new->separator = tmp;
- }
- c = p;
- state = 3;
- }
- break;
- case 3:
- if (*p == '\r' || *p == '\n') {
- /* Hold folding */
- state = 99;
- next_state = 3;
- err_state = 4;
- }
- else if (*(p + 1) == '\0') {
- state = 4;
- }
- else {
- p ++;
- }
- break;
- case 4:
- /* Copy header's value */
- l = p - c;
- tmp = memory_pool_alloc (task->task_pool, l + 1);
- tp = tmp;
- t_state = 0;
- while (l --) {
- if (t_state == 0) {
- /* Before folding */
- if (*c == '\n' || *c == '\r') {
- t_state = 1;
- c ++;
- *tp ++ = ' ';
- }
- else {
- *tp ++ = *c ++;
- }
- }
- else if (t_state == 1) {
- /* Inside folding */
- if (g_ascii_isspace (*c)) {
- c++;
- }
- else {
- t_state = 0;
- *tp ++ = *c ++;
- }
- }
- }
- /* Strip last space that can be added by \r\n parsing */
- if (*(tp - 1) == ' ') {
- tp --;
- }
- *tp = '\0';
- new->value = tmp;
- new->next = NULL;
- if ((lp = g_hash_table_lookup (task->raw_headers, new->name)) != NULL) {
- while (lp->next != NULL) {
- lp = lp->next;
- }
- lp->next = new;
- }
- else {
- g_hash_table_insert (task->raw_headers, new->name, new);
- }
- debug_task ("add raw header %s: %s", new->name, new->value);
- state = 0;
- break;
- case 5:
- /* Header has only name, no value */
- new->next = NULL;
- if ((lp = g_hash_table_lookup (task->raw_headers, new->name)) != NULL) {
- while (lp->next != NULL) {
- lp = lp->next;
- }
- lp->next = new;
- }
- else {
- g_hash_table_insert (task->raw_headers, new->name, new);
- }
- state = 0;
- debug_task ("add raw header %s: %s", new->name, new->value);
- break;
- case 99:
- /* Folding state */
- if (*(p + 1) == '\0') {
- state = err_state;
- }
- else {
- if (*p == '\r' || *p == '\n') {
- p ++;
- valid_folding = FALSE;
- }
- else if (*p == '\t' || *p == ' ') {
- /* Valid folding */
- p ++;
- valid_folding = TRUE;
- }
- else {
- if (valid_folding) {
- debug_task ("go to state: %d->%d", state, next_state);
- state = next_state;
- }
- else {
- /* Fall back */
- debug_task ("go to state: %d->%d", state, err_state);
- state = err_state;
- }
- }
- }
- break;
- case 100:
- /* Fail state, skip line */
- if (*p == '\r') {
- if (*(p + 1) == '\n') {
- p ++;
- }
- p ++;
- state = next_state;
- }
- else if (*p == '\n') {
- if (*(p + 1) == '\r') {
- p ++;
- }
- p ++;
- state = next_state;
- }
- else if (*(p + 1) == '\0') {
- state = next_state;
- p ++;
- }
- else {
- p ++;
- }
- break;
- }
- }
- }
-
- static void
- free_byte_array_callback (void *pointer)
- {
- GByteArray *arr = (GByteArray *) pointer;
- g_byte_array_free (arr, TRUE);
- }
-
- static GByteArray *
- convert_text_to_utf (struct worker_task *task, GByteArray * part_content, GMimeContentType * type, struct mime_text_part *text_part)
- {
- GError *err = NULL;
- gsize read_bytes, write_bytes;
- const gchar *charset;
- gchar *res_str;
- GByteArray *result_array;
-
- if (task->cfg->raw_mode) {
- text_part->is_raw = TRUE;
- return part_content;
- }
-
- if ((charset = g_mime_content_type_get_parameter (type, "charset")) == NULL) {
- text_part->is_raw = TRUE;
- return part_content;
- }
-
- if (g_ascii_strcasecmp (charset, "utf-8") == 0 || g_ascii_strcasecmp (charset, "utf8") == 0) {
- if (g_utf8_validate (part_content->data, part_content->len, NULL)) {
- text_part->is_raw = FALSE;
- text_part->is_utf = TRUE;
- return part_content;
- }
- else {
- msg_info ("<%s>: contains invalid utf8 characters, assume it as raw", task->message_id);
- text_part->is_raw = TRUE;
- return part_content;
- }
- }
-
- res_str = g_convert_with_fallback (part_content->data, part_content->len, UTF8_CHARSET, charset, NULL, &read_bytes, &write_bytes, &err);
- if (res_str == NULL) {
- msg_warn ("<%s>: cannot convert from %s to utf8: %s", task->message_id, charset, err ? err->message : "unknown problem");
- text_part->is_raw = TRUE;
- return part_content;
- }
-
- result_array = memory_pool_alloc (task->task_pool, sizeof (GByteArray));
- result_array->data = res_str;
- result_array->len = write_bytes;
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_free, res_str);
- text_part->is_raw = FALSE;
- text_part->is_utf = TRUE;
-
- return result_array;
- }
-
- static void
- process_text_part (struct worker_task *task, GByteArray *part_content, GMimeContentType *type,
- GMimeObject *part, GMimeObject *parent, gboolean is_empty)
- {
- struct mime_text_part *text_part;
- const gchar *cd;
-
- /* Skip attachements */
- #ifndef GMIME24
- cd = g_mime_part_get_content_disposition (GMIME_PART (part));
- if (cd && g_ascii_strcasecmp (cd, "attachment") == 0 && !task->cfg->check_text_attachements) {
- debug_task ("skip attachments for checking as text parts");
- return;
- }
- #else
- cd = g_mime_object_get_disposition (GMIME_OBJECT (part));
- if (cd && g_ascii_strcasecmp (cd, GMIME_DISPOSITION_ATTACHMENT) == 0 && !task->cfg->check_text_attachements) {
- debug_task ("skip attachments for checking as text parts");
- return;
- }
- #endif
-
- if (g_mime_content_type_is_type (type, "text", "html") || g_mime_content_type_is_type (type, "text", "xhtml")) {
- debug_task ("got urls from text/html part");
-
- text_part = memory_pool_alloc0 (task->task_pool, sizeof (struct mime_text_part));
- text_part->is_html = TRUE;
- if (is_empty) {
- text_part->is_empty = TRUE;
- text_part->orig = NULL;
- text_part->content = NULL;
- task->text_parts = g_list_prepend (task->text_parts, text_part);
- return;
- }
- text_part->orig = convert_text_to_utf (task, part_content, type, text_part);
- text_part->is_balanced = TRUE;
- text_part->html_nodes = NULL;
- text_part->parent = parent;
-
- text_part->content = strip_html_tags (task, task->task_pool, text_part, text_part->orig, NULL);
-
- if (text_part->html_nodes == NULL) {
- url_parse_text (task->task_pool, task, text_part, FALSE);
- }
- else {
- decode_entitles (text_part->content->data, &text_part->content->len);
- url_parse_text (task->task_pool, task, text_part, FALSE);
- #if 0
- url_parse_text (task->task_pool, task, text_part, TRUE);
- #endif
- }
-
- fuzzy_init_part (text_part, task->task_pool, task->cfg->max_diff);
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) free_byte_array_callback, text_part->content);
- task->text_parts = g_list_prepend (task->text_parts, text_part);
- }
- else if (g_mime_content_type_is_type (type, "text", "*")) {
- debug_task ("got urls from text/plain part");
-
- text_part = memory_pool_alloc0 (task->task_pool, sizeof (struct mime_text_part));
- text_part->is_html = FALSE;
- text_part->parent = parent;
- if (is_empty) {
- text_part->is_empty = TRUE;
- text_part->orig = NULL;
- text_part->content = NULL;
- task->text_parts = g_list_prepend (task->text_parts, text_part);
- return;
- }
- text_part->orig = convert_text_to_utf (task, part_content, type, text_part);
- text_part->content = text_part->orig;
- url_parse_text (task->task_pool, task, text_part, FALSE);
- fuzzy_init_part (text_part, task->task_pool, task->cfg->max_diff);
- task->text_parts = g_list_prepend (task->text_parts, text_part);
- }
- }
-
- #ifdef GMIME24
- static void
- mime_foreach_callback (GMimeObject * parent, GMimeObject * part, gpointer user_data)
- #else
- static void
- mime_foreach_callback (GMimeObject * part, gpointer user_data)
- #endif
- {
- struct worker_task *task = (struct worker_task *)user_data;
- struct mime_part *mime_part;
- GMimeContentType *type;
- GMimeDataWrapper *wrapper;
- GMimeStream *part_stream;
- GByteArray *part_content;
-
- task->parts_count++;
-
- /* 'part' points to the current part node that g_mime_message_foreach_part() is iterating over */
-
- /* find out what class 'part' is... */
- if (GMIME_IS_MESSAGE_PART (part)) {
- /* message/rfc822 or message/news */
- GMimeMessage *message;
-
- /* g_mime_message_foreach_part() won't descend into
- child message parts, so if we want to count any
- subparts of this child message, we'll have to call
- g_mime_message_foreach_part() again here. */
-
- message = g_mime_message_part_get_message ((GMimeMessagePart *) part);
- if (task->parser_recursion++ < RECURSION_LIMIT) {
- #ifdef GMIME24
- g_mime_message_foreach (message, mime_foreach_callback, task);
- #else
- g_mime_message_foreach_part (message, mime_foreach_callback, task);
- #endif
- }
- else {
- msg_err ("endless recursion detected: %d", task->parser_recursion);
- return;
- }
- #ifndef GMIME24
- g_object_unref (message);
- #endif
- }
- else if (GMIME_IS_MESSAGE_PARTIAL (part)) {
- /* message/partial */
-
- /* this is an incomplete message part, probably a
- large message that the sender has broken into
- smaller parts and is sending us bit by bit. we
- could save some info about it so that we could
- piece this back together again once we get all the
- parts? */
- }
- else if (GMIME_IS_MULTIPART (part)) {
- /* multipart/mixed, multipart/alternative, multipart/related, multipart/signed, multipart/encrypted, etc... */
- task->parser_parent_part = part;
- #ifndef GMIME24
- debug_task ("detected multipart part");
- /* we'll get to finding out if this is a signed/encrypted multipart later... */
- if (task->parser_recursion++ < RECURSION_LIMIT) {
- g_mime_multipart_foreach ((GMimeMultipart *) part, mime_foreach_callback, task);
- }
- else {
- msg_err ("endless recursion detected: %d", task->parser_recursion);
- return;
- }
- #endif
- }
- else if (GMIME_IS_PART (part)) {
- /* a normal leaf part, could be text/plain or image/jpeg etc */
- #ifdef GMIME24
- type = (GMimeContentType *) g_mime_object_get_content_type (GMIME_OBJECT (part));
- #else
- type = (GMimeContentType *) g_mime_part_get_content_type (GMIME_PART (part));
- #endif
- if (type == NULL) {
- msg_warn ("type of part is unknown, assume text/plain");
- type = g_mime_content_type_new ("text", "plain");
- #ifdef GMIME24
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_object_unref, type);
- #else
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_mime_content_type_destroy, type);
- #endif
- }
- wrapper = g_mime_part_get_content_object (GMIME_PART (part));
- #ifdef GMIME24
- if (wrapper != NULL && GMIME_IS_DATA_WRAPPER (wrapper)) {
- #else
- if (wrapper != NULL) {
- #endif
- part_stream = g_mime_stream_mem_new ();
- if (g_mime_data_wrapper_write_to_stream (wrapper, part_stream) != -1) {
- g_mime_stream_mem_set_owner (GMIME_STREAM_MEM (part_stream), FALSE);
- part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (part_stream));
- g_object_unref (part_stream);
- mime_part = memory_pool_alloc (task->task_pool, sizeof (struct mime_part));
- mime_part->type = type;
- mime_part->content = part_content;
- mime_part->parent = task->parser_parent_part;
- mime_part->filename = g_mime_part_get_filename (GMIME_PART (part));
- debug_task ("found part with content-type: %s/%s", type->type, type->subtype);
- task->parts = g_list_prepend (task->parts, mime_part);
- /* Skip empty parts */
- process_text_part (task, part_content, type, part, task->parser_parent_part, (part_content->len <= 0));
- }
- else {
- msg_warn ("write to stream failed: %d, %s", errno, strerror (errno));
- }
- #ifndef GMIME24
- g_object_unref (wrapper);
- #endif
- }
- else {
- msg_warn ("cannot get wrapper for mime part, type of part: %s/%s", type->type, type->subtype);
- }
- }
- else {
- g_assert_not_reached ();
- }
- }
-
- static void
- destroy_message (void *pointer)
- {
- GMimeMessage *msg = pointer;
-
- msg_debug ("freeing pointer %p", msg);
- g_object_unref (msg);
- }
-
- gint
- process_message (struct worker_task *task)
- {
- GMimeMessage *message;
- GMimeParser *parser;
- GMimeStream *stream;
- GByteArray *tmp;
- GList *first, *cur;
- GMimePart *part;
- GMimeDataWrapper *wrapper;
- struct received_header *recv;
- gchar *mid, *url_str, *p, *end, *url_end;
- struct uri *subject_url;
- gsize len;
- gint rc;
-
- tmp = memory_pool_alloc (task->task_pool, sizeof (GByteArray));
- tmp->data = task->msg->begin;
- tmp->len = task->msg->len;
-
- stream = g_mime_stream_mem_new_with_byte_array (tmp);
- /*
- * This causes g_mime_stream not to free memory by itself as it is memory allocated by
- * pool allocator
- */
- g_mime_stream_mem_set_owner (GMIME_STREAM_MEM (stream), FALSE);
-
- if (task->is_mime) {
-
- debug_task ("construct mime parser from string length %d", (gint)task->msg->len);
- /* create a new parser object to parse the stream */
- parser = g_mime_parser_new_with_stream (stream);
- g_object_unref (stream);
-
- /* parse the message from the stream */
- message = g_mime_parser_construct_message (parser);
-
- if (message == NULL) {
- msg_warn ("cannot construct mime from stream");
- return -1;
- }
-
- task->message = message;
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) destroy_message, task->message);
-
- /* Save message id for future use */
- task->message_id = g_mime_message_get_message_id (task->message);
- if (task->message_id == NULL) {
- task->message_id = "undef";
- }
-
- task->parser_recursion = 0;
- #ifdef GMIME24
- g_mime_message_foreach (message, mime_foreach_callback, task);
- #else
- /*
- * This is rather strange, but gmime 2.2 do NOT pass top-level part to foreach callback
- * so we need to set up parent part by hands
- */
- task->parser_parent_part = g_mime_message_get_mime_part (message);
- g_object_unref (task->parser_parent_part);
- g_mime_message_foreach_part (message, mime_foreach_callback, task);
- #endif
-
- debug_task ("found %d parts in message", task->parts_count);
- if (task->queue_id == NULL) {
- task->queue_id = "undef";
- }
-
- #ifdef GMIME24
- task->raw_headers_str = g_mime_object_get_headers (GMIME_OBJECT (task->message));
- #else
- task->raw_headers_str = g_mime_message_get_headers (task->message);
- #endif
-
- process_images (task);
-
- /* Parse received headers */
- first = message_get_header (task->task_pool, message, "Received", FALSE);
- cur = first;
- while (cur) {
- recv = memory_pool_alloc0 (task->task_pool, sizeof (struct received_header));
- parse_recv_header (task->task_pool, cur->data, recv);
- task->received = g_list_prepend (task->received, recv);
- cur = g_list_next (cur);
- }
- if (first) {
- g_list_free (first);
- }
-
- if (task->raw_headers_str) {
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_free, task->raw_headers_str);
- process_raw_headers (task);
- }
-
- task->rcpts = g_mime_message_get_all_recipients (message);
- if (task->rcpts) {
- #ifdef GMIME24
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_object_unref, task->rcpts);
- #else
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) internet_address_list_destroy, task->rcpts);
- #endif
- }
-
-
- /* free the parser (and the stream) */
- g_object_unref (parser);
- }
- else {
- /* We got only message, no mime headers or anything like this */
- /* Construct fake message for it */
- task->message = g_mime_message_new (TRUE);
- if (task->from) {
- g_mime_message_set_sender (task->message, task->from);
- }
- /* Construct part for it */
- part = g_mime_part_new_with_type ("text", "html");
- #ifdef GMIME24
- wrapper = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_8BIT);
- #else
- wrapper = g_mime_data_wrapper_new_with_stream (stream, GMIME_PART_ENCODING_8BIT);
- #endif
- g_mime_part_set_content_object (part, wrapper);
- g_mime_message_set_mime_part (task->message, GMIME_OBJECT (part));
- /* Register destructors */
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_object_unref, wrapper);
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_object_unref, part);
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) destroy_message, task->message);
- /* Now parse in a normal way */
- task->parser_recursion = 0;
- #ifdef GMIME24
- g_mime_message_foreach (task->message, mime_foreach_callback, task);
- #else
- g_mime_message_foreach_part (task->message, mime_foreach_callback, task);
- #endif
- /* Generate message ID */
- mid = g_mime_utils_generate_message_id ("localhost.localdomain");
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_free, mid);
- g_mime_message_set_message_id (task->message, mid);
- task->message_id = mid;
- task->queue_id = mid;
- /* Set headers for message */
- if (task->subject) {
- g_mime_message_set_subject (task->message, task->subject);
- }
-
- /* Add recipients */
- #ifndef GMIME24
- if (task->rcpt) {
- cur = task->rcpt;
- while (cur) {
- g_mime_message_add_recipient (task->message, GMIME_RECIPIENT_TYPE_TO, NULL, (gchar *)cur->data);
- cur = g_list_next (cur);
- }
- }
- #endif
- }
-
- /* Parse urls inside Subject header */
- cur = message_get_header (task->task_pool, task->message, "Subject", FALSE);
- if (cur) {
- p = cur->data;
- len = strlen (p);
- end = p + len;
-
- while (p < end) {
- /* Search to the end of url */
- if (url_try_text (task->task_pool, p, end - p, NULL, &url_end, &url_str)) {
- if (url_str != NULL) {
- subject_url = memory_pool_alloc0 (task->task_pool, sizeof (struct uri));
- if (subject_url != NULL) {
- /* Try to parse url */
- rc = parse_uri (subject_url, url_str, task->task_pool);
- if ((rc == URI_ERRNO_OK || rc == URI_ERRNO_NO_SLASHES || rc == URI_ERRNO_NO_HOST_SLASH) &&
- subject_url->hostlen > 0) {
- if (subject_url->protocol != PROTOCOL_MAILTO) {
- if (!g_tree_lookup (task->urls, subject_url)) {
- g_tree_insert (task->urls, subject_url, subject_url);
- }
- }
- }
- else if (rc != URI_ERRNO_OK) {
- msg_info ("extract of url '%s' failed: %s", url_str, url_strerror (rc));
- }
- }
- }
- }
- else {
- break;
- }
- p = url_end + 1;
- }
- /* Free header's list */
- g_list_free (cur);
- }
-
- return 0;
- }
-
- struct gmime_raw_header {
- struct raw_header *next;
- gchar *name;
- gchar *value;
- };
-
- typedef struct _GMimeHeader {
- GHashTable *hash;
- GHashTable *writers;
- struct raw_header *headers;
- } local_GMimeHeader;
-
-
- /* known header field types */
- enum {
- HEADER_FROM = 0,
- HEADER_REPLY_TO,
- HEADER_TO,
- HEADER_CC,
- HEADER_BCC,
- HEADER_SUBJECT,
- HEADER_DATE,
- HEADER_MESSAGE_ID,
- HEADER_UNKNOWN
- };
-
- /*
- * Iterate throught all headers and make a list
- */
- #ifndef GMIME24
- static void
- header_iterate (memory_pool_t * pool, struct gmime_raw_header *h, GList ** ret, const gchar *field, gboolean strong)
- {
- while (h) {
- if (G_LIKELY (!strong)) {
- if (h->value && !g_ascii_strncasecmp (field, h->name, strlen (field))) {
- if (pool != NULL) {
- *ret = g_list_prepend (*ret, memory_pool_strdup (pool, h->value));
- }
- else {
- *ret = g_list_prepend (*ret, g_strdup (h->value));
- }
- }
- }
- else {
- if (h->value && !strncmp (field, h->name, strlen (field))) {
- if (pool != NULL) {
- *ret = g_list_prepend (*ret, memory_pool_strdup (pool, h->value));
- }
- else {
- *ret = g_list_prepend (*ret, g_strdup (h->value));
- }
- }
- }
- h = (struct gmime_raw_header *)h->next;
- }
- }
- #else
- static void
- header_iterate (memory_pool_t * pool, GMimeHeaderList * ls, GList ** ret, const gchar *field, gboolean strong)
- {
- /* Use iterator in case of gmime 2.4 */
- GMimeHeaderIter *iter;
- const gchar *name;
-
- if (ls == NULL) {
- *ret = NULL;
- return;
- }
-
- iter = g_mime_header_iter_new ();
- if (g_mime_header_list_get_iter (ls, iter) && g_mime_header_iter_first (iter)) {
- /* Iterate throught headers */
- while (g_mime_header_iter_is_valid (iter)) {
- name = g_mime_header_iter_get_name (iter);
- if (G_LIKELY (!strong)) {
- if (!g_ascii_strncasecmp (field, name, strlen (name))) {
- if (pool != NULL) {
- *ret = g_list_prepend (*ret, memory_pool_strdup (pool, g_mime_header_iter_get_value (iter)));
- }
- else {
- *ret = g_list_prepend (*ret, g_strdup (g_mime_header_iter_get_value (iter)));
- }
- }
- }
- else {
- if (!strncmp (field, name, strlen (name))) {
- if (pool != NULL) {
- *ret = g_list_prepend (*ret, memory_pool_strdup (pool, g_mime_header_iter_get_value (iter)));
- }
- else {
- *ret = g_list_prepend (*ret, g_strdup (g_mime_header_iter_get_value (iter)));
- }
- }
- }
- if (!g_mime_header_iter_next (iter)) {
- break;
- }
- }
- }
- g_mime_header_iter_free (iter);
- }
- #endif
-
-
- struct multipart_cb_data {
- GList *ret;
- memory_pool_t *pool;
- const gchar *field;
- gboolean try_search;
- gboolean strong;
- gint rec;
- };
-
- #define MAX_REC 10
-
- static void
- #ifdef GMIME24
- multipart_iterate (GMimeObject * parent, GMimeObject * part, gpointer user_data)
- #else
- multipart_iterate (GMimeObject * part, gpointer user_data)
- #endif
- {
- struct multipart_cb_data *data = user_data;
- #ifndef GMIME24
- struct gmime_raw_header *h;
- #endif
- GList *l = NULL;
-
- if (data->try_search && part != NULL && GMIME_IS_PART (part)) {
- #ifdef GMIME24
- GMimeHeaderList *ls;
-
- ls = g_mime_object_get_header_list (GMIME_OBJECT (part));
- header_iterate (data->pool, ls, &l, data->field, data->strong);
- #else
- h = (struct gmime_raw_header *)part->headers->headers;
- header_iterate (data->pool, h, &l, data->field, data->strong);
- #endif
- if (l == NULL) {
- /* Header not found, abandon search results */
- data->try_search = FALSE;
- g_list_free (data->ret);
- data->ret = NULL;
- }
- else {
- data->ret = g_list_concat (l, data->ret);
- }
- }
- else if (data->try_search && GMIME_IS_MULTIPART (part)) {
- /* Maybe endless recursion here ? */
- if (data->rec++ < MAX_REC) {
- g_mime_multipart_foreach (GMIME_MULTIPART (part), multipart_iterate, data);
- }
- else {
- msg_info ("maximum recurse limit is over, stop recursing, %d", data->rec);
- data->try_search = FALSE;
- }
- }
- }
-
- static GList *
- local_message_get_header (memory_pool_t * pool, GMimeMessage * message, const gchar *field, gboolean strong)
- {
- GList *gret = NULL;
- GMimeObject *part;
- struct multipart_cb_data cb = {
- .try_search = TRUE,
- .rec = 0,
- .ret = NULL,
- };
- cb.pool = pool;
- cb.field = field;
- cb.strong = strong;
-
- #ifndef GMIME24
- struct gmime_raw_header *h;
-
- if (field == NULL) {
- return NULL;
- }
-
- msg_debug ("iterate over headers to find header %s", field);
- h = (struct gmime_raw_header *) (GMIME_OBJECT (message)->headers->headers);
- header_iterate (pool, h, &gret, field, strong);
-
- if (gret == NULL) {
- /* Try to iterate with mime part headers */
- msg_debug ("iterate over headers of mime part to find header %s", field);
- part = g_mime_message_get_mime_part (message);
- if (part) {
- h = (struct gmime_raw_header *)part->headers->headers;
- header_iterate (pool, h, &gret, field, strong);
- if (gret == NULL && GMIME_IS_MULTIPART (part)) {
- msg_debug ("iterate over headers of each multipart's subparts %s", field);
- g_mime_multipart_foreach (GMIME_MULTIPART (part), multipart_iterate, &cb);
- if (cb.ret != NULL) {
- gret = cb.ret;
- }
- }
- #ifndef GMIME24
- g_object_unref (part);
- #endif
- }
- }
-
- return gret;
- #else
- GMimeHeaderList *ls;
-
- ls = g_mime_object_get_header_list (GMIME_OBJECT (message));
- header_iterate (pool, ls, &gret, field, strong);
- if (gret == NULL) {
- /* Try to iterate with mime part headers */
- part = g_mime_message_get_mime_part (message);
- if (part) {
- ls = g_mime_object_get_header_list (GMIME_OBJECT (part));
- header_iterate (pool, ls, &gret, field, strong);
- if (gret == NULL && GMIME_IS_MULTIPART (part)) {
- g_mime_multipart_foreach (GMIME_MULTIPART (part), multipart_iterate, &cb);
- if (cb.ret != NULL) {
- gret = cb.ret;
- }
- }
- #ifndef GMIME24
- g_object_unref (part);
- #endif
- }
- }
-
-
- return gret;
- #endif
- }
-
- /**
- * g_mime_message_set_date_from_string: Set the message sent-date
- * @message: MIME Message
- * @string: A string of date
- *
- * Set the sent-date on a MIME Message.
- **/
- void
- local_mime_message_set_date_from_string (GMimeMessage * message, const gchar * string)
- {
- time_t date;
- gint offset = 0;
-
- date = g_mime_utils_header_decode_date (string, &offset);
- g_mime_message_set_date (message, date, offset);
- }
-
- /*
- * Replacements for standart gmime functions but converting adresses to IA
- */
- static const gchar *
- local_message_get_sender (GMimeMessage * message)
- {
- gchar *res;
- const gchar *from = g_mime_message_get_sender (message);
- InternetAddressList *ia;
-
- #ifndef GMIME24
- ia = internet_address_parse_string (from);
- #else
- ia = internet_address_list_parse_string (from);
- #endif
- if (!ia) {
- return NULL;
- }
- res = internet_address_list_to_string (ia, FALSE);
- #ifndef GMIME24
- internet_address_list_destroy (ia);
- #else
- g_object_unref (ia);
- #endif
-
- return res;
- }
-
- static const gchar *
- local_message_get_reply_to (GMimeMessage * message)
- {
- gchar *res;
- const gchar *from = g_mime_message_get_reply_to (message);
- InternetAddressList *ia;
-
- #ifndef GMIME24
- ia = internet_address_parse_string (from);
- #else
- ia = internet_address_list_parse_string (from);
- #endif
- if (!ia) {
- return NULL;
- }
- res = internet_address_list_to_string (ia, FALSE);
- #ifndef GMIME24
- internet_address_list_destroy (ia);
- #else
- g_object_unref (ia);
- #endif
-
- return res;
- }
-
- #ifdef GMIME24
-
- # define ADD_RECIPIENT_TEMPLATE(type,def) \
- static void \
- local_message_add_recipients_from_string_##type (GMimeMessage *message, const gchar *string, const gchar *value) \
- { \
- InternetAddressList *il, *new; \
- \
- il = g_mime_message_get_recipients (message, (def)); \
- new = internet_address_list_parse_string (string); \
- internet_address_list_append (il, new); \
- } \
-
- ADD_RECIPIENT_TEMPLATE (to, GMIME_RECIPIENT_TYPE_TO)
- ADD_RECIPIENT_TEMPLATE (cc, GMIME_RECIPIENT_TYPE_CC)
- ADD_RECIPIENT_TEMPLATE (bcc, GMIME_RECIPIENT_TYPE_BCC)
- # define GET_RECIPIENT_TEMPLATE(type,def) \
- static InternetAddressList* \
- local_message_get_recipients_##type (GMimeMessage *message, const gchar *unused) \
- { \
- return g_mime_message_get_recipients (message, (def)); \
- }
- GET_RECIPIENT_TEMPLATE (to, GMIME_RECIPIENT_TYPE_TO)
- GET_RECIPIENT_TEMPLATE (cc, GMIME_RECIPIENT_TYPE_CC)
- GET_RECIPIENT_TEMPLATE (bcc, GMIME_RECIPIENT_TYPE_BCC)
- #endif
- /* different declarations for different types of set and get functions */
- typedef const gchar *(*GetFunc) (GMimeMessage * message);
- typedef InternetAddressList *(*GetRcptFunc) (GMimeMessage * message, const gchar *type);
- typedef GList *(*GetListFunc) (memory_pool_t * pool, GMimeMessage * message, const gchar *type, gboolean strong);
- typedef void (*SetFunc) (GMimeMessage * message, const gchar *value);
- typedef void (*SetListFunc) (GMimeMessage * message, const gchar *field, const gchar *value);
-
- /** different types of functions
- *
- * FUNC_CHARPTR
- * - function with no arguments
- * - get returns gchar*
- *
- * FUNC_IA (from Internet Address)
- * - function with additional "field" argument from the fieldfunc table,
- * - get returns Glist*
- *
- * FUNC_LIST
- * - function with additional "field" argument (given arbitrary header field name)
- * - get returns Glist*
- **/
- enum {
- FUNC_CHARPTR = 0,
- FUNC_CHARFREEPTR,
- FUNC_IA,
- FUNC_LIST
- };
-
- /**
- * fieldfunc struct: structure of MIME fields and corresponding get and set
- * functions.
- **/
- static struct {
- gchar *name;
- GetFunc func;
- GetRcptFunc rcptfunc;
- GetListFunc getlistfunc;
- SetFunc setfunc;
- SetListFunc setlfunc;
- gint functype;
- } fieldfunc[] =
- {
- {
- "From", local_message_get_sender, NULL, NULL, g_mime_message_set_sender, NULL, FUNC_CHARFREEPTR}, {
- "Reply-To", local_message_get_reply_to, NULL, NULL, g_mime_message_set_reply_to, NULL, FUNC_CHARFREEPTR},
- #ifndef GMIME24
- {
- "To", NULL, (GetRcptFunc) g_mime_message_get_recipients, NULL, NULL, (SetListFunc) g_mime_message_add_recipients_from_string, FUNC_IA}, {
- "Cc", NULL, (GetRcptFunc) g_mime_message_get_recipients, NULL, NULL, (SetListFunc) g_mime_message_add_recipients_from_string, FUNC_IA}, {
- "Bcc", NULL, (GetRcptFunc) g_mime_message_get_recipients, NULL, NULL, (SetListFunc) g_mime_message_add_recipients_from_string, FUNC_IA}, {
- "Date", (GetFunc) g_mime_message_get_date_string, NULL, NULL, local_mime_message_set_date_from_string, NULL, FUNC_CHARFREEPTR},
- #else
- {
- "To", NULL, local_message_get_recipients_to, NULL, NULL, local_message_add_recipients_from_string_to, FUNC_IA}, {
- "Cc", NULL, local_message_get_recipients_cc, NULL, NULL, local_message_add_recipients_from_string_cc, FUNC_IA}, {
- "Bcc", NULL, local_message_get_recipients_bcc, NULL, NULL, local_message_add_recipients_from_string_bcc, FUNC_IA}, {
- "Date", (GetFunc)g_mime_message_get_date_as_string, NULL, NULL, local_mime_message_set_date_from_string, NULL, FUNC_CHARFREEPTR},
- #endif
- {
- "Subject", g_mime_message_get_subject, NULL, NULL, g_mime_message_set_subject, NULL, FUNC_CHARPTR}, {
- "Message-Id", g_mime_message_get_message_id, NULL, NULL, g_mime_message_set_message_id, NULL, FUNC_CHARPTR},
- #ifndef GMIME24
- {
- NULL, NULL, NULL, local_message_get_header, NULL, g_mime_message_add_header, FUNC_LIST}
- #else
- {
- NULL, NULL, NULL, local_message_get_header, NULL, (SetListFunc)g_mime_object_append_header, FUNC_LIST}
- #endif
- };
-
- /**
- * message_set_header: set header of any type excluding special (Content- and MIME-Version:)
- **/
- void
- message_set_header (GMimeMessage * message, const gchar *field, const gchar *value)
- {
- gint i;
-
- if (!g_ascii_strcasecmp (field, "MIME-Version:") || !g_ascii_strncasecmp (field, "Content-", 8)) {
- return;
- }
- for (i = 0; i <= HEADER_UNKNOWN; ++i) {
- if (!fieldfunc[i].name || !g_ascii_strncasecmp (field, fieldfunc[i].name, strlen (fieldfunc[i].name))) {
- switch (fieldfunc[i].functype) {
- case FUNC_CHARPTR:
- (*(fieldfunc[i].setfunc)) (message, value);
- break;
- case FUNC_IA:
- (*(fieldfunc[i].setlfunc)) (message, fieldfunc[i].name, value);
- break;
- case FUNC_LIST:
- (*(fieldfunc[i].setlfunc)) (message, field, value);
- break;
- }
- break;
- }
- }
- }
-
-
- /**
- * message_get_header: returns the list of 'any header' values
- * (except of unsupported yet Content- and MIME-Version special headers)
- *
- * You should free the GList list by yourself.
- **/
- GList *
- message_get_header (memory_pool_t * pool, GMimeMessage * message, const gchar *field, gboolean strong)
- {
- gint i;
- gchar *ret = NULL, *ia_string;
- GList *gret = NULL;
- InternetAddressList *ia_list = NULL, *ia;
-
- for (i = 0; i <= HEADER_UNKNOWN; ++i) {
- if (!fieldfunc[i].name || !g_ascii_strncasecmp (field, fieldfunc[i].name, strlen (fieldfunc[i].name))) {
- switch (fieldfunc[i].functype) {
- case FUNC_CHARFREEPTR:
- ret = (gchar *)(*(fieldfunc[i].func)) (message);
- break;
- case FUNC_CHARPTR:
- ret = (gchar *)(*(fieldfunc[i].func)) (message);
- break;
- case FUNC_IA:
- ia_list = (*(fieldfunc[i].rcptfunc)) (message, field);
- ia = ia_list;
- #ifndef GMIME24
- while (ia && ia->address) {
-
- ia_string = internet_address_to_string ((InternetAddress *) ia->address, FALSE);
- if (pool != NULL) {
- memory_pool_add_destructor (pool, (pool_destruct_func) g_free, ia_string);
- }
- gret = g_list_prepend (gret, ia_string);
- ia = ia->next;
- }
- #else
- i = internet_address_list_length (ia);
- while (--i >= 0) {
- ia_string = internet_address_to_string (internet_address_list_get_address (ia, i), FALSE);
- if (pool != NULL) {
- memory_pool_add_destructor (pool, (pool_destruct_func) g_free, ia_string);
- }
- gret = g_list_prepend (gret, ia_string);
- }
- #endif
- break;
- case FUNC_LIST:
- gret = (*(fieldfunc[i].getlistfunc)) (pool, message, field, strong);
- break;
- }
- break;
- }
- }
- if (gret == NULL && ret != NULL) {
- if (pool != NULL) {
- gret = g_list_prepend (gret, memory_pool_strdup (pool, ret));
- }
- else {
- gret = g_list_prepend (gret, g_strdup (ret));
- }
- }
- if (fieldfunc[i].functype == FUNC_CHARFREEPTR && ret) {
- g_free (ret);
- }
-
- return gret;
- }
-
- GList*
- message_get_raw_header (struct worker_task *task, const gchar *field, gboolean strong)
- {
- GList *gret = NULL;
- struct raw_header *rh;
-
- rh = g_hash_table_lookup (task->raw_headers, field);
-
- if (rh == NULL) {
- return NULL;
- }
-
- while (rh) {
- if (strong) {
- if (strcmp (rh->name, field) == 0) {
- gret = g_list_prepend (gret, rh);
- }
- }
- else {
- if (g_ascii_strcasecmp (rh->name, field) == 0) {
- gret = g_list_prepend (gret, rh);
- }
- }
- rh = rh->next;
- }
-
- if (gret != NULL) {
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func)g_list_free, gret);
- }
-
- return gret;
- }
|