* Added the ability to compose messages with file attachments uploaded from
authorArt Cancro <ajc@citadel.org>
Wed, 30 Apr 2003 05:00:00 +0000 (05:00 +0000)
committerArt Cancro <ajc@citadel.org>
Wed, 30 Apr 2003 05:00:00 +0000 (05:00 +0000)
  the browser

webcit/ChangeLog
webcit/context_loop.c
webcit/cookie_conversion.c
webcit/messages.c
webcit/mime_parser.c
webcit/serv_func.c
webcit/tools.c
webcit/webcit.c
webcit/webcit.h

index 7b8bcafaa5368a26287d81a6c4e9abc9e835f680..62a91e0c6ed368a076db323e0eac64e8861dc075 100644 (file)
@@ -1,4 +1,8 @@
 $Log$
+Revision 410.15  2003/04/30 05:00:00  ajc
+* Added the ability to compose messages with file attachments uploaded from
+  the browser
+
 Revision 410.14  2003/04/27 04:46:02  ajc
 * Replace LKRN/LKRO in "folders view" room list with a single LKRA that
   observes the flag designating the presence of new messages.  (The "rooms
@@ -1344,3 +1348,4 @@ Sun Dec  6 19:50:55 EST 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
 
 1998-12-03 Nathan Bryant <bryant@cs.usm.maine.edu>
        * webserver.c: warning fix
+
index 743c4f3839e683278f04493090e14445b17fd528..dffd6eeb8ac7f378c244008b3a9352cbfa680b24 100644 (file)
@@ -45,6 +45,19 @@ struct wcsession *SessionList = NULL;
 
 pthread_key_t MyConKey;                         /* TSD key for MySession() */
 
+
+void free_attachments(struct wcsession *sess) {
+       struct wc_attachment *att;
+
+       while (sess->first_attachment != NULL) {
+               att = sess->first_attachment;
+               sess->first_attachment = sess->first_attachment->next;
+               free(att->data);
+               free(att);
+       }
+}
+
+
 void do_housekeeping(void)
 {
        struct wcsession *sptr, *ss, *session_to_kill;
@@ -88,6 +101,7 @@ BREAKOUT:    pthread_mutex_unlock(&SessionListMutex);
                        if (session_to_kill->preferences != NULL) {
                                free(session_to_kill->preferences);
                        }
+                       free_attachments(session_to_kill);
                        pthread_mutex_unlock(&session_to_kill->SessionMutex);
                        free(session_to_kill);
                }
index b9db3f90969588d5393e542a44be70daad830fea..5c35d378ca743f2921e6b34799f71407e124e037 100644 (file)
 #define FALSE 0
 
 typedef unsigned char byte;          /* Byte type */
-static byte dtable[SIZ];             /* base64 encode / decode table */
-
-/*
- * CtdlDecodeBase64() and encode_base64() are adaptations of code by
- * John Walker, found in full in the file "base64.c" included with the Citadel
- * server.  The difference between those functions and these is that
- * these are intended to encode/decode small string buffers, and those are
- * intended to encode/decode entire MIME parts.
- */
-
-void encode_base64(char *dest, char *source)
-{
-    int i, hiteof = FALSE;
-    int spos = 0;
-    int dpos = 0;
-
-    /* Fill dtable with character encodings.  */
-
-    for (i = 0; i < 26; i++) {
-        dtable[i] = 'A' + i;
-        dtable[26 + i] = 'a' + i;
-    }
-    for (i = 0; i < 10; i++) {
-        dtable[52 + i] = '0' + i;
-    }
-    dtable[62] = '+';
-    dtable[63] = '/';
-
-    while (!hiteof) {
-       byte igroup[3], ogroup[4];
-       int c, n;
-
-       igroup[0] = igroup[1] = igroup[2] = 0;
-       for (n = 0; n < 3; n++) {
-           c = source[spos++];
-           if (c == 0) {
-               hiteof = TRUE;
-               break;
-           }
-           igroup[n] = (byte) c;
-       }
-       if (n > 0) {
-           ogroup[0] = dtable[igroup[0] >> 2];
-           ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
-           ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
-           ogroup[3] = dtable[igroup[2] & 0x3F];
-
-            /* Replace characters in output stream with "=" pad
-              characters if fewer than three characters were
-              read from the end of the input stream. */
-
-           if (n < 3) {
-                ogroup[3] = '=';
-               if (n < 2) {
-                    ogroup[2] = '=';
-               }
-           }
-           for (i = 0; i < 4; i++) {
-               dest[dpos++] = ogroup[i];
-               dest[dpos] = 0;
-           }
-       }
-    }
-}
-
-
-/* 
- * Convert base64-encoded to binary.  Returns the length of the decoded data.
- * It will stop after reading 'length' bytes.
- */
-int CtdlDecodeBase64(char *dest, char *source, size_t length)
-{
-    int i, c;
-    int dpos = 0;
-    int spos = 0;
-
-    for (i = 0; i < 255; i++) {
-       dtable[i] = 0x80;
-    }
-    for (i = 'A'; i <= 'Z'; i++) {
-        dtable[i] = 0 + (i - 'A');
-    }
-    for (i = 'a'; i <= 'z'; i++) {
-        dtable[i] = 26 + (i - 'a');
-    }
-    for (i = '0'; i <= '9'; i++) {
-        dtable[i] = 52 + (i - '0');
-    }
-    dtable['+'] = 62;
-    dtable['/'] = 63;
-    dtable['='] = 0;
-
-    /*CONSTANTCONDITION*/
-    while (TRUE) {
-       byte a[4], b[4], o[3];
-
-       for (i = 0; i < 4; i++) {
-           if (spos >= length) {
-               return(dpos);
-           }
-           c = source[spos++];
-
-           if (c == 0) {
-               if (i > 0) {
-                   return(dpos);
-               }
-               return(dpos);
-           }
-           if (dtable[c] & 0x80) {
-               /* Ignoring errors: discard invalid character. */
-               i--;
-               continue;
-           }
-           a[i] = (byte) c;
-           b[i] = (byte) dtable[c];
-       }
-       o[0] = (b[0] << 2) | (b[1] >> 4);
-       o[1] = (b[1] << 4) | (b[2] >> 2);
-       o[2] = (b[2] << 6) | b[3];
-        i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
-       if (i>=1) dest[dpos++] = o[0];
-       if (i>=2) dest[dpos++] = o[1];
-       if (i>=3) dest[dpos++] = o[2];
-       dest[dpos] = 0;
-       if (i < 3) {
-           return(dpos);
-       }
-    }
-}
-
-
 
 /*
  * Pack all session info into one easy-to-digest cookie.  Healthy and delicious!
@@ -164,7 +33,7 @@ void stuff_to_cookie(char *cookie, int session, char *user, char *pass, char *ro
        char buf[SIZ];
 
        sprintf(buf, "%d|%s|%s|%s", session, user, pass, room);
-       encode_base64(cookie, buf);
+       CtdlEncodeBase64(cookie, buf, strlen(buf));
 }
 
 
index 3130d394672e43ed0439f79bc5db7a9e4bf42424..6c1bc97cd81b51a4e1a53997964e42b7853f81c5 100644 (file)
@@ -982,6 +982,67 @@ DONE:
 }
 
 
+/*
+ * Back end for post_message() ... this is where the actual message
+ * gets transmitted to the server.
+ */
+void post_mime_to_server(void) {
+       char boundary[SIZ];
+       int is_multipart = 0;
+       static int seq = 0;
+       struct wc_attachment *att;
+       char *encoded;
+       size_t encoded_length;
+       
+       /* If there are attachments, we have to do multipart/mixed */
+       if (WC->first_attachment != NULL) {
+               is_multipart = 1;
+       }
+
+       if (is_multipart) {
+               sprintf(boundary, "---Citadel-Multipart-%s-%04x%04x---",
+                       serv_info.serv_fqdn,
+                       getpid(),
+                       ++seq
+               );
+
+               /* Remember, serv_printf() appends an extra newline */
+               serv_printf("Content-type: multipart/mixed; "
+                       "boundary=\"%s\"\n", boundary);
+               serv_printf("This is a multipart message in MIME format.\n");
+               serv_printf("--%s", boundary);
+       }
+
+       serv_puts("Content-type: text/html");
+       serv_puts("");
+       text_to_server(bstr("msgtext"), 1);
+
+       if (is_multipart) {
+
+               /* Add in the attachments */
+               for (att = WC->first_attachment; att!=NULL; att=att->next) {
+
+                       encoded_length = ((att->length * 150) / 100);
+                       encoded = malloc(encoded_length);
+                       if (encoded == NULL) break;
+                       CtdlEncodeBase64(encoded, att->data, att->length);
+
+                       serv_printf("--%s", boundary);
+                       serv_printf("Content-type: %s", att->content_type);
+                       serv_printf("Content-disposition: attachment; "
+                               "filename=\"%s\"", att->filename);
+                       serv_puts("Content-transfer-encoding: base64");
+                       serv_puts("");
+                       serv_write(encoded, strlen(encoded));
+                       serv_puts("");
+                       serv_puts("");
+                       free(encoded);
+               }
+               serv_printf("--%s--", boundary);
+       }
+
+       serv_puts("000");
+}
 
 
 /*
@@ -999,11 +1060,31 @@ void post_message(void)
 {
        char buf[SIZ];
        static long dont_post = (-1L);
+       struct wc_attachment *att;
+
+       if (WC->upload_length > 0) {
+
+               att = malloc(sizeof(struct wc_attachment));
+               memset(att, 0, sizeof(struct wc_attachment));
+               att->next = WC->first_attachment;
+               WC->first_attachment = att;
+               att->length = WC->upload_length;
+               strcpy(att->content_type, WC->upload_content_type);
+               strcpy(att->filename, WC->upload_filename);
+
+               /* Transfer control of this memory from the upload struct
+                * to the attachment struct.
+                */
+               att->data = WC->upload;
+               WC->upload_length = 0;
+               WC->upload = NULL;
+               display_enter();
+               return;
+       }
 
        output_headers(1);
 
-       strcpy(buf, bstr("sc"));
-       if (strcasecmp(buf, "Save message")) {
+       if (strcasecmp(bstr("sc"), "Save message")) {
                wprintf("Cancelled.  Message was not posted.<BR>\n");
        } else if (atol(bstr("postseq")) == dont_post) {
                wprintf("Automatically cancelled because you have already "
@@ -1015,10 +1096,7 @@ void post_message(void)
                serv_puts(buf);
                serv_gets(buf);
                if (buf[0] == '4') {
-                       serv_puts("Content-type: text/html");
-                       serv_puts("");
-                       text_to_server(bstr("msgtext"), 1);
-                       serv_puts("000");
+                       post_mime_to_server();
                        wprintf("Message has been posted.<BR>\n");
                        dont_post = atol(bstr("postseq"));
                } else {
@@ -1027,6 +1105,7 @@ void post_message(void)
        }
 
        wDumpContent(1);
+       free_attachments(WC);
 }
 
 
@@ -1040,6 +1119,7 @@ void display_enter(void)
        char buf[SIZ];
        long now;
        struct tm *tm;
+       struct wc_attachment *att;
 
        output_headers(1);
 
@@ -1074,20 +1154,39 @@ void display_enter(void)
        wprintf("in %s&gt; ", WC->wc_roomname);
        wprintf("</B></FONT><BR><CENTER>\n");
 
-       wprintf("<FORM METHOD=\"POST\" ACTION=\"/post\" "
+       wprintf("<FORM ENCTYPE=\"multipart/form-data\" "
+               "METHOD=\"POST\" ACTION=\"/post\" "
                "NAME=\"enterform\">\n");
        wprintf("<INPUT TYPE=\"hidden\" NAME=\"recp\" VALUE=\"%s\">\n",
                bstr("recp"));
        wprintf("<INPUT TYPE=\"hidden\" NAME=\"postseq\" VALUE=\"%ld\">\n",
                now);
        wprintf("<FONT SIZE=-1>Subject (optional):</FONT>"
-               "<INPUT TYPE=\"text\" NAME=\"subject\" MAXLENGTH=70>"
+               "<INPUT TYPE=\"text\" NAME=\"subject\" VALUE=\"");
+       escputs(bstr("subject"));
+       wprintf("\" MAXLENGTH=70>"
                "&nbsp;&nbsp;&nbsp;"
                "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Save message\">"
                "<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Cancel\"><BR>\n");
 
        wprintf("<TEXTAREA NAME=\"msgtext\" wrap=soft ROWS=30 COLS=80 "
-               "WIDTH=80></TEXTAREA><P>\n");
+               "WIDTH=80>");
+       escputs(bstr("msgtext"));
+       wprintf("</TEXTAREA><BR>\n");
+
+       /* Enumerate any attachments which are already in place... */
+       for (att = WC->first_attachment; att != NULL; att = att->next) {
+               wprintf("<IMG SRC=\"/static/attachment.gif\" "
+                       "BORDER=0 ALIGN=MIDDLE> Attachment: ");
+               escputs(att->filename);
+               wprintf(" (%s, %d bytes)<BR>\n",
+                       att->content_type, att->length);
+       }
+
+       /* Now offer the ability to attach additional files... */
+       wprintf("Attach file: <input NAME=\"attachfile\" "
+               "SIZE=48 TYPE=\"file\">\n&nbsp;&nbsp;"
+               "<input type=\"submit\" name=\"attach\" value=\"Add\">\n");
 
        wprintf("</FORM></CENTER>\n");
 DONE:  wDumpContent(1);
index 8fe888197365a7e0329a9004c56da33fd1976ed6..c412c3dc1350c5f7055826fce15c8874895ff555 100644 (file)
@@ -3,14 +3,11 @@
  *
  * This is the MIME parser for Citadel.  Sometimes it actually works.
  *
- * Copyright (c) 1998-2001 by Art Cancro
+ * Copyright (c) 1998-2003 by Art Cancro
  * This code is distributed under the terms of the GNU General Public License.
  *
  */
 
-#ifdef DLL_EXPORT
-#define IN_LIBCIT
-#endif
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -23,6 +20,7 @@
 #include <errno.h>
 
 #include "webcit.h"
+
 #include "mime_parser.h"
 
 
@@ -274,7 +272,9 @@ void the_mime_parser(char *partnum,
        size_t content_length;
        char *encoding;
        char *disposition;
-       char *name;
+       char *name = NULL;
+       char *content_type_name;
+       char *content_disposition_name;
        char *filename;
        int is_multipart;
        int part_seq = 0;
@@ -303,8 +303,11 @@ void the_mime_parser(char *partnum,
        encoding = mallok(SIZ);
        memset(encoding, 0, SIZ);
 
-       name = mallok(SIZ);
-       memset(name, 0, SIZ);
+       content_type_name = mallok(SIZ);
+       memset(content_type_name, 0, SIZ);
+
+       content_disposition_name = mallok(SIZ);
+       memset(content_disposition_name, 0, SIZ);
 
        filename = mallok(SIZ);
        memset(filename, 0, SIZ);
@@ -331,7 +334,7 @@ void the_mime_parser(char *partnum,
                if (!isspace(buf[0])) {
                        if (!strncasecmp(header, "Content-type: ", 14)) {
                                strcpy(content_type, &header[14]);
-                               extract_key(name, content_type, "name");
+                               extract_key(content_type_name, content_type, "name");
                                /* Deal with weird headers */
                                if (strchr(content_type, ' '))
                                        *(strchr(content_type, ' ')) = '\0';
@@ -340,6 +343,7 @@ void the_mime_parser(char *partnum,
                        }
                        if (!strncasecmp(header, "Content-Disposition: ", 21)) {
                                strcpy(disposition, &header[21]);
+                               extract_key(content_disposition_name, disposition, "name");
                                extract_key(filename, disposition, "filename");
                        }
                        if (!strncasecmp(header, "Content-length: ", 16)) {
@@ -413,7 +417,16 @@ void the_mime_parser(char *partnum,
                                part_end = ptr;
                                ++ptr;
                        }
-               } while ( (strcasecmp(ptr, endary)) && (ptr <= content_end) );
+                       /* If we pass out of scope in the MIME multipart (by
+                        * hitting the end boundary), force the pointer out
+                        * of scope so this loop ends.
+                        */
+                       if (ptr < content_end) {
+                               if (!strcasecmp(ptr, endary)) {
+                                       ptr = content_end++;
+                               }
+                       }
+               } while (ptr <= content_end);
                if (PostMultiPartCallBack != NULL) {
                        PostMultiPartCallBack("", "", partnum, "", NULL,
                                content_type, 0, encoding, userdata);
@@ -430,11 +443,25 @@ void the_mime_parser(char *partnum,
                        ++length;
                }
                part_end = content_end;
+               /* fix an off-by-one error */
+               --part_end;
+               --length;
                
                /* Truncate if the header told us to */
                if ( (content_length > 0) && (length > content_length) ) {
                        length = content_length;
                }
+
+               /* Sometimes the "name" field is tacked on to Content-type,
+                * and sometimes it's tacked on to Content-disposition.  Use
+                * whichever one we have.
+                */
+               if (strlen(content_disposition_name) > strlen(content_type_name)) {
+                       name = content_disposition_name;
+               }
+               else {
+                       name = content_type_name;
+               }
                
                mime_decode(partnum,
                            part_start, length,
@@ -451,7 +478,8 @@ end_parser: /* free the buffers!  end the oppression!! */
        phree(header);
        phree(content_type);
        phree(encoding);
-       phree(name);
+       phree(content_type_name);
+       phree(content_disposition_name);
        phree(filename);
        phree(disposition);
 }
@@ -511,3 +539,4 @@ void mime_parser(char *content_start,
                        PostMultiPartCallBack,
                        userdata, dont_decode);
 }
+
index 652ab857940e3fb07cb9720f2457204daec0133c..36e65da7740a52078a7cada2132847e248175339 100644 (file)
@@ -248,6 +248,7 @@ void read_server_binary(char *buffer, size_t total_len) {
                serv_gets(buf);
                if (buf[0] == '6') {
                        thisblock = (size_t)atoi(&buf[4]);
+                       if (!WC->connected) return;
                        serv_read(&buffer[bytes], thisblock);
                        bytes += thisblock;
                }
index ec84ddf4bf21628d43788e6fc4660fbc49aea2ce..6a2bad457162b5458809fcf46b18896b8dc7d843 100644 (file)
 #include "webcit.h"
 #include "webserver.h"
 
+typedef unsigned char byte;
+
+#define FALSE 0
+#define TRUE 1
 
 char *ascmonths[] = {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
@@ -35,6 +39,7 @@ char *ascdays[] = {
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
 };
 
+static byte dtable[256];       /* base64 encode / decode table */
 
 char *safestrncpy(char *dest, const char *src, size_t n)
 {
@@ -52,40 +57,42 @@ char *safestrncpy(char *dest, const char *src, size_t n)
 /*
  * num_tokens()  -  discover number of parameters/tokens in a string
  */
-int num_tokens(char *source, char tok) {
+int num_tokens(char *source, char tok)
+{
        int a;
        int count = 1;
 
-       if (source == NULL) return(0);
-       for (a=0; a<strlen(source); ++a) {
-               if (source[a]==tok) ++count;
+       if (source == NULL)
+               return (0);
+       for (a = 0; a < strlen(source); ++a) {
+               if (source[a] == tok)
+                       ++count;
        }
-       return(count);
+       return (count);
 }
 
 /*
  * extract_token()  -  a smarter string tokenizer
  */
-void extract_token(char *dest, char *source, int parmnum, char separator) 
+void extract_token(char *dest, char *source, int parmnum, char separator)
 {
        int i;
        int len;
        int curr_parm;
 
-       strcpy(dest,"");
+       strcpy(dest, "");
        len = 0;
        curr_parm = 0;
 
-       if (strlen(source)==0) {
+       if (strlen(source) == 0) {
                return;
        }
 
-       for (i=0; i<strlen(source); ++i) {
-               if (source[i]==separator) {
+       for (i = 0; i < strlen(source); ++i) {
+               if (source[i] == separator) {
                        ++curr_parm;
-               }
-               else if (curr_parm == parmnum) {
-                       dest[len+1] = 0;
+               } else if (curr_parm == parmnum) {
+                       dest[len + 1] = 0;
                        dest[len++] = source[i];
                }
        }
@@ -108,25 +115,26 @@ void remove_token(char *source, int parmnum, char separator)
        start = (-1);
        end = (-1);
 
-       if (strlen(source)==0) {
+       if (strlen(source) == 0) {
                return;
-               }
+       }
 
-       for (i=0; i<strlen(source); ++i) {
-               if ( (start < 0) && (curr_parm == parmnum) ) {
+       for (i = 0; i < strlen(source); ++i) {
+               if ((start < 0) && (curr_parm == parmnum)) {
                        start = i;
                }
 
-               if ( (end < 0) && (curr_parm == (parmnum+1)) ) {
+               if ((end < 0) && (curr_parm == (parmnum + 1))) {
                        end = i;
                }
 
-               if (source[i]==separator) {
+               if (source[i] == separator) {
                        ++curr_parm;
                }
        }
 
-       if (end < 0) end = strlen(source);
+       if (end < 0)
+               end = strlen(source);
 
        strcpy(&source[start], &source[end]);
 }
@@ -140,9 +148,9 @@ void remove_token(char *source, int parmnum, char separator)
 int extract_int(char *source, int parmnum)
 {
        char buf[SIZ];
-       
+
        extract_token(buf, source, parmnum, '|');
-       return(atoi(buf));
+       return (atoi(buf));
 }
 
 /*
@@ -151,9 +159,9 @@ int extract_int(char *source, int parmnum)
 long extract_long(char *source, long int parmnum)
 {
        char buf[SIZ];
-       
+
        extract_token(buf, source, parmnum, '|');
-       return(atol(buf));
+       return (atol(buf));
 }
 
 
@@ -189,24 +197,25 @@ char ch;
 /*
  * Format a date/time stamp for output 
  */
-void fmt_date(char *buf, time_t thetime) {
+void fmt_date(char *buf, time_t thetime)
+{
        struct tm *tm;
        int hour;
 
        strcpy(buf, "");
        tm = localtime(&thetime);
        hour = tm->tm_hour;
-       if (hour == 0) hour = 12;
-       else if (hour > 12) hour = hour - 12;
+       if (hour == 0)
+               hour = 12;
+       else if (hour > 12)
+               hour = hour - 12;
 
        sprintf(buf, "%s %d %d %2d:%02d%s",
                ascmonths[tm->tm_mon],
                tm->tm_mday,
                tm->tm_year + 1900,
-               hour,
-               tm->tm_min,
-               ( (tm->tm_hour > 12) ? "pm" : "am" )
-       );
+               hour, tm->tm_min, ((tm->tm_hour > 12) ? "pm" : "am")
+           );
 }
 
 
@@ -214,21 +223,22 @@ void fmt_date(char *buf, time_t thetime) {
 /*
  * Format TIME ONLY for output 
  */
-void fmt_time(char *buf, time_t thetime) {
+void fmt_time(char *buf, time_t thetime)
+{
        struct tm *tm;
        int hour;
 
        strcpy(buf, "");
        tm = localtime(&thetime);
        hour = tm->tm_hour;
-       if (hour == 0) hour = 12;
-       else if (hour > 12) hour = hour - 12;
+       if (hour == 0)
+               hour = 12;
+       else if (hour > 12)
+               hour = hour - 12;
 
        sprintf(buf, "%d:%02d%s",
-               hour,
-               tm->tm_min,
-               ( (tm->tm_hour > 12) ? "pm" : "am" )
-       );
+               hour, tm->tm_min, ((tm->tm_hour > 12) ? "pm" : "am")
+           );
 }
 
 
@@ -237,7 +247,8 @@ void fmt_time(char *buf, time_t thetime) {
 /*
  * Format a date/time stamp to the format used in HTTP headers
  */
-void httpdate(char *buf, time_t thetime) {
+void httpdate(char *buf, time_t thetime)
+{
        struct tm *tm;
 
        strcpy(buf, "");
@@ -247,11 +258,7 @@ void httpdate(char *buf, time_t thetime) {
                ascdays[tm->tm_wday],
                tm->tm_mday,
                ascmonths[tm->tm_mon],
-               tm->tm_year + 1900,
-               tm->tm_hour,
-               tm->tm_min,
-               tm->tm_sec
-       );
+               tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
 }
 
 
@@ -264,24 +271,24 @@ void httpdate(char *buf, time_t thetime) {
  */
 char *memreadline(char *start, char *buf, int maxlen)
 {
-        char ch;
-        char *ptr;
-        int len = 0;    /* tally our own length to avoid strlen() delays */
-
-        ptr = start;
-        memset(buf, 0, maxlen);
-
-        while (1) {
-                ch = *ptr++;
-                if ( (len < (maxlen - 1)) && (ch != 13) && (ch != 10) ) {
-                        buf[strlen(buf) + 1] = 0;
-                        buf[strlen(buf)] = ch;
-                        ++len;
-                }
-                if ((ch == 10) || (ch == 0)) {
-                        return ptr;
-                }
-        }
+       char ch;
+       char *ptr;
+       int len = 0;            /* tally our own length to avoid strlen() delays */
+
+       ptr = start;
+       memset(buf, 0, maxlen);
+
+       while (1) {
+               ch = *ptr++;
+               if ((len < (maxlen - 1)) && (ch != 13) && (ch != 10)) {
+                       buf[strlen(buf) + 1] = 0;
+                       buf[strlen(buf)] = ch;
+                       ++len;
+               }
+               if ((ch == 10) || (ch == 0)) {
+                       return ptr;
+               }
+       }
 }
 
 
@@ -291,12 +298,13 @@ char *memreadline(char *start, char *buf, int maxlen)
  */
 int pattern2(char *search, char *patn)
 {
-        int a;
-        for (a=0; a<strlen(search); ++a) {
-                if (!strncasecmp(&search[a],patn,strlen(patn))) return(a);
-                }
-        return(-1);
-        }
+       int a;
+       for (a = 0; a < strlen(search); ++a) {
+               if (!strncasecmp(&search[a], patn, strlen(patn)))
+                       return (a);
+       }
+       return (-1);
+}
 
 
 /*
@@ -304,10 +312,10 @@ int pattern2(char *search, char *patn)
  */
 void striplt(char *buf)
 {
-        while ((strlen(buf) > 0) && (isspace(buf[0])))
-                strcpy(buf, &buf[1]);
-        while (isspace(buf[strlen(buf) - 1]))
-                buf[strlen(buf) - 1] = 0;
+       while ((strlen(buf) > 0) && (isspace(buf[0])))
+               strcpy(buf, &buf[1]);
+       while (isspace(buf[strlen(buf) - 1]))
+               buf[strlen(buf) - 1] = 0;
 }
 
 
@@ -315,7 +323,8 @@ void striplt(char *buf)
  * Determine whether the specified message number is contained within the
  * specified set.
  */
-int is_msg_in_mset(char *mset, long msgnum) {
+int is_msg_in_mset(char *mset, long msgnum)
+{
        int num_sets;
        int s;
        char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
@@ -325,26 +334,27 @@ int is_msg_in_mset(char *mset, long msgnum) {
         * Now set it for all specified messages.
         */
        num_sets = num_tokens(mset, ',');
-       for (s=0; s<num_sets; ++s) {
+       for (s = 0; s < num_sets; ++s) {
                extract_token(setstr, mset, s, ',');
 
                extract_token(lostr, setstr, 0, ':');
                if (num_tokens(setstr, ':') >= 2) {
                        extract_token(histr, setstr, 1, ':');
                        if (!strcmp(histr, "*")) {
-                               snprintf(histr, sizeof histr, "%ld", LONG_MAX);
+                               snprintf(histr, sizeof histr, "%ld",
+                                        LONG_MAX);
                        }
-               } 
-               else {
+               } else {
                        strcpy(histr, lostr);
                }
                lo = atol(lostr);
                hi = atol(histr);
 
-               if ((msgnum >= lo) && (msgnum <= hi)) return(1);
+               if ((msgnum >= lo) && (msgnum <= hi))
+                       return (1);
        }
 
-       return(0);
+       return (0);
 }
 
 
@@ -354,25 +364,28 @@ int is_msg_in_mset(char *mset, long msgnum) {
  *
  * This improved version can strip out *multiple* boundarized substrings.
  */
-void stripout(char *str, char leftboundary, char rightboundary) {
+void stripout(char *str, char leftboundary, char rightboundary)
+{
        int a;
-        int lb = (-1);
-        int rb = (-1);
+       int lb = (-1);
+       int rb = (-1);
 
        do {
                lb = (-1);
                rb = (-1);
-       
-               for (a = 0; a < strlen(str); ++a) {
-                       if (str[a] == leftboundary) lb = a;
-                       if (str[a] == rightboundary) rb = a;
-               }
 
-               if ( (lb > 0) && (rb > lb) ) {
-                       strcpy(&str[lb - 1], &str[rb + 1]);
-               }
+               for (a = 0; a < strlen(str); ++a) {
+                       if (str[a] == leftboundary)
+                               lb = a;
+                       if (str[a] == rightboundary)
+                               rb = a;
+               }
+
+               if ((lb > 0) && (rb > lb)) {
+                       strcpy(&str[lb - 1], &str[rb + 1]);
+               }
 
-       } while ( (lb > 0) && (rb > lb) );
+       } while ((lb > 0) && (rb > lb));
 
 }
 
@@ -390,3 +403,149 @@ void sleeeeeeeeeep(int seconds)
        select(0, NULL, NULL, NULL, &tv);
 }
 
+
+
+/*
+ * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by
+ * John Walker, copied over from the Citadel server.
+ */
+
+void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen)
+{
+       int i, hiteof = FALSE;
+       int spos = 0;
+       int dpos = 0;
+       int thisline = 0;
+
+       /*  Fill dtable with character encodings.  */
+
+       for (i = 0; i < 26; i++) {
+               dtable[i] = 'A' + i;
+               dtable[26 + i] = 'a' + i;
+       }
+       for (i = 0; i < 10; i++) {
+               dtable[52 + i] = '0' + i;
+       }
+       dtable[62] = '+';
+       dtable[63] = '/';
+
+       while (!hiteof) {
+               byte igroup[3], ogroup[4];
+               int c, n;
+
+               igroup[0] = igroup[1] = igroup[2] = 0;
+               for (n = 0; n < 3; n++) {
+                       if (spos >= sourcelen) {
+                               hiteof = TRUE;
+                               break;
+                       }
+                       c = source[spos++];
+                       igroup[n] = (byte) c;
+               }
+               if (n > 0) {
+                       ogroup[0] = dtable[igroup[0] >> 2];
+                       ogroup[1] =
+                           dtable[((igroup[0] & 3) << 4) |
+                                  (igroup[1] >> 4)];
+                       ogroup[2] =
+                           dtable[((igroup[1] & 0xF) << 2) |
+                                  (igroup[2] >> 6)];
+                       ogroup[3] = dtable[igroup[2] & 0x3F];
+
+                       /* Replace characters in output stream with "=" pad
+                          characters if fewer than three characters were
+                          read from the end of the input stream. */
+
+                       if (n < 3) {
+                               ogroup[3] = '=';
+                               if (n < 2) {
+                                       ogroup[2] = '=';
+                               }
+                       }
+                       for (i = 0; i < 4; i++) {
+                               dest[dpos++] = ogroup[i];
+                               dest[dpos] = 0;
+                       }
+                       thisline += 4;
+                       if (thisline > 70) {
+                               dest[dpos++] = '\r';
+                               dest[dpos++] = '\n';
+                               dest[dpos] = 0;
+                               thisline = 0;
+                       }
+               }
+       }
+       if (thisline > 70) {
+               dest[dpos++] = '\r';
+               dest[dpos++] = '\n';
+               dest[dpos] = 0;
+               thisline = 0;
+       }
+}
+
+
+/* 
+ * Convert base64-encoded to binary.  Returns the length of the decoded data.
+ * It will stop after reading 'length' bytes.
+ */
+int CtdlDecodeBase64(char *dest, const char *source, size_t length)
+{
+       int i, c;
+       int dpos = 0;
+       int spos = 0;
+
+       for (i = 0; i < 255; i++) {
+               dtable[i] = 0x80;
+       }
+       for (i = 'A'; i <= 'Z'; i++) {
+               dtable[i] = 0 + (i - 'A');
+       }
+       for (i = 'a'; i <= 'z'; i++) {
+               dtable[i] = 26 + (i - 'a');
+       }
+       for (i = '0'; i <= '9'; i++) {
+               dtable[i] = 52 + (i - '0');
+       }
+       dtable['+'] = 62;
+       dtable['/'] = 63;
+       dtable['='] = 0;
+
+        /*CONSTANTCONDITION*/ while (TRUE) {
+               byte a[4], b[4], o[3];
+
+               for (i = 0; i < 4; i++) {
+                       if (spos >= length) {
+                               return (dpos);
+                       }
+                       c = source[spos++];
+
+                       if (c == 0) {
+                               if (i > 0) {
+                                       return (dpos);
+                               }
+                               return (dpos);
+                       }
+                       if (dtable[c] & 0x80) {
+                               /* Ignoring errors: discard invalid character. */
+                               i--;
+                               continue;
+                       }
+                       a[i] = (byte) c;
+                       b[i] = (byte) dtable[c];
+               }
+               o[0] = (b[0] << 2) | (b[1] >> 4);
+               o[1] = (b[1] << 4) | (b[2] >> 2);
+               o[2] = (b[2] << 6) | b[3];
+               i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
+               if (i >= 1)
+                       dest[dpos++] = o[0];
+               if (i >= 2)
+                       dest[dpos++] = o[1];
+               if (i >= 3)
+                       dest[dpos++] = o[2];
+               dest[dpos] = 0;
+               if (i < 3) {
+                       return (dpos);
+               }
+       }
+}
index 59c3f3fe16df5ca50ec53f6f10d8d4dabf38da42..0961971818e07bbbe9447be9edc4e8379a572fa4 100644 (file)
@@ -684,18 +684,36 @@ void upload_handler(char *name, char *filename, char *partnum, char *disp,
                        void *content, char *cbtype, size_t length,
                        char *encoding, void *userdata)
 {
+       struct urlcontent *u;
 
        lprintf(5, "UPLOAD HANDLER CALLED\n");
-       lprintf(5, "    name = %s\n", name);
-       lprintf(5, "filename = %s\n", filename);
-       lprintf(5, "encoding = %s\n", encoding);
-       lprintf(5, "    type = %s\n", cbtype);
-       lprintf(5, "  length = %ld\n", (long)length);
+       lprintf(5, "       name = %s\n", name);
+       lprintf(5, "   filename = %s\n", filename);
+       lprintf(5, "   encoding = %s\n", encoding);
+       lprintf(5, "       type = %s\n", cbtype);
+       lprintf(5, "     length = %ld\n", (long)length);
+       lprintf(5, "disposition = %s\n", disp);
+
+       /* Form fields */
+       if ( (length > 0) && (strlen(cbtype) == 0) ) {
+               u = (struct urlcontent *) malloc(sizeof(struct urlcontent));
+               u->next = WC->urlstrings;
+               WC->urlstrings = u;
+               safestrncpy(u->url_key, name, sizeof(u->url_key));
+               u->url_data = malloc(length + 1);
+               memcpy(u->url_data, content, length);
+               u->url_data[length] = 0;
+       }
 
-       if (length > 0) {
+       /* Uploaded files */
+       if ( (length > 0) && (strlen(cbtype) > 0) ) {
                WC->upload = malloc(length);
                if (WC->upload != NULL) {
                        WC->upload_length = length;
+                       safestrncpy(WC->upload_filename, filename,
+                                       sizeof(WC->upload_filename));
+                       safestrncpy(WC->upload_content_type, cbtype,
+                                       sizeof(WC->upload_content_type));
                        memcpy(WC->upload, content, length);
                }
                else {
@@ -703,6 +721,7 @@ void upload_handler(char *name, char *filename, char *partnum, char *disp,
                                strerror(errno));
                }
        }
+
 }
 
 
@@ -725,6 +744,7 @@ void session_loop(struct httprequest *req)
        struct httprequest *hptr;
        char browser_host[SIZ];
        char user_agent[SIZ];
+       int body_start;
 
        /* We stuff these with the values coming from the client cookies,
         * so we can use them to reconnect a timed out session if we have to.
@@ -779,12 +799,16 @@ void session_loop(struct httprequest *req)
 
        if (ContentLength > 0) {
                lprintf(5, "Content length: %d\n", ContentLength);
-               content = malloc(ContentLength + 1);
-               memset(content, 0, ContentLength+1);
-               BytesRead = 0;
+               content = malloc(ContentLength + SIZ);
+               memset(content, 0, ContentLength + SIZ);
+               sprintf(content, "Content-type: %s\n"
+                               "Content-length: %d\n\n",
+                               ContentType, ContentLength);
+               body_start = strlen(content);
 
+               BytesRead = 0;
                while (BytesRead < ContentLength) {
-                       a=read(WC->http_sock, &content[BytesRead],
+                       a=read(WC->http_sock, &content[BytesRead+body_start],
                                ContentLength - BytesRead);
                        if (a <= 0) BytesRead = ContentLength;
                        else BytesRead += a;
@@ -792,7 +816,7 @@ void session_loop(struct httprequest *req)
 
                if (!strncasecmp(ContentType,
                              "application/x-www-form-urlencoded", 33)) {
-                       addurls(content);
+                       addurls(&content[body_start]);
                } else if (!strncasecmp(ContentType, "multipart", 9)) {
                        content_end = content + ContentLength;
                        mime_parser(content, content_end, *upload_handler,
index 22d2edabc7ad7bfedc5cfd3e4231f7993279ca1a..ead83e788edd983d5038dcb68dca586fc51015d3 100644 (file)
@@ -151,7 +151,16 @@ enum {
        WCS_FUNCTION,
        WCS_SERVCMD
 };
-       
+
+
+struct wc_attachment {
+       struct wc_attachment *next;
+       size_t length;
+       char content_type[SIZ];
+       char filename[SIZ];
+       char *data;
+};
+
 
 /*
  * One of these is kept for each active Citadel session.
@@ -177,6 +186,8 @@ struct wcsession {
        long uglsn;
        int upload_length;
        char *upload;
+       char upload_filename[SIZ];
+       char upload_content_type[SIZ];
        int new_mail;
        int remember_new_mail;
        int need_regi;                  /* This user needs to register. */
@@ -201,6 +212,7 @@ struct wcsession {
        long *cal_msgnum;               /* store calendar msgids for display */
        int num_cal;
 #endif
+       struct wc_attachment *first_attachment;
 };
 
 #define extract(dest,source,parmnum)   extract_token(dest,source,parmnum,'|')
@@ -338,7 +350,6 @@ char *memreadline(char *start, char *buf, int maxlen);
 int num_tokens (char *source, char tok);
 void extract_token(char *dest, char *source, int parmnum, char separator);
 void remove_token(char *source, int parmnum, char separator);
-int CtdlDecodeBase64(char *dest, char *source, size_t length);
 char *load_mimepart(long msgnum, char *partnum);
 int pattern2(char *search, char *patn);
 void do_edit_vcard(long, char *, char *);
@@ -378,6 +389,10 @@ void do_calendar_view(void);
 void free_calendar_buffer(void);
 void calendar_summary_view(void);
 int load_msg_ptrs(char *servcmd);
+void CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen);
+int CtdlDecodeBase64(char *dest, const char *source, size_t length);
+void free_attachments(struct wcsession *sess);
+
 
 #ifdef HAVE_ICAL_H
 void display_edit_task(void);