/*
* Text description of this software
*/
-#define CITADEL "Citadel 6.83"
+#define CITADEL "Citadel 6.84"
/*
* REV_LEVEL is the current version number (multiplied by 100 to avoid having
* usually more strict because you're not really supposed to dump/load and
* upgrade at the same time.
*/
-#define REV_LEVEL 683 /* This version */
+#define REV_LEVEL 684 /* This version */
#define REV_MIN 591 /* Oldest compatible database */
#define EXPORT_REV_MIN 655 /* Oldest compatible export files */
"Rebuilding EUID index for <%s>\n",
rplist->name);
usergoto(rplist->name, 0, 0, NULL, NULL);
- CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL,
+ CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL,
rebuild_euid_index_for_msg, NULL);
}
}
* API function to perform an operation for each qualifying message in the
* current room. (Returns the number of messages processed.)
*/
-int CtdlForEachMessage(int mode, long ref,
+int CtdlForEachMessage(int mode, long ref, char *search_string,
char *content_type,
struct CtdlMessage *compare,
void (*CallBack) (long, void *),
void *userdata)
{
- int a;
+ int a, i, j;
struct visit vbuf;
struct cdbdata *cdbfr;
long *msglist = NULL;
int is_seen = 0;
long lastold = 0L;
int printed_lastold = 0;
+ int num_search_msgs = 0;
+ long *search_msgs = NULL;
/* Learn about the user and room in question */
get_mm();
}
}
+ /* If a search string was specified, get a message list from
+ * the full text index and remove messages which aren't on both
+ * lists.
+ *
+ * How this works:
+ * Since the lists are sorted and strictly ascending, and the
+ * output list is guaranteed to be shorter than or equal to the
+ * input list, we overwrite the bottom of the input list. This
+ * eliminates the need to memmove big chunks of the list over and
+ * over again.
+ */
+ if ( (num_msgs > 0) && (mode == MSGS_SEARCH) && (search_string) ) {
+ ft_search(&num_search_msgs, &search_msgs, search_string);
+ if (num_search_msgs > 0) {
+ int orig_num_msgs;
+
+ orig_num_msgs = num_msgs;
+ num_msgs = 0;
+ for (i=0; i<orig_num_msgs; ++i) {
+ for (j=0; j<num_search_msgs; ++j) {
+ if (msglist[i] == search_msgs[j]) {
+ msglist[num_msgs++] = msglist[j];
+ }
+ }
+ }
+ }
+ else {
+ num_msgs = 0; /* No messages qualify */
+ }
+ if (search_msgs != NULL) free(search_msgs);
+
+ /* Now that we've purged messages which don't contain the search
+ * string, treat a MSGS_SEARCH just like a MSGS_ALL from this
+ * point on.
+ */
+ mode = MSGS_ALL;
+ }
+
/*
* Now iterate through the message list, according to the
* criteria supplied by the caller.
int with_template = 0;
struct CtdlMessage *template = NULL;
int with_headers = 0;
+ char search_string[1024];
extract_token(which, cmdbuf, 0, '|', sizeof which);
cm_ref = extract_int(cmdbuf, 1);
+ extract_token(search_string, cmdbuf, 1, '|', sizeof search_string);
with_template = extract_int(cmdbuf, 2);
with_headers = extract_int(cmdbuf, 3);
- mode = MSGS_ALL;
strcat(which, " ");
if (!strncasecmp(which, "OLD", 3))
mode = MSGS_OLD;
mode = MSGS_LAST;
else if (!strncasecmp(which, "GT", 2))
mode = MSGS_GT;
+ else if (!strncasecmp(which, "SEARCH", 6))
+ mode = MSGS_SEARCH;
+ else
+ mode = MSGS_ALL;
if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
cprintf("%d not logged in\n", ERROR + NOT_LOGGED_IN);
return;
}
+ if ( (mode == MSGS_SEARCH) && (!config.c_enable_fulltext) ) {
+ cprintf("%d Full text index is not enabled on this server.\n",
+ ERROR + CMD_NOT_SUPPORTED);
+ return;
+ }
+
if (with_template) {
unbuffer_output();
cprintf("%d Send template then receive message list\n",
}
CtdlForEachMessage(mode,
- cm_ref,
+ ( (mode == MSGS_SEARCH) ? 0 : cm_ref ),
+ ( (mode == MSGS_SEARCH) ? search_string : NULL ),
NULL,
template,
(with_headers ? headers_listing : simple_listing),
/* We want the last (and probably only) config in this room */
begin_critical_section(S_CONFIG);
config_msgnum = (-1L);
- CtdlForEachMessage(MSGS_LAST, 1, sysconfname, NULL,
+ CtdlForEachMessage(MSGS_LAST, 1, NULL, sysconfname, NULL,
CtdlGetSysConfigBackend, NULL);
msgnum = config_msgnum;
end_critical_section(S_CONFIG);
#define aide_message(text) quickie_message("Citadel",NULL,AIDEROOM,text,0,NULL)
-#define MSGS_ALL 0
-#define MSGS_OLD 1
-#define MSGS_NEW 2
-#define MSGS_FIRST 3
-#define MSGS_LAST 4
-#define MSGS_GT 5
-#define MSGS_EQ 6
+enum {
+ MSGS_ALL,
+ MSGS_OLD,
+ MSGS_NEW,
+ MSGS_FIRST,
+ MSGS_LAST,
+ MSGS_GT,
+ MSGS_EQ,
+ MSGS_SEARCH
+};
/*
* Possible return codes from CtdlOutputMsg()
void AdjRefCount(long, int);
void simple_listing(long, void *);
int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template);
-int CtdlForEachMessage(int mode, long ref,
+int CtdlForEachMessage(int mode,
+ long ref,
+ char *searchstring,
char *content_type,
struct CtdlMessage *compare,
void (*CallBack) (long, void *),
/* Take a spin through the user's personal address book */
if (getroom(&CC->room, USERCONTACTSROOM) == 0) {
- CtdlForEachMessage(MSGS_ALL, 0, "text/x-vcard", NULL,
+ CtdlForEachMessage(MSGS_ALL, 0, NULL, "text/x-vcard", NULL,
hunt_for_autocomplete, search_string);
}
/* FIXME try the global address book */
if (getroom(&CC->room, ADDRESS_BOOK_ROOM) == 0) {
- CtdlForEachMessage(MSGS_ALL, 0, "text/x-vcard", NULL,
+ CtdlForEachMessage(MSGS_ALL, 0, NULL, "text/x-vcard", NULL,
hunt_for_autocomplete, search_string);
}
cprintf("%d Conflicting events:\n", LISTING_FOLLOWS);
- CtdlForEachMessage(MSGS_ALL, 0, "text/calendar",
+ CtdlForEachMessage(MSGS_ALL, 0, NULL, "text/calendar",
NULL,
ical_hunt_for_conflicts_backend,
(void *) cal
/* Add busy time from events */
lprintf(CTDL_DEBUG, "Adding busy time from events\n");
- CtdlForEachMessage(MSGS_ALL, 0, "text/calendar",
+ CtdlForEachMessage(MSGS_ALL, 0, NULL, "text/calendar",
NULL, ical_freebusy_backend, (void *)fb
);
icalcomponent_set_method(encaps, ICAL_METHOD_PUBLISH);
/* Now go through the room encapsulating all calendar items. */
- CtdlForEachMessage(MSGS_ALL, 0, "text/calendar",
+ CtdlForEachMessage(MSGS_ALL, 0, NULL,
+ "text/calendar",
NULL,
ical_getics_backend,
(void *) encaps
void do_fsck_room(struct ctdlroom *qrbuf, void *data)
{
getroom(&CC->room, qrbuf->QRname);
- CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, do_fsck_msg, NULL);
+ CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL, do_fsck_msg, NULL);
}
/*
void ft_index_room(struct ctdlroom *qrbuf, void *data)
{
getroom(&CC->room, qrbuf->QRname);
- CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, ft_index_msg, NULL);
+ CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL, ft_index_msg, NULL);
}
void inetcfg_init(void) {
if (getroom(&CC->room, SYSCONFIGROOM) != 0) return;
- CtdlForEachMessage(MSGS_LAST, 1, INTERNETCFG, NULL,
+ CtdlForEachMessage(MSGS_LAST, 1, NULL, INTERNETCFG, NULL,
inetcfg_init_backend, NULL);
}
}
/* Do something useful */
- CtdlForEachMessage(MSGS_GT, sc.lastsent, NULL, NULL,
+ CtdlForEachMessage(MSGS_GT, sc.lastsent, NULL, NULL, NULL,
network_spool_msg, &sc);
/* If we wrote a digest, deliver it and then close it */
if (!found_node) return(-1);
/* Send ALL messages */
- num_spooled = CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL,
+ num_spooled = CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL,
network_spool_msg, &sc);
/* Concise cleanup because we know there's only one node in the sc */
if (getroom(&CC->room, MAILROOM) != 0) return(-1);
/* Load up the messages */
- CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL,
+ CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL,
pop3_add_message, NULL);
/* Figure out which are old and which are new */
lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
return;
}
- CtdlForEachMessage(MSGS_ALL, 0L,
+ CtdlForEachMessage(MSGS_ALL, 0L, NULL,
SPOOLMIME, NULL, smtp_do_procmsg, NULL);
lprintf(CTDL_INFO, "SMTP: queue run completed\n");
/* format of message list export is all message numbers output
* one per line terminated by a 0.
*/
- CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL,
+ CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL,
artv_export_room_msg, NULL);
cprintf("0\n");
CtdlDirectoryInit();
/* We want *all* vCards in this room */
- CtdlForEachMessage(MSGS_ALL, 0, "text/x-vcard",
+ CtdlForEachMessage(MSGS_ALL, 0, NULL, "text/x-vcard",
NULL, vcard_add_to_directory, NULL);
getroom(&CC->room, hold_rm); /* return to saved room */
/* We want the last (and probably only) vcard in this room */
VCmsgnum = (-1);
- CtdlForEachMessage(MSGS_LAST, 1, "text/x-vcard",
+ CtdlForEachMessage(MSGS_LAST, 1, NULL, "text/x-vcard",
NULL, vcard_gu_backend, (void *)&VCmsgnum );
getroom(&CC->room, hold_rm); /* return to saved room */
/* First remove any addresses we already have in the address book */
usergoto(aptr->roomname, 0, 0, NULL, NULL);
- CtdlForEachMessage(MSGS_ALL, 0, "text/x-vcard", NULL,
+ CtdlForEachMessage(MSGS_ALL, 0, NULL, "text/x-vcard", NULL,
strip_addresses_already_have, aptr->collected_addresses);
if (strlen(aptr->collected_addresses) > 0)
int home=0;
char relhome[PATH_MAX]="";
char ctdldir[PATH_MAX]=CTDLDIR;
+#ifdef HAVE_RUN_DIR
struct stat filestats;
+#endif
/* initialize the master context */
InitializeMasterCC();
number, for the corresponding effect; or "gt" plus a number, to list all
messages in the current room with a message number greater than the one
specified. If no parameters are specified, "all" is assumed.
+
+ In addition, the first parameter may be set to "search", in which case the
+third parameter must be a search string. This will request all messages
+in the current room whose text contains the search string.
The third argument, 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