]> code.citadel.org Git - citadel.git/blobdiff - citadel/msgbase.c
* Replication fixes
[citadel.git] / citadel / msgbase.c
index 2b77e3d1506659aba844b1304e262cde11cf6125..73bc6944e56072462c6fecfefc79ac23ec46f02d 100644 (file)
 
 #define desired_section ((char *)CtdlGetUserData(SYM_DESIRED_SECTION))
 #define ma ((struct ma_info *)CtdlGetUserData(SYM_MA_INFO))
+#define msg_repl ((struct repl *)CtdlGetUserData(SYM_REPL))
 
 extern struct config config;
 
+char *msgkeys[] = {
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", 
+       "from",
+       "", "", "", "", "", "", 
+       "hnod",
+       "msgn",
+       "", "", "",
+       "text",
+       "node",
+       "room",
+       "path",
+       "",
+       "rcpt",
+       "",
+       "time",
+       "subj",
+       "",
+       "",
+       "",
+       "",
+       ""
+};
+
 /*
  * This function is self explanatory.
  * (What can I say, I'm in a weird mood today...)
@@ -183,12 +215,45 @@ void simple_listing(long msgnum)
 }
 
 
+
+/* Determine if a given message matches the fields in a message template.
+ * Return 0 for a successful match.
+ */
+int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
+       int i;
+
+       /* If there aren't any fields in the template, all messages will
+        * match.
+        */
+       if (template == NULL) return(0);
+
+       /* Null messages are bogus. */
+       if (msg == NULL) return(1);
+
+       for (i='A'; i<='Z'; ++i) {
+               if (template->cm_fields[i] != NULL) {
+                       if (msg->cm_fields[i] == NULL) {
+                               return 1;
+                       }
+                       if (strcasecmp(msg->cm_fields[i],
+                               template->cm_fields[i])) return 1;
+               }
+       }
+
+       /* All compares succeeded: we have a match! */
+       return 0;
+}
+
+
+
+
 /*
  * API function to perform an operation for each qualifying message in the
  * current room.
  */
 void CtdlForEachMessage(int mode, long ref,
                        char *content_type,
+                       struct CtdlMessage *compare,
                        void (*CallBack) (long msgnum))
 {
 
@@ -199,6 +264,7 @@ void CtdlForEachMessage(int mode, long ref,
        int num_msgs = 0;
        long thismsg;
        struct SuppMsgInfo smi;
+       struct CtdlMessage *msg;
 
        /* Learn about the user and room in question */
        get_mm();
@@ -231,6 +297,24 @@ void CtdlForEachMessage(int mode, long ref,
                                }
 
        num_msgs = sort_msglist(msglist, num_msgs);
+
+       /* If a template was supplied, filter out the messages which
+        * don't match.  (This could induce some delays!)
+        */
+       if (num_msgs > 0) {
+               if (compare != NULL) {
+                       for (a = 0; a < num_msgs; ++a) {
+                               msg = CtdlFetchMessage(msglist[a]);
+                               if (msg != NULL) {
+                                       if (CtdlMsgCmp(msg, compare)) {
+                                               msglist[a] = 0L;
+                                       }
+                                       CtdlFreeMessage(msg);
+                               }
+                       }
+               }
+       }
+
        
        /*
         * Now iterate through the message list, according to the
@@ -268,10 +352,17 @@ void cmd_msgs(char *cmdbuf)
 {
        int mode = 0;
        char which[256];
+       char buf[256];
+       char tfield[256];
+       char tvalue[256];
        int cm_ref = 0;
+       int i;
+       int with_template = 0;
+       struct CtdlMessage *template = NULL;
 
        extract(which, cmdbuf, 0);
        cm_ref = extract_int(cmdbuf, 1);
+       with_template = extract_int(cmdbuf, 2);
 
        mode = MSGS_ALL;
        strcat(which, "   ");
@@ -290,8 +381,30 @@ void cmd_msgs(char *cmdbuf)
                cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
                return;
        }
-       cprintf("%d Message list...\n", LISTING_FOLLOWS);
-       CtdlForEachMessage(mode, cm_ref, NULL, simple_listing);
+
+       if (with_template) {
+               cprintf("%d Send template then receive message list\n",
+                       START_CHAT_MODE);
+               template = (struct CtdlMessage *)
+                       mallok(sizeof(struct CtdlMessage));
+               memset(template, 0, sizeof(struct CtdlMessage));
+               while(client_gets(buf), strcmp(buf,"000")) {
+                       extract(tfield, buf, 0);
+                       extract(tvalue, buf, 1);
+                       for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
+                               if (!strcasecmp(tfield, msgkeys[i])) {
+                                       template->cm_fields[i] =
+                                               strdoop(tvalue);
+                               }
+                       }
+               }
+       }
+       else {
+               cprintf("%d Message list...\n", LISTING_FOLLOWS);
+       }
+
+       CtdlForEachMessage(mode, cm_ref, NULL, template, simple_listing);
+       if (template != NULL) CtdlFreeMessage(template);
        cprintf("000\n");
 }
 
@@ -561,6 +674,13 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum)
        } while ((field_length > 0) && (field_header != 'M'));
 
        cdb_free(dmsgtext);
+
+       /* Perform "before read" hooks (aborting if any return nonzero) */
+       if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
+               CtdlFreeMessage(ret);
+               return NULL;
+       }
+
        return (ret);
 }
 
@@ -606,10 +726,12 @@ void CtdlFreeMessage(struct CtdlMessage *msg)
 void output_message(char *msgid, int mode, int headers_only)
 {
        long msg_num;
-       int a, i;
+       int a, i, k;
        char buf[1024];
        time_t xtime;
        CIT_UBYTE ch;
+       char allkeys[256];
+       char display_name[256];
 
        struct CtdlMessage *TheMessage = NULL;
 
@@ -699,46 +821,44 @@ void output_message(char *msgid, int mode, int headers_only)
 
        if ((mode == MT_CITADEL) || (mode == MT_MIME)) {
 
-               if (TheMessage->cm_fields['P']) {
-                       cprintf("path=%s\n", TheMessage->cm_fields['P']);
-               }
-               if (TheMessage->cm_fields['I']) {
-                       cprintf("msgn=%s\n", TheMessage->cm_fields['I']);
-               }
-               if (TheMessage->cm_fields['T']) {
-                       cprintf("time=%s\n", TheMessage->cm_fields['T']);
-               }
+               strcpy(display_name, "<unknown>");
                if (TheMessage->cm_fields['A']) {
                        strcpy(buf, TheMessage->cm_fields['A']);
                        PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
                        if (TheMessage->cm_anon_type == MES_ANON)
-                               cprintf("from=****");
+                               strcpy(display_name, "****");
                        else if (TheMessage->cm_anon_type == MES_AN2)
-                               cprintf("from=anonymous");
+                               strcpy(display_name, "anonymous");
                        else
-                               cprintf("from=%s", buf);
+                               strcpy(display_name, buf);
                        if ((is_room_aide())
                            && ((TheMessage->cm_anon_type == MES_ANON)
                             || (TheMessage->cm_anon_type == MES_AN2))) {
-                               cprintf(" [%s]", buf);
+                               sprintf(&display_name[strlen(display_name)],
+                                       " [%s]", buf);
                        }
-                       cprintf("\n");
-               }
-               if (TheMessage->cm_fields['O']) {
-                       cprintf("room=%s\n", TheMessage->cm_fields['O']);
-               }
-               if (TheMessage->cm_fields['N']) {
-                       cprintf("node=%s\n", TheMessage->cm_fields['N']);
                }
-               if (TheMessage->cm_fields['H']) {
-                       cprintf("hnod=%s\n", TheMessage->cm_fields['H']);
-               }
-               if (TheMessage->cm_fields['R']) {
-                       cprintf("rcpt=%s\n", TheMessage->cm_fields['R']);
-               }
-               if (TheMessage->cm_fields['U']) {
-                       cprintf("subj=%s\n", TheMessage->cm_fields['U']);
+
+               strcpy(allkeys, FORDER);
+               for (i=0; i<strlen(allkeys); ++i) {
+                       k = (int) allkeys[i];
+                       if (k != 'M') {
+                               if (TheMessage->cm_fields[k] != NULL) {
+                                       if (k == 'A') {
+                                               cprintf("%s=%s\n",
+                                                       msgkeys[k],
+                                                       display_name);
+                                       }
+                                       else {
+                                               cprintf("%s=%s\n",
+                                                       msgkeys[k],
+                                                       TheMessage->cm_fields[k]
+                                       );
+                                       }
+                               }
+                       }
                }
+
        }
 
        /* begin header processing loop for RFC822 transfer format */
@@ -912,7 +1032,7 @@ void cmd_msg3(char *cmdbuf)
 {
        long msgnum;
        struct CtdlMessage *msg;
-       struct sermsgret smr;
+       struct ser_ret smr;
 
        if (CC->internal_pgm == 0) {
                cprintf("%d This command is for internal programs only.\n",
@@ -986,7 +1106,7 @@ long send_message(struct CtdlMessage *msg, /* pointer to buffer */
        long newmsgid;
        long retval;
        char msgidbuf[32];
-        struct sermsgret smr;
+        struct ser_ret smr;
 
        /* Get a new message number */
        newmsgid = get_new_message_number();
@@ -1036,11 +1156,11 @@ long send_message(struct CtdlMessage *msg,      /* pointer to buffer */
 /*
  * Serialize a struct CtdlMessage into the format used on disk and network.
  * 
- * This function returns a "struct sermsgret" (defined in msgbase.h) which
+ * This function loads up a "struct ser_ret" (defined in server.h) which
  * contains the length of the serialized message and a pointer to the
  * serialized message in memory.  THE LATTER MUST BE FREED BY THE CALLER.
  */
-void serialize_message(struct sermsgret *ret,          /* return values */
+void serialize_message(struct ser_ret *ret,            /* return values */
                        struct CtdlMessage *msg)        /* unserialized msg */
 {
        size_t wlen;
@@ -1071,8 +1191,8 @@ void serialize_message(struct sermsgret *ret,             /* return values */
        ret->ser[1] = msg->cm_anon_type;
        ret->ser[2] = msg->cm_format_type;
        wlen = 3;
-       lprintf(9, "stuff\n");
 
+       lprintf(9, "stuff\n");
        for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
                ret->ser[wlen++] = (char)forder[i];
                strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]);
@@ -1080,12 +1200,80 @@ void serialize_message(struct sermsgret *ret,           /* return values */
        }
        if (ret->len != wlen) lprintf(3, "ERROR: len=%d wlen=%d\n",
                ret->len, wlen);
+       lprintf(9, "done serializing\n");
 
        return;
 }
 
 
 
+/*
+ * Back end for the ReplicationChecks() function
+ */
+void check_repl(long msgnum) {
+       struct CtdlMessage *msg;
+       time_t timestamp;
+
+       msg = NULL;  /* FIX change to get */
+
+
+       if (msg->cm_fields['T'] != NULL) {
+               timestamp = atol(msg->cm_fields['T']);
+               if (timestamp > msg_repl->highest) {
+                       msg_repl->highest = timestamp;  /* newer! */
+                       return;
+               }
+       }
+
+       /* Existing isn't newer?  Then delete the old one(s). */
+       CtdlDeleteMessages(&CC->quickroom.QRname, msgnum, NULL);
+}
+
+
+/*
+ * Check to see if any messages already exist which carry the same Extended ID
+ * as this one.  
+ *
+ * If any are found:
+ * -> With older timestamps: delete them and return 0.  Message will be saved.
+ * -> With newer timestamps: return 1.  Message save will be aborted.
+ */
+int ReplicationChecks(struct CtdlMessage *msg) {
+       struct CtdlMessage *template;
+       int abort_this = 0;
+
+       /* No extended id?  Don't do anything. */
+       if (msg->cm_fields['E'] == NULL) return 0;
+       if (strlen(msg->cm_fields['E']) == 0) return 0;
+
+       CtdlAllocUserData(SYM_REPL, sizeof(struct repl));
+       strcpy(msg_repl->extended_id, msg->cm_fields['E']);
+       msg_repl->highest = (-1L);
+
+       template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
+       memset(template, 0, sizeof(struct CtdlMessage));
+       template->cm_fields['E'] = strdoop(msg->cm_fields['E']);
+
+       CtdlForEachMessage(MSGS_ALL, 0L, NULL, template, check_repl);
+
+       /* If a newer message exists with the same Extended ID, abort
+        * this save.
+        */
+       if (msg_repl->highest > atol(msg->cm_fields['T']) ) {
+               abort_this = 1;
+               }
+
+       CtdlFreeMessage(template);
+       return(abort_this);
+}
+
+
+
+
+
+
+
+
 /*
  * Save a message to disk
  */
@@ -1172,6 +1360,12 @@ void CtdlSaveMsg(struct CtdlMessage *msg,        /* message to save */
                }
        }
 
+       /* Perform "before save" hooks (aborting if any return nonzero) */
+       if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return;
+
+       /* If this message has an Extended ID, perform replication checks */
+       if (ReplicationChecks(msg) > 0) return;
+
        /* Network mail - send a copy to the network program. */
        if ((strlen(recipient) > 0) && (mailtype != MES_LOCAL)) {
                sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x",
@@ -1267,6 +1461,9 @@ void CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */
        smi.smi_refcount = successful_local_recipients;
        safestrncpy(smi.smi_content_type, content_type, 64);
        PutSuppMsgInfo(&smi);
+
+       /* Perform "after save" hooks */
+       PerformMessageHooks(msg, EVT_AFTERSAVE);
 }
 
 
@@ -1646,13 +1843,15 @@ void cmd_ent3(char *entargs)
                tempbuf[0] = 0;
                do {
                        client_read(&ch, 1);
+                       --msglen;
                        a = strlen(tempbuf);
                        tempbuf[a+1] = 0;
                        tempbuf[a] = ch;
-               } while ( (a != 0) && (msglen > 0) );
+               } while ( (ch != 0) && (msglen > 0) );
                msg->cm_fields[which_field] = strdoop(tempbuf);
        }
 
+       msg->cm_flags = CM_SKIP_HOOKS;
        CtdlSaveMsg(msg, recp, "", e, 0);
        CtdlFreeMessage(msg);
        phree(tempbuf);
@@ -1747,7 +1946,8 @@ void cmd_dele(char *delstr)
        getuser(&CC->usersupp, CC->curr_user);
        if ((CC->usersupp.axlevel < 6)
            && (CC->usersupp.usernum != CC->quickroom.QRroomaide)
-           && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) {
+           && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)
+           && (!(CC->internal_pgm))) {
                cprintf("%d Higher access required.\n",
                        ERROR + HIGHER_ACCESS_REQUIRED);
                return;
@@ -1902,9 +2102,10 @@ void AdjRefCount(long msgnum, int incr)
 void CtdlWriteObject(char *req_room,           /* Room to stuff it in */
                        char *content_type,     /* MIME type of this object */
                        char *tempfilename,     /* Where to fetch it from */
-                       int is_mailbox,         /* Private mailbox room? */
+                       struct usersupp *is_mailbox,    /* Mailbox room? */
                        int is_binary,          /* Is encoding necessary? */
-                       int is_unique           /* Del others of this type? */
+                       int is_unique,          /* Del others of this type? */
+                       unsigned int flags      /* Internal save flags */
                        )
 {
 
@@ -1917,12 +2118,11 @@ void CtdlWriteObject(char *req_room,            /* Room to stuff it in */
        struct CtdlMessage *msg;
        size_t len;
 
-       lprintf(9, "CtdlWriteObject() called\n");
-
-       if (is_mailbox)
-               MailboxName(roomname, &CC->usersupp, req_room);
+       if (is_mailbox != NULL)
+               MailboxName(roomname, is_mailbox, req_room);
        else
                safestrncpy(roomname, req_room, sizeof(roomname));
+       lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags);
 
        strcpy(filename, tmpnam(NULL));
        fp = fopen(filename, "w");
@@ -1962,9 +2162,10 @@ void CtdlWriteObject(char *req_room,             /* Room to stuff it in */
        msg->cm_anon_type = MES_NORMAL;
        msg->cm_format_type = 4;
        msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
-       msg->cm_fields['O'] = strdoop(roomname);
+       msg->cm_fields['O'] = strdoop(req_room);
        msg->cm_fields['N'] = strdoop(config.c_nodename);
        msg->cm_fields['H'] = strdoop(config.c_humannode);
+       msg->cm_flags = flags;
        
        lprintf(9, "Loading\n");
        fp = fopen(filename, "rb");