mutt stable branch with some hacks
at master 542 lines 13 kB view raw
1/* 2 * Copyright (C) 1996-2000,2003,2013 Michael R. Elkins <me@mutt.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 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. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 */ 18 19#if HAVE_CONFIG_H 20# include "config.h" 21#endif 22 23#include "mutt.h" 24#include "mutt_menu.h" 25#include "mutt_idna.h" 26#include "mapping.h" 27 28#include <string.h> 29#include <stdlib.h> 30#include <ctype.h> 31 32typedef struct query 33{ 34 int num; 35 ADDRESS *addr; 36 char *name; 37 char *other; 38 struct query *next; 39} QUERY; 40 41typedef struct entry 42{ 43 int tagged; 44 QUERY *data; 45} ENTRY; 46 47static const struct mapping_t QueryHelp[] = { 48 { N_("Exit"), OP_EXIT }, 49 { N_("Mail"), OP_MAIL }, 50 { N_("New Query"), OP_QUERY }, 51 { N_("Make Alias"), OP_CREATE_ALIAS }, 52 { N_("Search"), OP_SEARCH }, 53 { N_("Help"), OP_HELP }, 54 { NULL, 0 } 55}; 56 57static void query_menu (char *buf, size_t buflen, QUERY *results, int retbuf); 58 59static ADDRESS *result_to_addr (QUERY *r) 60{ 61 static ADDRESS *tmp; 62 63 if (!(tmp = rfc822_cpy_adr (r->addr, 0))) 64 return NULL; 65 66 if(!tmp->next && !tmp->personal) 67 tmp->personal = safe_strdup (r->name); 68 69 mutt_addrlist_to_intl (tmp, NULL); 70 return tmp; 71} 72 73static void free_query (QUERY **query) 74{ 75 QUERY *p; 76 77 if (!query) 78 return; 79 80 while (*query) 81 { 82 p = *query; 83 *query = (*query)->next; 84 85 rfc822_free_address (&p->addr); 86 FREE (&p->name); 87 FREE (&p->other); 88 FREE (&p); 89 } 90} 91 92static QUERY *run_query (char *s, int quiet) 93{ 94 FILE *fp; 95 QUERY *first = NULL; 96 QUERY *cur = NULL; 97 char cmd[_POSIX_PATH_MAX]; 98 char *buf = NULL; 99 size_t buflen; 100 int dummy = 0; 101 char msg[STRING]; 102 char *p; 103 pid_t thepid; 104 105 106 mutt_expand_file_fmt (cmd, sizeof(cmd), QueryCmd, s); 107 108 if ((thepid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) 109 { 110 dprint (1, (debugfile, "unable to fork command: %s", cmd)); 111 return 0; 112 } 113 if (!quiet) 114 mutt_message _("Waiting for response..."); 115 fgets (msg, sizeof (msg), fp); 116 if ((p = strrchr (msg, '\n'))) 117 *p = '\0'; 118 while ((buf = mutt_read_line (buf, &buflen, fp, &dummy, 0)) != NULL) 119 { 120 if ((p = strtok(buf, "\t\n"))) 121 { 122 if (first == NULL) 123 { 124 first = (QUERY *) safe_calloc (1, sizeof (QUERY)); 125 cur = first; 126 } 127 else 128 { 129 cur->next = (QUERY *) safe_calloc (1, sizeof (QUERY)); 130 cur = cur->next; 131 } 132 133 cur->addr = rfc822_parse_adrlist (cur->addr, p); 134 p = strtok(NULL, "\t\n"); 135 if (p) 136 { 137 cur->name = safe_strdup (p); 138 p = strtok(NULL, "\t\n"); 139 if (p) 140 cur->other = safe_strdup (p); 141 } 142 } 143 } 144 FREE (&buf); 145 safe_fclose (&fp); 146 if (mutt_wait_filter (thepid)) 147 { 148 dprint (1, (debugfile, "Error: %s\n", msg)); 149 if (!quiet) mutt_error ("%s", msg); 150 } 151 else 152 { 153 if (!quiet) 154 mutt_message ("%s", msg); 155 } 156 157 return first; 158} 159 160static int query_search (MUTTMENU *m, regex_t *re, int n) 161{ 162 ENTRY *table = (ENTRY *) m->data; 163 164 if (table[n].data->name && !regexec (re, table[n].data->name, 0, NULL, 0)) 165 return 0; 166 if (table[n].data->other && !regexec (re, table[n].data->other, 0, NULL, 0)) 167 return 0; 168 if (table[n].data->addr) 169 { 170 if (table[n].data->addr->personal && 171 !regexec (re, table[n].data->addr->personal, 0, NULL, 0)) 172 return 0; 173 if (table[n].data->addr->mailbox && 174 !regexec (re, table[n].data->addr->mailbox, 0, NULL, 0)) 175 return 0; 176#ifdef EXACT_ADDRESS 177 if (table[n].data->addr->val && 178 !regexec (re, table[n].data->addr->val, 0, NULL, 0)) 179 return 0; 180#endif 181 } 182 183 return REG_NOMATCH; 184} 185 186static const char * query_format_str (char *dest, size_t destlen, size_t col, int cols, 187 char op, const char *src, 188 const char *fmt, const char *ifstring, 189 const char *elsestring, 190 unsigned long data, format_flag flags) 191{ 192 ENTRY *entry = (ENTRY *)data; 193 QUERY *query = entry->data; 194 char tmp[SHORT_STRING]; 195 char buf2[STRING] = ""; 196 int optional = (flags & MUTT_FORMAT_OPTIONAL); 197 198 switch (op) 199 { 200 case 'a': 201 rfc822_write_address (buf2, sizeof (buf2), query->addr, 1); 202 snprintf (tmp, sizeof (tmp), "%%%ss", fmt); 203 snprintf (dest, destlen, tmp, buf2); 204 break; 205 case 'c': 206 snprintf (tmp, sizeof (tmp), "%%%sd", fmt); 207 snprintf (dest, destlen, tmp, query->num + 1); 208 break; 209 case 'e': 210 if (!optional) 211 { 212 snprintf (tmp, sizeof (tmp), "%%%ss", fmt); 213 snprintf (dest, destlen, tmp, NONULL (query->other)); 214 } 215 else if (!query->other || !*query->other) 216 optional = 0; 217 break; 218 case 'n': 219 snprintf (tmp, sizeof (tmp), "%%%ss", fmt); 220 snprintf (dest, destlen, tmp, NONULL (query->name)); 221 break; 222 case 't': 223 snprintf (tmp, sizeof (tmp), "%%%sc", fmt); 224 snprintf (dest, destlen, tmp, entry->tagged ? '*' : ' '); 225 break; 226 default: 227 snprintf (tmp, sizeof (tmp), "%%%sc", fmt); 228 snprintf (dest, destlen, tmp, op); 229 break; 230 } 231 232 if (optional) 233 mutt_FormatString (dest, destlen, col, cols, ifstring, query_format_str, data, 0); 234 else if (flags & MUTT_FORMAT_OPTIONAL) 235 mutt_FormatString (dest, destlen, col, cols, elsestring, query_format_str, data, 0); 236 237 return src; 238} 239 240static void query_entry (char *s, size_t slen, MUTTMENU *m, int num) 241{ 242 ENTRY *entry = &((ENTRY *) m->data)[num]; 243 244 entry->data->num = num; 245 mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, NONULL (QueryFormat), query_format_str, 246 (unsigned long) entry, MUTT_FORMAT_ARROWCURSOR); 247} 248 249static int query_tag (MUTTMENU *menu, int n, int m) 250{ 251 ENTRY *cur = &((ENTRY *) menu->data)[n]; 252 int ot = cur->tagged; 253 254 cur->tagged = m >= 0 ? m : !cur->tagged; 255 return cur->tagged - ot; 256} 257 258int mutt_query_complete (char *buf, size_t buflen) 259{ 260 QUERY *results = NULL; 261 ADDRESS *tmpa; 262 263 if (!QueryCmd) 264 { 265 mutt_error _("Query command not defined."); 266 return 0; 267 } 268 269 results = run_query (buf, 1); 270 if (results) 271 { 272 /* only one response? */ 273 if (results->next == NULL) 274 { 275 tmpa = result_to_addr (results); 276 mutt_addrlist_to_local (tmpa); 277 buf[0] = '\0'; 278 rfc822_write_address (buf, buflen, tmpa, 0); 279 rfc822_free_address (&tmpa); 280 free_query (&results); 281 mutt_clear_error (); 282 return (0); 283 } 284 /* multiple results, choose from query menu */ 285 query_menu (buf, buflen, results, 1); 286 } 287 return (0); 288} 289 290void mutt_query_menu (char *buf, size_t buflen) 291{ 292 if (!QueryCmd) 293 { 294 mutt_error _("Query command not defined."); 295 return; 296 } 297 298 if (buf == NULL) 299 { 300 char buffer[STRING] = ""; 301 302 query_menu (buffer, sizeof (buffer), NULL, 0); 303 } 304 else 305 { 306 query_menu (buf, buflen, NULL, 1); 307 } 308} 309 310static void query_menu (char *buf, size_t buflen, QUERY *results, int retbuf) 311{ 312 MUTTMENU *menu; 313 HEADER *msg = NULL; 314 ENTRY *QueryTable = NULL; 315 QUERY *queryp = NULL; 316 int i, done = 0; 317 int op; 318 char helpstr[LONG_STRING]; 319 char title[STRING]; 320 321 snprintf (title, sizeof (title), _("Query")); /* FIXME */ 322 323 menu = mutt_new_menu (MENU_QUERY); 324 menu->make_entry = query_entry; 325 menu->search = query_search; 326 menu->tag = query_tag; 327 menu->title = title; 328 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_QUERY, QueryHelp); 329 330 if (results == NULL) 331 { 332 /* Prompt for Query */ 333 if (mutt_get_field (_("Query: "), buf, buflen, 0) == 0 && buf[0]) 334 { 335 results = run_query (buf, 0); 336 } 337 } 338 339 if (results) 340 { 341 snprintf (title, sizeof (title), _("Query '%s'"), buf); 342 343 /* count the number of results */ 344 for (queryp = results; queryp; queryp = queryp->next) 345 menu->max++; 346 347 menu->data = QueryTable = (ENTRY *) safe_calloc (menu->max, sizeof (ENTRY)); 348 349 for (i = 0, queryp = results; queryp; queryp = queryp->next, i++) 350 QueryTable[i].data = queryp; 351 352 while (!done) 353 { 354 switch ((op = mutt_menuLoop (menu))) 355 { 356 case OP_QUERY_APPEND: 357 case OP_QUERY: 358 if (mutt_get_field (_("Query: "), buf, buflen, 0) == 0 && buf[0]) 359 { 360 QUERY *newresults = NULL; 361 362 newresults = run_query (buf, 0); 363 364 menu->redraw = REDRAW_FULL; 365 if (newresults) 366 { 367 snprintf (title, sizeof (title), _("Query '%s'"), buf); 368 369 if (op == OP_QUERY) 370 { 371 free_query (&results); 372 results = newresults; 373 FREE (&QueryTable); 374 } 375 else 376 { 377 /* append */ 378 for (queryp = results; queryp->next; queryp = queryp->next); 379 380 queryp->next = newresults; 381 } 382 383 384 menu->current = 0; 385 mutt_menuDestroy (&menu); 386 menu = mutt_new_menu (MENU_QUERY); 387 menu->make_entry = query_entry; 388 menu->search = query_search; 389 menu->tag = query_tag; 390 menu->title = title; 391 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_QUERY, QueryHelp); 392 393 /* count the number of results */ 394 for (queryp = results; queryp; queryp = queryp->next) 395 menu->max++; 396 397 if (op == OP_QUERY) 398 { 399 menu->data = QueryTable = 400 (ENTRY *) safe_calloc (menu->max, sizeof (ENTRY)); 401 402 for (i = 0, queryp = results; queryp; 403 queryp = queryp->next, i++) 404 QueryTable[i].data = queryp; 405 } 406 else 407 { 408 int clear = 0; 409 410 /* append */ 411 safe_realloc (&QueryTable, menu->max * sizeof (ENTRY)); 412 413 menu->data = QueryTable; 414 415 for (i = 0, queryp = results; queryp; 416 queryp = queryp->next, i++) 417 { 418 /* once we hit new entries, clear/init the tag */ 419 if (queryp == newresults) 420 clear = 1; 421 422 QueryTable[i].data = queryp; 423 if (clear) 424 QueryTable[i].tagged = 0; 425 } 426 } 427 } 428 } 429 break; 430 431 case OP_CREATE_ALIAS: 432 if (menu->tagprefix) 433 { 434 ADDRESS *naddr = NULL; 435 436 for (i = 0; i < menu->max; i++) 437 if (QueryTable[i].tagged) 438 { 439 ADDRESS *a = result_to_addr(QueryTable[i].data); 440 rfc822_append (&naddr, a, 0); 441 rfc822_free_address (&a); 442 } 443 444 mutt_create_alias (NULL, naddr); 445 } 446 else 447 { 448 ADDRESS *a = result_to_addr(QueryTable[menu->current].data); 449 mutt_create_alias (NULL, a); 450 rfc822_free_address (&a); 451 } 452 break; 453 454 case OP_GENERIC_SELECT_ENTRY: 455 if (retbuf) 456 { 457 done = 2; 458 break; 459 } 460 /* fall through to OP_MAIL */ 461 462 case OP_MAIL: 463 msg = mutt_new_header (); 464 msg->env = mutt_new_envelope (); 465 if (!menu->tagprefix) 466 { 467 msg->env->to = result_to_addr(QueryTable[menu->current].data); 468 } 469 else 470 { 471 for (i = 0; i < menu->max; i++) 472 if (QueryTable[i].tagged) 473 { 474 ADDRESS *a = result_to_addr(QueryTable[i].data); 475 rfc822_append (&msg->env->to, a, 0); 476 rfc822_free_address (&a); 477 } 478 } 479 ci_send_message (0, msg, NULL, Context, NULL); 480 menu->redraw = REDRAW_FULL; 481 break; 482 483 case OP_EXIT: 484 done = 1; 485 break; 486 } 487 } 488 489 /* if we need to return the selected entries */ 490 if (retbuf && (done == 2)) 491 { 492 int tagged = 0; 493 size_t curpos = 0; 494 495 memset (buf, 0, buflen); 496 497 /* check for tagged entries */ 498 for (i = 0; i < menu->max; i++) 499 { 500 if (QueryTable[i].tagged) 501 { 502 if (curpos == 0) 503 { 504 ADDRESS *tmpa = result_to_addr (QueryTable[i].data); 505 mutt_addrlist_to_local (tmpa); 506 tagged = 1; 507 rfc822_write_address (buf, buflen, tmpa, 0); 508 curpos = mutt_strlen (buf); 509 rfc822_free_address (&tmpa); 510 } 511 else if (curpos + 2 < buflen) 512 { 513 ADDRESS *tmpa = result_to_addr (QueryTable[i].data); 514 mutt_addrlist_to_local (tmpa); 515 strcat (buf, ", "); /* __STRCAT_CHECKED__ */ 516 rfc822_write_address ((char *) buf + curpos + 1, buflen - curpos - 1, 517 tmpa, 0); 518 curpos = mutt_strlen (buf); 519 rfc822_free_address (&tmpa); 520 } 521 } 522 } 523 /* then enter current message */ 524 if (!tagged) 525 { 526 ADDRESS *tmpa = result_to_addr (QueryTable[menu->current].data); 527 mutt_addrlist_to_local (tmpa); 528 rfc822_write_address (buf, buflen, tmpa, 0); 529 rfc822_free_address (&tmpa); 530 } 531 532 } 533 534 free_query (&results); 535 FREE (&QueryTable); 536 537 /* tell whoever called me to redraw the screen when I return */ 538 set_option (OPTNEEDREDRAW); 539 } 540 541 mutt_menuDestroy (&menu); 542}