* more whitch-hunt on strlen with isemptystr replacement.
[citadel.git] / citadel / modules / autocompletion / serv_autocompletion.c
1 /*
2  * $Id$
3  *
4  * Autocompletion of email recipients, etc.
5  */
6
7 #include "sysdep.h"
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <fcntl.h>
12 #include <ctype.h>
13 #include <signal.h>
14 #include <pwd.h>
15 #include <errno.h>
16 #include <sys/types.h>
17
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
20 # include <time.h>
21 #else
22 # if HAVE_SYS_TIME_H
23 #  include <sys/time.h>
24 # else
25 #  include <time.h>
26 # endif
27 #endif
28
29 #include <sys/wait.h>
30 #include <string.h>
31 #include <limits.h>
32 #include "citadel.h"
33 #include "server.h"
34 #include "citserver.h"
35 #include "support.h"
36 #include "config.h"
37 #include "tools.h"
38 #include "msgbase.h"
39 #include "user_ops.h"
40 #include "room_ops.h"
41 #include "database.h"
42 #include "vcard.h"
43 #include "serv_autocompletion.h"
44
45 #include "ctdl_module.h"
46
47
48 #ifndef HAVE_SNPRINTF
49 #include "snprintf.h"
50 #endif
51
52
53
54 /*
55  * Convert a structured name into a friendly name.  Caller must free the
56  * returned pointer.
57  */
58 char *n_to_fn(char *value) {
59         char *nnn = NULL;
60         int i;
61
62         nnn = malloc(strlen(value) + 10);
63         strcpy(nnn, "");
64         extract_token(&nnn[strlen(nnn)] , value, 3, ';', 999);
65         strcat(nnn, " ");
66         extract_token(&nnn[strlen(nnn)] , value, 1, ';', 999);
67         strcat(nnn, " ");
68         extract_token(&nnn[strlen(nnn)] , value, 2, ';', 999);
69         strcat(nnn, " ");
70         extract_token(&nnn[strlen(nnn)] , value, 0, ';', 999);
71         strcat(nnn, " ");
72         extract_token(&nnn[strlen(nnn)] , value, 4, ';', 999);
73         strcat(nnn, " ");
74         for (i=0; i<strlen(nnn); ++i) {
75                 if (!strncmp(&nnn[i], "  ", 2)) strcpy(&nnn[i], &nnn[i+1]);
76         }
77         striplt(nnn);
78         return(nnn);
79 }
80
81
82
83
84 /*
85  * Back end for cmd_auto()
86  */
87 void hunt_for_autocomplete(long msgnum, char *search_string) {
88         struct CtdlMessage *msg;
89         struct vCard *v;
90         char *value = NULL;
91         char *value2 = NULL;
92         int i = 0;
93         char *nnn = NULL;
94
95         msg = CtdlFetchMessage(msgnum, 1);
96         if (msg == NULL) return;
97
98         v = vcard_load(msg->cm_fields['M']);
99         CtdlFreeMessage(msg);
100
101         /*
102          * Try to match from a friendly name (the "fn" field).  If there is
103          * a match, return the entry in the form of:
104          *     Display Name <user@domain.org>
105          */
106         value = vcard_get_prop(v, "fn", 0, 0, 0);
107         if (value != NULL) if (bmstrcasestr(value, search_string)) {
108                 value2 = vcard_get_prop(v, "email", 1, 0, 0);
109                 if (value2 == NULL) value2 = "";
110                 cprintf("%s <%s>\n", value, value2);
111                 vcard_free(v);
112                 return;
113         }
114
115         /*
116          * Try to match from a structured name (the "n" field).  If there is
117          * a match, return the entry in the form of:
118          *     Display Name <user@domain.org>
119          */
120         value = vcard_get_prop(v, "n", 0, 0, 0);
121         if (value != NULL) if (bmstrcasestr(value, search_string)) {
122
123                 value2 = vcard_get_prop(v, "email", 1, 0, 0);
124                 if (value2 == NULL) value2 = "";
125                 nnn = n_to_fn(value);
126                 cprintf("%s <%s>\n", nnn, value2);
127                 free(nnn);
128                 vcard_free(v);
129                 return;
130         }
131
132         /*
133          * Try a partial match on all listed email addresses.
134          */
135         i = 0;
136         while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) {
137                 if (bmstrcasestr(value, search_string)) {
138                         if (vcard_get_prop(v, "fn", 0, 0, 0)) {
139                                 cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value);
140                         }
141                         else if (vcard_get_prop(v, "n", 0, 0, 0)) {
142                                 nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0));
143                                 cprintf("%s <%s>\n", nnn, value);
144                                 free(nnn);
145                         
146                         }
147                         else {
148                                 cprintf("%s\n", value);
149                         }
150                         vcard_free(v);
151                         return;
152                 }
153         }
154
155         vcard_free(v);
156 }
157
158
159
160 /*
161  * Attempt to autocomplete an address based on a partial...
162  */
163 void cmd_auto(char *argbuf) {
164         char hold_rm[ROOMNAMELEN];
165         char search_string[256];
166         long *msglist = NULL;
167         int num_msgs = 0;
168         long *fts_msgs = NULL;
169         int fts_num_msgs = 0;
170         struct cdbdata *cdbfr;
171         int r = 0;
172         int i = 0;
173         int j = 0;
174         int search_match = 0;
175         char *rooms_to_try[] = { USERCONTACTSROOM, ADDRESS_BOOK_ROOM };
176                 
177         if (CtdlAccessCheck(ac_logged_in)) return;
178         extract_token(search_string, argbuf, 0, '|', sizeof search_string);
179         if (IsEmptyStr(search_string)) {
180                 cprintf("%d You supplied an empty partial.\n",
181                         ERROR + ILLEGAL_VALUE);
182                 return;
183         }
184
185         strcpy(hold_rm, CC->room.QRname);       /* save current room */
186         cprintf("%d try these:\n", LISTING_FOLLOWS);
187
188         /*
189          * Gather up message pointers in rooms containing vCards
190          */
191         for (r=0; r < (sizeof(rooms_to_try) / sizeof(char *)); ++r) {
192                 if (getroom(&CC->room, rooms_to_try[r]) == 0) {
193                         cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
194                         if (cdbfr != NULL) {
195                                 msglist = realloc(msglist, (num_msgs * sizeof(long)) + cdbfr->len + 1);
196                                 memcpy(&msglist[num_msgs], cdbfr->ptr, cdbfr->len);
197                                 num_msgs += (cdbfr->len / sizeof(long));
198                                 cdb_free(cdbfr);
199                         }
200                 }
201         }
202
203         /*
204          * Search-reduce the results if we have the full text index available
205          */
206         if (config.c_enable_fulltext) {
207                 CtdlModuleDoSearch(&fts_num_msgs, &fts_msgs, search_string, "fulltext");
208                 if (fts_msgs) {
209                         for (i=0; i<num_msgs; ++i) {
210                                 search_match = 0;
211                                 for (j=0; j<fts_num_msgs; ++j) {
212                                         if (msglist[i] == fts_msgs[j]) {
213                                                 search_match = 1;
214                                                 j = fts_num_msgs + 1;   /* end the search */
215                                         }
216                                 }
217                                 if (!search_match) {
218                                         msglist[i] = 0;         /* invalidate this result */
219                                 }
220                         }
221                         free(fts_msgs);
222                 }
223                 else {
224                         /* If no results, invalidate the whole list */
225                         free(msglist);
226                         msglist = NULL;
227                         num_msgs = 0;
228                 }
229         }
230
231         /*
232          * Now output the ones that look interesting
233          */
234         if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
235                 if (msglist[i] != 0) {
236                         hunt_for_autocomplete(msglist[i], search_string);
237                 }
238         }
239         
240         cprintf("000\n");
241         if (strcmp(CC->room.QRname, hold_rm)) {
242                 getroom(&CC->room, hold_rm);    /* return to saved room */
243         }
244
245         if (msglist) {
246                 free(msglist);
247         }
248         
249 }
250
251
252 CTDL_MODULE_INIT(autocompletion) {
253         CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion");
254
255         /* return our Subversion id for the Log */
256         return "$Id$";
257 }