* CtdlSaveMsg() is now CtdlSubmitMsg() and can accept any combination of
authorArt Cancro <ajc@citadel.org>
Sun, 30 Dec 2001 21:26:01 +0000 (21:26 +0000)
committerArt Cancro <ajc@citadel.org>
Sun, 30 Dec 2001 21:26:01 +0000 (21:26 +0000)
  local, IGnet, and Internet recipients.  Caller must supply a "struct
  recptypes" structure, which is generated by validate_recipients(), if the
  message is mail (or NULL for an ordinary post).
* BROKEN BUILD ALERT!  I haven't rewritten serv_smtp.c and serv_network.c to
  deliver messages using the new framework yet.  Don't use this code!

15 files changed:
citadel/aidepost.c
citadel/citadel.c
citadel/citadel.h
citadel/client_chat.c
citadel/docs/citadel-with-berkeley-db.txt
citadel/docs/copyright.txt
citadel/imap_misc.c
citadel/messages.c
citadel/messages.h
citadel/msgbase.c
citadel/msgbase.h
citadel/serv_chat.c
citadel/serv_network.c
citadel/serv_smtp.c
citadel/serv_vcard.c

index 37d1a295e92e8b1d4844bd61967d11070a92f646..c7a7265c60787eb995c01b9dea6e5d641cf2be38 100644 (file)
 #endif
 
 
-static void make_message(FILE *fp, char *target_room, char *author)
+/*
+ * Simplified function to generate a message in our format
+ */
+static void ap_make_message(FILE *fp, char *target_room, char *author)
 {
        int a;
        long bb, cc;
@@ -94,7 +97,6 @@ int main(int argc, char **argv)
                }
        }
 
-
        snprintf(tempspool, sizeof tempspool,
                "./network/spoolin/ap.%04x",
                getpid());
@@ -106,7 +108,7 @@ int main(int argc, char **argv)
        }
 
        /* Generate a message from stdin */
-       make_message(tempfp, target_room, author);
+       ap_make_message(tempfp, target_room, author);
 
        /* Copy it to a new temp file in the spool directory */
        rewind(tempfp);
@@ -116,8 +118,9 @@ int main(int argc, char **argv)
                perror("cannot open spool file");
                exit(errno);
        }
-       while (ch = getc(tempfp), (ch >= 0))
+       while (ch = getc(tempfp), (ch >= 0)) {
                putc(ch, spoolfp);
+       }
 
        fclose(tempfp);
        fclose(spoolfp);
index 1534b02b97da005472600faf88531de2b484d35a..ce8c2889a2bff8b3e5f44115cee96c96471507bc 100644 (file)
@@ -90,6 +90,7 @@ int timescalled;
 int posted;
 unsigned userflags;
 long usernum = 0L;             /* user number */
+time_t lastcall = 0L;          /* Date/time of previous login */
 char newnow;
 long highest_msg_read;         /* used for <A>bandon room cmd */
 long maxmsgnum;                        /* used for <G>oto */
@@ -232,6 +233,7 @@ void load_user_info(char *params)
        posted = extract_int(params, 3);
        userflags = extract_int(params, 4);
        usernum = extract_long(params, 5);
+       lastcall = extract_long(params, 6);
 }
 
 
@@ -1107,9 +1109,15 @@ PWOK:
                        enable_color = 0;
        }
 
-       printf("%s\nAccess level: %d (%s)\nUser #%ld / Call #%d\n",
-              fullname, axlevel, axdefs[(int) axlevel],
-              usernum, timescalled);
+       printf("%s\nAccess level: %d (%s)\n"
+               "User #%ld / Login #%d",
+               fullname, axlevel, axdefs[(int) axlevel],
+               usernum, timescalled);
+       if (lastcall > 0L) {
+               printf(" / Last login: %s\n",
+                       asctime(localtime(&lastcall)) );
+       }
+       printf("\n");
 
        serv_puts("CHEK");
        serv_gets(aaa);
@@ -1581,7 +1589,7 @@ TERMN8:   printf("%s logged out.\n", fullname);
                remove_march(march->march_name, 0);
        }
        if (mcmd == 30) {
-               printf("\n\nType 'off' to hang up, or next user...\n");
+               printf("\n\nType 'off' to disconnect, or next user...\n");
        }
        snprintf(aaa, sizeof aaa, "LOUT");
        serv_puts(aaa);
index 8b1e0860247f04e2dae7bfd53ad330fc2cd579b1..357df0e21f32a2637e0fd7e1b73ac007ebce1ad2 100644 (file)
@@ -208,8 +208,8 @@ struct quickroom {
 /* Miscellaneous                                                            */
 
 #define MES_NORMAL     65              /* Normal message                   */
-#define MES_ANON       66              /* "****" header                    */
-#define MES_AN2                67              /* "Anonymous" header               */
+#define MES_ANONONLY   66              /* "****" header                    */
+#define MES_ANONOPT    67              /* "Anonymous" header               */
 
 #define MES_ERROR      (-1)    /* Can't send message due to bad address   */
 #define MES_LOCAL      0       /* Local message, do no network processing */
index 49418a5d26bc190b93e0b1c58d89155b052c1065..268d47d558516382aa4f1bd2b5813d596f2331fa 100644 (file)
@@ -256,7 +256,7 @@ void page_user()
                        printf("%s\n", &buf[4]);
                        return;
                }
-               if (make_message(temp, touser, 0, 0, 0) != 0) {
+               if (client_make_message(temp, touser, 0, 0, 0) != 0) {
                        printf("No message sent.\n");
                        return;
                }
index b7fefdd98c3154cbb960871551c0d5f14065cf48..e7518a44cf79c69e2183847338678f55e8a3b323 100644 (file)
@@ -3,7 +3,7 @@
                                       
 Abstract
 
-   [1]Citadel/UX can now use the robust and scalable [2]Berkeley DB from
+   Citadel/UX can now use the robust and scalable Berkeley DB from
    Sleepycat Software as its data store, for increased scalability,
    reliability, and recoverability.
    
@@ -16,16 +16,16 @@ History and introduction
    concurrent use, and aspirations of becoming a world-class
    messaging/groupware platform someday, the developers made the decision
    to switch to an embedded database.  The Free Software Foundation's
-   [3]GDBM product was chosen for its simple API and its free license
+   GDBM product was chosen for its simple API and its free license
    (the LGPL).
    
    Somewhat less than trouble-free operation from 1998 through 2000,
    however, proved that GDBM was not the best choice.  Heavily utilized
    systems experienced occasional database corruption, often resulting in
    repeated crashes of the Citadel server.  As a result, we made the
-   decision to switch to [4]Berkeley DB.
+   decision to switch to Berkeley DB.
    
-   Berkeley DB offers [5]numerous features which help Citadel/UX to meet
+   Berkeley DB offers numerous features which help Citadel/UX to meet
    its goals as a high-end messaging platform:
      * Database sizes can scale to hundreds of terabytes
      * A transaction-based logging system
@@ -37,17 +37,23 @@ History and introduction
    THE USE OF GDBM IS DEPRECATED AND STRONGLY DISCOURAGED.  If you are
    bringing up a new site you should use Berkeley DB, period.  If you are
    maintaining an existing site using GDBM you should migrate it to Berkeley
-   DB as soon as possible.
+   DB as soon as possible, because support for it will be dropped in a future
+   release.
  
     
 Building Citadel/UX with DB support
+   If you are running Citadel/UX on a Red Hat Linux system, you don't have
+   to build Berekeley DB from source.  Simply install the RPM's for "db3"
+   and "db3-devel" (the latter may be on the second CD) and you're ready to
+   run Citadel.
 
-   Here are the steps required to get Citadel/UX running with Berkeley
-   DB as its back end data store.
+   Otherwise, here are the steps required to get Citadel running with
+   Berkeley DB as its back end data store.
    
     1. First, you must download and build Berkeley DB itself.  Citadel
        has been developed and tested with DB 3.1.17, which can be
-       downloaded from [6]www.sleepycat.com.  Follow the "[7]Building for
+       downloaded from www.sleepycat.com.  Follow the "Building for
        UNIX" instructions.  Make sure that you run the test suite, and
        perhaps test with some of the sample applications, before moving
        on.
@@ -72,7 +78,7 @@ Migrating an existing GDBM-based Citadel to a DB-based Citadel
 
    If you have an existing system, you must export your databases,
    rebuild Citadel with DB support, and then re-import your databases
-   into the new system.  Please refer to the document "[8]How to use the
+   into the new system.  Please refer to the document "How to use the
    importer/exporter" for detailed instructions on this.
    
    After you export your database, but before you re-import it, you must
@@ -92,10 +98,10 @@ Care and feeding of your DB-powered Citadel
    So do you have to keep these log files around forever?  No, but there
    are some rules you should follow:
      * Don't remove a log file if it's the only log file there.
-     * If it's not listed in the output of the db_archive[9] command, it's
+     * If it's not listed in the output of the db_archive command, it's
        not safe to remove.
      * After a successful backup (see below) log files listed by the
-       db_archive[9] command may be removed to conserve disk space, if 
+       db_archive command may be removed to conserve disk space, if 
        those log files were backed up.
 
    You may think that it's going to keep writing to that one log file
@@ -110,14 +116,3 @@ Care and feeding of your DB-powered Citadel
    server will automatically remove them when they're no longer needed.
  
  
-References
-
-   1. http://uncensored.citadel.org/citadel
-   2. http://www.sleepycat.com/
-   3. http://www.gnu.org/software/gdbm/gdbm.html
-   4. http://www.sleepycat.com/
-   5. http://www.sleepycat.com/xactfeatures.html
-   6. http://www.sleepycat.com/
-   7. http://www.sleepycat.com/docs/ref/build_unix/intro.html
-   8. http://pixel.citadel.org/citadel/docs/export.html
-   9. http://www.sleepycat.com/docs/utility/db_archive.html
index caf2d72bce3098feab163de0ce7019ab80489d78..56c12c58e8dfbb29b580ea6b526ff6dafc98365f 100644 (file)
      Brian Costello     (cosmetics, additional commands)
      Andru Luvisi       (troubleshooting and development assistance)
      Daniel Malament    (string compare function for IMAP server)
-     Stu Mark           (IGnet/Open protocol design)
+     Stu Mark           (additional client features, IGnet protocol design)
      Ben Mehlman        (additional client features)
      Ari Samson         (project management)
      John Walker        (author of public domain base64 encoder/decoder)
      Steve Williams     (documentation)
-     Ethan Young        (IGnet/Open protocol design)
+     Ethan Young        (IGnet protocol design)
     
  ------------------------------------------------------------------------------
  
index add72d2fb87c2ae9763ea54d57b3aaeeca3e425f..75e4b0d33138c072c9e9d298e366716e10ef53ee 100644 (file)
@@ -279,7 +279,7 @@ void imap_append(int num_parms, char *parms[]) {
        else {
                /* Yes ... go ahead and post! */
                if (msg != NULL) {
-                       CtdlSaveMsg(msg, "", "", 0);
+                       CtdlSubmitMsg(msg, NULL, "");
                }
                cprintf("%s OK APPEND completed\r\n", parms[0]);
        }
index 3939839d35fb812525c7024dbc209669f19a6757..bf43fd8f06c2f7d3818d32767681e837d8e29225 100644 (file)
@@ -599,7 +599,7 @@ void replace_string(char *filename, long int startpos)
 /*
  * Function to begin composing a new message
  */
-int make_message(char *filename,       /* temporary file name */
+int client_make_message(char *filename,        /* temporary file name */
                char *recipient,        /* NULL if it's not mail */
                int anon_type,          /* see MES_ types in header file */
                int format_type,
@@ -922,7 +922,7 @@ int entmsg(int is_reply,    /* nonzero if this was a <R>eply command */
        }
 
        /* Now compose the message... */
-       if (make_message(temp, buf, b, 0, c) != 0) {
+       if (client_make_message(temp, buf, b, 0, c) != 0) {
                return (2);
        }
 
index 2c4f47e4390eb3dc611ae1e6109c1a91107b2537..030e5fa1de06512e9d376c4b3c013e1d00dca89e 100644 (file)
@@ -6,7 +6,7 @@ void edit_system_message(char *which_message);
 pid_t ka_wait(int *kstatus);
 void list_urls(void);
 void check_message_base(void);
-int make_message(char *filename,       /* temporary file name */
+int client_make_message(char *filename,        /* temporary file name */
                char *recipient,        /* NULL if it's not mail */
                int anon_type,          /* see MES_ types in header file */
                int format_type,
index a7c6373ac8d385272844ec2a4f201ba6abe898ea..ba2921be9ccdb3c8d196fbbb323b441252c02098 100644 (file)
@@ -26,6 +26,7 @@
 # endif
 #endif
 
+
 #include <ctype.h>
 #include <string.h>
 #include <syslog.h>
@@ -793,6 +794,7 @@ void CtdlFreeMessage(struct CtdlMessage *msg)
 {
        int i;
 
+       lprintf(9, "CtdlFreeMessage() called\n");
        if (is_valid_message(msg) == 0) return;
 
        for (i = 0; i < 256; ++i)
@@ -1037,7 +1039,7 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage,
        }
 
        /* nhdr=yes means that we're only displaying headers, no body */
-       if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) {
+       if ((TheMessage->cm_anon_type == MES_ANONONLY) && (mode == MT_CITADEL)) {
                if (do_proto) cprintf("nhdr=yes\n");
        }
 
@@ -1049,15 +1051,18 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage,
                if (TheMessage->cm_fields['A']) {
                        strcpy(buf, TheMessage->cm_fields['A']);
                        PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
-                       if (TheMessage->cm_anon_type == MES_ANON)
+                       if (TheMessage->cm_anon_type == MES_ANONONLY) {
                                strcpy(display_name, "****");
-                       else if (TheMessage->cm_anon_type == MES_AN2)
+                       }
+                       else if (TheMessage->cm_anon_type == MES_ANONOPT) {
                                strcpy(display_name, "anonymous");
-                       else
+                       }
+                       else {
                                strcpy(display_name, buf);
+                       }
                        if ((is_room_aide())
-                           && ((TheMessage->cm_anon_type == MES_ANON)
-                            || (TheMessage->cm_anon_type == MES_AN2))) {
+                           && ((TheMessage->cm_anon_type == MES_ANONONLY)
+                            || (TheMessage->cm_anon_type == MES_ANONOPT))) {
                                sprintf(&display_name[strlen(display_name)],
                                        " [%s]", buf);
                        }
@@ -1487,7 +1492,7 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
  * Message base operation to send a message to the master file
  * (returns new message number)
  *
- * This is the back end for CtdlSaveMsg() and should not be directly
+ * This is the back end for CtdlSubmitMsg() and should not be directly
  * called by server-side modules.
  *
  */
@@ -1661,14 +1666,13 @@ int ReplicationChecks(struct CtdlMessage *msg) {
 
 
 /*
- * Save a message to disk
+ * Save a message to disk and submit it into the delivery system.
  */
-long CtdlSaveMsg(struct CtdlMessage *msg,      /* message to save */
-               char *rec,                      /* Recipient (mail) */
-               char *force,                    /* force a particular room? */
-               int supplied_mailtype)          /* local or remote type */
-{
-       char aaa[100];
+long CtdlSubmitMsg(struct CtdlMessage *msg,    /* message to save */
+               struct recptypes *recps,        /* recipients (if mail) */
+               char *force                     /* force a particular room? */
+) {
+       char aaa[SIZ];
        char hold_rm[ROOMNAMELEN];
        char actual_rm[ROOMNAMELEN];
        char force_room[ROOMNAMELEN];
@@ -1677,17 +1681,17 @@ long CtdlSaveMsg(struct CtdlMessage *msg,       /* message to save */
        long newmsgid;
        char *mptr = NULL;
        struct usersupp userbuf;
-       int a;
+       int a, i;
        struct MetaData smi;
        FILE *network_fp = NULL;
        static int seqnum = 1;
-       struct CtdlMessage *imsg;
+       struct CtdlMessage *imsg = NULL;
        char *instr;
-       int mailtype;
+       struct ser_ret smr;
+       char *hold_R, *hold_D;
 
-       lprintf(9, "CtdlSaveMsg() called\n");
+       lprintf(9, "CtdlSubmitMsg() called\n");
        if (is_valid_message(msg) == 0) return(-1);     /* self check */
-       mailtype = supplied_mailtype;
 
        /* If this message has no timestamp, we take the liberty of
         * giving it one, right now.
@@ -1717,27 +1721,6 @@ long CtdlSaveMsg(struct CtdlMessage *msg,        /* message to save */
 
        strcpy(force_room, force);
 
-       /* Strip non-printable characters out of the recipient name */
-       lprintf(9, "Checking recipient (if present)\n");
-       strcpy(recipient, rec);
-       for (a = 0; a < strlen(recipient); ++a)
-               if (!isprint(recipient[a]))
-                       strcpy(&recipient[a], &recipient[a + 1]);
-
-       /* Change "user @ xxx" to "user" if xxx is an alias for this host */
-       for (a=0; a<strlen(recipient); ++a) {
-               if (recipient[a] == '@') {
-                       if (CtdlHostAlias(&recipient[a+1]) 
-                          == hostalias_localhost) {
-                               recipient[a] = 0;
-                               lprintf(7, "Changed to <%s>\n", recipient);
-                               mailtype = MES_LOCAL;
-                       }
-               }
-       }
-
-       lprintf(9, "Recipient is <%s>, mailtype is %d\n", recipient, mailtype);
-
        /* Learn about what's inside, because it's what's inside that counts */
        lprintf(9, "Learning what's inside\n");
        if (msg->cm_fields['M'] == NULL) {
@@ -1777,7 +1760,7 @@ long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
        lprintf(9, "Switching rooms\n");
        strcpy(hold_rm, CC->quickroom.QRname);
        strcpy(actual_rm, CC->quickroom.QRname);
-       if (strlen(recipient) > 0) {
+       if (recps != NULL) {
                strcpy(actual_rm, SENTITEMS);
        }
 
@@ -1815,25 +1798,9 @@ long CtdlSaveMsg(struct CtdlMessage *msg,        /* message to save */
        lprintf(9, "Performing replication checks\n");
        if (ReplicationChecks(msg) > 0) return(-1);
 
-       /* Network mail - send a copy to the network program. */
-       if ((strlen(recipient) > 0) && (mailtype == MES_IGNET)) {
-               lprintf(9, "Sending network spool\n");
-               sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
-                       (long) getpid(), CC->cs_pid, ++seqnum);
-               lprintf(9, "Saving a copy to %s\n", aaa);
-               network_fp = fopen(aaa, "ab+");
-               if (network_fp == NULL)
-                       lprintf(2, "ERROR: %s\n", strerror(errno));
-       }
-
        /* Save it to disk */
        lprintf(9, "Saving to disk\n");
-       newmsgid = send_message(msg, network_fp);
-       if (network_fp != NULL) {
-               fclose(network_fp);
-               /* FIXME start a network run here */
-       }
-
+       newmsgid = send_message(msg, NULL);
        if (newmsgid <= 0L) return(-1);
 
        /* Write a supplemental message info record.  This doesn't have to
@@ -1862,7 +1829,8 @@ long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
        }
 
        /* For internet mail, drop a copy in the outbound queue room */
-       if (mailtype == MES_INTERNET) {
+       if (recps != NULL)
+        if (recps->num_internet > 0) {
                CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, newmsgid, 0);
        }
 
@@ -1875,16 +1843,18 @@ long CtdlSaveMsg(struct CtdlMessage *msg,       /* message to save */
        /* If this is private, local mail, make a copy in the
         * recipient's mailbox and bump the reference count.
         */
-       if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) {
+       if (recps != NULL)
+        if (recps->num_local > 0)
+         for (i=0; i<num_tokens(recps->recp_local, '|'); ++i) {
+               extract(recipient, recps->recp_local, i);
+               lprintf(9, "Delivering private local mail to <%s>\n",
+                       recipient);
                if (getuser(&userbuf, recipient) == 0) {
-                       lprintf(9, "Delivering private mail\n");
                        MailboxName(actual_rm, &userbuf, MAILROOM);
                        CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
                }
                else {
-                       lprintf(9, "No user <%s>, saving in %s> instead\n",
-                               recipient, AIDEROOM);
-                       CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0);
+                       lprintf(9, "No user <%s>\n", recipient);
                }
        }
 
@@ -1892,24 +1862,71 @@ long CtdlSaveMsg(struct CtdlMessage *msg,       /* message to save */
        lprintf(9, "Performing after-save hooks\n");
        PerformMessageHooks(msg, EVT_AFTERSAVE);
 
-       /* */
+       /* For IGnet mail, we have to save a new copy into the spooler for
+        * each recipient, with the R and D fields set to the recipient and
+        * destination-node.  This has two ugly side effects: all other
+        * recipients end up being unlisted in this recipient's copy of the
+        * message, and it has to deliver multiple messages to the same
+        * node.  We'll revisit this again in a year or so when everyone has
+        * a network spool receiver that can handle the new style messages.
+        */
+       if (recps != NULL)
+        if (recps->num_ignet > 0)
+         for (i=0; i<num_tokens(recps->recp_ignet, '|'); ++i) {
+               extract(recipient, recps->recp_ignet, i);
+
+               hold_R = msg->cm_fields['R'];
+               hold_D = msg->cm_fields['D'];
+               msg->cm_fields['R'] = mallok(SIZ);
+               msg->cm_fields['D'] = mallok(SIZ);
+               extract_token(msg->cm_fields['R'], recipient, 0, '@');
+               extract_token(msg->cm_fields['D'], recipient, 1, '@');
+               
+               serialize_message(&smr, msg);
+               if (smr.len > 0) {
+                       sprintf(aaa,
+                               "./network/spoolin/netmail.%04lx.%04x.%04x",
+                               (long) getpid(), CC->cs_pid, ++seqnum);
+                       network_fp = fopen(aaa, "wb+");
+                       if (network_fp != NULL) {
+                               fwrite(smr.ser, smr.len, 1, network_fp);
+                               fclose(network_fp);
+                       }
+                       phree(smr.ser);
+               }
+
+               phree(msg->cm_fields['R']);
+               phree(msg->cm_fields['D']);
+               msg->cm_fields['R'] = hold_R;
+               msg->cm_fields['D'] = hold_D;
+       }
+
+       /* Go back to the room we started from */
        lprintf(9, "Returning to original room\n");
        if (strcasecmp(hold_rm, CC->quickroom.QRname))
                getroom(&CC->quickroom, hold_rm);
 
-       /* For internet mail, generate delivery instructions 
-        * (Yes, this is recursive!   Deal with it!)
+       /* For internet mail, generate delivery instructions.
+        * Yes, this is recursive.  Deal with it.  Infinite recursion does
+        * not happen because the delivery instructions message does not
+        * contain a recipient.
         */
-       if (mailtype == MES_INTERNET) {
+       if (recps != NULL)
+        if (recps->num_internet > 0) {
                lprintf(9, "Generating delivery instructions\n");
-               instr = mallok(2048);
+               instr = mallok(SIZ * 2);
                sprintf(instr,
                        "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
-                       "bounceto|%s@%s\n"
-                       "remote|%s|0||\n",
+                       "bounceto|%s@%s\n",
                        SPOOLMIME, newmsgid, (long)time(NULL),
-                       msg->cm_fields['A'], msg->cm_fields['N'],
-                       recipient );
+                       msg->cm_fields['A'], msg->cm_fields['N']
+               );
+
+               for (i=0; i<num_tokens(recps->recp_internet, '|'); ++i) {
+                       extract(recipient, recps->recp_internet, i);
+                       sprintf(&instr[strlen(instr)],
+                               "remote|%s|0||\n", recipient);
+               }
 
                imsg = mallok(sizeof(struct CtdlMessage));
                memset(imsg, 0, sizeof(struct CtdlMessage));
@@ -1918,7 +1935,7 @@ long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
                imsg->cm_format_type = FMT_RFC822;
                imsg->cm_fields['A'] = strdoop("Citadel");
                imsg->cm_fields['M'] = instr;
-               CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
+               CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM);
                CtdlFreeMessage(imsg);
        }
 
@@ -1946,7 +1963,7 @@ void quickie_message(char *from, char *to, char *room, char *text)
                msg->cm_fields['R'] = strdoop(to);
        msg->cm_fields['M'] = strdoop(text);
 
-       CtdlSaveMsg(msg, "", room, MES_LOCAL);
+       CtdlSubmitMsg(msg, NULL, room);
        CtdlFreeMessage(msg);
        syslog(LOG_NOTICE, text);
 }
@@ -1975,10 +1992,15 @@ char *CtdlReadMessageBody(char *terminator,     /* token signalling EOT */
                m = reallok(exist, strlen(exist) + 4096);
                if (m == NULL) phree(exist);
        }
+
+       /* flush the input if we have nowhere to store it */
        if (m == NULL) {
                while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) ;;
                return(NULL);
-       } else {
+       }
+
+       /* otherwise read it into memory */
+       else {
                buffer_len = 4096;
                m[0] = 0;
                message_len = 0;
@@ -2007,9 +2029,10 @@ char *CtdlReadMessageBody(char *terminator,      /* token signalling EOT */
                        }
                }
 
-               /* Add the new line to the buffer.  We avoid using strcat()
-                * because that would involve traversing the entire message
-                * after each line, and this function needs to run fast.
+               /* 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);
                m[message_len + linelen] = '\n';
@@ -2038,13 +2061,10 @@ static struct CtdlMessage *make_message(
        char *recipient,                /* NULL if it's not mail */
        char *room,                     /* room where it's going */
        int type,                       /* see MES_ types in header file */
-       int net_type,                   /* see MES_ types in header file */
-       int format_type,                /* local or remote (see citadel.h) */
-       char *fake_name)                /* who we're masquerading as */
-{
-
-       int a;
-       char dest_node[32];
+       int format_type,                /* variformat, plain text, MIME... */
+       char *fake_name                 /* who we're masquerading as */
+) {
+       char dest_node[SIZ];
        char buf[SIZ];
        struct CtdlMessage *msg;
 
@@ -2057,24 +2077,7 @@ static struct CtdlMessage *make_message(
        /* Don't confuse the poor folks if it's not routed mail. */
        strcpy(dest_node, "");
 
-       /* If net_type is MES_IGNET, split out the destination node. */
-       if (net_type == MES_IGNET) {
-               strcpy(dest_node, NODENAME);
-               for (a = 0; a < strlen(recipient); ++a) {
-                       if (recipient[a] == '@') {
-                               recipient[a] = 0;
-                               strcpy(dest_node, &recipient[a + 1]);
-                       }
-               }
-       }
-
-       /* if net_type is MES_INTERNET, set the dest node to 'internet' */
-       if (net_type == MES_INTERNET) {
-               strcpy(dest_node, "internet");
-       }
-
-       while (isspace(recipient[strlen(recipient) - 1]))
-               recipient[strlen(recipient) - 1] = 0;
+       striplt(recipient);
 
        sprintf(buf, "cit%ld", author->usernum);                /* Path */
        msg->cm_fields['P'] = strdoop(buf);
@@ -2087,24 +2090,26 @@ static struct CtdlMessage *make_message(
        else
                msg->cm_fields['A'] = strdoop(author->fullname);
 
-       if (CC->quickroom.QRflags & QR_MAILBOX)                 /* room */
+       if (CC->quickroom.QRflags & QR_MAILBOX) {               /* room */
                msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]);
-       else
+       }
+       else {
                msg->cm_fields['O'] = strdoop(CC->quickroom.QRname);
+       }
 
        msg->cm_fields['N'] = strdoop(NODENAME);                /* nodename */
        msg->cm_fields['H'] = strdoop(HUMANNODE);               /* hnodename */
 
-       if (recipient[0] != 0)
+       if (recipient[0] != 0) {
                msg->cm_fields['R'] = strdoop(recipient);
-       if (dest_node[0] != 0)
+       }
+       if (dest_node[0] != 0) {
                msg->cm_fields['D'] = strdoop(dest_node);
-
+       }
 
        msg->cm_fields['M'] = CtdlReadMessageBody("000",
                                                config.c_maxmsglen, NULL);
 
-
        return(msg);
 }
 
@@ -2145,16 +2150,18 @@ int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf) {
 }
 
 
-#ifdef NOT_YET_FINISHED /* FIXME */
 /*
  * Validate recipients, count delivery types and errors, and handle aliasing
+ * FIXME check for dupes!!!!!
  */
 struct recptypes *validate_recipients(char *recipients) {
        struct recptypes *ret;
        char this_recp[SIZ];
+       char append[SIZ];
        int num_recps;
        int i;
        int mailtype;
+       int invalid;
        struct usersupp tempUS;
 
        /* Initialize */
@@ -2167,48 +2174,99 @@ struct recptypes *validate_recipients(char *recipients) {
        ret->num_ignet = 0;
        ret->num_error = 0;
 
-       if (recipients == NULL) return(ret);
-       if (strlen(recipients) == NULL) return(ret);
-
-       /* Allow either , or ; separators by changing ; to , */
-       for (i=0; i<strlen(recipients); ++i) {
-               if (recipients[i] == ';') {
-                       recipients[i] = ',';
+       if (recipients == NULL) {
+               num_recps = 0;
+       }
+       else if (strlen(recipients) == 0) {
+               num_recps = 0;
+       }
+       else {
+               /* Change all valid separator characters to commas */
+               for (i=0; i<strlen(recipients); ++i) {
+                       if ((recipients[i] == ';') || (recipients[i] == '|')) {
+                               recipients[i] = ',';
+                       }
                }
+
+               /* Count 'em up */
+               num_recps = num_tokens(recipients, ',');
        }
 
-       /* Count 'em up */
-       num_recps = num_tokens(recipients, ',');
-       
-       for (i=0; i<num_recps; ++i) {
+       if (num_recps > 0) for (i=0; i<num_recps; ++i) {
                extract_token(this_recp, recipients, i, ',');
+               striplt(this_recp);
                lprintf(9, "Evaluating recipient #%d <%s>\n", i, this_recp);
                mailtype = alias(this_recp);
+               invalid = 0;
                switch(mailtype) {
                        case MES_LOCAL:
-                               if (getuser(&tempUS, buf) == 0) {
+                               if (getuser(&tempUS, this_recp) == 0) {
                                        ++ret->num_local;
+                                       strcpy(this_recp, tempUS.fullname);
+                                       if (strlen(ret->recp_local) > 0) {
+                                               strcat(ret->recp_local, "|");
+                                       }
+                                       strcat(ret->recp_local, this_recp);
                                }
                                else {
                                        ++ret->num_error;
+                                       invalid = 1;
                                }
                                break;
                        case MES_INTERNET:
                                ++ret->num_internet;
+                               if (strlen(ret->recp_internet) > 0) {
+                                       strcat(ret->recp_internet, "|");
+                               }
+                               strcat(ret->recp_internet, this_recp);
                                break;
                        case MES_IGNET:
                                ++ret->num_ignet;
+                               if (strlen(ret->recp_ignet) > 0) {
+                                       strcat(ret->recp_ignet, "|");
+                               }
+                               strcat(ret->recp_ignet, this_recp);
                                break;
                        case MES_ERROR:
                                ++ret->num_error;
+                               invalid = 1;
                                break;
                }
+               if (invalid) {
+                       if (strlen(ret->errormsg) == 0) {
+                               sprintf(append,
+                                       "Invalid recipient: %s",
+                                       this_recp);
+                       }
+                       else {
+                               sprintf(append, 
+                                       ", %s", this_recp);
+                       }
+                       if ( (strlen(ret->errormsg) + strlen(append)) < SIZ) {
+                               strcat(ret->errormsg, append);
+                       }
+               }
+               else {
+                       if (strlen(ret->display_recp) == 0) {
+                               strcpy(append, this_recp);
+                       }
+                       else {
+                               sprintf(append, ", %s", this_recp);
+                       }
+                       if ( (strlen(ret->display_recp)+strlen(append)) < SIZ) {
+                               strcat(ret->display_recp, append);
+                       }
+               }
+       }
 
+       if ((ret->num_local + ret->num_internet +
+          ret->num_ignet + ret->num_error) == 0) {
+               ++ret->num_error;
+               strcpy(ret->errormsg, "No recipients specified.");
        }
 
        return(ret);
 }
-#endif /* FIXME */
 
 
 
@@ -2218,141 +2276,139 @@ struct recptypes *validate_recipients(char *recipients) {
 void cmd_ent0(char *entargs)
 {
        int post = 0;
-       char recipient[SIZ];
+       char recp[SIZ];
+       char masquerade_as[SIZ];
        int anon_flag = 0;
        int format_type = 0;
        char newusername[SIZ];
        struct CtdlMessage *msg;
-       int a, b;
-       int e = 0;
+       int anonymous = 0;
        int mtsflag = 0;
-       struct usersupp tempUS;
-       char buf[SIZ];
+       char errmsg[SIZ];
        int err = 0;
+       struct recptypes *valid = NULL;
 
        post = extract_int(entargs, 0);
-       extract(recipient, entargs, 1);
+       extract(recp, entargs, 1);
        anon_flag = extract_int(entargs, 2);
        format_type = extract_int(entargs, 3);
 
        /* first check to make sure the request is valid. */
 
-       err = CtdlDoIHavePermissionToPostInThisRoom(buf);
+       err = CtdlDoIHavePermissionToPostInThisRoom(errmsg);
        if (err) {
-               cprintf("%d %s\n", err, buf);
+               cprintf("%d %s\n", err, errmsg);
                return;
        }
 
        /* Check some other permission type things. */
 
        if (post == 2) {
-               if (CC->usersupp.axlevel < 6) {
+               if (CC->usersupp.axlevel < 6) {
                        cprintf("%d You don't have permission to masquerade.\n",
                                ERROR + HIGHER_ACCESS_REQUIRED);
                        return;
                }
                extract(newusername, entargs, 4);
-               memset(CC->fake_postname, 0, 32);
-               strcpy(CC->fake_postname, newusername);
-               cprintf("%d Ok\n", OK);
+               memset(CC->fake_postname, 0, sizeof(CC->fake_postname) );
+               safestrncpy(CC->fake_postname, newusername,
+                       sizeof(CC->fake_postname) );
+               cprintf("%d ok\n", OK);
                return;
        }
        CC->cs_flags |= CS_POSTING;
 
-       buf[0] = 0;
        if (CC->quickroom.QRflags & QR_MAILBOX) {
-               if (CC->usersupp.axlevel >= 2) {
-                       strcpy(buf, recipient);
-               }
-               else {
-                       strcpy(buf, "sysop");
-               }
-               e = alias(buf); /* alias and mail type */
-               if ((buf[0] == 0) || (e == MES_ERROR)) {
-                       cprintf("%d Unknown address - cannot send message.\n",
-                               ERROR + NO_SUCH_USER);
+               if (CC->usersupp.axlevel < 2) {
+                       strcpy(recp, "sysop");
+               }
+
+               valid = validate_recipients(recp);
+               lprintf(9, "validate_recipients(%s)\n", recp);
+               lprintf(9, " num_local    = %d\n", valid->num_local);
+               lprintf(9, " num_internet = %d\n", valid->num_internet);
+               lprintf(9, " num_ignet    = %d\n", valid->num_ignet);
+               lprintf(9, " num_error    = %d\n", valid->num_error);
+               lprintf(9, " errormsg     = %s\n", valid->errormsg);
+               lprintf(9, " display_recp = %s\n", valid->display_recp);
+
+               if (valid->num_error > 0) {
+                       cprintf("%d %s\n",
+                               ERROR + NO_SUCH_USER,
+                               valid->errormsg);
+                       phree(valid);
                        return;
                }
-               if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) {
-                       cprintf("%d Net privileges required for network mail.\n",
+
+               if ( ( (valid->num_internet + valid->num_ignet) > 0)
+                  && (CC->usersupp.axlevel < 4) ) {
+                       cprintf("%d Higher access required for network mail.\n",
                                ERROR + HIGHER_ACCESS_REQUIRED);
+                       phree(valid);
                        return;
                }
-               if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET)
+       
+               if ((RESTRICT_INTERNET == 1) && (valid->num_internet > 0)
                    && ((CC->usersupp.flags & US_INTERNET) == 0)
                    && (!CC->internal_pgm)) {
                        cprintf("%d You don't have access to Internet mail.\n",
                                ERROR + HIGHER_ACCESS_REQUIRED);
+                       phree(valid);
                        return;
                }
-               if (!strcasecmp(buf, "sysop")) {
+
+               if (!strcasecmp(recp, "sysop")) {
                        mtsflag = 1;
                }
-               else if (e == MES_LOCAL) {      /* don't search local file */
-
-
-/*** We can probably do this now.
-                       if (!strcasecmp(buf, CC->usersupp.fullname)) {
-                               cprintf("%d Can't send mail to yourself!\n",
-                                       ERROR + NO_SUCH_USER);
-                               return;
-                       }
-  ***  hi from Stu
-*/
+       }
 
-                       /* Check to make sure the user exists; also get the correct
-                        * upper/lower casing of the name.
-                        */
-                       a = getuser(&tempUS, buf);
-                       if (a != 0) {
-                               cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
-                               return;
-                       }
-                       strcpy(buf, tempUS.fullname);
+       /* Is this a room which has anonymous-only or anonymous-option? */
+       anonymous = MES_NORMAL;
+       if (CC->quickroom.QRflags & QR_ANONONLY) {
+               anonymous = MES_ANONONLY;
+       }
+       if (CC->quickroom.QRflags & QR_ANONOPT) {
+               if (anon_flag == 1) {   /* only if the user requested it */
+                       anonymous = MES_ANONOPT;
                }
        }
 
-       b = MES_NORMAL;
-       if (CC->quickroom.QRflags & QR_ANONONLY)
-               b = MES_ANON;
-       if (CC->quickroom.QRflags & QR_ANONOPT) {
-               if (anon_flag == 1)
-                       b = MES_AN2;
+       if ((CC->quickroom.QRflags & QR_MAILBOX) == 0) {
+               recp[0] = 0;
        }
-       if ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
-               buf[0] = 0;
 
        /* If we're only checking the validity of the request, return
         * success without creating the message.
         */
        if (post == 0) {
-               cprintf("%d %s\n", OK, buf);
+               cprintf("%d %s\n", OK,
+                       ((valid != NULL) ? valid->display_recp : "") );
+               phree(valid);
                return;
        }
 
-       cprintf("%d send message\n", SEND_LISTING);
-
-       /* Read in the message from the client. */
+       /* Handle author masquerading */
        if (CC->fake_postname[0]) {
-               msg = make_message(&CC->usersupp, buf,
-                       CC->quickroom.QRname, b, e, format_type,
-                       CC->fake_postname);
+               strcpy(masquerade_as, CC->fake_postname);
        }
        else if (CC->fake_username[0]) {
-               msg = make_message(&CC->usersupp, buf,
-                       CC->quickroom.QRname, b, e, format_type,
-                       CC->fake_username);
+               strcpy(masquerade_as, CC->fake_username);
        }
        else {
-               msg = make_message(&CC->usersupp, buf,
-                       CC->quickroom.QRname, b, e, format_type, "");
+               strcpy(masquerade_as, "");
        }
 
+       /* Read in the message from the client. */
+       cprintf("%d send message\n", SEND_LISTING);
+       msg = make_message(&CC->usersupp, recp,
+               CC->quickroom.QRname, anonymous, format_type, masquerade_as);
+
        if (msg != NULL) {
-               CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e);
+               CtdlSubmitMsg(msg, valid, (mtsflag ? AIDEROOM : "") );
                CtdlFreeMessage(msg);
        }
        CC->fake_postname[0] = '\0';
+       phree(valid);
        return;
 }
 
@@ -2532,7 +2588,9 @@ void cmd_move(char *args)
        /* Now delete the message from the source room,
         * if this is a 'move' rather than a 'copy' operation.
         */
-       if (is_copy == 0) CtdlDeleteMessages(CC->quickroom.QRname, num, "");
+       if (is_copy == 0) {
+               CtdlDeleteMessages(CC->quickroom.QRname, num, "");
+       }
 
        cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") );
 }
@@ -2574,10 +2632,10 @@ void PutMetaData(struct MetaData *smibuf)
 {
        long TheIndex;
 
-       /* Use the negative of the message number for its supp record index */
+       /* Use the negative of the message number for the metadata db index */
        TheIndex = (0L - smibuf->meta_msgnum);
 
-       lprintf(9, "PuttMetaData(%ld) - ref count is %d\n",
+       lprintf(9, "PutMetaData(%ld) - ref count is %d\n",
                smibuf->meta_msgnum, smibuf->meta_refcount);
 
        cdb_store(CDB_MSGMAIN,
@@ -2617,6 +2675,8 @@ void AdjRefCount(long msgnum, int incr)
                lprintf(9, "Deleting message <%ld>\n", msgnum);
                delnum = msgnum;
                cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
+
+               /* We have to delete the metadata record too! */
                delnum = (0L - msgnum);
                cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long));
        }
@@ -2720,7 +2780,7 @@ void CtdlWriteObject(char *req_room,              /* Room to stuff it in */
                        CtdlDeleteMessages(roomname, 0L, content_type));
        }
        /* Now write the data */
-       CtdlSaveMsg(msg, "", roomname, MES_LOCAL);
+       CtdlSubmitMsg(msg, NULL, roomname);
        CtdlFreeMessage(msg);
 }
 
index 90dae425615f17d38188b3238af0531113179c3a..ea521a39212fc0a0b0c3058856cdfee2bd839c59 100644 (file)
@@ -50,6 +50,10 @@ struct recptypes {
         int num_ignet;
         int num_error;
        char errormsg[SIZ];
+       char recp_local[SIZ];
+       char recp_internet[SIZ];
+       char recp_ignet[SIZ];
+       char display_recp[SIZ];
 };
 
 
@@ -68,7 +72,7 @@ void cmd_msg4 (char *cmdbuf);
 void cmd_opna (char *cmdbuf);
 long send_message (struct CtdlMessage *, FILE *);
 void loadtroom (void);
-long CtdlSaveMsg(struct CtdlMessage *, char *, char *, int);
+long CtdlSubmitMsg(struct CtdlMessage *, struct recptypes *, char *);
 void quickie_message (char *, char *, char *, char *);
 void cmd_ent0 (char *entargs);
 void cmd_dele (char *delstr);
index 0ac6799e3e349849506fb8d01fb49621ee26e106..396455e26ea8b2afef715ba6b17416e892cc50b7 100644 (file)
@@ -585,7 +585,7 @@ int send_express_message(char *lun, char *x_user, char *x_msg)
                 * creating the room if necessary.
                 */
                create_room(PAGELOGROOM, 4, "", 0, 1);
-               msgnum = CtdlSaveMsg(logmsg, "", PAGELOGROOM, MES_LOCAL);
+               msgnum = CtdlSubmitMsg(logmsg, NULL, PAGELOGROOM);
 
                /* Now save a copy in the global log room, if configured */
                if (strlen(config.c_logpages) > 0) {
index a66f577c280c739cde9231e2de536dbcb7d545de..86e10c3b764668de8996d87c9cdeca55e741c5bf 100644 (file)
@@ -484,7 +484,7 @@ void network_spool_msg(long msgnum, void *userdata) {
                imsg->cm_fields['M'] = instr;
        
                /* Save delivery instructions in spoolout room */
-               CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
+               CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM);
                CtdlFreeMessage(imsg);
        }
        
@@ -983,7 +983,7 @@ void network_process_buffer(char *buffer, long size) {
 
        /* save the message into a room */
        msg->cm_flags = CM_SKIP_HOOKS;
-        CtdlSaveMsg(msg, "", target_room, 0);
+        CtdlSubmitMsg(msg, NULL, target_room);
        CtdlFreeMessage(msg);
 }
 
index 03e336f65fca5f1dea0e0cb70e2dff98a18a8b72..65ef7e75fe15457917c28ae862640f569bbca886 100644 (file)
@@ -545,10 +545,9 @@ int smtp_message_delivery(struct CtdlMessage *msg) {
        if (msg->cm_fields['O']==NULL) msg->cm_fields['O'] = strdoop(MAILROOM);
 
        /* Save the message in the queue */
-       msgid = CtdlSaveMsg(msg,
-               "",
-               SMTP_SPOOLOUT_ROOM,
-               MES_LOCAL);
+       msgid = CtdlSubmitMsg(msg,
+               NULL,
+               SMTP_SPOOLOUT_ROOM);
        ++successful_saves;
 
        instr = mallok(1024);
@@ -610,13 +609,13 @@ int smtp_message_delivery(struct CtdlMessage *msg) {
                imsg->cm_anon_type = MES_NORMAL;
                imsg->cm_format_type = FMT_RFC822;
                imsg->cm_fields['M'] = instr;
-               CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
+               CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM);
                CtdlFreeMessage(imsg);
        }
 
        /* If there are no remote spools, delete the message */ 
        else {
-               phree(instr);   /* only needed here, because CtdlSaveMsg()
+               phree(instr);   /* only needed here, because CtdlSubmitMsg()
                                 * would free this buffer otherwise */
                CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgid, ""); 
        }
@@ -1193,22 +1192,23 @@ void smtp_do_bounce(char *instr) {
                        lprintf(7, "No bounce address specified\n");
                        bounce_msgid = (-1L);
                }
+/* FIXME this won't work
                else if (mes_type = alias(bounceto), mes_type == MES_ERROR) {
                        lprintf(7, "Invalid bounce address <%s>\n", bounceto);
                        bounce_msgid = (-1L);
                }
                else {
-                       bounce_msgid = CtdlSaveMsg(bmsg,
+                       bounce_msgid = CtdlSubmitMsg(bmsg,
                                bounceto,
                                "", mes_type);
                }
+ */
                TRACE;
 
                /* Otherwise, go to the Aide> room */
                lprintf(9, "bounce to room?\n");
-               if (bounce_msgid < 0L) bounce_msgid = CtdlSaveMsg(bmsg,
-                       "", AIDEROOM,
-                       MES_LOCAL);
+               if (bounce_msgid < 0L) bounce_msgid = CtdlSubmitMsg(bmsg,
+                       NULL, AIDEROOM);
        }
 
        CtdlFreeMessage(bmsg);
@@ -1428,7 +1428,7 @@ void smtp_do_procmsg(long msgnum, void *userdata) {
                        "retry|%ld\n",
                        SPOOLMIME, instr, (long)time(NULL), (long)retry );
                phree(instr);
-               CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
+               CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
                CtdlFreeMessage(msg);
        }
 
index 102b2d76636539cb935f2448471a98c0b91bf03a..c3b27f092b33d92c34bc891624cb6db1acc09e57 100644 (file)
@@ -451,7 +451,7 @@ void vcard_purge(char *username, long usernum) {
 
        msg->cm_fields['S'] = strdoop("CANCEL");
 
-        CtdlSaveMsg(msg, "", ADDRESS_BOOK_ROOM, MES_LOCAL);
+        CtdlSubmitMsg(msg, NULL, ADDRESS_BOOK_ROOM);
         CtdlFreeMessage(msg);
 }