From: Art Cancro Date: Wed, 30 Apr 2003 05:00:00 +0000 (+0000) Subject: * Added the ability to compose messages with file attachments uploaded from X-Git-Tag: v7.86~5927 X-Git-Url: https://code.citadel.org/?p=citadel.git;a=commitdiff_plain;h=42e5566f6512bc39f9377d183f26dddfc36d10ea * Added the ability to compose messages with file attachments uploaded from the browser --- diff --git a/webcit/ChangeLog b/webcit/ChangeLog index 7b8bcafaa..62a91e0c6 100644 --- a/webcit/ChangeLog +++ b/webcit/ChangeLog @@ -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 1998-12-03 Nathan Bryant * webserver.c: warning fix + diff --git a/webcit/context_loop.c b/webcit/context_loop.c index 743c4f383..dffd6eeb8 100644 --- a/webcit/context_loop.c +++ b/webcit/context_loop.c @@ -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); } diff --git a/webcit/cookie_conversion.c b/webcit/cookie_conversion.c index b9db3f909..5c35d378c 100644 --- a/webcit/cookie_conversion.c +++ b/webcit/cookie_conversion.c @@ -24,137 +24,6 @@ #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)); } diff --git a/webcit/messages.c b/webcit/messages.c index 3130d3946..6c1bc97cd 100644 --- a/webcit/messages.c +++ b/webcit/messages.c @@ -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.
\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.
\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> ", WC->wc_roomname); wprintf("
\n"); - wprintf("
\n"); wprintf("\n", bstr("recp")); wprintf("\n", now); wprintf("Subject (optional):" - "" + "" "   " "" "
\n"); wprintf("

\n"); + "WIDTH=80>"); + escputs(bstr("msgtext")); + wprintf("
\n"); + + /* Enumerate any attachments which are already in place... */ + for (att = WC->first_attachment; att != NULL; att = att->next) { + wprintf(" Attachment: "); + escputs(att->filename); + wprintf(" (%s, %d bytes)
\n", + att->content_type, att->length); + } + + /* Now offer the ability to attach additional files... */ + wprintf("Attach file: \n  " + "\n"); wprintf("

\n"); DONE: wDumpContent(1); diff --git a/webcit/mime_parser.c b/webcit/mime_parser.c index 8fe888197..c412c3dc1 100644 --- a/webcit/mime_parser.c +++ b/webcit/mime_parser.c @@ -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 #include @@ -23,6 +20,7 @@ #include #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); } + diff --git a/webcit/serv_func.c b/webcit/serv_func.c index 652ab8579..36e65da77 100644 --- a/webcit/serv_func.c +++ b/webcit/serv_func.c @@ -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; } diff --git a/webcit/tools.c b/webcit/tools.c index ec84ddf4b..6a2bad457 100644 --- a/webcit/tools.c +++ b/webcit/tools.c @@ -25,6 +25,10 @@ #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; atm_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 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= 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); + } + } +} diff --git a/webcit/webcit.c b/webcit/webcit.c index 59c3f3fe1..096197181 100644 --- a/webcit/webcit.c +++ b/webcit/webcit.c @@ -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, diff --git a/webcit/webcit.h b/webcit/webcit.h index 22d2edabc..ead83e788 100644 --- a/webcit/webcit.h +++ b/webcit/webcit.h @@ -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);