CtdlDeleteMessages() now has a bulk API. Updated all of the
[citadel.git] / citadel / msgbase.c
index eeb19d35728a7992f1300d059dafd870e3225031..a95ecd450ff8c4807de54f61dd76eb8541076d96 100644 (file)
@@ -1837,33 +1837,44 @@ void cmd_opna(char *cmdbuf)
        CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1, NULL);
 }                      
 
-
 /*
- * Save a message pointer into a specified room
+ * Save one or more message pointers into a specified room
  * (Returns 0 for success, nonzero for failure)
  * roomname may be NULL to use the current room
  *
  * Note that the 'supplied_msg' field may be set to NULL, in which case
  * the message will be fetched from disk, by number, if we need to perform
  * replication checks.  This adds an additional database read, so if the
- * caller already has the message in memory then it should be supplied.
+ * caller already has the message in memory then it should be supplied.  (Obviously
+ * this mode of operation only works if we're saving a single message.)
  */
-int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int do_repl_check,
-                               struct CtdlMessage *supplied_msg) {
-       int i;
+int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newmsgs,
+                               int do_repl_check, struct CtdlMessage *supplied_msg)
+{
+       int i, j, unique;
        char hold_rm[ROOMNAMELEN];
        struct cdbdata *cdbfr;
        int num_msgs;
        long *msglist;
        long highest_msg = 0L;
+
+       long msgid = 0;
        struct CtdlMessage *msg = NULL;
 
-       /*lprintf(CTDL_DEBUG,
-               "CtdlSaveMsgPointerInRoom(room=%s, msgid=%ld, repl=%d)\n",
-               roomname, msgid, do_repl_check);*/
+       long *msgs_to_be_merged = NULL;
+       int num_msgs_to_be_merged = 0;
+
+       lprintf(CTDL_DEBUG,
+               "CtdlSaveMsgPointersInRoom(room=%s, num_msgs=%d, repl=%d)\n",
+               roomname, num_newmsgs, do_repl_check);
 
        strcpy(hold_rm, CC->room.QRname);
 
+       /* Sanity checks */
+       if (newmsgidlist == NULL) return(ERROR + INTERNAL_ERROR);
+       if (num_newmsgs < 1) return(ERROR + INTERNAL_ERROR);
+       if (num_newmsgs > 1) supplied_msg = NULL;
+
        /* Now the regular stuff */
        if (lgetroom(&CC->room,
           ((roomname != NULL) ? roomname : CC->room.QRname) )
@@ -1872,6 +1883,11 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int do_repl_check,
                return(ERROR + ROOM_NOT_FOUND);
        }
 
+
+       msgs_to_be_merged = malloc(sizeof(long) * num_newmsgs);
+       num_msgs_to_be_merged = 0;
+
+
        cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
        if (cdbfr == NULL) {
                msglist = NULL;
@@ -1883,27 +1899,34 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int do_repl_check,
                cdb_free(cdbfr);
        }
 
-       /* Make sure the message doesn't already exist in this room.  It
-        * is absolutely taboo to have more than one reference to the same
-        * message in a room.
+
+       /* Create a list of msgid's which were supplied by the caller, but do
+        * not already exist in the target room.  It is absolutely taboo to
+        * have more than one reference to the same message in a room.
         */
-       if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
-               if (msglist[i] == msgid) {
-                       lputroom(&CC->room);    /* unlock the room */
-                       getroom(&CC->room, hold_rm);
-                       free(msglist);
-                       return(ERROR + ALREADY_EXISTS);
+       for (i=0; i<num_newmsgs; ++i) {
+               unique = 1;
+               if (num_msgs > 0) for (j=0; j<num_msgs; ++j) {
+                       if (msglist[j] == newmsgidlist[i]) {
+                               unique = 0;
+                       }
+               }
+               if (unique) {
+                       msgs_to_be_merged[num_msgs_to_be_merged++] = newmsgidlist[i];
                }
        }
 
-       /* Now add the new message */
-       ++num_msgs;
-       msglist = realloc(msglist, (num_msgs * sizeof(long)));
+       lprintf(9, "%d unique messages to be merged\n", num_msgs_to_be_merged);
 
+       /*
+        * Now merge the new messages
+        */
+       msglist = realloc(msglist, (sizeof(long) * (num_msgs + num_msgs_to_be_merged)) );
        if (msglist == NULL) {
                lprintf(CTDL_ALERT, "ERROR: can't realloc message list!\n");
        }
-       msglist[num_msgs - 1] = msgid;
+       memcpy(&msglist[num_msgs], msgs_to_be_merged, (sizeof(long) * num_msgs_to_be_merged) );
+       num_msgs += num_msgs_to_be_merged;
 
        /* Sort the message list, so all the msgid's are in order */
        num_msgs = sort_msglist(msglist, num_msgs);
@@ -1924,43 +1947,69 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int do_repl_check,
 
        /* Perform replication checks if necessary */
        if ( (DoesThisRoomNeedEuidIndexing(&CC->room)) && (do_repl_check) ) {
-               if (supplied_msg != NULL) {
-                       msg = supplied_msg;
-               }
-               else {
-                       msg = CtdlFetchMessage(msgid, 0);
-               }
-
-               if (msg != NULL) {
-                       ReplicationChecks(msg);
-               }
+               lprintf(CTDL_DEBUG, "CtdlSaveMsgPointerInRoom() doing repl checks\n");
 
-       }
+               for (i=0; i<num_msgs_to_be_merged; ++i) {
+                       msgid = msgs_to_be_merged[i];
+       
+                       if (supplied_msg != NULL) {
+                               msg = supplied_msg;
+                       }
+                       else {
+                               msg = CtdlFetchMessage(msgid, 0);
+                       }
+       
+                       if (msg != NULL) {
+                               ReplicationChecks(msg);
+               
+                               /* If the message has an Exclusive ID, index that... */
+                               if (msg->cm_fields['E'] != NULL) {
+                                       index_message_by_euid(msg->cm_fields['E'], &CC->room, msgid);
+                               }
 
-       /* If the message has an Exclusive ID, index that... */
-       if (msg != NULL) {
-               if (msg->cm_fields['E'] != NULL) {
-                       index_message_by_euid(msg->cm_fields['E'],
-                                               &CC->room, msgid);
+                               /* Free up the memory we may have allocated */
+                               if (msg != supplied_msg) {
+                                       CtdlFreeMessage(msg);
+                               }
+                       }
+       
                }
        }
 
-       /* Free up the memory we may have allocated */
-       if ( (msg != NULL) && (msg != supplied_msg) ) {
-               CtdlFreeMessage(msg);
+       else {
+               lprintf(CTDL_DEBUG, "CtdlSaveMsgPointerInRoom() skips repl checks\n");
        }
 
        /* Go back to the room we were in before we wandered here... */
        getroom(&CC->room, hold_rm);
 
-       /* Bump the reference count for this message. */
-       AdjRefCount(msgid, +1);
+       /* Bump the reference count for all messages which were merged */
+       for (i=0; i<num_msgs_to_be_merged; ++i) {
+               AdjRefCount(msgs_to_be_merged[i], +1);
+       }
+
+       /* Free up memory... */
+       if (msgs_to_be_merged != NULL) {
+               free(msgs_to_be_merged);
+       }
 
        /* Return success. */
        return (0);
 }
 
 
+/*
+ * This is the same as CtdlSaveMsgPointersInRoom() but it only accepts
+ * a single message.
+ */
+int CtdlSaveMsgPointerInRoom(char *roomname, long msgid,
+                       int do_repl_check, struct CtdlMessage *supplied_msg)
+{
+       return CtdlSaveMsgPointersInRoom(roomname, &msgid, 1, do_repl_check, supplied_msg);
+}
+
+
+
 
 /*
  * Message base operation to save a new message to the message store
@@ -1980,7 +2029,7 @@ long send_message(struct CtdlMessage *msg) {
 
        /* Get a new message number */
        newmsgid = get_new_message_number();
-       snprintf(msgidbuf, sizeof msgidbuf, "%ld@%s", newmsgid, config.c_fqdn);
+       snprintf(msgidbuf, sizeof msgidbuf, "%010ld@%s", newmsgid, config.c_fqdn);
 
        /* Generate an ID if we don't have one already */
        if (msg->cm_fields['I']==NULL) {
@@ -2116,7 +2165,7 @@ void ReplicationChecks(struct CtdlMessage *msg) {
        old_msgnum = locate_message_by_euid(msg->cm_fields['E'], &CC->room);
        if (old_msgnum > 0L) {
                lprintf(CTDL_DEBUG, "ReplicationChecks() replacing message %ld\n", old_msgnum);
-               CtdlDeleteMessages(CC->room.QRname, old_msgnum, "", 0);
+               CtdlDeleteMessages(CC->room.QRname, &old_msgnum, 1, "", 0);
        }
 }
 
@@ -3246,7 +3295,8 @@ void cmd_ent0(char *entargs)
  * (returns the actual number of messages deleted)
  */
 int CtdlDeleteMessages(char *room_name,                /* which room */
-                       long dmsgnum,           /* or "0" for any */
+                       long *dmsgnums,         /* array of msg numbers to be deleted */
+                       int num_dmsgnums,       /* number of msgs to be deleted, or 0 for "any" */
                        char *content_type,     /* or "" for any */
                        int deferred            /* let TDAP sweep it later */
 )
@@ -3257,13 +3307,13 @@ int CtdlDeleteMessages(char *room_name,         /* which room */
        long *msglist = NULL;
        long *dellist = NULL;
        int num_msgs = 0;
-       int i;
+       int i, j;
        int num_deleted = 0;
        int delete_this;
        struct MetaData smi;
 
-       lprintf(CTDL_DEBUG, "CtdlDeleteMessages(%s, %ld, %s, %d)\n",
-               room_name, dmsgnum, content_type, deferred);
+       lprintf(CTDL_DEBUG, "CtdlDeleteMessages(%s, %d msgs, %s, %d)\n",
+               room_name, num_dmsgnums, content_type, deferred);
 
        /* get room record, obtaining a lock... */
        if (lgetroom(&qrbuf, room_name) != 0) {
@@ -3286,9 +3336,20 @@ int CtdlDeleteMessages(char *room_name,          /* which room */
 
                        /* Set/clear a bit for each criterion */
 
-                       if ((dmsgnum == 0L) || (msglist[i] == dmsgnum)) {
+                       /* 0 messages in the list or a null list means that we are
+                        * interested in deleting any messages which meet the other criteria.
+                        */
+                       if ((num_dmsgnums == 0) || (dmsgnums == NULL)) {
                                delete_this |= 0x01;
                        }
+                       else {
+                               for (j=0; j<num_dmsgnums; ++j) {
+                                       if (msglist[i] == dmsgnums[j]) {
+                                               delete_this |= 0x01;
+                                       }
+                               }
+                       }
+
                        if (strlen(content_type) == 0) {
                                delete_this |= 0x02;
                        } else {
@@ -3321,11 +3382,11 @@ int CtdlDeleteMessages(char *room_name,         /* which room */
         * DELETED_MSGS_ROOM.  This will cause the reference count to remain
         * at least 1, which will save the user from having to synchronously
         * wait for various disk-intensive operations to complete.
+        *
+        * Slick -- we now use the new bulk API for moving messages.
         */
        if ( (deferred) && (num_deleted) ) {
-               for (i=0; i<num_deleted; ++i) {
-                       CtdlCopyMsgToRoom(dellist[i], DELETED_MSGS_ROOM);
-               }
+               CtdlCopyMsgsToRoom(dellist, num_deleted, DELETED_MSGS_ROOM);
        }
 
        /* Go through the messages we pulled out of the index, and decrement
@@ -3382,7 +3443,7 @@ void cmd_dele(char *delstr)
        }
        delnum = extract_long(delstr, 0);
 
-       num_deleted = CtdlDeleteMessages(CC->room.QRname, delnum, "", 1);
+       num_deleted = CtdlDeleteMessages(CC->room.QRname, &delnum, 1, "", 1);
 
        if (num_deleted) {
                cprintf("%d %d message%s deleted.\n", CIT_OK,
@@ -3394,18 +3455,26 @@ void cmd_dele(char *delstr)
 
 
 /*
- * Back end API function for moves and deletes
+ * Back end API function for moves and deletes (multiple messages)
  */
-int CtdlCopyMsgToRoom(long msgnum, char *dest) {
+int CtdlCopyMsgsToRoom(long *msgnums, int num_msgs, char *dest) {
        int err;
 
-       err = CtdlSaveMsgPointerInRoom(dest, msgnum, 1, NULL);
+       err = CtdlSaveMsgPointersInRoom(dest, msgnums, num_msgs, 1, NULL);
        if (err != 0) return(err);
 
        return(0);
 }
 
 
+/*
+ * Back end API function for moves and deletes (single message)
+ */
+int CtdlCopyMsgToRoom(long msgnum, char *dest) {
+       return CtdlCopyMsgsToRoom(&msgnum, 1, dest);
+}
+
+
 
 /*
  * move or copy a message to another room
@@ -3475,7 +3544,7 @@ void cmd_move(char *args)
         * if this is a 'move' rather than a 'copy' operation.
         */
        if (is_copy == 0) {
-               CtdlDeleteMessages(CC->room.QRname, num, "", 0);
+               CtdlDeleteMessages(CC->room.QRname, &num, 1, "", 0);
        }
 
        cprintf("%d Message %s.\n", CIT_OK, (is_copy ? "copied" : "moved") );
@@ -3685,7 +3754,7 @@ void CtdlWriteObject(char *req_room,              /* Room to stuff it in */
         */
        if (is_unique) {
                lprintf(CTDL_DEBUG, "Deleted %d other msgs of this type\n",
-                       CtdlDeleteMessages(roomname, 0L, content_type, 0)
+                       CtdlDeleteMessages(roomname, NULL, 0, content_type, 0)
                );
        }
        /* Now write the data */