]> code.citadel.org Git - citadel.git/blobdiff - citadel/msgbase.c
* rework imap tokenizer, we no longer copy the stuff around, we keep a reference...
[citadel.git] / citadel / msgbase.c
index 9f4c5f222be5cae08cb154852d5cbe05564cf9e7..87641e3eef8a96e690bc72cd7982525b6f652b1c 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Implements the message store.
  * 
- * Copyright (c) 1987-2009 by the citadel.org team
+ * Copyright (c) 1987-2010 by the citadel.org team
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -309,10 +309,7 @@ void headers_euid(long msgnum, void *userdata)
                return;
        }
 
-       cprintf("%ld|%s|\n",
-               msgnum,
-               (msg->cm_fields['U'] ? msg->cm_fields['U'] : "")
-       );
+       cprintf("%ld|%s|\n", msgnum, (msg->cm_fields['E'] ? msg->cm_fields['E'] : ""));
        CtdlFreeMessage(msg);
 }
 
@@ -760,6 +757,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string,
                                       || ((mode == MSGS_LAST) && (a >= (num_msgs - ref)))
                                   || ((mode == MSGS_FIRST) && (a < ref))
                                || ((mode == MSGS_GT) && (thismsg > ref))
+                               || ((mode == MSGS_LT) && (thismsg < ref))
                                || ((mode == MSGS_EQ) && (thismsg == ref))
                            )
                            ) {
@@ -827,6 +825,8 @@ void cmd_msgs(char *cmdbuf)
                mode = MSGS_LAST;
        else if (!strncasecmp(which, "GT", 2))
                mode = MSGS_GT;
+       else if (!strncasecmp(which, "LT", 2))
+               mode = MSGS_LT;
        else if (!strncasecmp(which, "SEARCH", 6))
                mode = MSGS_SEARCH;
        else
@@ -927,79 +927,92 @@ void do_help_subst(char *buffer)
  */
 void memfmout(
        char *mptr,             /* where are we going to get our text from? */
-       char subst,             /* nonzero if we should do substitutions */
-       char *nl)               /* string to terminate lines with */
+       const char *nl)         /* string to terminate lines with */
 {
-       int a, b, c;
-       int real = 0;
-       int old = 0;
-       cit_uint8_t ch;
-       char aaa[140];
-       char buffer[SIZ];
-       static int width = 80;
-
-       strcpy(aaa, "");
-       old = 255;
-       strcpy(buffer, "");
-       c = 1;                  /* c is the current pos */
-
+       StrBuf *OutBuf;
+       char *LineStart;
+       char *LastBlank;
+       size_t len;
+       size_t NLLen;
+       char *eptr;
+       int NLFound, NLFoundLastTime;
+       int Found;
+
+       len = strlen (mptr);
+       NLLen = strlen (nl);
+       eptr = mptr + len;
+
+       OutBuf = NewStrBufPlain(NULL, 200);
+       
+       NLFound = NLFoundLastTime = 0;
        do {
-               if (subst) {
-                       while (ch = *mptr, ((ch != 0) && (strlen(buffer) < 126))) {
-                               ch = *mptr++;
-                               buffer[strlen(buffer) + 1] = 0;
-                               buffer[strlen(buffer)] = ch;
-                       }
-
-                       if (buffer[0] == '^')
-                               do_help_subst(buffer);
-
-                       buffer[strlen(buffer) + 1] = 0;
-                       a = buffer[0];
-                       strcpy(buffer, &buffer[1]);
-               } else {
-                       ch = *mptr++;
-               }
-
-               old = real;
-               real = ch;
-
-               if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10)) {
-                       ch = 32;
-               }
-               if (((old == 13) || (old == 10)) && (isspace(real))) {
-                       cprintf("%s", nl);
-                       c = 1;
-               }
-               if (ch > 32) {
-                       if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
-                               cprintf("%s%s", nl, aaa);
-                               c = strlen(aaa);
-                               aaa[0] = 0;
-                       }
-                       b = strlen(aaa);
-                       aaa[b] = ch;
-                       aaa[b + 1] = 0;
-               }
-               if (ch == 32) {
-                       if ((strlen(aaa) + c) > (width - 5)) {
-                               cprintf("%s", nl);
-                               c = 1;
+               size_t i;
+
+               LineStart = LastBlank = mptr;
+               Found = 'x';
+               i = 0;
+               while (Found == 'x')
+               {
+                       if (LineStart[i] == '\n')
+                               Found = '\n';
+                       else if (LineStart[i] == '\r')
+                               Found = '\r';
+                       else if (LineStart[i] == ' ') 
+                       {
+                               LastBlank = &LineStart[i];
+                               i++;
                        }
-                       cprintf("%s ", aaa);
-                       ++c;
-                       c = c + strlen(aaa);
-                       strcpy(aaa, "");
+                       else if ((i > 80) && (LineStart != LastBlank))
+                               Found = ' ';
+                       else if (LineStart[i] == '\0')
+                               Found = '\0';
+                       else i++;
                }
-               if ((ch == 13) || (ch == 10)) {
-                       cprintf("%s%s", aaa, nl);
-                       c = 1;
-                       strcpy(aaa, "");
+               switch (Found)
+               {
+               case '\n':
+                       if (LineStart[i + 1] == '\r')
+                               mptr = &LineStart[i + 2];
+                       else 
+                               mptr = &LineStart[i + 1];
+                       i--;
+                       NLFound = 1;
+                       break;
+               case '\r':
+                       if (LineStart[i + 1] == '\n')
+                               mptr = &LineStart[i + 2];
+                       else 
+                               mptr = &LineStart[i + 1];
+                       i--;
+                       NLFound = 1;
+                       break;
+               case '\0':
+                       mptr = &LineStart[i + 1];
+                       i--;
+                       NLFound = 0;
+                       break;
+               case ' ':
+                       mptr = LastBlank + 1;
+                       i = LastBlank - LineStart;
+                       NLFound = 0;
+                       break;
+               case 'x':
+                       /* WHUT? */
+                       while (*mptr != '\0') mptr++;
+                       break;
                }
-
-       } while (ch > 0);
-
-       cprintf("%s%s", aaa, nl);
+               if (NLFoundLastTime)
+                       StrBufPlain(OutBuf, HKEY(" "));
+               else
+                       FlushStrBuf(OutBuf);
+               StrBufAppendBufPlain(OutBuf, LineStart, i, 0);
+               StrBufAppendBufPlain(OutBuf, nl, NLLen, 0);
+
+               cputbuf(OutBuf);
+               NLFoundLastTime = NLFound;
+       } while (*mptr != '\0');
+
+       FreeStrBuf(&OutBuf);
 }
 
 
@@ -1015,8 +1028,15 @@ void list_this_part(char *name, char *filename, char *partnum, char *disp,
        
        ma = (struct ma_info *)cbuserdata;
        if (ma->is_ma == 0) {
-               cprintf("part=%s|%s|%s|%s|%s|%ld|%s\n",
-                       name, filename, partnum, disp, cbtype, (long)length, cbid);
+               cprintf("part=%s|%s|%s|%s|%s|%ld|%s|%s\n",
+                       name, 
+                       filename, 
+                       partnum, 
+                       disp, 
+                       cbtype, 
+                       (long)length, 
+                       cbid, 
+                       cbcharset);
        }
 }
 
@@ -1104,11 +1124,12 @@ void mime_spew_section(char *name, char *filename, char *partnum, char *disp,
        ||      (!IsEmptyStr(cbid) && (!strcasecmp(CC->download_desired_section, cbid)))
        ) {
                *found_it = 1;
-               cprintf("%d %d|-1|%s|%s\n",
+               cprintf("%d %d|-1|%s|%s|%s\n",
                        BINARY_FOLLOWS,
                        (int)length,
                        filename,
-                       cbtype
+                       cbtype,
+                       cbcharset
                );
                client_write(content, length);
        }
@@ -1743,7 +1764,7 @@ int CtdlOutputPreLoadedMsg(
        char allkeys[30];
        char display_name[256];
        char *mptr, *mpptr;
-       char *nl;       /* newline string */
+       const char *nl; /* newline string */
        int suppress_f = 0;
        int subject_found = 0;
        struct ma_info ma;
@@ -2132,19 +2153,49 @@ START_TEXT:
         */
        if (TheMessage->cm_format_type == FMT_FIXED) {
                int buflen;
+               int xlline = 0;
+               int nllen = strlen (nl);
                if (mode == MT_MIME) {
                        cprintf("Content-type: text/plain\n\n");
                }
                *buf = '\0';
                buflen = 0;
                while (ch = *mptr++, ch > 0) {
-                       if (ch == 13)
-                               ch = 10;
-                       if ((ch == 10) || (buflen > 250)) {
+                       if (ch == '\n')
+                               ch = '\r';
+
+                       if ((buflen > 250) && (!xlline)){
+                               int tbuflen;
+                               tbuflen = buflen;
+
+                               while ((buflen > 0) && 
+                                      (!isspace(buf[buflen])))
+                                       buflen --;
+                               if (buflen == 0) {
+                                       xlline = 1;
+                               }
+                               else {
+                                       mptr -= tbuflen - buflen;
+                                       buf[buflen] = '\0';
+                                       ch = '\r';
+                               }
+                       }
+                       /* if we reach the outer bounds of our buffer, 
+                          abort without respect what whe purge. */
+                       if (xlline && 
+                           ((isspace(ch)) || 
+                            (buflen > SIZ - nllen - 2)))
+                               ch = '\r';
+
+                       if (ch == '\r') {
+                               memcpy (&buf[buflen], nl, nllen);
+                               buflen += nllen;
                                buf[buflen] = '\0';
-                               cprintf("%s%s", buf, nl);
+
+                               client_write(buf, buflen);
                                *buf = '\0';
                                buflen = 0;
+                               xlline = 0;
                        } else {
                                buf[buflen] = ch;
                                buflen++;
@@ -2166,7 +2217,7 @@ START_TEXT:
                if (mode == MT_MIME) {
                        cprintf("Content-type: text/x-citadel-variformat\n\n");
                }
-               memfmout(mptr, 0, nl);
+               memfmout(mptr, nl);
        }
 
        /* If the message on disk is format 4 (MIME), we've gotta hand it
@@ -2702,7 +2753,7 @@ void ReplicationChecks(struct CtdlMessage *msg) {
        /*CtdlLogPrintf(CTDL_DEBUG, "Exclusive ID: <%s> for room <%s>\n",
                msg->cm_fields['E'], CC->room.QRname);*/
 
-       old_msgnum = locate_message_by_euid(msg->cm_fields['E'], &CC->room);
+       old_msgnum = CtdlLocateMessageByEuid(msg->cm_fields['E'], &CC->room);
        if (old_msgnum > 0L) {
                CtdlLogPrintf(CTDL_DEBUG, "ReplicationChecks() replacing message %ld\n", old_msgnum);
                CtdlDeleteMessages(CC->room.QRname, &old_msgnum, 1, "");
@@ -2717,7 +2768,7 @@ void ReplicationChecks(struct CtdlMessage *msg) {
 long CtdlSubmitMsg(struct CtdlMessage *msg,    /* message to save */
                   struct recptypes *recps,     /* recipients (if mail) */
                   char *force,                 /* force a particular room? */
-                  int flags                    /* should the bessage be exported clean? */
+                  int flags                    /* should the message be exported clean? */
 ) {
        char submit_filename[128];
        char generated_timestamp[32];
@@ -2727,7 +2778,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        char content_type[SIZ];                 /* We have to learn this */
        char recipient[SIZ];
        long newmsgid;
-       char *mptr = NULL;
+       const char *mptr = NULL;
        struct ctdluser userbuf;
        int a, i;
        struct MetaData smi;
@@ -2742,7 +2793,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        struct addresses_to_be_filed *aptr = NULL;
        char *saved_rfc822_version = NULL;
        int qualified_for_journaling = 0;
-       struct CitContext *CCC = CC;            /* CachedCitContext - performance boost */
+       CitContext *CCC = CC;           /* CachedCitContext - performance boost */
        char bounce_to[1024] = "";
        size_t tmp = 0;
        int rv = 0;
@@ -2962,7 +3013,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
                        msg->cm_fields['W'] = strdup(recipient);
                        CtdlMailboxName(actual_rm, sizeof actual_rm, &userbuf, MAILROOM);
                        CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0, msg);
-                       BumpNewMailCounter(userbuf.usernum);
+                       CtdlBumpNewMailCounter(userbuf.usernum);
                        if (!IsEmptyStr(config.c_funambol_host) || !IsEmptyStr(config.c_pager_program)) {
                        /* Generate a instruction message for the Funambol notification
                         * server, in the same style as the SMTP queue
@@ -3202,105 +3253,102 @@ void quickie_message(const char *from, const char *fromaddr, char *to, char *roo
 /*
  * Back end function used by CtdlMakeMessage() and similar functions
  */
-char *CtdlReadMessageBody(char *terminator,    /* token signalling EOT */
-                       size_t maxlen,          /* maximum message length */
-                       char *exist,            /* if non-null, append to it;
-                                                  exist is ALWAYS freed  */
-                       int crlf,               /* CRLF newlines instead of LF */
-                       int sock                /* socket handle or 0 for this session's client socket */
-                       ) {
-       char buf[1024];
-       int linelen;
-       size_t message_len = 0;
-       size_t buffer_len = 0;
-       char *ptr;
-       char *m;
+StrBuf *CtdlReadMessageBodyBuf(char *terminator,       /* token signalling EOT */
+                              long tlen,
+                              size_t maxlen,           /* maximum message length */
+                              char *exist,             /* if non-null, append to it;
+                                                          exist is ALWAYS freed  */
+                              int crlf,                /* CRLF newlines instead of LF */
+                              int *sock                /* socket handle or 0 for this session's client socket */
+                       ) 
+{
+       StrBuf *Message;
+       StrBuf *LineBuf;
        int flushing = 0;
        int finished = 0;
        int dotdot = 0;
 
+       LineBuf = NewStrBufPlain(NULL, SIZ);
        if (exist == NULL) {
-               m = malloc(4096);
-               m[0] = 0;
-               buffer_len = 4096;
-               message_len = 0;
+               Message = NewStrBufPlain(NULL, 4 * SIZ);
        }
        else {
-               message_len = strlen(exist);
-               buffer_len = message_len + 4096;
-               m = realloc(exist, buffer_len);
-               if (m == NULL) {
-                       free(exist);
-                       return m;
-               }
+               Message = NewStrBufPlain(exist, -1);
+               free(exist);
        }
 
        /* Do we need to change leading ".." to "." for SMTP escaping? */
-       if (!strcmp(terminator, ".")) {
+       if ((tlen == 1) && (*terminator == '.')) {
                dotdot = 1;
        }
 
-       /* flush the input if we have nowhere to store it */
-       if (m == NULL) {
-               flushing = 1;
-       }
-
        /* read in the lines of message text one by one */
        do {
-               if (sock > 0) {
-                       if (sock_getln(sock, buf, (sizeof buf - 3)) < 0) finished = 1;
-               }
-               else {
-                       if (client_getln(buf, (sizeof buf - 3)) < 1) finished = 1;
-               }
-               if (!strcmp(buf, terminator)) finished = 1;
-               if (crlf) {
-                       strcat(buf, "\r\n");
+               if (sock != NULL) {
+                       if ((CtdlSockGetLine(sock, LineBuf) < 0) ||
+                           (*sock == -1))
+                               finished = 1;
                }
                else {
-                       strcat(buf, "\n");
-               }
-
-               /* Unescape SMTP-style input of two dots at the beginning of the line */
-               if (dotdot) {
-                       if (!strncmp(buf, "..", 2)) {
-                               strcpy(buf, &buf[1]);
-                       }
+                       if (CtdlClientGetLine(LineBuf) < 0) finished = 1;
                }
+               if ((StrLength(LineBuf) == tlen) && 
+                   (!strcmp(ChrPtr(LineBuf), terminator)))
+                       finished = 1;
 
                if ( (!flushing) && (!finished) ) {
-                       /* Measure the line */
-                       linelen = strlen(buf);
-       
-                       /* augment the buffer if we have to */
-                       if ((message_len + linelen) >= buffer_len) {
-                               ptr = realloc(m, (buffer_len * 2) );
-                               if (ptr == NULL) {      /* flush if can't allocate */
-                                       flushing = 1;
-                               } else {
-                                       buffer_len = (buffer_len * 2);
-                                       m = ptr;
-                                       CtdlLogPrintf(CTDL_DEBUG, "buffer_len is now %ld\n", (long)buffer_len);
-                               }
+                       if (crlf) {
+                               StrBufAppendBufPlain(LineBuf, HKEY("\r\n"), 0);
                        }
-       
-                       /* Add the new line to the buffer.  NOTE: this loop must avoid
-                       * using functions like strcat() and strlen() because they
-                       * traverse the entire buffer upon every call, and doing that
-                       * for a multi-megabyte message slows it down beyond usability.
-                       */
-                       strcpy(&m[message_len], buf);
-                       message_len += linelen;
+                       else {
+                               StrBufAppendBufPlain(LineBuf, HKEY("\n"), 0);
+                       }
+                       
+                       /* Unescape SMTP-style input of two dots at the beginning of the line */
+                       if ((dotdot) &&
+                           (StrLength(LineBuf) == 2) && 
+                           (!strcmp(ChrPtr(LineBuf), "..")))
+                       {
+                               StrBufCutLeft(LineBuf, 1);
+                       }
+                       
+                       StrBufAppendBuf(Message, LineBuf, 0);
                }
 
                /* if we've hit the max msg length, flush the rest */
-               if (message_len >= maxlen) flushing = 1;
+               if (StrLength(Message) >= maxlen) flushing = 1;
 
        } while (!finished);
-       return(m);
+       FreeStrBuf(&LineBuf);
+       return Message;
 }
 
 
+/*
+ * Back end function used by CtdlMakeMessage() and similar functions
+ */
+char *CtdlReadMessageBody(char *terminator,    /* token signalling EOT */
+                         long tlen,
+                         size_t maxlen,                /* maximum message length */
+                         char *exist,          /* if non-null, append to it;
+                                                  exist is ALWAYS freed  */
+                         int crlf,             /* CRLF newlines instead of LF */
+                         int *sock             /* socket handle or 0 for this session's client socket */
+       ) 
+{
+       StrBuf *Message;
+
+       Message = CtdlReadMessageBodyBuf(terminator,
+                                        tlen,
+                                        maxlen,
+                                        exist,
+                                        crlf,
+                                        sock);
+       if (Message == NULL)
+               return NULL;
+       else
+               return SmashStrBuf(&Message);
+}
 
 
 /*
@@ -3426,7 +3474,7 @@ struct CtdlMessage *CtdlMakeMessage(
                msg->cm_fields['M'] = preformatted_text;
        }
        else {
-               msg->cm_fields['M'] = CtdlReadMessageBody("000", config.c_maxmsglen, NULL, 0, 0);
+               msg->cm_fields['M'] = CtdlReadMessageBody(HKEY("000"), config.c_maxmsglen, NULL, 0, 0);
        }
 
        return(msg);
@@ -3548,7 +3596,7 @@ int CtdlCheckInternetMailPermission(struct ctdluser *who) {
  *
  * Caller needs to free the result using free_recipients()
  */
-struct recptypes *validate_recipients(char *supplied_recipients, 
+struct recptypes *validate_recipients(const char *supplied_recipients, 
                                      const char *RemoteIdentifier, 
                                      int Flags) {
        struct recptypes *ret;
@@ -4204,7 +4252,10 @@ int CtdlDeleteMessages(char *room_name,          /* which room */
                cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, (int)sizeof(long),
                          msglist, (int)(num_msgs * sizeof(long)));
 
-               qrbuf.QRhighest = msglist[num_msgs - 1];
+               if (num_msgs > 0)
+                       qrbuf.QRhighest = msglist[num_msgs - 1];
+               else
+                       qrbuf.QRhighest = 0;
        }
        CtdlPutRoomLock(&qrbuf);