* MSG4 (and CtdlOutputMsg() as well) now accepts an optional MIME part
[citadel.git] / citadel / serv_autocompletion.c
1 /*
2  * $Id$
3  *
4  * Autocompletion of email recipients, etc.
5  */
6
7 #ifdef DLL_EXPORT
8 #define IN_LIBCIT
9 #endif
10
11 #include "sysdep.h"
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <fcntl.h>
16 #include <ctype.h>
17 #include <signal.h>
18 #include <pwd.h>
19 #include <errno.h>
20 #include <sys/types.h>
21
22 #if TIME_WITH_SYS_TIME
23 # include <sys/time.h>
24 # include <time.h>
25 #else
26 # if HAVE_SYS_TIME_H
27 #  include <sys/time.h>
28 # else
29 #  include <time.h>
30 # endif
31 #endif
32
33 #include <sys/wait.h>
34 #include <string.h>
35 #include <limits.h>
36 #include "citadel.h"
37 #include "server.h"
38 #include "serv_extensions.h"
39 #include "sysdep_decls.h"
40 #include "citserver.h"
41 #include "support.h"
42 #include "config.h"
43 #include "tools.h"
44 #include "msgbase.h"
45 #include "user_ops.h"
46 #include "room_ops.h"
47 #include "database.h"
48 #include "vcard.h"
49 #include "serv_autocompletion.h"
50
51
52 #ifndef HAVE_SNPRINTF
53 #include "snprintf.h"
54 #endif
55
56
57
58 /*
59  * Convert a structured name into a friendly name.  Caller must free the
60  * returned pointer.
61  */
62 char *n_to_fn(char *value) {
63         char *nnn = NULL;
64         int i;
65
66         nnn = malloc(strlen(value) + 10);
67         strcpy(nnn, "");
68         extract_token(&nnn[strlen(nnn)] , value, 3, ';', 999);
69         strcat(nnn, " ");
70         extract_token(&nnn[strlen(nnn)] , value, 1, ';', 999);
71         strcat(nnn, " ");
72         extract_token(&nnn[strlen(nnn)] , value, 2, ';', 999);
73         strcat(nnn, " ");
74         extract_token(&nnn[strlen(nnn)] , value, 0, ';', 999);
75         strcat(nnn, " ");
76         extract_token(&nnn[strlen(nnn)] , value, 4, ';', 999);
77         strcat(nnn, " ");
78         for (i=0; i<strlen(nnn); ++i) {
79                 if (!strncmp(&nnn[i], "  ", 2)) strcpy(&nnn[i], &nnn[i+1]);
80         }
81         striplt(nnn);
82         return(nnn);
83 }
84
85
86
87
88 /*
89  * Back end for cmd_auto()
90  */
91 void hunt_for_autocomplete(long msgnum, void *data) {
92         char *search_string;
93         struct CtdlMessage *msg;
94         struct vCard *v;
95         char *value = NULL;
96         char *value2 = NULL;
97         int i = 0;
98         char *nnn = NULL;
99
100         search_string = (char *) data;
101
102         msg = CtdlFetchMessage(msgnum, 1);
103         if (msg == NULL) return;
104
105         v = vcard_load(msg->cm_fields['M']);
106         CtdlFreeMessage(msg);
107
108         /*
109          * Try to match from a friendly name (the "fn" field).  If there is
110          * a match, return the entry in the form of:
111          *     Display Name <user@domain.org>
112          */
113         value = vcard_get_prop(v, "fn", 0, 0, 0);
114         if (value != NULL) if (bmstrcasestr(value, search_string)) {
115                 value2 = vcard_get_prop(v, "email", 1, 0, 0);
116                 if (value2 == NULL) value2 = "";
117                 cprintf("%s <%s>\n", value, value2);
118                 vcard_free(v);
119                 return;
120         }
121
122         /*
123          * Try to match from a structured name (the "n" field).  If there is
124          * a match, return the entry in the form of:
125          *     Display Name <user@domain.org>
126          */
127         value = vcard_get_prop(v, "n", 0, 0, 0);
128         if (value != NULL) if (bmstrcasestr(value, search_string)) {
129
130                 value2 = vcard_get_prop(v, "email", 1, 0, 0);
131                 if (value2 == NULL) value2 = "";
132                 nnn = n_to_fn(value);
133                 cprintf("%s <%s>\n", nnn, value2);
134                 free(nnn);
135                 vcard_free(v);
136                 return;
137         }
138
139         /*
140          * Try a partial match on all listed email addresses.
141          */
142         i = 0;
143         while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) {
144                 if (bmstrcasestr(value, search_string)) {
145                         if (vcard_get_prop(v, "fn", 0, 0, 0)) {
146                                 cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value);
147                         }
148                         else if (vcard_get_prop(v, "n", 0, 0, 0)) {
149                                 nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0));
150                                 cprintf("%s <%s>\n", nnn, value);
151                                 free(nnn);
152                         
153                         }
154                         else {
155                                 cprintf("%s\n", value);
156                         }
157                         vcard_free(v);
158                         return;
159                 }
160         }
161
162         vcard_free(v);
163 }
164
165
166
167 /*
168  * Attempt to autocomplete an address based on a partial...
169  */
170 void cmd_auto(char *argbuf) {
171         char hold_rm[ROOMNAMELEN];
172         char search_string[256];
173
174         if (CtdlAccessCheck(ac_logged_in)) return;
175         extract_token(search_string, argbuf, 0, '|', sizeof search_string);
176         if (strlen(search_string) == 0) {
177                 cprintf("%d You supplied an empty partial.\n",
178                         ERROR + ILLEGAL_VALUE);
179                 return;
180         }
181
182         strcpy(hold_rm, CC->room.QRname);       /* save current room */
183         cprintf("%d try these:\n", LISTING_FOLLOWS);
184
185         /* Take a spin through the user's personal address book */
186         if (getroom(&CC->room, USERCONTACTSROOM) == 0) {
187                 CtdlForEachMessage(MSGS_ALL, 0, "text/x-vcard", NULL,
188                                         hunt_for_autocomplete, search_string);
189         }
190         
191         /* FIXME try the global address book */
192         if (getroom(&CC->room, ADDRESS_BOOK_ROOM) == 0) {
193                 CtdlForEachMessage(MSGS_ALL, 0, "text/x-vcard", NULL,
194                                         hunt_for_autocomplete, search_string);
195         }
196         
197         cprintf("000\n");
198         if (strcmp(CC->room.QRname, hold_rm)) {
199                 getroom(&CC->room, hold_rm);    /* return to saved room */
200         }
201 }
202
203
204 char *serv_autocompletion_init(void)
205 {
206         CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion");
207         return "$Id$";
208 }