Back to index

citadel  8.12
serv_autocompletion.c
Go to the documentation of this file.
00001 /*
00002  * Autocompletion of email recipients, etc.
00003  *
00004  * Copyright (c) 1987-2012 by the citadel.org team
00005  *
00006  *  This program is open source software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License version 3.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  */
00014 #include "ctdl_module.h"
00015 
00016 
00017 #include "serv_autocompletion.h"
00018 
00019 
00020 
00021 /*
00022  * Convert a structured name into a friendly name.  Caller must free the
00023  * returned pointer.
00024  */
00025 char *n_to_fn(char *value) {
00026        char *nnn = NULL;
00027        int i;
00028 
00029        nnn = malloc(strlen(value) + 10);
00030        strcpy(nnn, "");
00031        extract_token(&nnn[strlen(nnn)] , value, 3, ';', 999);
00032        strcat(nnn, " ");
00033        extract_token(&nnn[strlen(nnn)] , value, 1, ';', 999);
00034        strcat(nnn, " ");
00035        extract_token(&nnn[strlen(nnn)] , value, 2, ';', 999);
00036        strcat(nnn, " ");
00037        extract_token(&nnn[strlen(nnn)] , value, 0, ';', 999);
00038        strcat(nnn, " ");
00039        extract_token(&nnn[strlen(nnn)] , value, 4, ';', 999);
00040        strcat(nnn, " ");
00041        for (i=0; i<strlen(nnn); ++i) {
00042               if (!strncmp(&nnn[i], "  ", 2)) strcpy(&nnn[i], &nnn[i+1]);
00043        }
00044        striplt(nnn);
00045        return(nnn);
00046 }
00047 
00048 
00049 
00050 
00051 /*
00052  * Back end for cmd_auto()
00053  */
00054 void hunt_for_autocomplete(long msgnum, char *search_string) {
00055        struct CtdlMessage *msg;
00056        struct vCard *v;
00057        char *value = NULL;
00058        char *value2 = NULL;
00059        int i = 0;
00060        char *nnn = NULL;
00061 
00062        msg = CtdlFetchMessage(msgnum, 1);
00063        if (msg == NULL) return;
00064 
00065        v = vcard_load(msg->cm_fields['M']);
00066        CtdlFreeMessage(msg);
00067 
00068        /*
00069         * Try to match from a friendly name (the "fn" field).  If there is
00070         * a match, return the entry in the form of:
00071         *     Display Name <user@domain.org>
00072         */
00073        value = vcard_get_prop(v, "fn", 0, 0, 0);
00074        if (value != NULL) if (bmstrcasestr(value, search_string)) {
00075               value2 = vcard_get_prop(v, "email", 1, 0, 0);
00076               if (value2 == NULL) value2 = "";
00077               cprintf("%s <%s>\n", value, value2);
00078               vcard_free(v);
00079               return;
00080        }
00081 
00082        /*
00083         * Try to match from a structured name (the "n" field).  If there is
00084         * a match, return the entry in the form of:
00085         *     Display Name <user@domain.org>
00086         */
00087        value = vcard_get_prop(v, "n", 0, 0, 0);
00088        if (value != NULL) if (bmstrcasestr(value, search_string)) {
00089 
00090               value2 = vcard_get_prop(v, "email", 1, 0, 0);
00091               if (value2 == NULL) value2 = "";
00092               nnn = n_to_fn(value);
00093               cprintf("%s <%s>\n", nnn, value2);
00094               free(nnn);
00095               vcard_free(v);
00096               return;
00097        }
00098 
00099        /*
00100         * Try a partial match on all listed email addresses.
00101         */
00102        i = 0;
00103        while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) {
00104               if (bmstrcasestr(value, search_string)) {
00105                      if (vcard_get_prop(v, "fn", 0, 0, 0)) {
00106                             cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value);
00107                      }
00108                      else if (vcard_get_prop(v, "n", 0, 0, 0)) {
00109                             nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0));
00110                             cprintf("%s <%s>\n", nnn, value);
00111                             free(nnn);
00112                      
00113                      }
00114                      else {
00115                             cprintf("%s\n", value);
00116                      }
00117                      vcard_free(v);
00118                      return;
00119               }
00120        }
00121 
00122        vcard_free(v);
00123 }
00124 
00125 
00126 
00127 /*
00128  * Attempt to autocomplete an address based on a partial...
00129  */
00130 void cmd_auto(char *argbuf) {
00131        char hold_rm[ROOMNAMELEN];
00132        char search_string[256];
00133        long *msglist = NULL;
00134        int num_msgs = 0;
00135        long *fts_msgs = NULL;
00136        int fts_num_msgs = 0;
00137        struct cdbdata *cdbfr;
00138        int r = 0;
00139        int i = 0;
00140        int j = 0;
00141        int search_match = 0;
00142        char *rooms_to_try[] = { USERCONTACTSROOM, ADDRESS_BOOK_ROOM };
00143               
00144        if (CtdlAccessCheck(ac_logged_in)) return;
00145        extract_token(search_string, argbuf, 0, '|', sizeof search_string);
00146        if (IsEmptyStr(search_string)) {
00147               cprintf("%d You supplied an empty partial.\n",
00148                      ERROR + ILLEGAL_VALUE);
00149               return;
00150        }
00151 
00152        strcpy(hold_rm, CC->room.QRname);       /* save current room */
00153        cprintf("%d try these:\n", LISTING_FOLLOWS);
00154 
00155        /*
00156         * Gather up message pointers in rooms containing vCards
00157         */
00158        for (r=0; r < (sizeof(rooms_to_try) / sizeof(char *)); ++r) {
00159               if (CtdlGetRoom(&CC->room, rooms_to_try[r]) == 0) {
00160                      cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
00161                      if (cdbfr != NULL) {
00162                             msglist = realloc(msglist, (num_msgs * sizeof(long)) + cdbfr->len + 1);
00163                             memcpy(&msglist[num_msgs], cdbfr->ptr, cdbfr->len);
00164                             num_msgs += (cdbfr->len / sizeof(long));
00165                             cdb_free(cdbfr);
00166                      }
00167               }
00168        }
00169 
00170        /*
00171         * Search-reduce the results if we have the full text index available
00172         */
00173        if (config.c_enable_fulltext) {
00174               CtdlModuleDoSearch(&fts_num_msgs, &fts_msgs, search_string, "fulltext");
00175               if (fts_msgs) {
00176                      for (i=0; i<num_msgs; ++i) {
00177                             search_match = 0;
00178                             for (j=0; j<fts_num_msgs; ++j) {
00179                                    if (msglist[i] == fts_msgs[j]) {
00180                                           search_match = 1;
00181                                           j = fts_num_msgs + 1;       /* end the search */
00182                                    }
00183                             }
00184                             if (!search_match) {
00185                                    msglist[i] = 0;             /* invalidate this result */
00186                             }
00187                      }
00188                      free(fts_msgs);
00189               }
00190               else {
00191                      /* If no results, invalidate the whole list */
00192                      free(msglist);
00193                      msglist = NULL;
00194                      num_msgs = 0;
00195               }
00196        }
00197 
00198        /*
00199         * Now output the ones that look interesting
00200         */
00201        if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
00202               if (msglist[i] != 0) {
00203                      hunt_for_autocomplete(msglist[i], search_string);
00204               }
00205        }
00206        
00207        cprintf("000\n");
00208        if (strcmp(CC->room.QRname, hold_rm)) {
00209               CtdlGetRoom(&CC->room, hold_rm);    /* return to saved room */
00210        }
00211 
00212        if (msglist) {
00213               free(msglist);
00214        }
00215        
00216 }
00217 
00218 
00219 CTDL_MODULE_INIT(autocompletion) {
00220        if (!threading)
00221        {
00222               CtdlRegisterProtoHook(cmd_auto, "AUTO", "Do recipient autocompletion");
00223        }
00224        /* return our module name for the log */
00225        return "autocompletion";
00226 }