/* Copyright (C) 1999-2004 IC & S dbmail@ic-s.nl Modified by Mikhail Ramendik mr@ramendik.ru (MR) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Id: imaputil.c 1949 2005-12-30 09:41:56Z paul $ * * imaputil.c * * IMAP-server utility functions implementations */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "imaputil.h" #include "imap4.h" #include "debug.h" #include "db.h" #include "memblock.h" #include "dbsearch.h" #include "rfcmsg.h" #define BUFLEN 2048 #define SEND_BUF_SIZE 1024 #define MAX_ARGS 512 /* cache */ extern cache_t cached_msg; extern const char AcceptedChars[]; extern const char AcceptedTagChars[]; extern const char AcceptedMailboxnameChars[]; extern const char *month_desc[]; static const char *search_cost[] = { "b","b","c","c","c","d","d","d","d","c","e","e","b","b","j","j","j" }; char base64encodestring[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* returned by date_sql2imap() */ #define IMAP_STANDARD_DATE "Sat, 03-Nov-1979 00:00:00 +0000" char _imapdate[IMAP_INTERNALDATE_LEN] = IMAP_STANDARD_DATE; /* returned by date_imap2sql() */ #define SQL_STANDARD_DATE "1979-11-03 00:00:00" char _sqldate[SQL_INTERNALDATE_LEN + 1] = SQL_STANDARD_DATE; const int month_len[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; const char *item_desc[] = { "TEXT", "HEADER", "MIME", "HEADER.FIELDS", "HEADER.FIELDS.NOT" }; const char *envelope_items[] = { "from", "sender", "reply-to", "to", "cc", "bcc", NULL }; /* * retrieve_structure() * * retrieves the MIME-IMB structure of a message. The msg should be in the format * as build by db_fetch_headers(). * * shows extension data if show_extension_data != 0 * * returns -1 on error, 0 on success */ int retrieve_structure(FILE * outstream, mime_message_t * msg, int show_extension_data) { struct mime_record *mr; struct element *curr; struct list *header_to_use; mime_message_t rfcmsg; char *subtype, *extension, *newline; int is_mime_multipart = 0, is_rfc_multipart = 0; int rfc822 = 0; fprintf(outstream, "("); mime_findfield("content-type", &msg->mimeheader, &mr); is_mime_multipart = (mr && strncasecmp(mr->value, "multipart", strlen("multipart")) == 0 && !msg->message_has_errors); mime_findfield("content-type", &msg->rfcheader, &mr); is_rfc_multipart = (mr && strncasecmp(mr->value, "multipart", strlen("multipart")) == 0 && !msg->message_has_errors); /* eddy */ if (mr && strncasecmp(mr->value, "message/rfc822", strlen("message/rfc822")) == 0) { rfc822 = 1; } if (rfc822 || (!is_rfc_multipart && !is_mime_multipart)) { /* show basic fields: * content-type, content-subtype, (parameter list), * content-id, content-description, content-transfer-encoding, * size */ if (msg->mimeheader.start == NULL) header_to_use = &msg->rfcheader; /* we're dealing with a single-part RFC msg here */ else header_to_use = &msg->mimeheader; /* we're dealing with a pure-MIME header here */ mime_findfield("content-type", header_to_use, &mr); if (mr && strlen(mr->value) > 0) show_mime_parameter_list(outstream, mr, 1, 0); else fprintf(outstream, "\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\")"); /* default */ mime_findfield("content-id", header_to_use, &mr); if (mr && strlen(mr->value) > 0) { fprintf(outstream, " "); quoted_string_out(outstream, mr->value); } else fprintf(outstream, " NIL"); mime_findfield("content-description", header_to_use, &mr); if (mr && strlen(mr->value) > 0) { fprintf(outstream, " "); quoted_string_out(outstream, mr->value); } else fprintf(outstream, " NIL"); mime_findfield("content-transfer-encoding", header_to_use, &mr); if (mr && strlen(mr->value) > 0) { fprintf(outstream, " "); quoted_string_out(outstream, mr->value); } else fprintf(outstream, " \"7BIT\""); /* now output size */ /* add msg->bodylines because \n is dumped as \r\n */ if (msg->mimeheader.start && msg->rfcheader.start) fprintf(outstream, " %llu", msg->bodysize + msg->mimerfclines + msg->rfcheadersize - msg->rfcheaderlines); else fprintf(outstream, " %llu", msg->bodysize + msg->bodylines); /* now check special cases, first case: message/rfc822 */ mime_findfield("content-type", header_to_use, &mr); if (mr && strncasecmp(mr->value, "message/rfc822", strlen("message/rfc822")) == 0 && header_to_use != &msg->rfcheader) { /* msg/rfc822 found; extra items to be displayed: * (a) body envelope of rfc822 msg * (b) body structure of rfc822 msg * (c) msg size (lines) */ if (retrieve_envelope(outstream, &msg->rfcheader) == -1) return -1; fprintf(outstream, " "); memmove(&rfcmsg, msg, sizeof(rfcmsg)); rfcmsg.mimeheader.start = NULL; /* forget MIME-part */ if (retrieve_structure (outstream, &rfcmsg, show_extension_data) == -1) return -1; /* output # of lines */ fprintf(outstream, " %llu", msg->bodylines); } /* now check second special case: text * NOTE: if 'content-type' is absent, TEXT is assumed */ if ((mr && strncasecmp(mr->value, "text", strlen("text")) == 0) || !mr) { /* output # of lines */ if (msg->mimeheader.start && msg->rfcheader.start) fprintf(outstream, " %llu", msg->mimerfclines); else fprintf(outstream, " %llu", msg->bodylines); } if (show_extension_data) { mime_findfield("content-md5", header_to_use, &mr); if (mr && strlen(mr->value) > 0) { fprintf(outstream, " "); quoted_string_out(outstream, mr->value); } else fprintf(outstream, " NIL"); mime_findfield("content-disposition", header_to_use, &mr); if (mr && strlen(mr->value) > 0) { fprintf(outstream, " ("); show_mime_parameter_list(outstream, mr, 0, 0); fprintf(outstream, ")"); } else fprintf(outstream, " NIL"); mime_findfield("content-language", header_to_use, &mr); if (mr && strlen(mr->value) > 0) { fprintf(outstream, " "); quoted_string_out(outstream, mr->value); } else fprintf(outstream, " NIL"); } } else { /* check for a multipart message */ if (is_rfc_multipart || is_mime_multipart) { curr = list_getstart(&msg->children); while (curr) { if (retrieve_structure (outstream, (mime_message_t *) curr->data, show_extension_data) == -1) return -1; curr = curr->nextnode; } /* show multipart subtype */ if (is_mime_multipart) mime_findfield("content-type", &msg->mimeheader, &mr); else mime_findfield("content-type", &msg->rfcheader, &mr); subtype = strchr(mr->value, '/'); extension = strchr(subtype, ';'); if (!subtype) fprintf(outstream, " NIL"); else { if (!extension) { newline = strchr(subtype, '\n'); if (!newline) return -1; *newline = 0; fprintf(outstream, " "); quoted_string_out(outstream, subtype + 1); *newline = '\n'; } else { *extension = 0; fprintf(outstream, " "); quoted_string_out(outstream, subtype + 1); *extension = ';'; } } /* show extension data (after subtype) */ if (extension && show_extension_data) { show_mime_parameter_list(outstream, mr, 0, 1); /* FIXME: should give body-disposition & body-language here */ fprintf(outstream, " NIL NIL"); } } else { /* ??? */ } } fprintf(outstream, ")"); return 0; } /* * retrieve_envelope() * * retrieves the body envelope of an RFC-822 msg * * returns -1 on error, 0 on success */ int retrieve_envelope(FILE * outstream, struct list *rfcheader) { struct mime_record *mr; int idx; fprintf(outstream, "("); mime_findfield("date", rfcheader, &mr); if (mr && strlen(mr->value) > 0) { quoted_string_out(outstream, mr->value); fprintf(outstream, " "); } else fprintf(outstream, "NIL "); mime_findfield("subject", rfcheader, &mr); if (mr && strlen(mr->value) > 0) { quoted_string_out(outstream, mr->value); fprintf(outstream, " "); } else fprintf(outstream, "NIL "); /* now from, sender, reply-to, to, cc, bcc, in-reply-to fields; * note that multiple mailaddresses are separated by ',' */ for (idx = 0; envelope_items[idx]; idx++) { mime_findfield(envelope_items[idx], rfcheader, &mr); if (mr && strlen(mr->value) > 0) { show_address_list(outstream, mr); } else if (strcasecmp(envelope_items[idx], "reply-to") == 0) { /* default this field */ mime_findfield("from", rfcheader, &mr); if (mr && strlen(mr->value) > 0) show_address_list(outstream, mr); else /* no from field ??? */ fprintf(outstream, "((NIL NIL \"nobody\" \"nowhere.nirgendwo\"))"); } else if (strcasecmp(envelope_items[idx], "sender") == 0) { /* default this field */ mime_findfield("from", rfcheader, &mr); if (mr && strlen(mr->value) > 0) show_address_list(outstream, mr); else /* no from field ??? */ fprintf(outstream, "((NIL NIL \"nobody\" \"nowhere.nirgendwo\"))"); } else fprintf(outstream, "NIL"); fprintf(outstream, " "); } mime_findfield("in-reply-to", rfcheader, &mr); if (mr && strlen(mr->value) > 0) { quoted_string_out(outstream, mr->value); fprintf(outstream, " "); } else fprintf(outstream, "NIL "); mime_findfield("message-id", rfcheader, &mr); if (mr && strlen(mr->value) > 0) quoted_string_out(outstream, mr->value); else fprintf(outstream, "NIL"); fprintf(outstream, ")"); return 0; } /* * show_address_list() * * gives an address list, output to outstream */ int show_address_list(FILE * outstream, struct mime_record *mr) { int delimiter, i, inquote, start, has_split; char savechar; fprintf(outstream, "("); /* find ',' to split up multiple addresses */ delimiter = 0; do { fprintf(outstream, "("); start = delimiter; for (inquote = 0; mr->value[delimiter] && !(mr->value[delimiter] == ',' && !inquote); delimiter++) if (mr->value[delimiter] == '\"') inquote ^= 1; if (mr->value[delimiter]) mr->value[delimiter] = 0; /* replace ',' by NULL-termination */ else delimiter = -1; /* this will be the last one */ /* the address currently being processed is now contained within * &mr->value[start] 'till first '\0' */ /* possibilities for the mail address: * (1) name * (2) * (3) user@domain * scan for '<' to determine which case we should be dealing with; */ for (i = start, inquote = 0; mr->value[i] && !(mr->value[i] == '<' && !inquote); i++) if (mr->value[i] == '\"') inquote ^= 1; if (mr->value[i]) { if (i > start + 2) { /* name is contained in &mr->value[start] untill &mr->value[i-2] */ /* name might contain quotes */ savechar = mr->value[i - 1]; mr->value[i - 1] = '\0'; /* terminate string */ quoted_string_out(outstream, &mr->value[start]); mr->value[i - 1] = savechar; } else fprintf(outstream, "NIL"); start = i + 1; /* skip to after '<' */ } else fprintf(outstream, "NIL"); fprintf(outstream, " NIL "); /* source route ?? smtp at-domain-list ?? */ /* now display user domainname; &mr->value[start] is starting point */ fprintf(outstream, "\""); /* * added a check for whitespace within the address (not good) */ for (i = start, has_split = 0; mr->value[i] && mr->value[i] != '>' && !isspace(mr->value[i]); i++) { if (mr->value[i] == '@') { fprintf(outstream, "\" \""); has_split = 1; } else { if (mr->value[i] == '"') fprintf(outstream, "\\"); fprintf(outstream, "%c", mr->value[i]); } } if (!has_split) fprintf(outstream, "\" \"\""); /* '@' did not occur */ else fprintf(outstream, "\""); if (delimiter > 0) { mr->value[delimiter++] = ','; /* restore & prepare for next iteration */ while (isspace(mr->value[delimiter])) delimiter++; } fprintf(outstream, ")"); } while (delimiter > 0); fprintf(outstream, ")"); return 0; } /* * show_mime_parameter_list() * * shows mime name/value pairs, output to outstream * * if force_subtype != 0 'NIL' will be outputted if no subtype is specified * if only_extension != 0 only extension data (after first ';') will be shown */ int show_mime_parameter_list(FILE * outstream, struct mime_record *mr, int force_subtype, int only_extension) { int idx, delimiter, start, end; char *fieldvalue = (char *)dm_malloc(sizeof(char *)*255); char *tmpstring = (char *)dm_malloc(sizeof(char *)*255); /* find first delimiter */ for (delimiter = 0; mr->value[delimiter] && mr->value[delimiter] != ';'; delimiter++); /* are there non-whitespace chars after the delimiter? */ /* looking for the case where the mime type ends with a ";" */ /* if it is of type "text" it must have a default character set generated */ end = strlen(mr->value); for (start = delimiter + 1; (isspace(mr->value[start]) == 0 && start <= end); start++); end = start - delimiter - 1; start = 0; if (end && strstr(mr->value, "text")) start++; if (mr->value[delimiter]) mr->value[delimiter] = 0; else delimiter = -1; if (!only_extension) { /* find main type in value */ for (idx = 0; mr->value[idx] && mr->value[idx] != '/'; idx++); if (mr->value[idx] && (idx < delimiter || delimiter == -1)) { mr->value[idx] = 0; quoted_string_out(outstream, mr->value); fprintf(outstream, " "); quoted_string_out(outstream, &mr->value[idx + 1]); mr->value[idx] = '/'; } else { quoted_string_out(outstream, mr->value); fprintf(outstream, " %s", force_subtype ? "NIL" : ""); } } if (delimiter >= 0) { /* extra parameters specified */ mr->value[delimiter] = ';'; idx = delimiter; fprintf(outstream, " ("); if (start) fprintf(outstream, "\"CHARSET\" \"US-ASCII\""); /* extra params: = [; = [; ...etc...]] * note that both name and val may or may not be enclosed by * either single or double quotation marks */ do { /* skip whitespace */ for (idx++; isspace(mr->value[idx]); idx++); if (!mr->value[idx]) break; /* ?? */ /* check if quotation marks are specified */ if (mr->value[idx] == '\"' || mr->value[idx] == '\'') { start = ++idx; while (mr->value[idx] && mr->value[idx] != mr->value[start - 1]) idx++; if (!mr->value[idx] || mr->value[idx + 1] != '=') /* ?? no end quote */ break; end = idx; idx += 2; /* skip to after '=' */ } else { start = idx; while (mr->value[idx] && mr->value[idx] != '=') idx++; if (!mr->value[idx]) /* ?? no value specified */ break; end = idx; idx++; /* skip to after '=' */ } fprintf(outstream, "\"%.*s\" ", (end - start), &mr->value[start]); /* now process the value; practically same procedure */ if (mr->value[idx] == '\"' || mr->value[idx] == '\'') { start = ++idx; while (mr->value[idx] && mr->value[idx] != mr->value[start - 1]) idx++; if (!mr->value[idx]) /* ?? no end quote */ break; end = idx; idx++; } else { start = idx; while (mr->value[idx] && !isspace(mr->value[idx]) && mr->value[idx] != ';') idx++; end = idx; } /* Thunderbird doesn't like mime-values that are wrapped, so * we unwrap mime-values * * this code assumes mime values are no longer than 255 chars. * * PJS */ snprintf(tmpstring,255,"\"%.*s\"", (end - start), &mr->value[start]); mime_unwrap(fieldvalue,tmpstring); fprintf(outstream,fieldvalue); //fprintf(outstream, "\"%.*s\"", (end - start), &mr->value[start]); /* check for more name/val pairs */ while (mr->value[idx] && mr->value[idx] != ';') idx++; if (mr->value[idx]) fprintf(outstream, " "); } while (mr->value[idx]); fprintf(outstream, ")"); } else { fprintf(outstream, " NIL"); } dm_free(fieldvalue); dm_free(tmpstring); return 0; } /* * get_part_by_num() * * retrieves a msg part by it's numeric specifier * 'part' is assumed to be valid! (i.e '1.2.3.44') * returns NULL if there is no such part */ mime_message_t *get_part_by_num(mime_message_t * msg, const char *part) { int nextpart, j; char *endptr; struct element *curr; if (part == NULL || strlen(part) == 0 || msg == NULL) return msg; nextpart = strtoul(part, &endptr, 10); /* strtoul() stops at '.' */ for (j = 1, curr = list_getstart(&msg->children); j < nextpart && curr; j++, curr = curr->nextnode); if (!curr) return NULL; if (*endptr) return get_part_by_num((mime_message_t *) curr->data, &endptr[1]); /* skip dot in part */ return (mime_message_t *) curr->data; } /* * rfcheader_dump() * * dumps rfc-header fields belonging to rfcheader * the fields to be dumped are specified in fieldnames, an array containing nfields items * * if equal_type == 0 the field match criterium is inverted and non-matching fieldnames * will be selected * * to select every headerfield it suffices to set nfields and equal_type to 0 * * returns number of bytes written to outmem */ u64_t rfcheader_dump(MEM * outmem, struct list * rfcheader, char **fieldnames, int nfields, int equal_type) { struct mime_record *mr; struct element *curr; u64_t size = 0; curr = list_getstart(rfcheader); if (rfcheader == NULL || curr == NULL) { /*size += fprintf(outstream, "NIL\r\n"); */ return 0; } curr = list_getstart(rfcheader); while (curr) { mr = (struct mime_record *) curr->data; if (haystack_find(nfields, fieldnames, mr->field) == equal_type) { /* ok output this field */ size += mwrite(mr->field, strlen(mr->field), outmem); size += mwrite(": ", 2, outmem); size += mwrite(mr->value, strlen(mr->value), outmem); size += mwrite("\r\n", 2, outmem); } curr = curr->nextnode; } size += mwrite("\r\n", 2, outmem); return size; } /* * mimeheader_dump() * * dumps mime-header fields belonging to mimeheader * */ u64_t mimeheader_dump(MEM * outmem, struct list * mimeheader) { struct mime_record *mr; struct element *curr; u64_t size = 0; curr = list_getstart(mimeheader); if (mimeheader == NULL || curr == NULL) { /*size = fprintf(outstream, "NIL\r\n"); */ return 0; } while (curr) { mr = (struct mime_record *) curr->data; size += mwrite(mr->field, strlen(mr->field), outmem); size += mwrite(": ", 2, outmem); size += mwrite(mr->value, strlen(mr->value), outmem); size += mwrite("\r\n", 2, outmem); curr = curr->nextnode; } size += mwrite("\r\n", 2, outmem); return size; } /* * find a string in an array of strings */ int haystack_find(int haystacklen, char **haystack, const char *needle) { int i; for (i = 0; i < haystacklen; i++) if (strcasecmp(haystack[i], needle) == 0) return 1; return 0; } /* * next_fetch_item() * * retrieves next item to be fetched from an argument list starting at the given * index. The update index is returned being -1 on 'no-more' and -2 on error. * arglist is supposed to be formatted according to build_args_array() * */ int next_fetch_item(char **args, int idx, fetch_items_t * fi) { int invalidargs, indigit, ispeek, shouldclose, delimpos; unsigned int j = 0; memset(fi, 0, sizeof(fetch_items_t)); /* init */ fi->bodyfetch.itemtype = -1; /* expect no body fetches (a priori) */ invalidargs = 0; if (!args[idx]) return -1; /* no more */ if (args[idx][0] == '(') idx++; if (!args[idx]) return -2; /* error */ if (strcasecmp(args[idx], "flags") == 0) fi->getFlags = 1; else if (strcasecmp(args[idx], "internaldate") == 0) fi->getInternalDate = 1; else if (strcasecmp(args[idx], "uid") == 0) fi->getUID = 1; else if (strcasecmp(args[idx], "rfc822") == 0) { fi->getRFC822 = 1; fi->msgparse_needed = 1; } else if (strcasecmp(args[idx], "rfc822.peek") == 0) { fi->getRFC822Peek = 1; fi->msgparse_needed = 1; } else if (strcasecmp(args[idx], "rfc822.header") == 0) { fi->getRFC822Header = 1; fi->msgparse_needed = 1; } else if (strcasecmp(args[idx], "rfc822.size") == 0) { fi->getSize = 1; /* fi->msgparse_needed = 1;*//* after first calc, it will be in the dbase */ } else if (strcasecmp(args[idx], "rfc822.text") == 0) { fi->getRFC822Text = 1; fi->msgparse_needed = 1; } else if (strcasecmp(args[idx], "body") == 0 || strcasecmp(args[idx], "body.peek") == 0) { fi->msgparse_needed = 1; if (!args[idx + 1] || strcmp(args[idx + 1], "[") != 0) { if (strcasecmp(args[idx], "body.peek") == 0) return -2; /* error DONE */ else fi->getMIME_IMB_noextension = 1; /* just BODY specified */ } else { /* determine wheter or not to set the seen flag */ ispeek = (strcasecmp(args[idx], "body.peek") == 0); /* now read the argument list to body */ idx++; /* now pointing at '[' (not the last arg, parentheses are matched) */ idx++; /* now pointing at what should be the item type */ if (strcmp(args[idx], "]") == 0) { /* specified body[] or body.peek[] */ if (ispeek) fi->getBodyTotalPeek = 1; else fi->getBodyTotal = 1; /* check if octet start/cnt is specified */ if (args[idx + 1] && args[idx + 1][0] == '<') { idx++; /* advance */ /* check argument */ if (args[idx] [strlen(args[idx]) - 1] != '>') return -2; /* error DONE */ delimpos = -1; for (j = 1; j < strlen(args[idx]) - 1; j++) { if (args[idx][j] == '.') { if (delimpos != -1) { invalidargs = 1; break; } delimpos = j; } else if (!isdigit (args[idx][j])) { invalidargs = 1; break; } } if (invalidargs || delimpos == -1 || delimpos == 1 || delimpos == (int) (strlen(args[idx]) - 2)) return -2; /* no delimiter found or at first/last pos OR invalid args DONE */ /* read the numbers */ args[idx][strlen(args[idx]) - 1] = '\0'; args[idx][delimpos] = '\0'; fi->bodyfetch.octetstart = strtoll(&args[idx][1], NULL, 10); fi->bodyfetch.octetcnt = strtoll(&args[idx] [delimpos + 1], NULL, 10); /* restore argument */ args[idx][delimpos] = '.'; args[idx][strlen(args[idx]) - 1] = '>'; } else { fi->bodyfetch.octetstart = -1; fi->bodyfetch.octetcnt = -1; } return idx + 1; /* DONE */ } if (ispeek) fi->bodyfetch.noseen = 1; /* check for a partspecifier */ /* first check if there is a partspecifier (numbers & dots) */ indigit = 0; for (j = 0; args[idx][j]; j++) { if (isdigit(args[idx][j])) { indigit = 1; continue; } else if (args[idx][j] == '.') { if (!indigit) { /* error, single dot specified */ invalidargs = 1; break; } indigit = 0; continue; } else break; /* other char found */ } if (invalidargs) return -2; /* error DONE */ if (j > 0) { if (indigit && args[idx][j]) return -2; /* error DONE */ /* partspecifier present, save it */ if (j >= IMAP_MAX_PARTSPEC_LEN) return -2; /* error DONE */ strncpy(fi->bodyfetch.partspec, args[idx], j); } fi->bodyfetch.partspec[j] = '\0'; shouldclose = 0; if (strcasecmp(&args[idx][j], "text") == 0) { fi->bodyfetch.itemtype = BFIT_TEXT; shouldclose = 1; } else if (strcasecmp(&args[idx][j], "header") == 0) { fi->bodyfetch.itemtype = BFIT_HEADER; shouldclose = 1; } else if (strcasecmp(&args[idx][j], "mime") == 0) { if (j == 0) return -2; /* error DONE */ fi->bodyfetch.itemtype = BFIT_MIME; shouldclose = 1; } else if (strcasecmp(&args[idx][j], "header.fields") == 0) fi->bodyfetch.itemtype = BFIT_HEADER_FIELDS; else if (strcasecmp (&args[idx][j], "header.fields.not") == 0) fi->bodyfetch.itemtype = BFIT_HEADER_FIELDS_NOT; else if (args[idx][j] == '\0') { fi->bodyfetch.itemtype = BFIT_TEXT_SILENT; shouldclose = 1; } else return -2; /* error DONE */ if (shouldclose) { if (strcmp(args[idx + 1], "]") != 0) return -2; /* error DONE */ } else { idx++; /* should be at '(' now */ if (strcmp(args[idx], "(") != 0) return -2; /* error DONE */ idx++; /* at first item of field list now, remember idx */ fi->bodyfetch.argstart = idx; /* walk on untill list terminates (and it does 'cause parentheses are matched) */ while (strcmp(args[idx], ")") != 0) idx++; fi->bodyfetch.argcnt = idx - fi->bodyfetch.argstart; if (fi->bodyfetch.argcnt == 0 || strcmp(args[idx + 1], "]") != 0) return -2; /* error DONE */ } idx++; /* points to ']' now */ /* check if octet start/cnt is specified */ if (args[idx + 1] && args[idx + 1][0] == '<') { idx++; /* advance */ /* check argument */ if (args[idx][strlen(args[idx]) - 1] != '>') return -2; /* error DONE */ delimpos = -1; for (j = 1; j < strlen(args[idx]) - 1; j++) { if (args[idx][j] == '.') { if (delimpos != -1) { invalidargs = 1; break; } delimpos = j; } else if (!isdigit(args[idx][j])) { invalidargs = 1; break; } } if (invalidargs || delimpos == -1 || delimpos == 1 || delimpos == (int) (strlen(args[idx]) - 2)) return -2; /* no delimiter found or at first/last pos OR invalid args DONE */ /* read the numbers */ args[idx][strlen(args[idx]) - 1] = '\0'; args[idx][delimpos] = '\0'; fi->bodyfetch.octetstart = strtoll(&args[idx][1], NULL, 10); fi->bodyfetch.octetcnt = strtoll(&args[idx][delimpos + 1], NULL, 10); /* restore argument */ args[idx][delimpos] = '.'; args[idx][strlen(args[idx]) - 1] = '>'; } else { fi->bodyfetch.octetstart = -1; fi->bodyfetch.octetcnt = -1; } /* ok all done for body item */ } } else if (strcasecmp(args[idx], "all") == 0) { fi->getFlags = 1; fi->getInternalDate = 1; fi->getSize = 1; fi->getEnvelope = 1; fi->msgparse_needed = 1; } else if (strcasecmp(args[idx], "fast") == 0) { fi->getFlags = 1; fi->getInternalDate = 1; fi->getSize = 1; /* fi->msgparse_needed = 1; *//* size will be in dbase after first calc */ } else if (strcasecmp(args[idx], "full") == 0) { fi->getFlags = 1; fi->getInternalDate = 1; fi->getSize = 1; fi->getEnvelope = 1; fi->getMIME_IMB = 1; fi->msgparse_needed = 1; } else if (strcasecmp(args[idx], "bodystructure") == 0) { fi->getMIME_IMB = 1; fi->msgparse_needed = 1; } else if (strcasecmp(args[idx], "envelope") == 0) { fi->getEnvelope = 1; fi->msgparse_needed = 1; } else if (strcmp(args[idx], ")") == 0) { /* only allowed if last arg here */ if (args[idx + 1]) return -2; /* DONE */ else return -1; } else return -2; /* DONE */ trace(TRACE_DEBUG, "next_fetch_item(): args[idx = %d] = %s (returning %d)\n", idx, args[idx], idx + 1); return idx + 1; } /* * give_chunks() * * splits up a string delimited by a given character into a NULL-terminated array of strings * does not perform any quotation-mark checks */ char **give_chunks(const char *str, char delimiter) { int cnt, i; char **array, *cpy, *tmp; cpy = (char *) dm_malloc(sizeof(char) * (strlen(str) + 1)); if (!cpy) { trace(TRACE_ERROR, "give_chunks(): out of memory\n"); return NULL; } strcpy(cpy, str); tmp = cpy; /* save start of cpy */ for (i = 0, cnt = 0; str[i]; i++) if (str[i] == delimiter) { cnt++; cpy[i] = '\0'; } cnt++; /* add last part */ /* alloc mem */ cnt++; /* for NULL termination */ array = (char **) dm_malloc(sizeof(char *) * cnt); if (!array) { trace(TRACE_ERROR, "give_chunks(): out of memory\n"); dm_free(cpy); return NULL; } for (i = 0, cnt = 0; str[i]; i++) { if (str[i] == delimiter) { array[cnt++] = cpy; /* save this address */ cpy = &tmp[i + 1]; /* let cpy point to next string */ } } /* copy last part */ array[cnt++] = cpy; array[cnt] = NULL; return array; } /* * free_chunks() * * frees memory allocated using give_chunks() */ void free_chunks(char **chunks) { if (!chunks) return; if (chunks[0]) dm_free(chunks[0]); /* the entire array will be freed now */ dm_free(chunks); /* free ptrs to strings */ } /* * check_state_and_args() * * checks if the user is in the right state & the numbers of arguments; * a state of -1 specifies any state * arguments can be grouped by means of parentheses * * returns 1 on succes, 0 on failure */ int check_state_and_args(const char *command, const char *tag, char **args, int nargs, int state, ClientInfo * ci) { int i; imap_userdata_t *ud = (imap_userdata_t *) ci->userData; /* check state */ if (state != -1) { if (ud->state != state) { if (! (state == IMAPCS_AUTHENTICATED && ud->state == IMAPCS_SELECTED)) { fprintf(ci->tx, "%s BAD %s command received in invalid state\r\n", tag, command); return 0; } } } /* check args */ for (i = 0; i < nargs; i++) { if (!args[i]) { /* error: need more args */ fprintf(ci->tx, "%s BAD missing argument%s to %s\r\n", tag, (nargs == 1) ? "" : "(s)", command); return 0; } } for (i = 0; args[i]; i++); if (i > nargs) { /* error: too many args */ fprintf(ci->tx, "%s BAD too many arguments to %s\r\n", tag, command); return 0; } /* succes */ return 1; } /* * build_args_array() * * builds an dimensional array of strings containing arguments based upon * a series of arguments passed as a single string. * normal/square parentheses have special meaning: * '(body [all header])' will result in the following array: * [0] = '(' * [1] = 'body' * [2] = '[' * [3] = 'all' * [4] = 'header' * [5] = ']' * [6] = ')' * * quoted strings are those enclosed by double quotation marks and returned as a single argument * WITHOUT the enclosing quotation marks * * parentheses loose their special meaning if inside (double)quotation marks; * data should be 'clarified' (see clarify_data() function below) * * The returned array will be NULL-terminated. * Will return NULL upon errors. */ /* local defines */ #define NORMPAR 1 #define SQUAREPAR 2 #define NOPAR 0 char *the_args[MAX_ARGS]; char **build_args_array(const char *s) { int nargs = 0, inquote = 0, i, quotestart = 0; int nnorm = 0, nsquare = 0, paridx = 0, slen = 0, argstart = 0; char parlist[IMAP_MAX_LINESIZE]; unsigned int argsize = 0; if (!s) return NULL; /* check for empty string */ if (!(*s)) { the_args[0] = NULL; return the_args; } /* find the arguments */ paridx = 0; parlist[paridx] = NOPAR; inquote = 0; slen = strlen(s); for (i = 0, nargs = 0; i < slen && nargs < MAX_ARGS - 1; i++) { /* check quotes */ if (s[i] == '"' && ((i > 0 && s[i - 1] != '\\') || i == 0)) { if (inquote) { argsize = i - quotestart -1; // avoid copying " /* quotation end, treat quoted string as argument */ if (! (the_args[nargs] = (char *) dm_malloc(sizeof(char) * argsize + 1))) { /* out of mem */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } trace(TRACE_ERROR, "IMAPD: Not enough memory while building up argument array."); return NULL; } memset((void *)the_args[nargs],'\0', argsize + 1); memcpy((void *) the_args[nargs], (void *) &s[quotestart+1], argsize); nargs++; inquote = 0; } else { inquote = 1; quotestart = i; } continue; } if (inquote) continue; /* check for (, ), [ or ] in string */ if (s[i] == '(' || s[i] == ')' || s[i] == '[' || s[i] == ']') { /* check parenthese structure */ if (s[i] == ')') { if (paridx < 0 || parlist[paridx] != NORMPAR) paridx = -1; else { nnorm--; paridx--; } } else if (s[i] == ']') { if (paridx < 0 || parlist[paridx] != SQUAREPAR) paridx = -1; else { paridx--; nsquare--; } } else if (s[i] == '(') { parlist[++paridx] = NORMPAR; nnorm++; } else { /* s[i] == '[' */ parlist[++paridx] = SQUAREPAR; nsquare++; } if (paridx < 0) { /* error in parenthesis structure */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } return NULL; } /* add this parenthesis to the arg list and continue */ if (! (the_args[nargs] = (char *) dm_malloc(sizeof(" ") + 1))) { /* out of mem */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } trace(TRACE_ERROR, "IMAPD: Not enough memory while building up argument array."); return NULL; } the_args[nargs][0] = s[i]; the_args[nargs][1] = '\0'; nargs++; continue; } if (s[i] == ' ') continue; /* at an argument start now, walk on until next delimiter * and save argument */ for (argstart = i; i < slen && !strchr(" []()", s[i]); i++) if (s[i] == '"') { if (s[i - 1] == '\\') continue; else break; } argsize = i - argstart; // no +1 here to avoid copying delimiter if (! (the_args[nargs] = (char *) dm_malloc(sizeof(char) * argsize + 1))) { /* out of mem */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } trace(TRACE_ERROR, "IMAPD: Not enough memory while building up argument array."); return NULL; } memset((void *) the_args[nargs], '\0', argsize + 1); memcpy((void *) the_args[nargs], (void *) &s[argstart], argsize); nargs++; i--; /* walked one too far */ } if (paridx != 0) { /* error in parenthesis structure */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } return NULL; } the_args[nargs] = NULL; /* terminate */ /* dump args (debug) */ for (i = 0; the_args[i]; i++) { trace(TRACE_DEBUG, "arg[%d]: '%s'\n", i, the_args[i]); } return the_args; } /* * as build_args_array(), but reads strings on cmd line specified by {##}\0 * (\r\n had been removed from string) */ char **build_args_array_ext(const char *originalString, clientinfo_t * ci) { int nargs = 0, inquote = 0, quotestart = 0; int nnorm = 0, nsquare = 0, paridx = 0, argstart = 0; unsigned int i; char parlist[IMAP_MAX_LINESIZE]; char s[IMAP_MAX_LINESIZE]; char *tmp, *lastchar; int quotedSize, cnt, dataidx; int argsize=0; /* this is done for the possible extra lines to be read from the client: * the line is read into currline; s will always point to the line currently * being processed */ strncpy(s, originalString, IMAP_MAX_LINESIZE); if (!s) return NULL; /* check for empty string */ if (!(*s)) { the_args[0] = NULL; return the_args; } /* find the arguments */ paridx = 0; parlist[paridx] = NOPAR; inquote = 0; for (i = 0, nargs = 0; s[i] && nargs < MAX_ARGS - 1; i++) { /* check quotes */ if (s[i] == '"' && ((i > 0 && s[i - 1] != '\\') || i == 0)) { if (inquote) { argsize = i - quotestart - 1; // avoid copying " /* quotation end, treat quoted string as argument */ if (! (the_args[nargs] = (char *) dm_malloc(sizeof(char) * argsize + 1))) { /* out of mem */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } trace(TRACE_ERROR, "IMAPD: Not enough memory while building up argument array."); return NULL; } memset((void *) the_args[nargs], '\0', argsize + 1); memcpy((void *) the_args[nargs], (void *) &s[quotestart + 1], argsize); nargs++; inquote = 0; } else { inquote = 1; quotestart = i; } continue; } if (inquote) continue; /* check for (, ), [ or ] in string */ if (s[i] == '(' || s[i] == ')' || s[i] == '[' || s[i] == ']') { /* check parenthese structure */ if (s[i] == ')') { if (paridx < 0 || parlist[paridx] != NORMPAR) paridx = -1; else { nnorm--; paridx--; } } else if (s[i] == ']') { if (paridx < 0 || parlist[paridx] != SQUAREPAR) paridx = -1; else { paridx--; nsquare--; } } else if (s[i] == '(') { parlist[++paridx] = NORMPAR; nnorm++; } else { /* s[i] == '[' */ parlist[++paridx] = SQUAREPAR; nsquare++; } if (paridx < 0) { /* error in parenthesis structure */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } return NULL; } /* add this parenthesis to the arg list and continue */ if (! (the_args[nargs] = (char *) dm_malloc(sizeof(" ") + 1))) { /* out of mem */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } trace(TRACE_ERROR, "IMAPD: Not enough memory while building up argument array."); return NULL; } the_args[nargs][0] = s[i]; the_args[nargs][1] = '\0'; nargs++; continue; } if (s[i] == ' ') continue; /* check for {number}\0 */ if (s[i] == '{') { quotedSize = strtoul(&s[i + 1], &lastchar, 10); /* only continue if the number is followed by '}\0' */ trace(TRACE_DEBUG, "%s,%s: last char = %c", __FILE__, __func__, *lastchar); if ((*lastchar == '+' && *(lastchar + 1) == '}' && *(lastchar + 2) == '\0') || (*lastchar == '}' && *(lastchar + 1) == '\0')) { argsize = quotedSize + 1; /* allocate space for this argument (could be a message when used with APPEND) */ if (! (the_args[nargs] = (char *) dm_malloc(sizeof(char) * argsize + 1))) { /* out of mem */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } trace(TRACE_ERROR, "build_args_array_ext(): out of memory allocating [%u] bytes for extra string", argsize); return NULL; } memset((void *)the_args[nargs],'\0', argsize + 1); fprintf(ci->tx, "+ OK gimme that string\r\n"); alarm(ci->timeout); /* dont wait forever */ for (cnt = 0, dataidx = 0; cnt < quotedSize; cnt++) { the_args[nargs][dataidx] = fgetc(ci->rx); if (the_args[nargs][dataidx] != '\r') dataidx++; /* only store if it is not \r */ } alarm(0); nargs++; if (!ci->rx || !ci->tx || ferror(ci->rx) || ferror(ci->tx)) { /* timeout occurred or connection has gone away */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } trace(TRACE_ERROR, "build_args_array_ext(): timeout occurred"); return NULL; } /* now read the rest of this line */ alarm(ci->timeout); fgets(s, IMAP_MAX_LINESIZE, ci->rx); alarm(0); if (!ci->rx || !ci->tx || ferror(ci->rx) || ferror(ci->tx)) { /* timeout occurred */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } trace(TRACE_ERROR, "build_args_array_ext(): timeout occurred"); return NULL; } /* remove trailing \r\n */ tmp = &s[strlen(s)]; tmp--; /* go before trailing \0; watch this with empty strings! */ while (tmp >= s && (*tmp == '\r' || *tmp == '\n')) { *tmp = '\0'; tmp--; } trace(TRACE_DEBUG, "build_args_array_ext(): got extra line [%s]", s); /* start over! */ i = 0; continue; } } /* at an argument start now, walk on until next delimiter * and save argument */ for (argstart = i; i < strlen(s) && !strchr(" []()", s[i]); i++) if (s[i] == '"') { if (s[i - 1] == '\\') continue; else break; } argsize = i - argstart; // no +1 here to avoid copying delimiter. if (! (the_args[nargs] = (char *) dm_malloc(sizeof(char) * argsize + 1))) { /* out of mem */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } trace(TRACE_ERROR, "IMAPD: Not enough memory while building up argument array."); return NULL; } memset((void *) the_args[nargs], '\0', argsize + 1); memcpy((void *) the_args[nargs], (void *) &s[argstart], argsize); nargs++; i--; /* walked one too far */ } if (paridx != 0) { /* error in parenthesis structure */ while (--nargs >= 0) { dm_free(the_args[nargs]); the_args[nargs] = NULL; } return NULL; } the_args[nargs] = NULL; /* terminate */ /* dump args (debug) */ for (i = 0; the_args[i]; i++) { trace(TRACE_DEBUG, "arg[%d]: '%s'\n", i, the_args[i]); } return the_args; } #undef NOPAR #undef NORMPAR #undef RIGHTPAR /* * clarify_data() * * replaces all multiple spaces by a single one except for quoted spaces; * removes leading and trailing spaces and a single trailing newline (if present) */ void clarify_data(char *str) { int startidx, inquote, endidx; unsigned int i; /* remove leading spaces */ for (i = 0; str[i] == ' '; i++); memmove(str, &str[i], sizeof(char) * (strlen(&str[i]) + 1)); /* add one for \0 */ /* remove CR/LF */ endidx = strlen(str) - 1; if (endidx >= 0 && (str[endidx] == '\n' || str[endidx] == '\r')) endidx--; if (endidx >= 0 && (str[endidx] == '\n' || str[endidx] == '\r')) endidx--; if (endidx == 0) { /* only 1 char left and it is not a space */ str[1] = '\0'; return; } /* remove trailing spaces */ for (i = endidx; i > 0 && str[i] == ' '; i--); if (i == 0) { /* empty string remains */ *str = '\0'; return; } str[i + 1] = '\0'; /* scan for multiple spaces */ inquote = 0; for (i = 0; i < strlen(str); i++) { if (str[i] == '"') { if ((i > 0 && str[i - 1] != '\\') || i == 0) { /* toggle in-quote flag */ inquote ^= 1; } } if (str[i] == ' ' && !inquote) { for (startidx = i; str[i] == ' '; i++); if (i - startidx > 1) { /* multiple non-quoted spaces found --> remove 'm */ memmove(&str[startidx + 1], &str[i], sizeof(char) * (strlen(&str[i]) + 1)); /* update i */ i = startidx + 1; } } } } /* * is_textplain() * * checks if content-type is text/plain */ int is_textplain(struct list *hdr) { struct mime_record *mr; int i, len; if (!hdr) return 0; mime_findfield("content-type", hdr, &mr); if (!mr) return 0; len = strlen(mr->value); for (i = 0; len - i >= (int) sizeof("text/plain"); i++) if (strncasecmp (&mr->value[i], "text/plain", sizeof("text/plain") - 1) == 0) return 1; return 0; } /* * convert a mySQL date (yyyy-mm-dd hh:mm:ss) to a valid IMAP internal date: * [Mon, 30 May 2005 10:01:55 +0100] with mon characters (i.e. 'Apr' for april) * return value is valid until next function call. * NOTE: if date is not valid, IMAP_STANDARD_DATE is returned */ char *date_sql2imap(const char *sqldate) { struct tm tm_sql_date; struct tm *tm_imap_date; time_t ltime; char *last; memset(&tm_sql_date, 0, sizeof(struct tm)); last = strptime(sqldate,"%Y-%m-%d %T", &tm_sql_date); if ( (last == NULL) || (*last != '\0') ) { strcpy(_imapdate, IMAP_STANDARD_DATE); return _imapdate; } ltime = mktime (&tm_sql_date); tm_imap_date = localtime(<ime); strftime(_imapdate, sizeof(_imapdate), "%a, %d %b %Y %H:%M:%S %z", tm_imap_date); return _imapdate; } /* * convert TO a mySQL date (yyyy-mm-dd) FROM a valid IMAP internal date: * dd-mon-yyyy with mon characters (i.e. 'Apr' for april) * OR * d-mon-yyyy * return value is valid until next function call. */ char *date_imap2sql(const char *imapdate) { struct tm tm; char *last_char; memset(&tm, 0, sizeof(struct tm)); last_char = strptime(imapdate, "%d-%b-%Y", &tm); if (last_char == NULL || *last_char != '\0') { trace(TRACE_DEBUG, "%s,%s: error parsing IMAP date %s", __FILE__, __func__, imapdate); return NULL; } (void) strftime(_sqldate, SQL_INTERNALDATE_LEN, "%Y-%m-%d 00:00:00", &tm); return _sqldate; } /* * */ size_t stridx(const char *s, char ch) { size_t i; for (i = 0; s[i] && s[i] != ch; i++); return i; } /* * checkchars() * * performs a check to see if the read data is valid * returns 0 if invalid, 1 otherwise */ int checkchars(const char *s) { int i; for (i = 0; s[i]; i++) { if (!strchr(AcceptedChars, s[i])) { /* wrong char found */ return 0; } } return 1; } /* * checktag() * * performs a check to see if the read data is valid * returns 0 if invalid, 1 otherwise */ int checktag(const char *s) { int i; for (i = 0; s[i]; i++) { if (!strchr(AcceptedTagChars, s[i])) { /* wrong char found */ return 0; } } return 1; } /* * checkmailboxname() * * performs a check to see if the mailboxname is valid * returns 0 if invalid, 1 otherwise */ int checkmailboxname(const char *s) { int i; if (strlen(s) == 0) return 0; /* empty name is not valid */ if (strlen(s) >= IMAP_MAX_MAILBOX_NAMELEN) return 0; /* a too large string is not valid */ /* check for invalid characters */ for (i = 0; s[i]; i++) { if (!strchr(AcceptedMailboxnameChars, s[i])) { /* dirty hack to allow namespaces to function */ if (i == 0 && s[0] == '#') continue; /* wrong char found */ return 0; } } /* check for double '/' */ for (i = 1; s[i]; i++) { if (s[i] == '/' && s[i - 1] == '/') return 0; } /* check if the name consists of a single '/' */ if (strlen(s) == 1 && s[0] == '/') return 0; return 1; } /* * check_date() * * checks a date for IMAP-date validity: * dd-MMM-yyyy * 01234567890 * month three-letter specifier */ int check_date(const char *date) { char sub[4]; int days, i, j; if (strlen(date) != strlen("01-Jan-1970") && strlen(date) != strlen("1-Jan-1970")) return 0; j = (strlen(date) == strlen("1-Jan-1970")) ? 1 : 0; if (date[2 - j] != '-' || date[6 - j] != '-') return 0; days = strtoul(date, NULL, 10); strncpy(sub, &date[3 - j], 3); sub[3] = 0; for (i = 0; i < 12; i++) { if (strcasecmp(month_desc[i], sub) == 0) break; } if (i >= 12 || days > month_len[i]) return 0; for (i = 7; i < 11; i++) if (!isdigit(date[i - j])) return 0; return 1; } /* * check_msg_set() * * checks if s represents a valid message set */ int check_msg_set(const char *s) { int i, indigit; if (!s || !isdigit(s[0])) return 0; for (i = 1, indigit = 1; s[i]; i++) { if (isdigit(s[i])) indigit = 1; else if (s[i] == ',') { if (!indigit && s[i - 1] != '*') return 0; indigit = 0; } else if (s[i] == ':') { if (!indigit) return 0; indigit = 0; } else if (s[i] == '*') { if (s[i - 1] != ':') return 0; } } return 1; } /* * base64encode() * * encodes a string using base64 encoding */ void base64encode(char *in, char *out) { for (; strlen(in) >= 3; in += 3) { *out++ = base64encodestring[(in[0] & 0xFC) >> 2U]; *out++ = base64encodestring[((in[0] & 0x03) << 4U) | ((in[1] & 0xF0) >> 4U)]; *out++ = base64encodestring[((in[1] & 0x0F) << 2U) | ((in[2] & 0xC0) >> 6U)]; *out++ = base64encodestring[(in[2] & 0x3F)]; } if (strlen(in) == 2) { /* 16 bits left to encode */ *out++ = base64encodestring[(in[0] & 0xFC) >> 2U]; *out++ = base64encodestring[((in[0] & 0x03) << 4U) | ((in[1] & 0xF0) >> 4U)]; *out++ = base64encodestring[((in[1] & 0x0F) << 2U)]; *out++ = '='; return; } if (strlen(in) == 1) { /* 8 bits left to encode */ *out++ = base64encodestring[(in[0] & 0xFC) >> 2U]; *out++ = base64encodestring[((in[0] & 0x03) << 4U)]; *out++ = '='; *out++ = '='; return; } } /* * base64decode() * * decodes a base64 encoded string */ void base64decode(char *in, char *out) { for (; strlen(in) >= 4; in += 4) { *out++ = (stridx(base64encodestring, in[0]) << 2U) | ((stridx(base64encodestring, in[1]) & 0x30) >> 4U); *out++ = ((stridx(base64encodestring, in[1]) & 0x0F) << 4U) | ((stridx(base64encodestring, in[2]) & 0x3C) >> 2U); *out++ = ((stridx(base64encodestring, in[2]) & 0x03) << 6U) | (stridx(base64encodestring, in[3]) & 0x3F); } *out = 0; } /* * binary_search() * * performs a binary search on array to find key * array should be ascending in values * * returns -1 if not found. key_idx will hold key if found */ int binary_search(const u64_t * array, unsigned arraysize, u64_t key, unsigned int *key_idx) { unsigned low, high, mid = 1; assert(key_idx != NULL); *key_idx = 0; if (arraysize == 0) return -1; low = 0; high = arraysize - 1; while (low <= high) { mid = (high + low) / (unsigned) 2; if (array[mid] < key) low = mid + 1; else if (array[mid] > key) { if (mid > 0) high = mid - 1; else break; } else { *key_idx = mid; return 1; } } return -1; /* not found */ } /* * sends a string to outstream, escaping the following characters: * " --> \" * \ --> \\ * * double quotes are placed at the beginning and end of the string. * * returns the number of bytes outputted. */ int quoted_string_out(FILE * outstream, const char *s) { int i, cnt; char *t, *l = NULL; char first, last, penult = '\\'; l = strdup(s); t = l; /* strip off dquote */ first = s[0]; last = s[strlen(s)-1]; if (strlen(s) > 2) penult = s[strlen(s)-2]; if ((first == '"') && (last == '"') && (penult != '\\')) { l[strlen(l)-1] = '\0'; l++; } // check wheter we must use literal string for (i = 0; l[i]; i++) { if (!(l[i] & 0xe0) || (l[i] & 0x80) || (l[i] == '"') || (l[i] == '\\')) { cnt = fprintf(outstream, "{"); cnt += fprintf(outstream, "%lu", (unsigned long) strlen(l)); cnt += fprintf(outstream, "}\r\n"); cnt += fprintf(outstream, "%s", l); return cnt; } } cnt = fprintf(outstream, "\""); cnt += fprintf(outstream, "%s", l); cnt += fprintf(outstream, "\""); dm_free(t); return cnt; } /* * send_data() * * sends cnt bytes from a MEM structure to a FILE stream * uses a simple buffering system */ void send_data(FILE * to, MEM * from, int cnt) { char buf[SEND_BUF_SIZE]; for (cnt -= SEND_BUF_SIZE; cnt >= 0; cnt -= SEND_BUF_SIZE) { mread(buf, SEND_BUF_SIZE, from); fwrite(buf, SEND_BUF_SIZE, 1, to); } if (cnt < 0) { mread(buf, cnt + SEND_BUF_SIZE, from); fwrite(buf, cnt + SEND_BUF_SIZE, 1, to); } fflush(to); } /* * build_imap_search() * * builds a linked list of search items from a set of IMAP search keys * sl should be initialized; new search items are simply added to the list * * returns -1 on syntax error, -2 on memory error; 0 on success, 1 if ')' has been encountered */ int build_imap_search(char **search_keys, struct list *sl, int *idx, int sorted) { search_key_t key; int result; if (!search_keys || !search_keys[*idx]) return 0; memset(&key, 0, sizeof(key)); /* coming from _ic_sort */ if(sorted && (strcasecmp(search_keys[*idx], "arrival") == 0)) { key.type = IST_SORT; strncpy(key.search, "order by pms.internal_date", MAX_SEARCH_LEN); (*idx)++; } else if(sorted && (strcasecmp(search_keys[*idx], "from") == 0)) { key.type = IST_SORTHDR; strncpy(key.hdrfld, "from", MIME_FIELD_MAX); (*idx)++; } else if(sorted && (strcasecmp(search_keys[*idx], "subject") == 0)) { key.type = IST_SORTHDR; strncpy(key.hdrfld, "subject", MIME_FIELD_MAX); (*idx)++; } else if(sorted && (strcasecmp(search_keys[*idx], "cc") == 0)) { key.type = IST_SORTHDR; strncpy(key.hdrfld, "cc", MIME_FIELD_MAX); (*idx)++; } else if(sorted && (strcasecmp(search_keys[*idx], "to") == 0)) { key.type = IST_SORTHDR; strncpy(key.hdrfld, "to", MIME_FIELD_MAX); (*idx)++; } else if(sorted && (strcasecmp(search_keys[*idx], "reverse") == 0)) { /* TODO */ (*idx)++; } else if(sorted && (strcasecmp(search_keys[*idx], "size") == 0)) { /* TODO */ (*idx)++; } else if(sorted && (strcasecmp(search_keys[*idx], "iso-8859-1") == 0)) { /* TODO */ (*idx)++; // } else if(sorted && (strcasecmp(search_keys[*idx], "us-ascii") == 0)) { // /* TODO */ // (*idx)++; } else if(sorted && (strcasecmp(search_keys[*idx], "date") == 0)) { /* TODO */ (*idx)++; } else if(sorted && (strcasecmp(search_keys[*idx], "all") == 0)) { /* TODO */ (*idx)++; } else if (strcasecmp(search_keys[*idx], "all") == 0) { key.type = IST_SET; strcpy(key.search, "1:*"); (*idx)++; } else if (strcasecmp(search_keys[*idx], "uid") == 0) { key.type = IST_SET_UID; if (!search_keys[*idx + 1]) return -1; (*idx)++; strncpy(key.search, search_keys[(*idx)], MAX_SEARCH_LEN); if (!check_msg_set(key.search)) return -1; (*idx)++; } /* * FLAG search keys */ else if (strcasecmp(search_keys[*idx], "answered") == 0) { key.type = IST_FLAG; strncpy(key.search, "answered_flag=1", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "deleted") == 0) { key.type = IST_FLAG; strncpy(key.search, "deleted_flag=1", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "flagged") == 0) { key.type = IST_FLAG; strncpy(key.search, "flagged_flag=1", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "recent") == 0) { key.type = IST_FLAG; strncpy(key.search, "recent_flag=1", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "seen") == 0) { key.type = IST_FLAG; strncpy(key.search, "seen_flag=1", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "keyword") == 0) { /* no results from this one */ if (!search_keys[(*idx) + 1]) /* there should follow an argument */ return -1; (*idx)++; key.type = IST_SET; strcpy(key.search, "0"); (*idx)++; } else if (strcasecmp(search_keys[*idx], "draft") == 0) { key.type = IST_FLAG; strncpy(key.search, "draft_flag=1", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "new") == 0) { key.type = IST_FLAG; strncpy(key.search, "(seen_flag=0 AND recent_flag=1)", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "old") == 0) { key.type = IST_FLAG; strncpy(key.search, "recent_flag=0", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "unanswered") == 0) { key.type = IST_FLAG; strncpy(key.search, "answered_flag=0", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "undeleted") == 0) { key.type = IST_FLAG; strncpy(key.search, "deleted_flag=0", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "unflagged") == 0) { key.type = IST_FLAG; strncpy(key.search, "flagged_flag=0", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "unseen") == 0) { key.type = IST_FLAG; strncpy(key.search, "seen_flag=0", MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "unkeyword") == 0) { /* matches every msg */ if (!search_keys[(*idx) + 1]) return -1; (*idx)++; key.type = IST_SET; strcpy(key.search, "1:*"); (*idx)++; } else if (strcasecmp(search_keys[*idx], "undraft") == 0) { key.type = IST_FLAG; strncpy(key.search, "draft_flag=0", MAX_SEARCH_LEN); (*idx)++; } /* * HEADER search keys */ else if (strcasecmp(search_keys[*idx], "bcc") == 0) { key.type = IST_HDR; strncpy(key.hdrfld, "bcc", MIME_FIELD_MAX); if (!search_keys[(*idx) + 1]) return -1; (*idx)++; strncpy(key.search, search_keys[(*idx)], MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "cc") == 0) { key.type = IST_HDR; strncpy(key.hdrfld, "cc", MIME_FIELD_MAX); if (!search_keys[(*idx) + 1]) return -1; (*idx)++; strncpy(key.search, search_keys[(*idx)], MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "from") == 0) { key.type = IST_HDR; strncpy(key.hdrfld, "from", MIME_FIELD_MAX); if (!search_keys[(*idx) + 1]) return -1; (*idx)++; strncpy(key.search, search_keys[(*idx)], MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "to") == 0) { key.type = IST_HDR; strncpy(key.hdrfld, "to", MIME_FIELD_MAX); if (!search_keys[(*idx) + 1]) return -1; (*idx)++; strncpy(key.search, search_keys[(*idx)], MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "subject") == 0) { key.type = IST_HDR; strncpy(key.hdrfld, "subject", MIME_FIELD_MAX); if (!search_keys[(*idx) + 1]) return -1; (*idx)++; strncpy(key.search, search_keys[(*idx)], MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "header") == 0) { key.type = IST_HDR; if (!search_keys[(*idx) + 1] || !search_keys[(*idx) + 2]) return -1; strncpy(key.hdrfld, search_keys[(*idx) + 1], MIME_FIELD_MAX); strncpy(key.search, search_keys[(*idx) + 2], MAX_SEARCH_LEN); (*idx) += 3; } else if (strcasecmp(search_keys[*idx], "sentbefore") == 0) { key.type = IST_HDRDATE_BEFORE; strncpy(key.hdrfld, "date", MIME_FIELD_MAX); if (!search_keys[(*idx) + 1]) return -1; (*idx)++; strncpy(key.search, search_keys[(*idx)], MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "senton") == 0) { key.type = IST_HDRDATE_ON; strncpy(key.hdrfld, "date", MIME_FIELD_MAX); if (!search_keys[(*idx) + 1]) return -1; (*idx)++; strncpy(key.search, search_keys[(*idx)], MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "sentsince") == 0) { key.type = IST_HDRDATE_SINCE; strncpy(key.hdrfld, "date", MIME_FIELD_MAX); if (!search_keys[(*idx) + 1]) return -1; (*idx)++; strncpy(key.search, search_keys[(*idx)], MAX_SEARCH_LEN); (*idx)++; } /* * INTERNALDATE keys */ else if (strcasecmp(search_keys[*idx], "before") == 0) { key.type = IST_IDATE; if (!search_keys[(*idx) + 1]) return -1; (*idx)++; if (!check_date(search_keys[*idx])) return -1; strncpy(key.search, "internal_date<'", MAX_SEARCH_LEN); strncat(key.search, date_imap2sql(search_keys[*idx]), MAX_SEARCH_LEN - sizeof("internal_date<''")); strcat(key.search, "'"); (*idx)++; } else if (strcasecmp(search_keys[*idx], "on") == 0) { key.type = IST_IDATE; if (!search_keys[(*idx) + 1]) return -1; (*idx)++; if (!check_date(search_keys[*idx])) return -1; strncpy(key.search, "internal_date LIKE '", MAX_SEARCH_LEN); strncat(key.search, date_imap2sql(search_keys[*idx]), MAX_SEARCH_LEN - sizeof("internal_date LIKE 'x'")); strcat(key.search, "%'"); (*idx)++; } else if (strcasecmp(search_keys[*idx], "since") == 0) { key.type = IST_IDATE; if (!search_keys[(*idx) + 1]) return -1; (*idx)++; if (!check_date(search_keys[*idx])) return -1; strncpy(key.search, "internal_date>'", MAX_SEARCH_LEN); strncat(key.search, date_imap2sql(search_keys[*idx]), MAX_SEARCH_LEN - sizeof("internal_date>''")); strcat(key.search, "'"); (*idx)++; } /* * DATA-keys */ else if (strcasecmp(search_keys[*idx], "body") == 0) { key.type = IST_DATA_BODY; if (!search_keys[(*idx) + 1]) return -1; (*idx)++; strncpy(key.search, search_keys[(*idx)], MAX_SEARCH_LEN); (*idx)++; } else if (strcasecmp(search_keys[*idx], "text") == 0) { key.type = IST_DATA_TEXT; if (!search_keys[(*idx) + 1]) return -1; (*idx)++; strncpy(key.search, search_keys[(*idx)], MAX_SEARCH_LEN); (*idx)++; } /* * SIZE keys */ else if (strcasecmp(search_keys[*idx], "larger") == 0) { key.type = IST_SIZE_LARGER; if (!search_keys[(*idx) + 1]) return -1; (*idx)++; key.size = strtoull(search_keys[(*idx)], NULL, 10); (*idx)++; } else if (strcasecmp(search_keys[*idx], "smaller") == 0) { key.type = IST_SIZE_SMALLER; if (!search_keys[(*idx) + 1]) return -1; (*idx)++; key.size = strtoull(search_keys[(*idx)], NULL, 10); (*idx)++; } /* * NOT, OR, () */ else if (strcasecmp(search_keys[*idx], "not") == 0) { key.type = IST_SUBSEARCH_NOT; (*idx)++; if ((result = build_imap_search(search_keys, &key.sub_search, idx, sorted )) < 0) { list_freelist(&key.sub_search.start); return result; } /* a NOT should be unary */ if (key.sub_search.total_nodes != 1) { free_searchlist(&key.sub_search); return -1; } } else if (strcasecmp(search_keys[*idx], "or") == 0) { key.type = IST_SUBSEARCH_OR; (*idx)++; if ((result = build_imap_search(search_keys, &key.sub_search, idx, sorted)) < 0) { list_freelist(&key.sub_search.start); return result; } if ((result = build_imap_search(search_keys, &key.sub_search, idx, sorted )) < 0) { list_freelist(&key.sub_search.start); return result; } /* an OR should be binary */ if (key.sub_search.total_nodes != 2) { free_searchlist(&key.sub_search); return -1; } } else if (strcasecmp(search_keys[*idx], "(") == 0) { key.type = IST_SUBSEARCH_AND; (*idx)++; while ((result = build_imap_search(search_keys, &key.sub_search, idx, sorted)) == 0 && search_keys[*idx]); if (result < 0) { /* error */ list_freelist(&key.sub_search.start); return result; } if (result == 0) { /* no ')' encountered (should not happen, parentheses are matched at the command line) */ free_searchlist(&key.sub_search); return -1; } } else if (strcasecmp(search_keys[*idx], ")") == 0) { (*idx)++; return 1; } else if (check_msg_set(search_keys[*idx])) { key.type = IST_SET; strncpy(key.search, search_keys[*idx], MAX_SEARCH_LEN); (*idx)++; } else { trace(TRACE_DEBUG,"%s,%s: unknown search key [%s]", __FILE__, __func__, search_keys[*idx]); return -1; } if (!list_nodeadd(sl, &key, sizeof(key))) return -2; return 0; } /* * perform_imap_search() * * returns 0 on succes, -1 on dbase error, -2 on memory error, 1 if result set is too small * (new mail has been added to mailbox while searching, mailbox data out of sync) */ int perform_imap_search(unsigned int *rset, int setlen, search_key_t * sk, mailbox_t * mb, int sorted, int condition) { search_key_t *subsk; struct element *el; int result, i; unsigned int *newset = NULL; int subtype = IST_SUBSEARCH_OR; if (!rset) { trace(TRACE_ERROR, "%s,%s: can't perform search on empty set", __FILE__, __func__); return -2; /* stupidity */ } if (!sk) { trace(TRACE_ERROR, "%s,%s: can't perform search for empty searchkey", __FILE__, __func__); return -2; /* no search */ } newset = (unsigned int *) dm_malloc(sizeof(unsigned int) * setlen); if (!newset) return -2; memset(newset,'\0', sizeof(newset)); trace(TRACE_DEBUG, "%s,%s: search key type [%d], condition [%d]", __FILE__, __func__, sk->type, condition); switch (sk->type) { case IST_SET: build_set(rset, setlen, sk->search); break; case IST_SET_UID: build_uid_set(rset, setlen, sk->search, mb); break; case IST_SORT: result = db_search(rset, setlen, sk->search, mb, sk->type); dm_free(newset); return 0; break; case IST_SORTHDR: result = db_sort_parsed(rset, setlen, sk, mb, condition); dm_free(newset); return 0; break; case IST_FLAG: result = db_search(rset, setlen, sk->search, mb, sk->type); if (result != 0) { dm_free(newset); return result; } break; case IST_HDR: case IST_HDRDATE_BEFORE: case IST_HDRDATE_ON: case IST_HDRDATE_SINCE: case IST_DATA_BODY: case IST_DATA_TEXT: case IST_SIZE_LARGER: case IST_SIZE_SMALLER: /* these all have in common that a message should be parsed before matching is possible */ result = db_search_parsed(rset, setlen, sk, mb, condition); break; case IST_IDATE: result = db_search(rset, setlen, sk->search, mb, sk->type); if (result != 0) { dm_free(newset); return result; } break; case IST_SUBSEARCH_NOT: case IST_SUBSEARCH_AND: subtype = IST_SUBSEARCH_AND; case IST_SUBSEARCH_OR: el = list_getstart(&sk->sub_search); while (el) { subsk = (search_key_t *) el->data; if (subsk->type == IST_SUBSEARCH_OR) memset(newset, 0, sizeof(int) * setlen); else for (i = 0; i < setlen; i++) newset[i] = rset[i]; if ((result = perform_imap_search(newset, setlen, subsk, mb, sorted, sk->type))) { dm_free(newset); return result; } if (! sorted) combine_sets(rset, newset, setlen, subtype); else { for (i=0; inextnode; } if (sk->type == IST_SUBSEARCH_NOT) invert_set(rset, setlen); break; default: dm_free(newset); return -2; /* ??? */ } dm_free(newset); return 0; } /* * frees the search-list sl * */ void free_searchlist(struct list *sl) { search_key_t *sk; struct element *el; if (!sl) return; el = list_getstart(sl); while (el) { sk = (search_key_t *) el->data; free_searchlist(&sk->sub_search); list_freelist(&sk->sub_search.start); el = el->nextnode; } list_freelist(&sl->start); return; } void invert_set(unsigned int *set, int setlen) { int i; if (!set) return; for (i = 0; i < setlen; i++) set[i] = !set[i]; } void combine_sets(unsigned int *dest, unsigned int *sec, int setlen, int type) { int i; if (!dest || !sec) return; if (type == IST_SUBSEARCH_AND) { for (i = 0; i < setlen; i++) dest[i] = (sec[i] && dest[i]); } else if (type == IST_SUBSEARCH_OR) { for (i = 0; i < setlen; i++) dest[i] = (sec[i] || dest[i]); } } /* * build_set() * * builds a msn-set from a IMAP message set spec. the IMAP set is supposed to be correct, * no checks are performed. */ void build_set(unsigned int *set, unsigned int setlen, char *cset) { unsigned int i; u64_t num, num2; char *sep = NULL; if (!set) return; memset(set, 0, setlen * sizeof(int)); if (!cset) return; do { num = strtoull(cset, &sep, 10); if (num <= setlen && num > 0) { if (!*sep) set[num - 1] = 1; else if (*sep == ',') { set[num - 1] = 1; cset = sep + 1; } else { /* sep == ':' here */ sep++; if (*sep == '*') { for (i = num - 1; i < setlen; i++) set[i] = 1; cset = sep + 1; } else { cset = sep; num2 = strtoull(cset, &sep, 10); if (num2 > setlen) num2 = setlen; if (num2 > 0) { /* NOTE: here: num2 > 0, num > 0 */ if (num2 < num) { /* swap! */ i = num; num = num2; num2 = i; } for (i = num - 1; i < num2; i++) set[i] = 1; } if (*sep) cset = sep + 1; } } } else { /* invalid num, skip it */ if (*sep) { cset = sep + 1; sep++; } } } while (sep && *sep && cset && *cset); } /* * build_uid_set() * * as build_set() but takes uid's instead of MSN's */ void build_uid_set(unsigned int *set, unsigned int setlen, char *cset, mailbox_t * mb) { unsigned int i, msn, msn2; int result; int num2found = 0; u64_t num, num2; char *sep = NULL; if (!set) return; memset(set, 0, setlen * sizeof(int)); if (!cset || setlen == 0) return; do { num = strtoull(cset, &sep, 10); result = binary_search(mb->seq_list, mb->exists, num, &msn); if (result < 0 && num < mb->seq_list[mb->exists - 1]) { /* ok this num is not a UID, but if a range is specified (i.e. 1:*) * it is valid -> check *sep */ if (*sep == ':') { result = 1; /*MR as we have a valid number here, let's tell the code below that it should run*/ for (msn = 0; mb->seq_list[msn] < num; msn++); if (msn >= mb->exists) msn = mb->exists - 1; } } if (result >= 0) { if (!*sep) set[msn] = 1; else if (*sep == ',') { set[msn] = 1; cset = sep + 1; } else { /* sep == ':' here */ sep++; if (*sep == '*') { for (i = msn; i < setlen; i++) set[i] = 1; cset = sep + 1; } else { /* fetch second number */ cset = sep; num2 = strtoull(cset, &sep, 10); result = binary_search(mb->seq_list, mb->exists, num2, &msn2); if (result < 0) { /* in a range: (like 1:1000) so this number doesnt need to exist; * find the closest match below this UID value */ if (mb->exists == 0) num2found = 0; else { for (msn2 = mb->exists - 1;; msn2--) { if (msn2 == 0 && mb-> seq_list [msn2] > num2) { num2found = 0; break; } else if (mb-> seq_list [msn2] <= num2) { /* found! */ num2found = 1; break; } } } } else num2found = 1; if (num2found == 1) { if (msn2 < msn) { /* swap! */ i = msn; msn = msn2; msn2 = i; } for (i = msn; i <= msn2; i++) set[i] = 1; } if (*sep) cset = sep + 1; } } } else { /* invalid num, skip it */ if (*sep) { cset = sep + 1; sep++; } } } while (sep && *sep && cset && *cset); } void dumpsearch(search_key_t * sk, int level) { char *spaces = (char *) dm_malloc(level * 3 + 1); struct element *el; search_key_t *subsk; if (!spaces) return; memset(spaces, ' ', level * 3); spaces[level * 3] = 0; if (!sk) { trace(TRACE_DEBUG, "%s(null)\n", spaces); dm_free(spaces); return; } switch (sk->type) { case IST_SUBSEARCH_NOT: trace(TRACE_DEBUG, "%sNOT\n", spaces); el = list_getstart(&sk->sub_search); if (el) subsk = (search_key_t *) el->data; else subsk = NULL; dumpsearch(subsk, level + 1); break; case IST_SUBSEARCH_AND: trace(TRACE_DEBUG, "%sAND\n", spaces); el = list_getstart(&sk->sub_search); while (el) { subsk = (search_key_t *) el->data; dumpsearch(subsk, level + 1); el = el->nextnode; } break; case IST_SUBSEARCH_OR: trace(TRACE_DEBUG, "%sOR\n", spaces); el = list_getstart(&sk->sub_search); while (el) { subsk = (search_key_t *) el->data; dumpsearch(subsk, level + 1); el = el->nextnode; } break; default: trace(TRACE_DEBUG, "%s[type %d] \"%s\"\n", spaces, sk->type, sk->search); } dm_free(spaces); return; } /* * closes the msg cache */ void close_cache() { if (cached_msg.msg_parsed) db_free_msg(&cached_msg.msg); cached_msg.num = -1; cached_msg.msg_parsed = 0; memset(&cached_msg.msg, 0, sizeof(cached_msg.msg)); mclose(&cached_msg.memdump); mclose(&cached_msg.tmpdump); } /* * init cache */ int init_cache() { cached_msg.num = -1; cached_msg.msg_parsed = 0; memset(&cached_msg.msg, 0, sizeof(cached_msg.msg)); cached_msg.memdump = mopen(); if (!cached_msg.memdump) return -1; cached_msg.tmpdump = mopen(); if (!cached_msg.tmpdump) { mclose(&cached_msg.memdump); return -1; } cached_msg.file_dumped = 0; cached_msg.dumpsize = 0; return 0; } /* unwrap strings */ int mime_unwrap(char *to, const char *from) { while (*from) { if (((*from == '\n') || (*from == '\r')) && isspace(*(from+1))) { from+=2; continue; } *to++=*from++; } *to='\0'; return 0; } /* * sort_search() * * reorder searchlist by using search cost as the sort key * */ int sort_search(struct list *searchlist) { struct element *el; search_key_t *left = NULL, *right = NULL, *tmp=NULL; if (!searchlist) return 0; el = list_getstart(searchlist); while (el != NULL) { if (! el->nextnode) break; left = el->data; right = (el->nextnode)->data; /* recurse into sub_search if necessary */ switch(left->type) { case IST_SUBSEARCH_AND: case IST_SUBSEARCH_NOT: case IST_SUBSEARCH_OR: sort_search(&left->sub_search); break; ;; } /* switch elements to sorted order */ if (strcmp((char *)search_cost[left->type],(char *)search_cost[right->type]) > 0) { tmp = el->data; el->data = el->nextnode->data; el->nextnode->data = tmp; /* when in doubt, use brute force: starting over */ el = list_getstart(searchlist); continue; } el = el->nextnode; } return 0; }