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