mutt stable branch with some hacks
at jcs 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 BUFFER *cmd = NULL; 98 char *buf = NULL; 99 size_t buflen; 100 int dummy = 0; 101 char *msg = NULL; 102 size_t msglen; 103 char *p; 104 pid_t thepid; 105 106 cmd = mutt_buffer_pool_get (); 107 mutt_expand_file_fmt (cmd, QueryCmd, s); 108 109 if ((thepid = mutt_create_filter (mutt_b2s (cmd), NULL, &fp, NULL)) < 0) 110 { 111 dprint (1, (debugfile, "unable to fork command: %s", mutt_b2s (cmd))); 112 mutt_buffer_pool_release (&cmd); 113 return 0; 114 } 115 mutt_buffer_pool_release (&cmd); 116 117 if (!quiet) 118 mutt_message _("Waiting for response..."); 119 120 /* The query protocol first reads one NL-terminated line. If an error 121 * occurs, this is assumed to be an error message. Otherwise it's ignored. */ 122 msg = mutt_read_line (msg, &msglen, fp, &dummy, 0); 123 while ((buf = mutt_read_line (buf, &buflen, fp, &dummy, 0)) != NULL) 124 { 125 if ((p = strtok(buf, "\t\n"))) 126 { 127 if (first == NULL) 128 { 129 first = (QUERY *) safe_calloc (1, sizeof (QUERY)); 130 cur = first; 131 } 132 else 133 { 134 cur->next = (QUERY *) safe_calloc (1, sizeof (QUERY)); 135 cur = cur->next; 136 } 137 138 cur->addr = rfc822_parse_adrlist (cur->addr, p); 139 p = strtok(NULL, "\t\n"); 140 if (p) 141 { 142 cur->name = safe_strdup (p); 143 p = strtok(NULL, "\t\n"); 144 if (p) 145 cur->other = safe_strdup (p); 146 } 147 } 148 } 149 FREE (&buf); 150 safe_fclose (&fp); 151 if (mutt_wait_filter (thepid)) 152 { 153 dprint (1, (debugfile, "Error: %s\n", msg)); 154 if (!quiet) mutt_error ("%s", msg); 155 } 156 else 157 { 158 if (!quiet) 159 mutt_message ("%s", msg); 160 } 161 FREE (&msg); 162 163 return first; 164} 165 166static int query_search (MUTTMENU *m, regex_t *re, int n) 167{ 168 ENTRY *table = (ENTRY *) m->data; 169 170 if (table[n].data->name && !regexec (re, table[n].data->name, 0, NULL, 0)) 171 return 0; 172 if (table[n].data->other && !regexec (re, table[n].data->other, 0, NULL, 0)) 173 return 0; 174 if (table[n].data->addr) 175 { 176 if (table[n].data->addr->personal && 177 !regexec (re, table[n].data->addr->personal, 0, NULL, 0)) 178 return 0; 179 if (table[n].data->addr->mailbox && 180 !regexec (re, table[n].data->addr->mailbox, 0, NULL, 0)) 181 return 0; 182#ifdef EXACT_ADDRESS 183 if (table[n].data->addr->val && 184 !regexec (re, table[n].data->addr->val, 0, NULL, 0)) 185 return 0; 186#endif 187 } 188 189 return REG_NOMATCH; 190} 191 192static const char * query_format_str (char *dest, size_t destlen, size_t col, int cols, 193 char op, const char *src, 194 const char *fmt, const char *ifstring, 195 const char *elsestring, 196 unsigned long data, format_flag flags) 197{ 198 ENTRY *entry = (ENTRY *)data; 199 QUERY *query = entry->data; 200 char tmp[SHORT_STRING]; 201 char buf2[STRING] = ""; 202 int optional = (flags & MUTT_FORMAT_OPTIONAL); 203 204 switch (op) 205 { 206 case 'a': 207 rfc822_write_address (buf2, sizeof (buf2), query->addr, 1); 208 mutt_format_s (dest, destlen, fmt, buf2); 209 break; 210 case 'c': 211 snprintf (tmp, sizeof (tmp), "%%%sd", fmt); 212 snprintf (dest, destlen, tmp, query->num + 1); 213 break; 214 case 'e': 215 if (!optional) 216 mutt_format_s (dest, destlen, fmt, NONULL (query->other)); 217 else if (!query->other || !*query->other) 218 optional = 0; 219 break; 220 case 'n': 221 mutt_format_s (dest, destlen, fmt, NONULL (query->name)); 222 break; 223 case 't': 224 snprintf (tmp, sizeof (tmp), "%%%sc", fmt); 225 snprintf (dest, destlen, tmp, entry->tagged ? '*' : ' '); 226 break; 227 default: 228 snprintf (tmp, sizeof (tmp), "%%%sc", fmt); 229 snprintf (dest, destlen, tmp, op); 230 break; 231 } 232 233 if (optional) 234 mutt_FormatString (dest, destlen, col, cols, ifstring, query_format_str, data, 0); 235 else if (flags & MUTT_FORMAT_OPTIONAL) 236 mutt_FormatString (dest, destlen, col, cols, elsestring, query_format_str, data, 0); 237 238 return src; 239} 240 241static void query_entry (char *s, size_t slen, MUTTMENU *m, int num) 242{ 243 ENTRY *entry = &((ENTRY *) m->data)[num]; 244 245 entry->data->num = num; 246 mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, NONULL (QueryFormat), query_format_str, 247 (unsigned long) entry, MUTT_FORMAT_ARROWCURSOR); 248} 249 250static int query_tag (MUTTMENU *menu, int n, int m) 251{ 252 ENTRY *cur = &((ENTRY *) menu->data)[n]; 253 int ot = cur->tagged; 254 255 cur->tagged = m >= 0 ? m : !cur->tagged; 256 return cur->tagged - ot; 257} 258 259int mutt_query_complete (char *buf, size_t buflen) 260{ 261 QUERY *results = NULL; 262 ADDRESS *tmpa; 263 264 if (!QueryCmd) 265 { 266 mutt_error _("Query command not defined."); 267 return 0; 268 } 269 270 results = run_query (buf, 1); 271 if (results) 272 { 273 /* only one response? */ 274 if (results->next == NULL) 275 { 276 tmpa = result_to_addr (results); 277 mutt_addrlist_to_local (tmpa); 278 buf[0] = '\0'; 279 rfc822_write_address (buf, buflen, tmpa, 0); 280 rfc822_free_address (&tmpa); 281 free_query (&results); 282 mutt_clear_error (); 283 return (0); 284 } 285 /* multiple results, choose from query menu */ 286 query_menu (buf, buflen, results, 1); 287 } 288 return (0); 289} 290 291void mutt_query_menu (char *buf, size_t buflen) 292{ 293 if (!QueryCmd) 294 { 295 mutt_error _("Query command not defined."); 296 return; 297 } 298 299 if (buf == NULL) 300 { 301 char buffer[STRING] = ""; 302 303 query_menu (buffer, sizeof (buffer), NULL, 0); 304 } 305 else 306 { 307 query_menu (buf, buflen, NULL, 1); 308 } 309} 310 311static void query_menu (char *buf, size_t buflen, QUERY *results, int retbuf) 312{ 313 MUTTMENU *menu; 314 HEADER *msg = NULL; 315 ENTRY *QueryTable = NULL; 316 QUERY *queryp = NULL; 317 int i, done = 0; 318 int op; 319 char helpstr[LONG_STRING]; 320 char title[STRING]; 321 322 if (results == NULL) 323 { 324 /* Prompt for Query */ 325 if (mutt_get_field (_("Query: "), buf, buflen, 0) == 0 && buf[0]) 326 { 327 results = run_query (buf, 0); 328 } 329 } 330 331 if (results) 332 { 333 snprintf (title, sizeof (title), _("Query '%s'"), buf); 334 335 menu = mutt_new_menu (MENU_QUERY); 336 menu->make_entry = query_entry; 337 menu->search = query_search; 338 menu->tag = query_tag; 339 menu->title = title; 340 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_QUERY, QueryHelp); 341 mutt_push_current_menu (menu); 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_pop_current_menu (menu); 386 mutt_menuDestroy (&menu); 387 menu = mutt_new_menu (MENU_QUERY); 388 menu->make_entry = query_entry; 389 menu->search = query_search; 390 menu->tag = query_tag; 391 menu->title = title; 392 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_QUERY, QueryHelp); 393 mutt_push_current_menu (menu); 394 395 /* count the number of results */ 396 for (queryp = results; queryp; queryp = queryp->next) 397 menu->max++; 398 399 if (op == OP_QUERY) 400 { 401 menu->data = QueryTable = 402 (ENTRY *) safe_calloc (menu->max, sizeof (ENTRY)); 403 404 for (i = 0, queryp = results; queryp; 405 queryp = queryp->next, i++) 406 QueryTable[i].data = queryp; 407 } 408 else 409 { 410 int clear = 0; 411 412 /* append */ 413 safe_realloc (&QueryTable, menu->max * sizeof (ENTRY)); 414 415 menu->data = QueryTable; 416 417 for (i = 0, queryp = results; queryp; 418 queryp = queryp->next, i++) 419 { 420 /* once we hit new entries, clear/init the tag */ 421 if (queryp == newresults) 422 clear = 1; 423 424 QueryTable[i].data = queryp; 425 if (clear) 426 QueryTable[i].tagged = 0; 427 } 428 } 429 } 430 } 431 break; 432 433 case OP_CREATE_ALIAS: 434 if (menu->tagprefix) 435 { 436 ADDRESS *naddr = NULL; 437 438 for (i = 0; i < menu->max; i++) 439 if (QueryTable[i].tagged) 440 { 441 ADDRESS *a = result_to_addr(QueryTable[i].data); 442 rfc822_append (&naddr, a, 0); 443 rfc822_free_address (&a); 444 } 445 446 mutt_create_alias (NULL, naddr); 447 } 448 else 449 { 450 ADDRESS *a = result_to_addr(QueryTable[menu->current].data); 451 mutt_create_alias (NULL, a); 452 rfc822_free_address (&a); 453 } 454 break; 455 456 case OP_GENERIC_SELECT_ENTRY: 457 if (retbuf) 458 { 459 done = 2; 460 break; 461 } 462 /* fall through */ 463 464 case OP_MAIL: 465 msg = mutt_new_header (); 466 msg->env = mutt_new_envelope (); 467 if (!menu->tagprefix) 468 { 469 msg->env->to = result_to_addr(QueryTable[menu->current].data); 470 } 471 else 472 { 473 for (i = 0; i < menu->max; i++) 474 if (QueryTable[i].tagged) 475 { 476 ADDRESS *a = result_to_addr(QueryTable[i].data); 477 rfc822_append (&msg->env->to, a, 0); 478 rfc822_free_address (&a); 479 } 480 } 481 ci_send_message (0, msg, NULL, Context, NULL); 482 menu->redraw = REDRAW_FULL; 483 break; 484 485 case OP_EXIT: 486 done = 1; 487 break; 488 } 489 } 490 491 /* if we need to return the selected entries */ 492 if (retbuf && (done == 2)) 493 { 494 int tagged = 0; 495 size_t curpos = 0; 496 497 memset (buf, 0, buflen); 498 499 /* check for tagged entries */ 500 for (i = 0; i < menu->max; i++) 501 { 502 if (QueryTable[i].tagged) 503 { 504 if (curpos == 0) 505 { 506 ADDRESS *tmpa = result_to_addr (QueryTable[i].data); 507 mutt_addrlist_to_local (tmpa); 508 tagged = 1; 509 rfc822_write_address (buf, buflen, tmpa, 0); 510 curpos = mutt_strlen (buf); 511 rfc822_free_address (&tmpa); 512 } 513 else if (curpos + 2 < buflen) 514 { 515 ADDRESS *tmpa = result_to_addr (QueryTable[i].data); 516 mutt_addrlist_to_local (tmpa); 517 strcat (buf, ", "); /* __STRCAT_CHECKED__ */ 518 rfc822_write_address ((char *) buf + curpos + 1, buflen - curpos - 1, 519 tmpa, 0); 520 curpos = mutt_strlen (buf); 521 rfc822_free_address (&tmpa); 522 } 523 } 524 } 525 /* then enter current message */ 526 if (!tagged) 527 { 528 ADDRESS *tmpa = result_to_addr (QueryTable[menu->current].data); 529 mutt_addrlist_to_local (tmpa); 530 rfc822_write_address (buf, buflen, tmpa, 0); 531 rfc822_free_address (&tmpa); 532 } 533 534 } 535 536 free_query (&results); 537 FREE (&QueryTable); 538 539 mutt_pop_current_menu (menu); 540 mutt_menuDestroy (&menu); 541 } 542}