* Brought over the mime_parser from WebCit and began preliminary work
authorArt Cancro <ajc@citadel.org>
Fri, 15 Jan 1999 02:22:12 +0000 (02:22 +0000)
committerArt Cancro <ajc@citadel.org>
Fri, 15 Jan 1999 02:22:12 +0000 (02:22 +0000)
          on supporting MIME format messages.

citadel/ChangeLog
citadel/Makefile.in
citadel/citadel.h
citadel/citserver.c
citadel/mime_parser.c [new file with mode: 0644]
citadel/mime_parser.h [new file with mode: 0644]
citadel/msgbase.c
citadel/msgbase.h
citadel/serv_expire.c
citadel/server.h
citadel/techdoc/session.txt

index 2712474274e481317013e0e5aa5aaf495e21b063..d6b3ada4fc4a0c1aac245ad9e1fb5d5179230b07 100644 (file)
@@ -1,3 +1,7 @@
+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
index e39ee663659f4eacafd9bcc8c6781f458192a1cf..69ac0bd372e8c92d0ebd552b7cc7f6f5d7450f84 100644 (file)
@@ -57,7 +57,7 @@ SOURCES=aidepost.c citadel.c citmail.c citserver.c client_chat.c commands.c \
        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)
 
@@ -91,12 +91,13 @@ netpoll: netpoll.o config.o ipc_c_tcp.o tools.o $(SNPRINTF)
 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:
index 63c14416b790df16074e58e7623578f2ca28aeb6..44e5e127b1fc86cd2f1685904e6cc21316deeb0c 100644 (file)
@@ -238,13 +238,6 @@ struct floor {
 #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"
index 07b23e84040d6824d7550193867491c39f9c06a4..a539d81d2bb9b9e7871dd5711a616538da6a318d 100644 (file)
@@ -838,6 +838,10 @@ void *context_loop(struct CitContext *con)
                        cmd_msg3(&cmdbuf[5]);
                        }
 
+               else if (!strncasecmp(cmdbuf,"MSG4",4)) {
+                       cmd_msg4(&cmdbuf[5]);
+                       }
+
                else if (!strncasecmp(cmdbuf,"INFO",4)) {
                        cmd_info();
                        }
diff --git a/citadel/mime_parser.c b/citadel/mime_parser.c
new file mode 100644 (file)
index 0000000..f7ad28d
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * 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;
+                       }
+               }
+       }
diff --git a/citadel/mime_parser.h b/citadel/mime_parser.h
new file mode 100644 (file)
index 0000000..7199d87
--- /dev/null
@@ -0,0 +1,38 @@
+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)
+               );
index 69d1a3151f058cd553d5931fbb2cb65d55f8dba8..3ab5b691ce3fc160b6fc2fb5b9717c7137baf92e 100644 (file)
@@ -21,6 +21,7 @@
 #include "control.h"
 #include "dynloader.h"
 #include "tools.h"
+#include "mime_parser.h"
 
 #define MSGS_ALL       0
 #define MSGS_OLD       1
@@ -333,12 +334,77 @@ FMTEND:   cprintf("\n");
        }
 
 
+
+/*
+ */
+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;
@@ -450,7 +516,8 @@ time_t output_message(char *msgid, int mode,
 
        /* 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;
@@ -535,6 +602,14 @@ time_t output_message(char *msgid, int mode,
                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;
@@ -597,13 +672,11 @@ void cmd_msg0(char *cmdbuf)
 {
        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;
        }
 
@@ -619,7 +692,7 @@ void cmd_msg2(char *cmdbuf)
        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);
        }
 
 /* 
@@ -639,7 +712,19 @@ void cmd_msg3(char *cmdbuf)
        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);
        }
 
 
index 0907e865f1c5401dd9608c7c21e1c786485736fd..aa969dba24ab58154e00c1e21386f5e47d249912 100644 (file)
@@ -5,11 +5,12 @@ void cmd_msgs (char *cmdbuf);
 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);
index aaa1e146d183a7921a5886a208dd171caea99527..f098db9bc92b76dc040519206b295d0dc0a08af9 100644 (file)
@@ -152,7 +152,7 @@ void DoPurgeMessages(struct quickroom *qrbuf) {
                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))) {
index 89505dcd2b6c03d651dada3944bf9dc540d6db47..cf76cb14a587593c9199fa32d9d1776c0afe370b 100644 (file)
@@ -124,6 +124,15 @@ struct ChatLine {
 #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)
index 1994dd8a59e9dc248c787b26a6fb87e824885a85..cab7a35b3c661ccd72a0838002d08898ea997b6c 100644 (file)
@@ -1623,6 +1623,8 @@ fails for any reason, ERROR is returned.
  
     "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.