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