* Added search-by-header-fields to CtdlForEachMessage(), and then to the
authorArt Cancro <ajc@citadel.org>
Wed, 13 Oct 1999 04:24:20 +0000 (04:24 +0000)
committerArt Cancro <ajc@citadel.org>
Wed, 13 Oct 1999 04:24:20 +0000 (04:24 +0000)
  server MSGS command.  This will have lots of uses.

citadel/ChangeLog
citadel/msgbase.c
citadel/msgbase.h
citadel/netproc.c
citadel/serv_icq.c
citadel/serv_vcard.c
citadel/techdoc/session.txt

index cb9c376a3239dad7a6583ce3bc443a9152773634..0d816e2fe5f125e019920fad76300ad3880ccd25 100644 (file)
@@ -1,4 +1,8 @@
 $Log$
+Revision 1.383  1999/10/13 04:24:18  ajc
+* Added search-by-header-fields to CtdlForEachMessage(), and then to the
+  server MSGS command.  This will have lots of uses.
+
 Revision 1.382  1999/10/13 01:36:39  ajc
 * Starting some work on network zap (supersede) mode for replication
 
@@ -1304,3 +1308,4 @@ Sat Jul 11 00:20:48 EDT 1998 Nathan Bryant <bryant@cs.usm.maine.edu>
 
 Fri Jul 10 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
        * Initial CVS import 
+
index 796c725e7424a5fcc522a7471a16391ed5ccabc4..341b587cf75bb5a20087e5b360350f4cbd4cf099 100644 (file)
 
 extern struct config config;
 
+char *msgkeys[] = {
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", "", "", "", "", "", "", "", 
+       "", 
+       "from",
+       "", "", "", "", "", "", 
+       "hnod",
+       "msgn",
+       "", "", "",
+       "text",
+       "node",
+       "room",
+       "path",
+       "",
+       "rcpt",
+       ""
+       "time",
+       "subj",
+       "",
+       "",
+       "",
+       "",
+       "zaps"
+};
+
 /*
  * This function is self explanatory.
  * (What can I say, I'm in a weird mood today...)
@@ -183,12 +214,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 +263,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 +296,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 +351,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 +380,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");
 }
 
@@ -613,10 +725,11 @@ 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];
 
        struct CtdlMessage *TheMessage = NULL;
 
@@ -706,15 +819,6 @@ 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']);
-               }
                if (TheMessage->cm_fields['A']) {
                        strcpy(buf, TheMessage->cm_fields['A']);
                        PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG);
@@ -731,24 +835,19 @@ void output_message(char *msgid, int mode, int headers_only)
                        }
                        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']);
-               }
-               if (TheMessage->cm_fields['Z']) {
-                       cprintf("zaps=%s\n", TheMessage->cm_fields['Z']);
+
+               strcpy(allkeys, FORDER);
+               for (i=0; i<strlen(allkeys); ++i) {
+                       k = (int) allkeys[i];
+                       if ((k != 'A') && (k != 'M')) {
+                               if (TheMessage->cm_fields[k] != NULL)
+                                       cprintf("%s=%s\n",
+                                               msgkeys[k],
+                                               TheMessage->cm_fields[k]
+                                       );
+                       }
                }
+
        }
 
        /* begin header processing loop for RFC822 transfer format */
index 99670f9bd064489b362fe8ebffce80623678a792..2cf4c64778d92d2143b5efcb5b746c4dbd6bf2e7 100644 (file)
@@ -46,8 +46,10 @@ void GetSuppMsgInfo(struct SuppMsgInfo *, long);
 void PutSuppMsgInfo(struct SuppMsgInfo *);
 void AdjRefCount(long, int);
 void simple_listing(long);
+int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template);
 void CtdlForEachMessage(int mode, long ref,
                        char *content_type,
+                       struct CtdlMessage *compare,
                         void (*CallBack) (long msgnum) );
 int CtdlDeleteMessages(char *, long, char *);
 void CtdlWriteObject(char *, char *, char *, struct usersupp *,
index f6fbce515ff6fa7c4589752844cd3da985dd7820..e1301b607b2b0c74f9a64f3fc231e5bf40812d40 100644 (file)
@@ -865,6 +865,8 @@ void process_zaplist(void) {
 
                }
 
+       }
+
        fclose(zaplist);
 
 }
index 93860e800f46ad02c85263614674fbf57f9e59db..ae2ecb8c45c7edafdcd38154f1295de79ad57e28 100644 (file)
@@ -1802,7 +1802,8 @@ void CtdlICQ_Read_Config(void) {
        /* We want the last (and probably only) config in this room */
        lprintf(9, "We're in <%s> looking for config\n", 
                CC->quickroom.QRname);
-       CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_Config_Backend);
+       CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, NULL,
+               CtdlICQ_Read_Config_Backend);
        getroom(&CC->quickroom, hold_rm);
        return;
 }
@@ -1930,7 +1931,8 @@ void CtdlICQ_Read_CL(void) {
        }
 
        /* We want the last (and probably only) list in this room */
-       CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, CtdlICQ_Read_CL_Backend);
+       CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, NULL,
+               CtdlICQ_Read_CL_Backend);
        getroom(&CC->quickroom, hold_rm);
 }
 
index 9de886616892c2d784fe494b6f35b8ddc480b2cf..97088083adeca9a434385a47c3573ad9ff912636 100644 (file)
@@ -101,7 +101,7 @@ int vcard_upload_beforesave(struct CtdlMessage *msg) {
                        }
                        VC->msgnum = (-1L);
                        CtdlForEachMessage(MSGS_ALL, 0,
-                               "text/x-vcard", vcard_gm_backend);
+                               "text/x-vcard", NULL, vcard_gm_backend);
 
                        if (VC->msgnum >= 0) {
                                if (msg->cm_fields['Z'] != NULL)
@@ -209,7 +209,8 @@ struct vCard *vcard_get_user(struct usersupp *u) {
 
         /* We want the last (and probably only) vcard in this room */
        VC->msgnum = (-1);
-        CtdlForEachMessage(MSGS_LAST, 1, "text/x-vcard", vcard_gm_backend);
+        CtdlForEachMessage(MSGS_LAST, 1, "text/x-vcard",
+               NULL, vcard_gm_backend);
         getroom(&CC->quickroom, hold_rm);      /* return to saved room */
 
        if (VC->msgnum < 0L) return vcard_new();
index 809ed3a39ff0128a6b8a0ec20469b1c805b04ebc..dcc7337cfa553022387e3c6f04835abb635a9cb5 100644 (file)
@@ -376,22 +376,74 @@ user to the arrival of new mail during a session)
  This command obtains a listing of all the messages in the current room
 which the client may request.  This command may be passed a single parameter:
 either "all", "old", or "new" to request all messages, only old messages, or
-new messages.  Or it may be passed two parameters: "last" plus a number, in which
-case that many message pointers will be returned, or "first" plus a number, for
-the corresponding effect.  If no parameters are specified, "all" is assumed.
+new messages.  Or it may be passed two parameters: "last" plus a number, in
+which case that many message pointers will be returned, or "first" plus a
+number, for the corresponding effect.  If no parameters are specified, "all"
+is assumed.
  
- In Citadel/UX 5.00 and above, the client may also specify "gt" plus a number, to
-list all messages in the current room with a message number greater than the one
-specified.
+ In Citadel/UX 5.00 and above, the client may also specify "gt" plus a number,
+to list all messages in the current room with a message number greater than
+the one specified.
+  
+ The third argument, valid only in Citadel/UX 5.60 and above, may be either
+0 or 1.  If it is 1, this command behaves differently: before a listing is
+returned, the client must transmit a list of fields to search for.  The field
+headers are listed below in the writeup for the "MSG0" command.
  
- This command can return two possible results.  An ERROR code may be returned
+ This command can return three possible results.  An ERROR code may be returned
 if no user is currently logged in or if something else went wrong.  Otherwise,
 LISTING_FOLLOWS will be returned, and the listing will consist of zero or
 more message numbers, one per line.  The listing ends, as always, with the
 string "000" alone on a line by itself.  The listed message numbers can be used
-to request messages from the system.
+to request messages from the system.  If "search mode" is being used, the
+server will return START_CHAT_MODE, and the client is expected to transmit
+the search criteria, and then read the message list.
+ Since this is somewhat complex, here are some examples:
+ Example 1: Read all new messages
+ Client:   MSGS NEW
+ Server:   100 Message list...
+           523218
+           523293
+           523295
+           000
+ Example 2: Read the last five messages
+ Client:   MSGS LAST|5
+ Server:   100 Message list...
+           523190
+           523211
+           523218
+           523293
+           523295
+           000
+ Example 3: Read all messages written by "IGnatius T Foobar"
+ Client:   MSGS ALL|0|1
+ Server:   800 Send template then receive message list
+ Client:   from|IGnatius T Foobar
+           000
+ Server:   518604
+           519366
+           519801
+           520201
+           520268
+           520805
+           520852
+           521579
+           521720
+           522571
+           000
+ Note that in "search mode" the client may specify any number of search
+criteria.  These criteria are applied with an AND logic.
  
  
+  
  MSG0   (read MeSsaGe, mode 0)
    
  This is a command used to read the text of a message.  "Mode 0" implies that
@@ -433,6 +485,7 @@ seconds since midnight on January 1, 1970, GMT).
  room=   The name of the room the message originated in.
  node=   The short node name of the system this message originated on.
  hnod=   The long node name of the system this message originated on.
+ zaps=   The id/node of a message which this one zaps (supersedes).
  
  text    Note that there is no "=" after the word "text".  This string
 signifies that the message text begins on the next line.