on supporting MIME format messages.
+Thu Jan 14 21:21:15 EST 1999 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
+ * Brought over the mime_parser from WebCit and began preliminary work
+ on supporting MIME format messages.
+
Tue Jan 12 22:30:00 EST 1999 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
* Various changes to begin work on support for MIME messages
- Defined format type 4 for MIME
room_ops.c rooms.c routines.c routines2.c serv_chat.c \
serv_info.c serv_test.c serv_upgrade.c setup.c snprintf.c stats.c \
support.c sysdep.c tools.c user_ops.c userlist.c serv_expire.c \
- whobbs.c sendcommand.c
+ whobbs.c sendcommand.c mime_parser.c
DEP_FILES=$(SOURCES:.c=.d)
citserver: citserver.ro user_ops.ro support.ro room_ops.ro file_ops.ro \
msgbase.ro config.ro sysdep.ro locate_host.ro \
housekeeping.ro database.ro control.ro logging.ro \
- policy.ro dynloader.ro tools.ro $(SNPRINTF:.o=.ro)
+ policy.ro dynloader.ro tools.ro mime_parser.ro $(SNPRINTF:.o=.ro)
$(CC) \
citserver.ro user_ops.ro room_ops.ro file_ops.ro support.ro \
msgbase.ro config.ro sysdep.ro locate_host.ro \
housekeeping.ro database.ro control.ro logging.ro \
- policy.ro dynloader.ro tools.ro $(SNPRINTF:.o=.ro)\
+ policy.ro dynloader.ro tools.ro mime_parser.ro \
+ $(SNPRINTF:.o=.ro)\
$(LDFLAGS) $(SERVER_LDFLAGS) $(LIBS) -o citserver
.c.ro:
#define GF_SKIP 1 /* <;S>kip floor mode */
#define GF_ZAP 2 /* <;Z>ap floor mode */
-/* message transfer formats */
-#define MT_CITADEL 0 /* Citadel proprietary */
-#define MT_RFC822 2 /* RFC822 */
-#define MT_RAW 3 /* IGnet raw format */
-#define MT_DATE 4 /* We're only looking for the date */
-
-
#define BASEROOM "Lobby"
#define MAILROOM "Mail"
cmd_msg3(&cmdbuf[5]);
}
+ else if (!strncasecmp(cmdbuf,"MSG4",4)) {
+ cmd_msg4(&cmdbuf[5]);
+ }
+
else if (!strncasecmp(cmdbuf,"INFO",4)) {
cmd_info();
}
--- /dev/null
+/*
+ * mime_parser.c
+ *
+ * This is a really bad attempt at writing a parser to handle MIME-encoded
+ * data, including multipart messages. In the case of WebCit, the input data
+ * might be a form containing uploaded files. In the Citadel server, the data
+ * is more likely to be an actual MIME-encoded message.
+ *
+ * Copyright (c) 1998-1999 by Art Cancro
+ * This code is distributed under the terms of the GNU General Public License.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <string.h>
+#include "mime_parser.h"
+
+
+
+void extract_key(char *target, char *source, char *key) {
+ int a, b;
+
+ strcpy(target, source);
+ for (a=0; a<strlen(target); ++a) {
+ if ((!strncasecmp(&target[a], key, strlen(key)))
+ && (target[a+strlen(key)]=='=')) {
+ strcpy(target, &target[a+strlen(key)+1]);
+ if (target[0]==34) strcpy(target, &target[1]);
+ for (b=0; b<strlen(target); ++b)
+ if (target[b]==34) target[b]=0;
+ return;
+ }
+ }
+ strcpy(target, "");
+ }
+
+
+
+/*
+ * The very back end for the component handler
+ * (This function expects to be fed CONTENT ONLY, no headers)
+ */
+void do_something_with_it(char *content,
+ int length,
+ char *content_type,
+ char *content_disposition,
+ void (*CallBack)
+ (char *cbname,
+ char *cbfilename,
+ char *cbencoding,
+ void *cbcontent,
+ char *cbtype,
+ size_t cblength)
+ ) {
+ char name[256];
+ char filename[256];
+
+ extract_key(name, content_disposition, " name");
+ extract_key(filename, content_disposition, "filename");
+
+ /* Nested multipart gets recursively fed back into the parser */
+ if (!strncasecmp(content_type, "multipart", 9)) {
+ mime_parser(content, length, content_type, CallBack);
+ }
+
+ /**** OTHERWISE, HERE'S WHERE WE HANDLE THE STUFF!! *****/
+
+ CallBack(name, filename, "", content, content_type, length);
+
+ /**** END OF STUFF-HANDLER ****/
+
+ }
+
+
+/*
+ * Take a part, figure out its length, and do something with it
+ * (This function expects to be fed HEADERS+CONTENT)
+ */
+void handle_part(char *content,
+ int part_length,
+ char *supplied_content_type,
+ void (*CallBack)
+ (char *cbname,
+ char *cbfilename,
+ char *cbencoding,
+ void *cbcontent,
+ char *cbtype,
+ size_t cblength)
+ ) {
+ char content_type[256];
+ char content_disposition[256];
+ char *start;
+ char buf[512];
+ int crlf = 0; /* set to 1 for crlf-style newlines */
+ int actual_length;
+
+ strcpy(content_type, supplied_content_type);
+
+ /* Strip off any leading blank lines. */
+ start = content;
+ while ((!strncmp(start, "\r", 1)) || (!strncmp(start, "\n", 1))) {
+ ++start;
+ --part_length;
+ }
+
+ /* At this point all we have left is the headers and the content. */
+ do {
+ strcpy(buf, "");
+ do {
+ buf[strlen(buf)+1] = 0;
+ if (strlen(buf)<((sizeof buf)-1)) {
+ strncpy(&buf[strlen(buf)], start, 1);
+ }
+ ++start;
+ --part_length;
+ } while((buf[strlen(buf)-1] != 10) && (part_length>0));
+ if (part_length <= 0) return;
+ buf[strlen(buf)-1] = 0;
+ if (buf[strlen(buf)-1]==13) {
+ buf[strlen(buf)-1] = 0;
+ crlf = 1;
+ }
+ if (!strncasecmp(buf, "Content-type: ", 14)) {
+ strcpy(content_type, &buf[14]);
+ }
+ if (!strncasecmp(buf, "Content-disposition: ", 21)) {
+ strcpy(content_disposition, &buf[21]);
+ }
+ } while (strlen(buf)>0);
+
+ if (crlf) actual_length = part_length - 2;
+ else actual_length = part_length - 1;
+
+ /* Now that we've got this component isolated, what to do with it? */
+ do_something_with_it(start, actual_length,
+ content_type, content_disposition, CallBack);
+
+ }
+
+
+/*
+ * Break out the components of a multipart message
+ * (This function expects to be fed CONTENT ONLY, no headers)
+ */
+
+
+void mime_parser(char *content,
+ int ContentLength,
+ char *ContentType,
+ void (*CallBack)
+ (char *cbname,
+ char *cbfilename,
+ char *cbencoding,
+ void *cbcontent,
+ char *cbtype,
+ size_t cblength)
+ ) {
+ char boundary[256];
+ char endary[256];
+ int have_boundary = 0;
+ int a;
+ char *ptr;
+ char *beginning;
+ int bytes_processed = 0;
+ int part_length;
+
+ /* If it's not multipart, don't process it as multipart */
+ if (strncasecmp(ContentType, "multipart", 9)) {
+ do_something_with_it(content, ContentLength,
+ ContentType, "", CallBack);
+ return;
+ }
+
+ /* Figure out what the boundary is */
+ strcpy(boundary, ContentType);
+ for (a=0; a<strlen(boundary); ++a) {
+ if (!strncasecmp(&boundary[a], "boundary=", 9)) {
+ boundary[0]='-';
+ boundary[1]='-';
+ strcpy(&boundary[2], &boundary[a+9]);
+ have_boundary = 1;
+ a = 0;
+ }
+ if ((boundary[a]==13) || (boundary[a]==10)) {
+ boundary[a] = 0;
+ }
+ }
+ if (boundary[2]==34) {
+ strcpy(&boundary[2], &boundary[3]);
+ for (a=2; a<strlen(boundary); ++a)
+ if (boundary[a]==34) boundary[a]=0;
+ }
+
+ /* We can't process multipart messages without a boundary. */
+ if (have_boundary == 0) return;
+ strcpy(endary, boundary);
+ strcat(endary, "--");
+ fprintf(stderr, "BOUNDARY: %s\n", boundary);
+
+ ptr = content;
+
+ /* Seek to the beginning of the next boundary */
+ while (bytes_processed < ContentLength) {
+ /* && (strncasecmp(ptr, boundary, strlen(boundary))) ) { */
+
+ if (strncasecmp(ptr, boundary, strlen(boundary))) {
+ ++ptr;
+ ++bytes_processed;
+ }
+
+ /* See if we're at the end */
+ if (!strncasecmp(ptr, endary, strlen(endary))) {
+ return;
+ }
+
+ /* Seek to the end of the boundary string */
+ if (!strncasecmp(ptr, boundary, strlen(boundary))) {
+ fprintf(stderr, "FOUNDA BOUNDA\n");
+ while ( (bytes_processed < ContentLength)
+ && (strncasecmp(ptr, "\n", 1)) ) {
+ ++ptr;
+ ++bytes_processed;
+ }
+ beginning = ptr;
+ part_length = 0;
+ while ( (bytes_processed < ContentLength)
+ && (strncasecmp(ptr, boundary, strlen(boundary))) ) {
+ ++ptr;
+ ++bytes_processed;
+ ++part_length;
+ }
+ handle_part(beginning, part_length, "", CallBack);
+ /* Back off so we can see the next boundary */
+ --ptr;
+ --bytes_processed;
+ }
+ }
+ }
--- /dev/null
+void extract_key(char *target, char *source, char *key);
+
+void do_something_with_it(char *content,
+ int length,
+ char *content_type,
+ char *content_disposition,
+ void (*CallBack)
+ (char *cbname,
+ char *cbfilename,
+ char *cbencoding,
+ void *cbcontent,
+ char *cbtype,
+ size_t cblength)
+ );
+
+void handle_part(char *content,
+ int part_length,
+ char *supplied_content_type,
+ void (*CallBack)
+ (char *cbname,
+ char *cbfilename,
+ char *cbencoding,
+ void *cbcontent,
+ char *cbtype,
+ size_t cblength)
+ );
+
+void mime_parser(char *content,
+ int ContentLength,
+ char *ContentType,
+ void (*CallBack)
+ (char *cbname,
+ char *cbfilename,
+ char *cbencoding,
+ void *cbcontent,
+ char *cbtype,
+ size_t cblength)
+ );
#include "control.h"
#include "dynloader.h"
#include "tools.h"
+#include "mime_parser.h"
#define MSGS_ALL 0
#define MSGS_OLD 1
}
+
+/*
+ */
+void part_handler(char *name, char *filename, char *encoding,
+ void *content, char *cbtype, size_t length) {
+
+ cprintf("part=%s|%s|%s|%s|%d\n",
+ name, filename, encoding, cbtype, length);
+ }
+
+
+
+/*
+ * Feed MIME-encoded stuff to the mime_parser
+ */
+void output_mime_parts(char *msg) {
+ char content_type[256];
+ int content_length = (-1);
+ char buf[256];
+ CIT_UBYTE rch;
+ char *mptr, *meas;
+ int i;
+
+ strcpy(content_type, "");
+ mptr = msg;
+
+ while(1) {
+ buf[0] = 0;
+ do {
+ do {
+ buf[strlen(buf)+1] = 0;
+ rch = *mptr++;
+ if (strlen(buf)<((sizeof buf)-1))
+ buf[strlen(buf)] = rch;
+ } while ( (rch > 0) && (rch != 10) );
+ if (buf[strlen(buf)-1]==10) {
+ buf[strlen(buf)-1] = 0;
+ }
+ else {
+ return;
+ }
+ if (buf[strlen(buf)-1]==13) buf[strlen(buf)-1]=0;
+ } while (buf[strlen(buf)-1]==';');
+ for (i=0; i<strlen(buf); ++i) if (isspace(buf[i])) buf[i]=' ';
+ if (!strncasecmp(buf, "Content-type: ", 14))
+ strcpy(content_type, &buf[14]);
+ if (!strncasecmp(buf, "Content-length: ", 16))
+ content_length = atoi(&buf[16]);
+ if (strlen(buf)==0) {
+ if (content_length < 0) {
+ content_length = 0;
+ meas = mptr;
+ while (*mptr++ != 0) ++content_length;
+ }
+ cprintf("mime=type=%s\n", content_type);
+ cprintf("mime=len=%d\n", content_length);
+ mime_parser(mptr, content_length, content_type,
+ *part_handler);
+ return;
+ }
+ }
+ }
+
+
+
+
/*
* Get a message off disk. (return value is the message's timestamp)
*
*/
-time_t output_message(char *msgid, int mode,
- int headers_only, int desired_section) {
+time_t output_message(char *msgid, int mode, int headers_only) {
long msg_num;
int a;
CIT_UBYTE ch, rch;
/* begin header processing loop for Citadel message format */
- if (mode == MT_CITADEL) while(ch = *mptr++, (ch!='M' && ch!=0)) {
+ if ((mode == MT_CITADEL)||(mode == MT_MIME))
+ while(ch = *mptr++, (ch!='M' && ch!=0)) {
buf[0] = 0;
do {
buf[strlen(buf)+1] = 0;
return(xtime);
}
+ /* do some sort of MIME output */
+ if ( (mode == MT_MIME) && (format_type == 4) ) {
+ output_mime_parts(mptr);
+ cprintf("000\n");
+ cdb_free(dmsgtext);
+ return(xtime);
+ }
+
if (headers_only) {
/* give 'em a length */
msg_len = 0L;
{
char msgid[256];
int headers_only = 0;
- int desired_section = 0;
extract(msgid,cmdbuf,0);
headers_only = extract_int(cmdbuf, 1);
- desired_section = extract_int(cmdbuf, 2);
- output_message(msgid, MT_CITADEL, headers_only, desired_section);
+ output_message(msgid, MT_CITADEL, headers_only);
return;
}
extract(msgid,cmdbuf,0);
headers_only = extract_int(cmdbuf,1);
- output_message(msgid,MT_RFC822,headers_only,0);
+ output_message(msgid,MT_RFC822,headers_only);
}
/*
extract(msgid,cmdbuf,0);
headers_only = extract_int(cmdbuf,1);
- output_message(msgid,MT_RAW,headers_only,0);
+ output_message(msgid,MT_RAW,headers_only);
+ }
+
+/*
+ * display a message (mode 4 - MIME) (FIX ... still evolving, not complete)
+ */
+void cmd_msg4(char *cmdbuf)
+{
+ char msgid[256];
+
+ extract(msgid, cmdbuf, 0);
+
+ output_message(msgid, MT_MIME, 0);
}
void help_subst (char *strbuf, char *source, char *dest);
void do_help_subst (char *buffer);
void memfmout (int width, char *mptr, char subst);
-time_t output_message (char *msgid, int mode,
- int headers_only, int desired_section);
+void output_mime_parts(char *);
+time_t output_message (char *msgid, int mode, int headers_only);
void cmd_msg0 (char *cmdbuf);
void cmd_msg2 (char *cmdbuf);
void cmd_msg3 (char *cmdbuf);
+void cmd_msg4 (char *cmdbuf);
long int send_message (char *message_in_memory, size_t message_length,
int generate_id);
void loadtroom (void);
for (a=0; a<(CC->num_msgs); ++a) {
delnum = MessageFromList(a);
sprintf(msgid, "%ld", delnum);
- xtime = output_message(msgid, MT_DATE, 0, 0);
+ xtime = output_message(msgid, MT_DATE, 0);
if ((xtime > 0L)
&& (now - xtime > (time_t)(epbuf.expire_value * 86400L))) {
#define UPL_IMAGE 2
+/*
+ * message transfer formats
+ */
+#define MT_CITADEL 0 /* Citadel proprietary */
+#define MT_DATE 1 /* We're only looking for the date */
+#define MT_RFC822 2 /* RFC822 */
+#define MT_RAW 3 /* IGnet raw format */
+#define MT_MIME 4 /* We're only looking for the date */
+
/*
* Citadel DataBases (define one for each cdb we need to open)
"messages" (purge old messages out of each room)
"users" (purge old users from the userlog)
+ "rooms" (remove rooms which have not been posted in for some time)
+ "visits" (purge dereferenced user/room relationship records)
EXPI returns OK (probably after a long delay while it does its work) if it
succeeds; otherwise it returns an ERROR code.