4 * Autocompletion of email recipients, etc.
6 * Copyright (c) 1987-2009 by the citadel.org team
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include <sys/types.h>
34 #if TIME_WITH_SYS_TIME
35 # include <sys/time.h>
39 # include <sys/time.h>
48 #include <libcitadel.h>
51 #include "citserver.h"
57 #include "serv_autocompletion.h"
59 #include "ctdl_module.h"
69 * Convert a structured name into a friendly name. Caller must free the
72 char *n_to_fn(char *value) {
76 nnn = malloc(strlen(value) + 10);
78 extract_token(&nnn[strlen(nnn)] , value, 3, ';', 999);
80 extract_token(&nnn[strlen(nnn)] , value, 1, ';', 999);
82 extract_token(&nnn[strlen(nnn)] , value, 2, ';', 999);
84 extract_token(&nnn[strlen(nnn)] , value, 0, ';', 999);
86 extract_token(&nnn[strlen(nnn)] , value, 4, ';', 999);
88 for (i=0; i<strlen(nnn); ++i) {
89 if (!strncmp(&nnn[i], " ", 2)) strcpy(&nnn[i], &nnn[i+1]);
99 * Back end for cmd_auto()
101 void hunt_for_autocomplete(long msgnum, char *search_string) {
102 struct CtdlMessage *msg;
109 msg = CtdlFetchMessage(msgnum, 1);
110 if (msg == NULL) return;
112 v = vcard_load(msg->cm_fields['M']);
113 CtdlFreeMessage(msg);
116 * Try to match from a friendly name (the "fn" field). If there is
117 * a match, return the entry in the form of:
118 * Display Name <user@domain.org>
120 value = vcard_get_prop(v, "fn", 0, 0, 0);
121 if (value != NULL) if (bmstrcasestr(value, search_string)) {
122 value2 = vcard_get_prop(v, "email", 1, 0, 0);
123 if (value2 == NULL) value2 = "";
124 cprintf("%s <%s>\n", value, value2);
130 * Try to match from a structured name (the "n" field). If there is
131 * a match, return the entry in the form of:
132 * Display Name <user@domain.org>
134 value = vcard_get_prop(v, "n", 0, 0, 0);
135 if (value != NULL) if (bmstrcasestr(value, search_string)) {
137 value2 = vcard_get_prop(v, "email", 1, 0, 0);
138 if (value2 == NULL) value2 = "";
139 nnn = n_to_fn(value);
140 cprintf("%s <%s>\n", nnn, value2);
147 * Try a partial match on all listed email addresses.
150 while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) {
151 if (bmstrcasestr(value, search_string)) {
152 if (vcard_get_prop(v, "fn", 0, 0, 0)) {
153 cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value);
155 else if (vcard_get_prop(v, "n", 0, 0, 0)) {
156 nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0));
157 cprintf("%s <%s>\n", nnn, value);
162 cprintf("%s\n", value);
175 * Attempt to autocomplete an address based on a partial...
177 void cmd_auto(char *argbuf) {
178 char hold_rm[ROOMNAMELEN];
179 char search_string[256];
180 long *msglist = NULL;
182 long *fts_msgs = NULL;
183 int fts_num_msgs = 0;
184 struct cdbdata *cdbfr;
188 int search_match = 0;
189 char *rooms_to_try[] = { USERCONTACTSROOM, ADDRESS_BOOK_ROOM };
191 if (CtdlAccessCheck(ac_logged_in)) return;
192 extract_token(search_string, argbuf, 0, '|', sizeof search_string);
193 if (IsEmptyStr(search_string)) {
194 cprintf("%d You supplied an empty partial.\n",
195 ERROR + ILLEGAL_VALUE);
199 strcpy(hold_rm, CC->room.QRname); /* save current room */
200 cprintf("%d try these:\n", LISTING_FOLLOWS);
203 * Gather up message pointers in rooms containing vCards
205 for (r=0; r < (sizeof(rooms_to_try) / sizeof(char *)); ++r) {
206 if (CtdlGetRoom(&CC->room, rooms_to_try[r]) == 0) {
207 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
209 msglist = realloc(msglist, (num_msgs * sizeof(long)) + cdbfr->len + 1);
210 memcpy(&msglist[num_msgs], cdbfr->ptr, cdbfr->len);
211 num_msgs += (cdbfr->len / sizeof(long));
218 * Search-reduce the results if we have the full text index available
220 if (config.c_enable_fulltext) {
221 CtdlModuleDoSearch(&fts_num_msgs, &fts_msgs, search_string, "fulltext");
223 for (i=0; i<num_msgs; ++i) {
225 for (j=0; j<fts_num_msgs; ++j) {
226 if (msglist[i] == fts_msgs[j]) {
228 j = fts_num_msgs + 1; /* end the search */
232 msglist[i] = 0; /* invalidate this result */
238 /* If no results, invalidate the whole list */
246 * Now output the ones that look interesting
248 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
249 if (msglist[i] != 0) {
250 hunt_for_autocomplete(msglist[i], search_string);
255 if (strcmp(CC->room.QRname, hold_rm)) {
256 CtdlGetRoom(&CC->room, hold_rm); /* return to saved room */
266 CTDL_MODULE_INIT(autocompletion) {
269 CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion");
271 /* return our Subversion id for the Log */