1 // Autocompletion of email recipients, etc.
3 // Copyright (c) 1987-2023 by the citadel.org team
5 // This program is open source software. Use, duplication, or disclosure
6 // are subject to the terms of the GNU General Public License version 3.
8 #include "../../ctdl_module.h"
9 #include "serv_autocompletion.h"
10 #include "../../config.h"
11 #include "../../room_ops.h"
12 #include "../fulltext/serv_fulltext.h"
15 // Convert a structured name into a friendly name. Caller must free the
17 char *n_to_fn(char *value) {
21 nnn = malloc(strlen(value) + 10);
23 extract_token(&nnn[strlen(nnn)] , value, 3, ';', 999);
25 extract_token(&nnn[strlen(nnn)] , value, 1, ';', 999);
27 extract_token(&nnn[strlen(nnn)] , value, 2, ';', 999);
29 extract_token(&nnn[strlen(nnn)] , value, 0, ';', 999);
31 extract_token(&nnn[strlen(nnn)] , value, 4, ';', 999);
33 for (i=0; i<strlen(nnn); ++i) {
34 if (!strncmp(&nnn[i], " ", 2)) strcpy(&nnn[i], &nnn[i+1]);
41 // Back end for cmd_auto()
42 void hunt_for_autocomplete(long msgnum, char *search_string) {
43 struct CtdlMessage *msg;
50 msg = CtdlFetchMessage(msgnum, 1);
51 if (msg == NULL) return;
53 v = vcard_load(msg->cm_fields[eMesageText]);
56 // Try to match from a friendly name (the "fn" field). If there is
57 // a match, return the entry in the form of:
58 // Display Name <user@domain.org>
59 value = vcard_get_prop(v, "fn", 0, 0, 0);
60 if (value != NULL) if (bmstrcasestr(value, search_string)) {
61 value2 = vcard_get_prop(v, "email", 1, 0, 0);
62 if (value2 == NULL) value2 = "";
63 cprintf("%s <%s>\n", value, value2);
68 // Try to match from a structured name (the "n" field). If there is
69 // a match, return the entry in the form of:
70 // Display Name <user@domain.org>
71 value = vcard_get_prop(v, "n", 0, 0, 0);
72 if (value != NULL) if (bmstrcasestr(value, search_string)) {
74 value2 = vcard_get_prop(v, "email", 1, 0, 0);
75 if (value2 == NULL) value2 = "";
77 cprintf("%s <%s>\n", nnn, value2);
83 // Try a partial match on all listed email addresses.
85 while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) {
86 if (bmstrcasestr(value, search_string)) {
87 if (vcard_get_prop(v, "fn", 0, 0, 0)) {
88 cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value);
90 else if (vcard_get_prop(v, "n", 0, 0, 0)) {
91 nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0));
92 cprintf("%s <%s>\n", nnn, value);
96 cprintf("%s\n", value);
107 // Attempt to autocomplete an address based on a partial...
108 void cmd_auto(char *argbuf) {
109 char hold_rm[ROOMNAMELEN];
110 char search_string[256];
111 long *msglist = NULL;
116 int search_match = 0;
117 char *rooms_to_try[] = { USERCONTACTSROOM, ADDRESS_BOOK_ROOM };
120 if (CtdlAccessCheck(ac_logged_in)) return;
121 extract_token(search_string, argbuf, 0, '|', sizeof search_string);
122 if (IsEmptyStr(search_string)) {
123 cprintf("%d You supplied an empty partial.\n", ERROR + ILLEGAL_VALUE);
127 strcpy(hold_rm, CC->room.QRname); // save where we were so we can go back
128 cprintf("%d try these:\n", LISTING_FOLLOWS);
130 // Gather up message pointers in rooms containing vCards
131 for (r=0; r < (sizeof(rooms_to_try) / sizeof(char *)); ++r) {
132 if (CtdlGetRoom(&CC->room, rooms_to_try[r]) == 0) {
133 num_msgs = CtdlFetchMsgList(CC->room.QRnumber, &msglist);
135 // Search-reduce the results if we have the full text index available
136 if (CtdlGetConfigInt("c_enable_fulltext")) {
137 fts = CtdlFullTextSearch(search_string);
139 for (i=0; i<num_msgs; ++i) {
141 for (j=0; j<array_len(fts); ++j) {
143 memcpy(&smsgnum, array_get_element_at(fts, j), sizeof(long));
144 if (msglist[i] == smsgnum) {
146 j = array_len(fts) + 1; // end the search
150 msglist[i] = 0; // invalidate this result
156 // If no results, invalidate the whole list
163 // Now output the ones that look interesting
164 if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
165 if (msglist[i] != 0) {
166 hunt_for_autocomplete(msglist[i], search_string);
173 if (strcmp(CC->room.QRname, hold_rm)) {
174 CtdlGetRoom(&CC->room, hold_rm); // return to saved room
184 // Initialization function, called from modules_init.c
185 char *ctdl_module_init_autocompletion(void) {
187 CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion");
189 // return our module name for the log
190 return "autocompletion";