]> code.citadel.org Git - citadel.git/blob - citadel/server/modules/autocompletion/serv_autocompletion.c
Remove preprocessor tests for OpenSSL. It's a requirement.
[citadel.git] / citadel / server / modules / autocompletion / serv_autocompletion.c
1 // Autocompletion of email recipients, etc.
2 // Copyright (c) 1987-2023 by the citadel.org team
3 // This program is open source software.  Use, duplication, or disclosure is subject to the GNU General Public License version 3.
4
5 #include "../../ctdl_module.h"
6 #include "serv_autocompletion.h"
7 #include "../../config.h"
8 #include "../../room_ops.h"
9 #include "../fulltext/serv_fulltext.h"
10
11
12 // Convert a structured name into a friendly name.  Caller must free the
13 // returned pointer.
14 char *n_to_fn(char *value) {
15         char *nnn = NULL;
16         int i;
17
18         nnn = malloc(strlen(value) + 10);
19         strcpy(nnn, "");
20         extract_token(&nnn[strlen(nnn)] , value, 3, ';', 999);
21         strcat(nnn, " ");
22         extract_token(&nnn[strlen(nnn)] , value, 1, ';', 999);
23         strcat(nnn, " ");
24         extract_token(&nnn[strlen(nnn)] , value, 2, ';', 999);
25         strcat(nnn, " ");
26         extract_token(&nnn[strlen(nnn)] , value, 0, ';', 999);
27         strcat(nnn, " ");
28         extract_token(&nnn[strlen(nnn)] , value, 4, ';', 999);
29         strcat(nnn, " ");
30         for (i=0; i<strlen(nnn); ++i) {
31                 if (!strncmp(&nnn[i], "  ", 2)) strcpy(&nnn[i], &nnn[i+1]);
32         }
33         string_trim(nnn);
34         return(nnn);
35 }
36
37
38 // Back end for cmd_auto()
39 void hunt_for_autocomplete(long msgnum, char *search_string) {
40         struct CtdlMessage *msg;
41         struct vCard *v;
42         char *value = NULL;
43         char *value2 = NULL;
44         int i = 0;
45         char *nnn = NULL;
46
47         msg = CtdlFetchMessage(msgnum, 1);
48         if (msg == NULL) return;
49
50         v = vcard_load(msg->cm_fields[eMessageText]);
51         CM_Free(msg);
52
53         // Try to match from a friendly name (the "fn" field).  If there is
54         // a match, return the entry in the form of:
55         //     Display Name <user@domain.org>
56         value = vcard_get_prop(v, "fn", 0, 0, 0);
57         if (value != NULL) if (bmstrcasestr(value, search_string)) {
58                 value2 = vcard_get_prop(v, "email", 1, 0, 0);
59                 if (value2 == NULL) value2 = "";
60                 cprintf("%s <%s>\n", value, value2);
61                 vcard_free(v);
62                 return;
63         }
64
65         // Try to match from a structured name (the "n" field).  If there is
66         // a match, return the entry in the form of:
67         //     Display Name <user@domain.org>
68         value = vcard_get_prop(v, "n", 0, 0, 0);
69         if (value != NULL) if (bmstrcasestr(value, search_string)) {
70
71                 value2 = vcard_get_prop(v, "email", 1, 0, 0);
72                 if (value2 == NULL) value2 = "";
73                 nnn = n_to_fn(value);
74                 cprintf("%s <%s>\n", nnn, value2);
75                 free(nnn);
76                 vcard_free(v);
77                 return;
78         }
79
80         // Try a partial match on all listed email addresses.
81         i = 0;
82         while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) {
83                 if (bmstrcasestr(value, search_string)) {
84                         if (vcard_get_prop(v, "fn", 0, 0, 0)) {
85                                 cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value);
86                         }
87                         else if (vcard_get_prop(v, "n", 0, 0, 0)) {
88                                 nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0));
89                                 cprintf("%s <%s>\n", nnn, value);
90                                 free(nnn);
91                         }
92                         else {
93                                 cprintf("%s\n", value);
94                         }
95                         vcard_free(v);
96                         return;
97                 }
98         }
99
100         vcard_free(v);
101 }
102
103
104 // Attempt to autocomplete an address based on a partial...
105 void cmd_auto(char *argbuf) {
106         char hold_rm[ROOMNAMELEN];
107         char search_string[256];
108         long *msglist = NULL;
109         int num_msgs = 0;
110         int r = 0;
111         int i = 0;
112         int j = 0;
113         int search_match = 0;
114         char *rooms_to_try[] = { USERCONTACTSROOM, ADDRESS_BOOK_ROOM };
115         Array *fts = NULL;
116                 
117         if (CtdlAccessCheck(ac_logged_in)) return;
118         extract_token(search_string, argbuf, 0, '|', sizeof search_string);
119         if (IsEmptyStr(search_string)) {
120                 cprintf("%d You supplied an empty partial.\n", ERROR + ILLEGAL_VALUE);
121                 return;
122         }
123
124         strcpy(hold_rm, CC->room.QRname);                        // save where we were so we can go back
125         cprintf("%d try these:\n", LISTING_FOLLOWS);
126
127         // Gather up message pointers in rooms containing vCards
128         for (r=0; r < (sizeof(rooms_to_try) / sizeof(char *)); ++r) {
129                 if (CtdlGetRoom(&CC->room, rooms_to_try[r]) == 0) {
130                         num_msgs = CtdlFetchMsgList(CC->room.QRnumber, &msglist);
131
132                         // Search-reduce the results if we have the full text index available
133                         if (CtdlGetConfigInt("c_enable_fulltext")) {
134                                 fts = CtdlFullTextSearch(search_string);
135                                 if (fts) {
136                                         for (i=0; i<num_msgs; ++i) {
137                                                 search_match = 0;
138                                                 for (j=0; j<array_len(fts); ++j) {
139                                                         long smsgnum;
140                                                         memcpy(&smsgnum, array_get_element_at(fts, j), sizeof(long));
141                                                         if (msglist[i] == smsgnum) {
142                                                                 search_match = 1;
143                                                                 j = array_len(fts) + 1; // end the search 
144                                                         }
145                                                 }
146                                                 if (!search_match) {
147                                                         msglist[i] = 0;                 // invalidate this result
148                                                 }
149                                         }
150                                         array_free(fts);
151                                 }
152                                 else {
153                                         // If no results, invalidate the whole list
154                                         free(msglist);
155                                         msglist = NULL;
156                                         num_msgs = 0;
157                                 }
158                         }
159                 
160                         // Now output the ones that look interesting
161                         if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
162                                 if (msglist[i] != 0) {
163                                         hunt_for_autocomplete(msglist[i], search_string);
164                                 }
165                         }
166                 }
167         }
168         
169         cprintf("000\n");
170         if (strcmp(CC->room.QRname, hold_rm)) {
171                 CtdlGetRoom(&CC->room, hold_rm);                                        // return to saved room
172         }
173
174         if (msglist) {
175                 free(msglist);
176         }
177         
178 }
179
180
181 // Initialization function, called from modules_init.c
182 char *ctdl_module_init_autocompletion(void) {
183         if (!threading) {
184                 CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion");
185         }
186         // return our module name for the log
187         return "autocompletion";
188 }