2 * Autocompletion of email recipients, etc.
4 * Copyright (c) 1987-2012 by the citadel.org team
6 * This program is open source software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 #include "ctdl_module.h"
17 #include "serv_autocompletion.h"
22 * Convert a structured name into a friendly name. Caller must free the
25 char *n_to_fn(char *value) {
29 nnn = malloc(strlen(value) + 10);
31 extract_token(&nnn[strlen(nnn)] , value, 3, ';', 999);
33 extract_token(&nnn[strlen(nnn)] , value, 1, ';', 999);
35 extract_token(&nnn[strlen(nnn)] , value, 2, ';', 999);
37 extract_token(&nnn[strlen(nnn)] , value, 0, ';', 999);
39 extract_token(&nnn[strlen(nnn)] , value, 4, ';', 999);
41 for (i=0; i<strlen(nnn); ++i) {
42 if (!strncmp(&nnn[i], " ", 2)) strcpy(&nnn[i], &nnn[i+1]);
52 * Back end for cmd_auto()
54 void hunt_for_autocomplete(long msgnum, char *search_string) {
55 struct CtdlMessage *msg;
62 msg = CtdlFetchMessage(msgnum, 1);
63 if (msg == NULL) return;
65 v = vcard_load(msg->cm_fields['M']);
69 * Try to match from a friendly name (the "fn" field). If there is
70 * a match, return the entry in the form of:
71 * Display Name <user@domain.org>
73 value = vcard_get_prop(v, "fn", 0, 0, 0);
74 if (value != NULL) if (bmstrcasestr(value, search_string)) {
75 value2 = vcard_get_prop(v, "email", 1, 0, 0);
76 if (value2 == NULL) value2 = "";
77 cprintf("%s <%s>\n", value, value2);
83 * Try to match from a structured name (the "n" field). If there is
84 * a match, return the entry in the form of:
85 * Display Name <user@domain.org>
87 value = vcard_get_prop(v, "n", 0, 0, 0);
88 if (value != NULL) if (bmstrcasestr(value, search_string)) {
90 value2 = vcard_get_prop(v, "email", 1, 0, 0);
91 if (value2 == NULL) value2 = "";
93 cprintf("%s <%s>\n", nnn, value2);
100 * Try a partial match on all listed email addresses.
103 while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) {
104 if (bmstrcasestr(value, search_string)) {
105 if (vcard_get_prop(v, "fn", 0, 0, 0)) {
106 cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value);
108 else if (vcard_get_prop(v, "n", 0, 0, 0)) {
109 nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0));
110 cprintf("%s <%s>\n", nnn, value);
115 cprintf("%s\n", value);
128 * Attempt to autocomplete an address based on a partial...
130 void cmd_auto(char *argbuf) {
131 char hold_rm[ROOMNAMELEN];
132 char search_string[256];
133 long *msglist = NULL;
135 long *fts_msgs = NULL;
136 int fts_num_msgs = 0;
137 struct cdbdata *cdbfr;
141 int search_match = 0;
142 char *rooms_to_try[] = { USERCONTACTSROOM, ADDRESS_BOOK_ROOM };
144 if (CtdlAccessCheck(ac_logged_in)) return;
145 extract_token(search_string, argbuf, 0, '|', sizeof search_string);
146 if (IsEmptyStr(search_string)) {
147 cprintf("%d You supplied an empty partial.\n",
148 ERROR + ILLEGAL_VALUE);
152 strcpy(hold_rm, CC->room.QRname); /* save current room */
153 cprintf("%d try these:\n", LISTING_FOLLOWS);
156 * Gather up message pointers in rooms containing vCards
158 for (r=0; r < (sizeof(rooms_to_try) / sizeof(char *)); ++r) {
159 if (CtdlGetRoom(&CC->room, rooms_to_try[r]) == 0) {
160 cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
162 msglist = realloc(msglist, (num_msgs * sizeof(long)) + cdbfr->len + 1);
163 memcpy(&msglist[num_msgs], cdbfr->ptr, cdbfr->len);
164 num_msgs += (cdbfr->len / sizeof(long));
171 * Search-reduce the results if we have the full text index available
173 if (config.c_enable_fulltext) {
174 CtdlModuleDoSearch(&fts_num_msgs, &fts_msgs, search_string, "fulltext");
176 for (i=0; i<num_msgs; ++i) {
178 for (j=0; j<fts_num_msgs; ++j) {
179 if (msglist[i] == fts_msgs[j]) {
181 j = fts_num_msgs + 1; /* end the search */
185 msglist[i] = 0; /* invalidate this result */
191 /* If no results, invalidate the whole list */
199 * Now output the ones that look interesting
201 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
202 if (msglist[i] != 0) {
203 hunt_for_autocomplete(msglist[i], search_string);
208 if (strcmp(CC->room.QRname, hold_rm)) {
209 CtdlGetRoom(&CC->room, hold_rm); /* return to saved room */
219 CTDL_MODULE_INIT(autocompletion) {
222 CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion");
224 /* return our module name for the log */
225 return "autocompletion";