MSGS command can now do full text search on the room
[citadel.git] / citadel / 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 "serv_extensions.h"
35 #include "sysdep_decls.h"
36 #include "citserver.h"
37 #include "support.h"
38 #include "config.h"
39 #include "tools.h"
40 #include "msgbase.h"
41 #include "user_ops.h"
42 #include "room_ops.h"
43 #include "database.h"
44 #include "vcard.h"
45 #include "serv_autocompletion.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, void *data) {
88         char *search_string;
89         struct CtdlMessage *msg;
90         struct vCard *v;
91         char *value = NULL;
92         char *value2 = NULL;
93         int i = 0;
94         char *nnn = NULL;
95
96         search_string = (char *) data;
97
98         msg = CtdlFetchMessage(msgnum, 1);
99         if (msg == NULL) return;
100
101         v = vcard_load(msg->cm_fields['M']);
102         CtdlFreeMessage(msg);
103
104         /*
105          * Try to match from a friendly name (the "fn" field).  If there is
106          * a match, return the entry in the form of:
107          *     Display Name <user@domain.org>
108          */
109         value = vcard_get_prop(v, "fn", 0, 0, 0);
110         if (value != NULL) if (bmstrcasestr(value, search_string)) {
111                 value2 = vcard_get_prop(v, "email", 1, 0, 0);
112                 if (value2 == NULL) value2 = "";
113                 cprintf("%s <%s>\n", value, value2);
114                 vcard_free(v);
115                 return;
116         }
117
118         /*
119          * Try to match from a structured name (the "n" field).  If there is
120          * a match, return the entry in the form of:
121          *     Display Name <user@domain.org>
122          */
123         value = vcard_get_prop(v, "n", 0, 0, 0);
124         if (value != NULL) if (bmstrcasestr(value, search_string)) {
125
126                 value2 = vcard_get_prop(v, "email", 1, 0, 0);
127                 if (value2 == NULL) value2 = "";
128                 nnn = n_to_fn(value);
129                 cprintf("%s <%s>\n", nnn, value2);
130                 free(nnn);
131                 vcard_free(v);
132                 return;
133         }
134
135         /*
136          * Try a partial match on all listed email addresses.
137          */
138         i = 0;
139         while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) {
140                 if (bmstrcasestr(value, search_string)) {
141                         if (vcard_get_prop(v, "fn", 0, 0, 0)) {
142                                 cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value);
143                         }
144                         else if (vcard_get_prop(v, "n", 0, 0, 0)) {
145                                 nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0));
146                                 cprintf("%s <%s>\n", nnn, value);
147                                 free(nnn);
148                         
149                         }
150                         else {
151                                 cprintf("%s\n", value);
152                         }
153                         vcard_free(v);
154                         return;
155                 }
156         }
157
158         vcard_free(v);
159 }
160
161
162
163 /*
164  * Attempt to autocomplete an address based on a partial...
165  */
166 void cmd_auto(char *argbuf) {
167         char hold_rm[ROOMNAMELEN];
168         char search_string[256];
169
170         if (CtdlAccessCheck(ac_logged_in)) return;
171         extract_token(search_string, argbuf, 0, '|', sizeof search_string);
172         if (strlen(search_string) == 0) {
173                 cprintf("%d You supplied an empty partial.\n",
174                         ERROR + ILLEGAL_VALUE);
175                 return;
176         }
177
178         strcpy(hold_rm, CC->room.QRname);       /* save current room */
179         cprintf("%d try these:\n", LISTING_FOLLOWS);
180
181         /* Take a spin through the user's personal address book */
182         if (getroom(&CC->room, USERCONTACTSROOM) == 0) {
183                 CtdlForEachMessage(MSGS_ALL, 0, NULL, "text/x-vcard", NULL,
184                                         hunt_for_autocomplete, search_string);
185         }
186         
187         /* FIXME try the global address book */
188         if (getroom(&CC->room, ADDRESS_BOOK_ROOM) == 0) {
189                 CtdlForEachMessage(MSGS_ALL, 0, NULL, "text/x-vcard", NULL,
190                                         hunt_for_autocomplete, search_string);
191         }
192         
193         cprintf("000\n");
194         if (strcmp(CC->room.QRname, hold_rm)) {
195                 getroom(&CC->room, hold_rm);    /* return to saved room */
196         }
197 }
198
199
200 char *serv_autocompletion_init(void)
201 {
202         CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion");
203         return "$Id$";
204 }