aboutsummaryrefslogtreecommitdiff
path: root/src/mime.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mime.c')
-rw-r--r--src/mime.c242
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);
+
+}