4 * Autocompletion of email recipients, etc.
16 #include <sys/types.h>
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
23 # include <sys/time.h>
34 #include "serv_extensions.h"
35 #include "sysdep_decls.h"
36 #include "citserver.h"
45 #include "serv_fulltext.h"
46 #include "serv_autocompletion.h"
56 * Convert a structured name into a friendly name. Caller must free the
59 char *n_to_fn(char *value) {
63 nnn = malloc(strlen(value) + 10);
65 extract_token(&nnn[strlen(nnn)] , value, 3, ';', 999);
67 extract_token(&nnn[strlen(nnn)] , value, 1, ';', 999);
69 extract_token(&nnn[strlen(nnn)] , value, 2, ';', 999);
71 extract_token(&nnn[strlen(nnn)] , value, 0, ';', 999);
73 extract_token(&nnn[strlen(nnn)] , value, 4, ';', 999);
75 for (i=0; i<strlen(nnn); ++i) {
76 if (!strncmp(&nnn[i], " ", 2)) strcpy(&nnn[i], &nnn[i+1]);
86 * Back end for cmd_auto()
88 void hunt_for_autocomplete(long msgnum, char *search_string) {
89 struct CtdlMessage *msg;
96 msg = CtdlFetchMessage(msgnum, 1);
97 if (msg == NULL) return;
99 v = vcard_load(msg->cm_fields['M']);
100 CtdlFreeMessage(msg);
103 * Try to match from a friendly name (the "fn" field). If there is
104 * a match, return the entry in the form of:
105 * Display Name <user@domain.org>
107 value = vcard_get_prop(v, "fn", 0, 0, 0);
108 if (value != NULL) if (bmstrcasestr(value, search_string)) {
109 value2 = vcard_get_prop(v, "email", 1, 0, 0);
110 if (value2 == NULL) value2 = "";
111 cprintf("%s <%s>\n", value, value2);
117 * Try to match from a structured name (the "n" field). If there is
118 * a match, return the entry in the form of:
119 * Display Name <user@domain.org>
121 value = vcard_get_prop(v, "n", 0, 0, 0);
122 if (value != NULL) if (bmstrcasestr(value, search_string)) {
124 value2 = vcard_get_prop(v, "email", 1, 0, 0);
125 if (value2 == NULL) value2 = "";
126 nnn = n_to_fn(value);
127 cprintf("%s <%s>\n", nnn, value2);
134 * Try a partial match on all listed email addresses.
137 while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) {
138 if (bmstrcasestr(value, search_string)) {
139 if (vcard_get_prop(v, "fn", 0, 0, 0)) {
140 cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value);
142 else if (vcard_get_prop(v, "n", 0, 0, 0)) {
143 nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0));
144 cprintf("%s <%s>\n", nnn, value);
149 cprintf("%s\n", value);
162 * Attempt to autocomplete an address based on a partial...
164 void cmd_auto(char *argbuf) {
165 char hold_rm[ROOMNAMELEN];
166 char search_string[256];
167 long *msglist = NULL;
169 long *fts_msgs = NULL;
170 int fts_num_msgs = 0;
171 struct cdbdata *cdbfr;
175 int search_match = 0;
176 char *rooms_to_try[] = { USERCONTACTSROOM, ADDRESS_BOOK_ROOM };
178 if (CtdlAccessCheck(ac_logged_in)) return;
179 extract_token(search_string, argbuf, 0, '|', sizeof search_string);
180 if (strlen(search_string) == 0) {
181 cprintf("%d You supplied an empty partial.\n",
182 ERROR + ILLEGAL_VALUE);
186 strcpy(hold_rm, CC->room.QRname); /* save current room */
187 cprintf("%d try these:\n", LISTING_FOLLOWS);
190 * Gather up message pointers in rooms containing vCards
192 for (r=0; r < (sizeof(rooms_to_try) / sizeof(char *)); ++r) {
193 if (getroom(&CC->room, rooms_to_try[r]) == 0) {
194 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
196 msglist = realloc(msglist, (num_msgs * sizeof(long)) + cdbfr->len + 1);
197 memcpy(&msglist[num_msgs], cdbfr->ptr, cdbfr->len);
198 num_msgs += (cdbfr->len / sizeof(long));
205 * Search-reduce the results if we have the full text index available
207 if (config.c_enable_fulltext) {
208 ft_search(&fts_num_msgs, &fts_msgs, search_string);
210 for (i=0; i<num_msgs; ++i) {
212 for (j=0; j<fts_num_msgs; ++j) {
213 if (msglist[i] == fts_msgs[j]) {
215 j = fts_num_msgs + 1; /* end the search */
219 msglist[i] = 0; /* invalidate this result */
225 /* If no results, invalidate the whole list */
233 * Now output the ones that look interesting
235 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
236 if (msglist[i] != 0) {
237 hunt_for_autocomplete(msglist[i], search_string);
242 if (strcmp(CC->room.QRname, hold_rm)) {
243 getroom(&CC->room, hold_rm); /* return to saved room */
253 char *serv_autocompletion_init(void) {
254 CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion");