diff options
Diffstat (limited to 'src/mime.c')
-rw-r--r-- | src/mime.c | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/src/mime.c b/src/mime.c new file mode 100644 index 0000000..970f29a --- /dev/null +++ b/src/mime.c @@ -0,0 +1,242 @@ +/* + * mime.c + * + * Rudimentary MIME parser + * + * (c) 2002-2005 Thomas White <taw27@srcf.ucam.org> + * Part of TuxMessenger - GTK+-based MSN Messenger client + * + * This package 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; version 2 dated June, 1991. + * + * This package 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 package; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "routines.h" +#include "debug.h" + +/* Get the length of the MIME header, including the two newlines (i.e. the offset for the body). */ +size_t mime_headerlength(const char *mime) { + + int status; + size_t offs = 0; + + status = 0; + + /* Simply detect two consecutive newlines. */ + while ( offs < strlen(mime) ) { + + /* State machine :D */ + if ( (mime[offs] == '\r') && (status == 1) ) { + /* \r\r */ + assert(offs+1 <= strlen(mime)); + return offs+1; + } + if ( (mime[offs] == '\n') && (status == 4) ) { + /* \n\n */ + assert(offs+1 <= strlen(mime)); + return offs+1; + } + if ( (mime[offs] == '\n') && (status == 0) ) { + status = 4; + } + if ( (mime[offs] == '\r') && (status == 0) ) { + status = 1; + } + if ( (mime[offs] == '\n') && (status == 1) ) { + status = 2; + } + if ( (mime[offs] == '\r') && (status == 2) ) { + status = 3; + } + if ( (mime[offs] == '\n') && (status == 3) ) { + /* \r\n\r\n */ + assert(offs+1 <= strlen(mime)); + return offs+1; + } + + if ( (mime[offs] != '\r') && (mime[offs] != '\n') ) { + status = 0; + } + + offs++; + + } + + /* Whoops */ + return 0; + +} + +/* Return the body of a MIME message. Don't free() the returned string. */ +const char *mime_getbody(const char *mime) { + + return mime+mime_headerlength(mime); + +} + +/* Return the value of a given MIME field. Returned string is free-able. */ +static char *mime_getfield_internal(const char *mime, const char *given_field, int anywhere) { + + char *line; + char *minibuffer; + size_t offs = 0; + size_t mime_size; + char *field; + int end_headers = 0; + + mime_size = strlen(mime); + + /* Last character of "field" should be a colon. Add it if it's not already there. */ + if ( given_field[strlen(given_field)-1] != ':' ) { + + field = malloc(strlen(given_field) + 2); + strcpy(field, given_field); + field[strlen(given_field)] = ':'; + field[strlen(given_field)+1] = '\0'; + + } else { + field = strdup(given_field); + } + + line = malloc(strlen(mime)+1); /* Bigger than the maximum possible output size */ + while ( (offs < mime_size) && !end_headers ) { + + size_t line_offs = 0; + int found_eof_line = 0; + + while ( (found_eof_line == 0) && (offs<strlen(mime)) ) { + + if ( (mime[offs] == '\r') || (mime[offs] == '\n') ) { + + found_eof_line = 1; + line[line_offs]='\0'; + + if ( !anywhere ) { + if ( offs<(mime_size-4) ) { + if ( strncmp(mime+offs, "\r\n\r\n", 4) == 0 ) { + end_headers = 1; + } + } + if ( offs<(mime_size-2) ) { + if ( strncmp(mime+offs, "\n\n", 2) == 0 ) { + end_headers = 1; + } + } + if ( offs<(mime_size-4) ) { + if ( strncmp(mime+offs, "\r\r", 2) == 0 ) { + end_headers = 1; + } + } + } + + } else { + line[line_offs] = mime[offs]; + offs++; + line_offs++; + } + + } + if ( offs >= mime_size-1 ) { + line[line_offs]='\0'; + } + + minibuffer = routines_lindex(line, 0); + if ( strcmp(minibuffer, field) == 0 ) { + + char *fieldcontents; + free(minibuffer); + fieldcontents = routines_lindexend(line, 1); + free(line); + free(field); + return fieldcontents; + + } + free(minibuffer); + offs+=2; + } + + /* Field not found, so return an empty string (free()-able) */ + free(line); + free(field); + minibuffer = malloc(1); + minibuffer[0]='\0'; + return minibuffer; + +} + +char *mime_getfield(const char *mime, const char *given_field) { + return mime_getfield_internal(mime, given_field, 0); +} + +char *mime_getfield_anywhere(const char *mime, const char *given_field) { + return mime_getfield_internal(mime, given_field, 1); +} + +/* Remove a given header from a MIME message. */ +char *mime_removeheader(const char *mime, size_t mimelength, const char *field) { + + size_t i; + size_t fieldlen; + + fieldlen = strlen(field); + + for ( i=0; i<mimelength-fieldlen; i++ ) { + + if ( strncmp(mime+i, field, fieldlen) == 0 ) { + + size_t pos = i; + size_t endpos; + for ( pos=i; pos<mimelength-fieldlen; pos++ ) { + + /* Works with any sensible type of line-ending. */ + if ( (mime[pos] == '\r') || (mime[pos] == '\n') ) { + + char *new_mime; + if ( (mime[pos+1] == '\r') || (mime[pos+1] == '\n') ) { + endpos = pos+2; + } else { + endpos = pos+1; + } + new_mime = malloc(mimelength-(endpos-i)+1); + memcpy(new_mime, mime, i); + memcpy(new_mime+i, mime+endpos, mimelength-endpos); + new_mime[mimelength-(endpos-i)] = '\0'; + return new_mime; + } + + } + /* D'oh :( */ + debug_print("mime.c: didn't find end of field to be removed!\n"); + return strdup(mime); + + } + + if ( (strncmp(mime+i, "\r\n\r\n", 4) == 0) || (strncmp(mime+i, "\n\n", 2) == 0) || (strncmp(mime+i, "\r\r", 2) == 0) ) { + debug_print("Reached end of MIME headers before finding field to remove.\n"); + break; + } + + } + + return strdup(mime); + +} |