* New command 'WIKI history|<pagename>' to list the revision history for a wiki page.
[citadel.git] / citadel / msgbase.c
index a46067550e5ddff412e96cb487d8f1e31b9aef89..1e1b3a198388e7176050badd4265a9a2def1bafd 100644 (file)
@@ -2,7 +2,22 @@
  * $Id$
  *
  * Implements the message store.
+ * 
+ * Copyright (c) 1987-2009 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
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
  *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include "sysdep.h"
@@ -353,7 +368,6 @@ void CtdlSetSeen(long *target_msgnums, int num_target_msgnums,
        StrBuf *histr;
        const char *pvset;
        char *is_set;   /* actually an array of booleans */
-       int w = 0;
 
        /* Don't bother doing *anything* if we were passed a list of zero messages */
        if (num_target_msgnums < 1) {
@@ -469,14 +483,11 @@ void CtdlSetSeen(long *target_msgnums, int num_target_msgnums,
                        }
                }
 
-               w = 0;  /* set to 1 if we write something to the string */
-
                if ((was_seen == 0) && (is_seen == 1)) {
                        lo = msglist[i];
                }
                else if ((was_seen == 1) && (is_seen == 0)) {
                        hi = msglist[i-1];
-                       w = 1;
 
                        if (StrLength(vset) > 0) {
                                StrBufAppendBufPlain(vset, HKEY(","), 0);
@@ -488,8 +499,8 @@ void CtdlSetSeen(long *target_msgnums, int num_target_msgnums,
                                StrBufAppendPrintf(vset, "%ld:%ld", lo, hi);
                        }
                }
-               else if ((is_seen) && (i == num_msgs - 1)) {
-                       w = 1;
+
+               if ((is_seen) && (i == num_msgs - 1)) {
                        if (StrLength(vset) > 0) {
                                StrBufAppendBufPlain(vset, HKEY(","), 0);
                        }
@@ -504,36 +515,43 @@ void CtdlSetSeen(long *target_msgnums, int num_target_msgnums,
                was_seen = is_seen;
        }
 
-       while (StrLength(vset) > SIZ) {
+       /*
+        * We will have to stuff this string back into a 4096 byte buffer, so if it's
+        * larger than that now, truncate it by removing tokens from the beginning.
+        * The limit of 100 iterations is there to prevent an infinite loop in case
+        * something unexpected happens.
+        */
+       int number_of_truncations = 0;
+       while ( (StrLength(vset) > SIZ) && (number_of_truncations < 100) ) {
                StrBufRemove_token(vset, 0, ',');
-               w = 1;
+               ++number_of_truncations;
+       }
 
-               /*
-                * If we're truncating the sequence set of messages marked with the 'seen' flag,
-                * we want the earliest messages (the truncated ones) to be marked, not unmarked.
-                * Otherwise messages at the beginning will suddenly appear to be 'unseen'.
-                */
-               if (which_set == ctdlsetseen_seen) {
-                       StrBuf *first_tok;
-                       first_tok = NewStrBuf();
-                       StrBufExtract_token(first_tok, vset, 0, ',');
-                       StrBufRemove_token(vset, 0, ',');
-
-                       if (StrBufNum_tokens(first_tok, ':') > 1) {
-                               StrBufRemove_token(first_tok, 0, ':');
-                       }
-                       
-                       StrBuf *new_set;
-                       new_set = NewStrBuf();
-                       StrBufAppendBufPlain(new_set, HKEY("1:"), 0);
-                       StrBufAppendBuf(new_set, first_tok, 0);
-                       StrBufAppendBufPlain(new_set, HKEY(":"), 0);
-                       StrBufAppendBuf(new_set, vset, 0);
-
-                       FreeStrBuf(&vset);
-                       FreeStrBuf(&first_tok);
-                       vset = new_set;
+       /*
+        * If we're truncating the sequence set of messages marked with the 'seen' flag,
+        * we want the earliest messages (the truncated ones) to be marked, not unmarked.
+        * Otherwise messages at the beginning will suddenly appear to be 'unseen'.
+        */
+       if ( (which_set == ctdlsetseen_seen) && (number_of_truncations > 0) ) {
+               StrBuf *first_tok;
+               first_tok = NewStrBuf();
+               StrBufExtract_token(first_tok, vset, 0, ',');
+               StrBufRemove_token(vset, 0, ',');
+
+               if (StrBufNum_tokens(first_tok, ':') > 1) {
+                       StrBufRemove_token(first_tok, 0, ':');
                }
+               
+               StrBuf *new_set;
+               new_set = NewStrBuf();
+               StrBufAppendBufPlain(new_set, HKEY("1:"), 0);
+               StrBufAppendBuf(new_set, first_tok, 0);
+               StrBufAppendBufPlain(new_set, HKEY(":"), 0);
+               StrBufAppendBuf(new_set, vset, 0);
+
+               FreeStrBuf(&vset);
+               FreeStrBuf(&first_tok);
+               vset = new_set;
        }
 
        CtdlLogPrintf(CTDL_DEBUG, " after update: %s\n", ChrPtr(vset));
@@ -550,9 +568,7 @@ void CtdlSetSeen(long *target_msgnums, int num_target_msgnums,
 
        free(is_set);
        free(msglist);
-       if (w) {
-               CtdlSetRelationship(&vbuf, which_user, which_room);
-       }
+       CtdlSetRelationship(&vbuf, which_user, which_room);
        FreeStrBuf(&vset);
 }
 
@@ -1430,6 +1446,16 @@ void extract_encapsulated_message(char *name, char *filename, char *partnum, cha
 
 
 
+
+
+int CtdlDoIHavePermissionToReadMessagesInThisRoom(void) {
+       if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
+               return(om_not_logged_in);
+       }
+       return(om_ok);
+}
+
+
 /*
  * Get a message off disk.  (returns om_* values found in msgbase.h)
  * 
@@ -1440,21 +1466,29 @@ int CtdlOutputMsg(long msg_num,         /* message number (local) to fetch */
                  int do_proto,         /* do Citadel protocol responses? */
                  int crlf,             /* Use CRLF newlines instead of LF? */
                  char *section,        /* NULL or a message/rfc822 section */
-                 int flags             /* should the bessage be exported clean? */
+                 int flags             /* various flags; see msgbase.h */
 ) {
        struct CtdlMessage *TheMessage = NULL;
        int retcode = om_no_such_msg;
        struct encapmsg encap;
+       int r;
 
-       CtdlLogPrintf(CTDL_DEBUG, "CtdlOutputMsg() msgnum=%ld, mode=%d, section=%s\n", 
+       CtdlLogPrintf(CTDL_DEBUG, "CtdlOutputMsg(msgnum=%ld, mode=%d, section=%s)\n", 
                msg_num, mode,
                (section ? section : "<>")
        );
 
-       if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
-               if (do_proto) cprintf("%d Not logged in.\n",
-                       ERROR + NOT_LOGGED_IN);
-               return(om_not_logged_in);
+       r = CtdlDoIHavePermissionToReadMessagesInThisRoom();
+       if (r != om_ok) {
+               if (do_proto) {
+                       if (r == om_not_logged_in) {
+                               cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
+                       }
+                       else {
+                               cprintf("%d An unknown error has occurred.\n", ERROR);
+                       }
+               }
+               return(r);
        }
 
        /* FIXME: check message id against msglist for this room */
@@ -1706,6 +1740,13 @@ int CtdlOutputPreLoadedMsg(
                return(om_no_such_msg);
        }
 
+       /* Suppress envelope recipients if required to avoid disclosing BCC addresses.
+        * Pad it with spaces in order to avoid changing the RFC822 length of the message.
+        */
+       if ( (flags & SUPPRESS_ENV_TO) && (TheMessage->cm_fields['V'] != NULL) ) {
+               memset(TheMessage->cm_fields['V'], ' ', strlen(TheMessage->cm_fields['V']));
+       }
+               
        /* Are we downloading a MIME component? */
        if (mode == MT_DOWNLOAD) {
                if (TheMessage->cm_format_type != FMT_RFC822) {
@@ -1813,7 +1854,7 @@ int CtdlOutputPreLoadedMsg(
                      if (haschar(TheMessage->cm_fields['N'], '.') == 0) {
                        suppress_f = 1;
                }
-               
+
                /* Now spew the header fields in the order we like them. */
                safestrncpy(allkeys, FORDER, sizeof allkeys);
                for (i=0; i<strlen(allkeys); ++i) {
@@ -2302,7 +2343,7 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms
        if (num_newmsgs > 1) supplied_msg = NULL;
 
        /* Now the regular stuff */
-       if (lgetroom(&CC->room,
+       if (CtdlGetRoomLock(&CC->room,
           ((roomname != NULL) ? roomname : CC->room.QRname) )
           != 0) {
                CtdlLogPrintf(CTDL_ERR, "No such room <%s>\n", roomname);
@@ -2369,7 +2410,7 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms
 
        /* Update the highest-message pointer and unlock the room. */
        CC->room.QRhighest = highest_msg;
-       lputroom(&CC->room);
+       CtdlPutRoomLock(&CC->room);
 
        /* Perform replication checks if necessary */
        if ( (DoesThisRoomNeedEuidIndexing(&CC->room)) && (do_repl_check) ) {
@@ -2410,7 +2451,7 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms
        PerformRoomHooks(&CC->room);
 
        /* Go back to the room we were in before we wandered here... */
-       getroom(&CC->room, hold_rm);
+       CtdlGetRoom(&CC->room, hold_rm);
 
        /* Bump the reference count for all messages which were merged */
        for (i=0; i<num_msgs_to_be_merged; ++i) {
@@ -2764,8 +2805,8 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
 
        CtdlLogPrintf(CTDL_DEBUG, "Final selection: %s\n", actual_rm);
        if (strcasecmp(actual_rm, CCC->room.QRname)) {
-               /* getroom(&CCC->room, actual_rm); */
-               usergoto(actual_rm, 0, 1, NULL, NULL);
+               /* CtdlGetRoom(&CCC->room, actual_rm); */
+               CtdlUserGoto(actual_rm, 0, 1, NULL, NULL);
        }
 
        /*
@@ -2969,7 +3010,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        /* Go back to the room we started from */
        CtdlLogPrintf(CTDL_DEBUG, "Returning to original room %s\n", hold_rm);
        if (strcasecmp(hold_rm, CCC->room.QRname))
-               usergoto(hold_rm, 0, 1, NULL, NULL);
+               CtdlUserGoto(hold_rm, 0, 1, NULL, NULL);
 
        /* For internet mail, generate delivery instructions.
         * Yes, this is recursive.  Deal with it.  Infinite recursion does
@@ -3581,7 +3622,7 @@ struct recptypes *validate_recipients(char *supplied_recipients,
                                        strcat(ret->recp_room, this_recp);
                                }
                                else if ( (!strncasecmp(this_recp, "room_", 5))
-                                     && (!getroom(&tempQR, &this_recp_cooked[5])) ) {
+                                     && (!CtdlGetRoom(&tempQR, &this_recp_cooked[5])) ) {
 
                                        /* Save room so we can restore it later */
                                        tempQR2 = CC->room;
@@ -4071,7 +4112,7 @@ int CtdlDeleteMessages(char *room_name,           /* which room */
                room_name, num_dmsgnums, content_type);
 
        /* get room record, obtaining a lock... */
-       if (lgetroom(&qrbuf, room_name) != 0) {
+       if (CtdlGetRoomLock(&qrbuf, room_name) != 0) {
                CtdlLogPrintf(CTDL_ERR, "CtdlDeleteMessages(): Room <%s> not found\n",
                        room_name);
                if (need_to_free_re) regfree(&re);
@@ -4128,7 +4169,7 @@ int CtdlDeleteMessages(char *room_name,           /* which room */
 
                qrbuf.QRhighest = msglist[num_msgs - 1];
        }
-       lputroom(&qrbuf);
+       CtdlPutRoomLock(&qrbuf);
 
        /* Go through the messages we pulled out of the index, and decrement
         * their reference counts by 1.  If this is the only room the message
@@ -4245,7 +4286,7 @@ void cmd_move(char *args)
        targ[ROOMNAMELEN - 1] = 0;
        is_copy = extract_int(args, 2);
 
-       if (getroom(&qtemp, targ) != 0) {
+       if (CtdlGetRoom(&qtemp, targ) != 0) {
                cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, targ);
                return;
        }
@@ -4599,8 +4640,8 @@ void CtdlWriteObject(char *req_room,                      /* Room to stuff it in */
        msg->cm_fields['M'] = encoded_message;
 
        /* Create the requested room if we have to. */
-       if (getroom(&qrbuf, roomname) != 0) {
-               create_room(roomname, 
+       if (CtdlGetRoom(&qrbuf, roomname) != 0) {
+               CtdlCreateRoom(roomname, 
                        ( (is_mailbox != NULL) ? 5 : 3 ),
                        "", 0, 1, 0, VIEW_BBS);
        }
@@ -4635,8 +4676,8 @@ char *CtdlGetSysConfig(char *sysconfname) {
        char buf[SIZ];
        
        strcpy(hold_rm, CC->room.QRname);
-       if (getroom(&CC->room, SYSCONFIGROOM) != 0) {
-               getroom(&CC->room, hold_rm);
+       if (CtdlGetRoom(&CC->room, SYSCONFIGROOM) != 0) {
+               CtdlGetRoom(&CC->room, hold_rm);
                return NULL;
        }
 
@@ -4663,7 +4704,7 @@ char *CtdlGetSysConfig(char *sysconfname) {
                }
        }
 
-       getroom(&CC->room, hold_rm);
+       CtdlGetRoom(&CC->room, hold_rm);
 
        if (conf != NULL) do {
                extract_token(buf, conf, 0, '\n', sizeof buf);