* Holy war on strlen: use IsEmptyStr where apropriate.
[citadel.git] / citadel / msgbase.c
index 3579dcb33067ecae481b0855589e4440109e3635..961e9acb8f111cb8591193079d00f767593555f8 100644 (file)
 #include "html.h"
 #include "genstamp.h"
 #include "internet_addressing.h"
-#include "serv_fulltext.h"
 #include "vcard.h"
 #include "euidindex.h"
 #include "journaling.h"
 #include "citadel_dirs.h"
-#include "serv_network.h"
 
-#ifdef HAVE_LIBSIEVE
-# include "serv_sieve.h"
-#endif /* HAVE_LIBSIEVE */
 
 long config_msgnum;
 struct addresses_to_be_filed *atbf = NULL;
@@ -530,10 +525,12 @@ int CtdlForEachMessage(int mode, long ref, char *search_string,
        int num_search_msgs = 0;
        long *search_msgs = NULL;
        regex_t re;
+       int need_to_free_re = 0;
        regmatch_t pm;
 
-       if (content_type) if (strlen(content_type) > 0) {
+       if (content_type) if (!IsEmptyStr(content_type)) {
                regcomp(&re, content_type, 0);
+               need_to_free_re = 1;
        }
 
        /* Learn about the user and room in question */
@@ -548,6 +545,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string,
                num_msgs = cdbfr->len / sizeof(long);
                cdb_free(cdbfr);
        } else {
+               if (need_to_free_re) regfree(&re);
                return 0;       /* No messages at all?  No further action. */
        }
 
@@ -560,7 +558,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string,
                /* If the caller is looking for a specific MIME type, filter
                 * out all messages which are not of the type requested.
                 */
-               if (content_type != NULL) if (strlen(content_type) > 0) {
+               if (content_type != NULL) if (!IsEmptyStr(content_type)) {
 
                        /* This call to GetMetaData() sits inside this loop
                         * so that we only do the extra database read per msg
@@ -611,7 +609,13 @@ int CtdlForEachMessage(int mode, long ref, char *search_string,
         * over again.
         */
        if ( (num_msgs > 0) && (mode == MSGS_SEARCH) && (search_string) ) {
-               ft_search(&num_search_msgs, &search_msgs, search_string);
+
+               /* Call search module via hook mechanism.
+                * NULL means use any search function available.
+                * otherwise replace with a char * to name of search routine
+                */
+               CtdlModuleDoSearch(&num_search_msgs, &search_msgs, search_string, "fulltext");
+
                if (num_search_msgs > 0) {
        
                        int orig_num_msgs;
@@ -676,6 +680,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string,
                        }
                }
        free(msglist);          /* Clean up */
+       if (need_to_free_re) regfree(&re);
        return num_processed;
 }
 
@@ -992,7 +997,7 @@ void mime_spew_section(char *name, char *filename, char *partnum, char *disp,
 
        *found_it = 1;
 
-       cprintf("%d %d\n", BINARY_FOLLOWS, length);
+       cprintf("%d %d\n", BINARY_FOLLOWS, (int)length);
        client_write(content, length);
 }
 
@@ -1202,7 +1207,7 @@ void fixed_output(char *name, char *filename, char *partnum, char *disp,
        ma->did_print = 1;
 
        if ( (!strcasecmp(cbtype, "text/plain")) 
-          || (strlen(cbtype)==0) ) {
+          || (IsEmptyStr(cbtype)) ) {
                wptr = content;
                if (length > 0) {
                        client_write(wptr, length);
@@ -1303,12 +1308,12 @@ void output_preferred(char *name, char *filename, char *partnum, char *disp,
                                ++add_newline;
                        }
                        cprintf("Content-type: %s", cbtype);
-                       if (strlen(cbcharset) > 0) {
+                       if (!IsEmptyStr(cbcharset)) {
                                cprintf("; charset=%s", cbcharset);
                        }
                        cprintf("\nContent-length: %d\n",
                                (int)(length + add_newline) );
-                       if (strlen(encoding) > 0) {
+                       if (!IsEmptyStr(encoding)) {
                                cprintf("Content-transfer-encoding: %s\n", encoding);
                        }
                        else {
@@ -1409,7 +1414,7 @@ int CtdlOutputMsg(long msg_num,           /* message number (local) to fetch */
        /* Here is the weird form of this command, to process only an
         * encapsulated message/rfc822 section.
         */
-       if (section) if (strlen(section)>0) if (strcmp(section, "0")) {
+       if (section) if (!IsEmptyStr(section)) if (strcmp(section, "0")) {
                memset(&encap, 0, sizeof encap);
                safestrncpy(encap.desired_section, section, sizeof encap.desired_section);
                mime_parser(TheMessage->cm_fields['M'],
@@ -1600,7 +1605,7 @@ int CtdlOutputPreLoadedMsg(
                 */
                suppress_f = 0;
                if (TheMessage->cm_fields['N'] != NULL)
-                  if (strlen(TheMessage->cm_fields['N']) > 0)
+                  if (!IsEmptyStr(TheMessage->cm_fields['N']))
                      if (haschar(TheMessage->cm_fields['N'], '.') == 0) {
                        suppress_f = 1;
                }
@@ -1815,7 +1820,7 @@ START_TEXT:
                                buf[strlen(buf)] = ch;
                        }
                }
-               if (strlen(buf) > 0)
+               if (!IsEmptyStr(buf))
                        cprintf("%s%s", buf, nl);
        }
 
@@ -2139,15 +2144,8 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms
                lprintf(CTDL_DEBUG, "CtdlSaveMsgPointerInRoom() skips repl checks\n");
        }
 
-       /* Submit this room for net processing */
-       network_queue_room(&CC->room, NULL);
-
-#ifdef HAVE_LIBSIEVE
-       /* If this is someone's inbox, submit the room for sieve processing */
-       if (!strcasecmp(&CC->room.QRname[11], MAILROOM)) {
-               sieve_queue_room(&CC->room);
-       }
-#endif /* HAVE_LIBSIEVE */
+       /* Submit this room for processing by hooks */
+       PerformRoomHooks(&CC->room);
 
        /* Go back to the room we were in before we wandered here... */
        getroom(&CC->room, hold_rm);
@@ -2327,7 +2325,7 @@ void ReplicationChecks(struct CtdlMessage *msg) {
        /* No exclusive id?  Don't do anything. */
        if (msg == NULL) return;
        if (msg->cm_fields['E'] == NULL) return;
-       if (strlen(msg->cm_fields['E']) == 0) return;
+       if (IsEmptyStr(msg->cm_fields['E'])) return;
        /*lprintf(CTDL_DEBUG, "Exclusive ID: <%s> for room <%s>\n",
                msg->cm_fields['E'], CC->room.QRname);*/
 
@@ -2453,7 +2451,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        }
 
        /* ...or if this message is destined for Aide> then go there. */
-       if (strlen(force_room) > 0) {
+       if (!IsEmptyStr(force_room)) {
                strcpy(actual_rm, force_room);
        }
 
@@ -2576,7 +2574,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
                        MailboxName(actual_rm, sizeof actual_rm, &userbuf, MAILROOM);
                        CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0, msg);
                        BumpNewMailCounter(userbuf.usernum);
-                       if (strlen(config.c_funambol_host) > 0) {
+                       if (!IsEmptyStr(config.c_funambol_host)) {
                        /* Generate a instruction message for the Funambol notification
                         * server, in the same style as the SMTP queue
                         */
@@ -2927,6 +2925,7 @@ struct CtdlMessage *CtdlMakeMessage(
        char dest_node[256];
        char buf[1024];
        struct CtdlMessage *msg;
+       int i;
 
        msg = malloc(sizeof(struct CtdlMessage));
        memset(msg, 0, sizeof(struct CtdlMessage));
@@ -2940,8 +2939,21 @@ struct CtdlMessage *CtdlMakeMessage(
        striplt(recipient);
        striplt(recp_cc);
 
-       snprintf(buf, sizeof buf, "cit%ld", author->usernum);   /* Path */
-       msg->cm_fields['P'] = strdup(buf);
+       /* Path or Return-Path */
+       if (my_email == NULL) my_email = "";
+
+       if (!IsEmptyStr(my_email)) {
+               msg->cm_fields['P'] = strdup(my_email);
+       }
+       else {
+               snprintf(buf, sizeof buf, "%s", author->fullname);
+               msg->cm_fields['P'] = strdup(buf);
+       }
+       for (i=0; (msg->cm_fields['P'][i]!=0); ++i) {
+               if (isspace(msg->cm_fields['P'][i])) {
+                       msg->cm_fields['P'][i] = '_';
+               }
+       }
 
        snprintf(buf, sizeof buf, "%ld", (long)time(NULL));     /* timestamp */
        msg->cm_fields['T'] = strdup(buf);
@@ -2971,10 +2983,10 @@ struct CtdlMessage *CtdlMakeMessage(
                msg->cm_fields['D'] = strdup(dest_node);
        }
 
-       if (strlen(my_email) > 0) {
+       if (!IsEmptyStr(my_email)) {
                msg->cm_fields['F'] = strdup(my_email);
        }
-       else if ( (author == &CC->user) && (strlen(CC->cs_inet_email) > 0) ) {
+       else if ( (author == &CC->user) && (!IsEmptyStr(CC->cs_inet_email)) ) {
                msg->cm_fields['F'] = strdup(CC->cs_inet_email);
        }
 
@@ -3136,7 +3148,7 @@ struct recptypes *validate_recipients(char *supplied_recipients) {
 
        /* Now start extracting recipients... */
 
-       while (strlen(recipients) > 0) {
+       while (!IsEmptyStr(recipients)) {
 
                for (i=0; i<=strlen(recipients); ++i) {
                        if (recipients[i] == '\"') in_quotes = 1 - in_quotes;
@@ -3173,7 +3185,7 @@ struct recptypes *validate_recipients(char *supplied_recipients) {
                                if (!strcasecmp(this_recp, "sysop")) {
                                        ++ret->num_room;
                                        strcpy(this_recp, config.c_aideroom);
-                                       if (strlen(ret->recp_room) > 0) {
+                                       if (!IsEmptyStr(ret->recp_room)) {
                                                strcat(ret->recp_room, "|");
                                        }
                                        strcat(ret->recp_room, this_recp);
@@ -3181,7 +3193,7 @@ struct recptypes *validate_recipients(char *supplied_recipients) {
                                else if (getuser(&tempUS, this_recp) == 0) {
                                        ++ret->num_local;
                                        strcpy(this_recp, tempUS.fullname);
-                                       if (strlen(ret->recp_local) > 0) {
+                                       if (!IsEmptyStr(ret->recp_local)) {
                                                strcat(ret->recp_local, "|");
                                        }
                                        strcat(ret->recp_local, this_recp);
@@ -3189,7 +3201,7 @@ struct recptypes *validate_recipients(char *supplied_recipients) {
                                else if (getuser(&tempUS, this_recp_cooked) == 0) {
                                        ++ret->num_local;
                                        strcpy(this_recp, tempUS.fullname);
-                                       if (strlen(ret->recp_local) > 0) {
+                                       if (!IsEmptyStr(ret->recp_local)) {
                                                strcat(ret->recp_local, "|");
                                        }
                                        strcat(ret->recp_local, this_recp);
@@ -3197,7 +3209,7 @@ struct recptypes *validate_recipients(char *supplied_recipients) {
                                else if ( (!strncasecmp(this_recp, "room_", 5))
                                      && (!getroom(&tempQR, &this_recp_cooked[5])) ) {
                                        ++ret->num_room;
-                                       if (strlen(ret->recp_room) > 0) {
+                                       if (!IsEmptyStr(ret->recp_room)) {
                                                strcat(ret->recp_room, "|");
                                        }
                                        strcat(ret->recp_room, &this_recp_cooked[5]);
@@ -3214,13 +3226,13 @@ struct recptypes *validate_recipients(char *supplied_recipients) {
                                 * because if the address were valid, we would have
                                 * already translated it to a local address by now.
                                 */
-                               if (IsDirectory(this_recp)) {
+                               if (IsDirectory(this_recp, 0)) {
                                        ++ret->num_error;
                                        invalid = 1;
                                }
                                else {
                                        ++ret->num_internet;
-                                       if (strlen(ret->recp_internet) > 0) {
+                                       if (!IsEmptyStr(ret->recp_internet)) {
                                                strcat(ret->recp_internet, "|");
                                        }
                                        strcat(ret->recp_internet, this_recp);
@@ -3228,7 +3240,7 @@ struct recptypes *validate_recipients(char *supplied_recipients) {
                                break;
                        case MES_IGNET:
                                ++ret->num_ignet;
-                               if (strlen(ret->recp_ignet) > 0) {
+                               if (!IsEmptyStr(ret->recp_ignet)) {
                                        strcat(ret->recp_ignet, "|");
                                }
                                strcat(ret->recp_ignet, this_recp);
@@ -3239,7 +3251,7 @@ struct recptypes *validate_recipients(char *supplied_recipients) {
                                break;
                }
                if (invalid) {
-                       if (strlen(ret->errormsg) == 0) {
+                       if (IsEmptyStr(ret->errormsg)) {
                                snprintf(append, sizeof append,
                                         "Invalid recipient: %s",
                                         this_recp);
@@ -3252,7 +3264,7 @@ struct recptypes *validate_recipients(char *supplied_recipients) {
                        }
                }
                else {
-                       if (strlen(ret->display_recp) == 0) {
+                       if (IsEmptyStr(ret->display_recp)) {
                                strcpy(append, this_recp);
                        }
                        else {
@@ -3330,6 +3342,7 @@ void cmd_ent0(char *entargs)
        struct recptypes *valid_cc = NULL;
        struct recptypes *valid_bcc = NULL;
        char subject[SIZ];
+       int subject_required = 0;
        int do_confirm = 0;
        long msgnum;
        int i, j;
@@ -3369,7 +3382,7 @@ void cmd_ent0(char *entargs)
 
        /* Check some other permission type things. */
 
-       if (strlen(newusername) == 0) {
+       if (IsEmptyStr(newusername)) {
                strcpy(newusername, CC->user.fullname);
        }
        if (  (CC->user.axlevel < 6)
@@ -3384,15 +3397,15 @@ void cmd_ent0(char *entargs)
        }
 
 
-       if (strlen(newuseremail) == 0) {
+       if (IsEmptyStr(newuseremail)) {
                newuseremail_ok = 1;
        }
 
-       if (strlen(newuseremail) > 0) {
+       if (!IsEmptyStr(newuseremail)) {
                if (!strcasecmp(newuseremail, CC->cs_inet_email)) {
                        newuseremail_ok = 1;
                }
-               else if (strlen(CC->cs_inet_other_emails) > 0) {
+               else if (!IsEmptyStr(CC->cs_inet_other_emails)) {
                        j = num_tokens(CC->cs_inet_other_emails, '|');
                        for (i=0; i<j; ++i) {
                                extract_token(buf, CC->cs_inet_other_emails, i, '|', sizeof buf);
@@ -3413,13 +3426,15 @@ void cmd_ent0(char *entargs)
 
        CC->cs_flags |= CS_POSTING;
 
-       /* In the Mail> room we have to behave a little differently --
+       /* In mailbox rooms we have to behave a little differently --
         * make sure the user has specified at least one recipient.  Then
-        * validate the recipient(s).
+        * validate the recipient(s).  We do this for the Mail> room, as
+        * well as any room which has the "Mailbox" view set.
         */
-       if ( (CC->room.QRflags & QR_MAILBOX)
-          && (!strcasecmp(&CC->room.QRname[11], MAILROOM)) ) {
 
+       if (  ( (CC->room.QRflags & QR_MAILBOX) && (!strcasecmp(&CC->room.QRname[11], MAILROOM)) )
+          || ( (CC->room.QRflags & QR_MAILBOX) && (CC->curr_view == VIEW_MAILBOX) )
+       ) {
                if (CC->user.axlevel < 2) {
                        strcpy(recp, "sysop");
                        strcpy(cc, "");
@@ -3510,12 +3525,22 @@ void cmd_ent0(char *entargs)
                recp[0] = 0;
        }
 
+       /* Recommend to the client that the use of a message subject is
+        * strongly recommended in this room, if either the SUBJECTREQ flag
+        * is set, or if there is one or more Internet email recipients.
+        */
+       if (CC->room.QRflags2 & QR2_SUBJECTREQ) subject_required = 1;
+       if (valid_to) if (valid_to->num_internet > 0) subject_required = 1;
+       if (valid_cc) if (valid_cc->num_internet > 0) subject_required = 1;
+       if (valid_bcc) if (valid_bcc->num_internet > 0) subject_required = 1;
+
        /* If we're only checking the validity of the request, return
         * success without creating the message.
         */
        if (post == 0) {
-               cprintf("%d %s\n", CIT_OK,
-                       ((valid_to != NULL) ? valid_to->display_recp : "") );
+               cprintf("%d %s|%d\n", CIT_OK,
+                       ((valid_to != NULL) ? valid_to->display_recp : ""), 
+                       subject_required);
                free_recipients(valid_to);
                free_recipients(valid_cc);
                free_recipients(valid_bcc);
@@ -3537,7 +3562,7 @@ void cmd_ent0(char *entargs)
        msg = CtdlMakeMessage(&CC->user, recp, cc,
                CC->room.QRname, anonymous, format_type,
                newusername, newuseremail, subject,
-               ((strlen(supplied_euid) > 0) ? supplied_euid : NULL),
+               ((!IsEmptyStr(supplied_euid)) ? supplied_euid : NULL),
                NULL);
 
        /* Put together one big recipients struct containing to/cc/bcc all in
@@ -3545,19 +3570,19 @@ void cmd_ent0(char *entargs)
         */
        char *all_recps = malloc(SIZ * 3);
        strcpy(all_recps, recp);
-       if (strlen(cc) > 0) {
-               if (strlen(all_recps) > 0) {
+       if (!IsEmptyStr(cc)) {
+               if (!IsEmptyStr(all_recps)) {
                        strcat(all_recps, ",");
                }
                strcat(all_recps, cc);
        }
-       if (strlen(bcc) > 0) {
-               if (strlen(all_recps) > 0) {
+       if (!IsEmptyStr(bcc)) {
+               if (!IsEmptyStr(all_recps)) {
                        strcat(all_recps, ",");
                }
                strcat(all_recps, bcc);
        }
-       if (strlen(all_recps) > 0) {
+       if (!IsEmptyStr(all_recps)) {
                valid = validate_recipients(all_recps);
        }
        else {
@@ -3615,9 +3640,11 @@ int CtdlDeleteMessages(char *room_name,          /* which room */
        struct MetaData smi;
        regex_t re;
        regmatch_t pm;
+       int need_to_free_re = 0;
 
-       if (content_type) if (strlen(content_type) > 0) {
+       if (content_type) if (!IsEmptyStr(content_type)) {
                regcomp(&re, content_type, 0);
+               need_to_free_re = 1;
        }
        lprintf(CTDL_DEBUG, "CtdlDeleteMessages(%s, %d msgs, %s)\n",
                room_name, num_dmsgnums, content_type);
@@ -3626,6 +3653,7 @@ int CtdlDeleteMessages(char *room_name,           /* which room */
        if (lgetroom(&qrbuf, room_name) != 0) {
                lprintf(CTDL_ERR, "CtdlDeleteMessages(): Room <%s> not found\n",
                        room_name);
+               if (need_to_free_re) regfree(&re);
                return (0);     /* room not found */
        }
        cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
@@ -3657,7 +3685,7 @@ int CtdlDeleteMessages(char *room_name,           /* which room */
                                }
                        }
 
-                       if (strlen(content_type) == 0) {
+                       if (IsEmptyStr(content_type)) {
                                delete_this |= 0x02;
                        } else {
                                GetMetaData(&smi, msglist[i]);
@@ -3698,6 +3726,7 @@ int CtdlDeleteMessages(char *room_name,           /* which room */
        if (msglist != NULL) free(msglist);
        if (dellist != NULL) free(dellist);
        lprintf(CTDL_DEBUG, "%d message(s) deleted.\n", num_deleted);
+       if (need_to_free_re) regfree(&re);
        return (num_deleted);
 }
 
@@ -4055,11 +4084,9 @@ void TDAP_AdjRefCount(long msgnum, int incr)
         */
        if (smi.meta_refcount == 0) {
                lprintf(CTDL_DEBUG, "Deleting message <%ld>\n", msgnum);
-
-               /* Remove from fulltext index */
-               if (config.c_enable_fulltext) {
-                       ft_index_message(msgnum, 0);
-               }
+               
+               /* Call delete hooks with NULL room to show it has gone altogether */
+               PerformDeleteHooks(NULL, msgnum);
 
                /* Remove from message base */
                delnum = msgnum;
@@ -4243,7 +4270,7 @@ char *CtdlGetSysConfig(char *sysconfname) {
        if (conf != NULL) do {
                extract_token(buf, conf, 0, '\n', sizeof buf);
                strcpy(conf, &conf[strlen(buf)+1]);
-       } while ( (strlen(conf)>0) && (strlen(buf)>0) );
+       } while ( (!IsEmptyStr(conf)) && (!IsEmptyStr(buf)) );
 
        return(conf);
 }