pick.c (9149B)
1 /* 2 * Copy me if you can. 3 * by 20h 4 */ 5 6 #include <unistd.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <strings.h> 10 #include <string.h> 11 #include <ctype.h> 12 13 #include "ind.h" 14 #include "arg.h" 15 #include "cfg.h" 16 #include "llist.h" 17 #include "imap.h" 18 #include "mark.h" 19 #include "pick.h" 20 21 enum { 22 PICK_NONE = 'O', 23 PICK_STR = 'S', 24 PICK_DATE = 'D', 25 PICK_NUM = 'N', 26 PICK_EXPR = 'E', 27 PICK_KEY = 'K', 28 PICK_SEQ = 'Q', 29 PICK_HEADERPREP = 'H', 30 PICK_END = '\0' 31 }; 32 static char *desc[] = { 33 [PICK_NONE] = "None", 34 [PICK_STR] = "String", 35 [PICK_DATE] = "Date", 36 [PICK_NUM] = "Number", 37 [PICK_EXPR] = "Expression", 38 [PICK_KEY] = "Keyword", 39 [PICK_SEQ] = "Sequence (also: marks)", 40 [PICK_HEADERPREP] = "Header prepend" 41 }; 42 43 typedef struct expression_t expression_t; 44 struct expression_t { 45 char *expr; 46 char *syntax; 47 }; 48 49 static expression_t expressions[] = { 50 {"ANSWERED", ""}, 51 {"BCC", "S"}, 52 {"BEFORE", "D"}, 53 {"BODY", "S"}, 54 {"CC", "S"}, 55 {"DELETED", ""}, 56 {"DRAFT", ""}, 57 {"FLAGGED", ""}, 58 {"FROM", "HS"}, 59 {"HEADER", "SS"}, 60 {"KEYWORD", "K"}, 61 {"LARGER", "N"}, 62 {"NEW", ""}, 63 {"NOT", "E"}, 64 {"OLD", ""}, 65 {"ON", "D"}, 66 {"OR", "EE"}, 67 {"RECENT", ""}, 68 {"SEEN", ""}, 69 {"SENTBEFORE", "D"}, 70 {"SENTON", "D"}, 71 {"SENTSINCE", "D"}, 72 {"SEQ", "Q"}, 73 {"SINCE", "D"}, 74 {"SMALLER", "N"}, 75 {"SUBJECT", "HS"}, 76 {"TEXT", "S"}, 77 {"TO", "HS"}, 78 {"UID", "Q"}, 79 {"UNANSWERED", ""}, 80 {"UNDELETED", ""}, 81 {"UNDRAFTED", ""}, 82 {"UNFLAGGED", ""}, 83 {"UNKEYWORD", ""}, 84 {"UNSEEN", ""} 85 }; 86 87 static char *sortcriteria[] = { 88 "ARRIVAL", 89 "CC", 90 "DATE", 91 "FROM", 92 "REVERSE", 93 "SIZE", 94 "SUBJECT", 95 "TO" 96 }; 97 98 /* 99 * The order of this array is the order, in which the algorithm 100 * for threading is selected. 101 */ 102 static char *threadalgorithms[] = { 103 "REFERENCES", 104 "REFS", 105 "ORDEREDSUBJECT" 106 }; 107 108 char * 109 pick_mksearchstring(char *cfgn, char *mailbox, char **argv[]) 110 { 111 int j; 112 expression_t *expr; 113 char *rstr, *estr, *nestr, *astr, *idss; 114 llist_t *ids; 115 116 rstr = NULL; 117 estr = NULL; 118 119 do { 120 if ((*argv)[0][0] != ':') 121 die("Missing ':' at %s\n", (*argv)[0]); 122 123 expr = NULL; 124 for (j = 0; j < nelem(expressions); j++) { 125 if (!strcasecmp((*argv)[0]+1, expressions[j].expr)) { 126 expr = &expressions[j]; 127 break; 128 } 129 } 130 if (expr == NULL) 131 die("Expression '%s' unknown.\n", (*argv)[0]); 132 133 switch (expr->syntax[0]) { 134 case PICK_HEADERPREP: 135 nestr = smprintf("HEADER %s", expr->expr); 136 break; 137 case PICK_SEQ: 138 nestr = smprintf(""); 139 break; 140 default: 141 nestr = smprintf("%s", expr->expr); 142 break; 143 } 144 if (estr == NULL) { 145 estr = nestr; 146 } else { 147 estr = smprintf("%s %s", estr, nestr); 148 } 149 150 *argv = &(*argv)[1]; 151 for (j = 0; expr->syntax[j] != PICK_END; j++) { 152 if (!(*argv)[0]) { 153 die("Expected argument of type %s at '%s'\n", 154 desc[(int)expr->syntax[j]], 155 (*argv)[-1]); 156 } 157 158 astr = estr; 159 switch (expr->syntax[j]) { 160 case PICK_HEADERPREP: 161 continue; 162 case PICK_STR: 163 estr = smprintf("%s \"%s\"", estr, (*argv)[0]); 164 *argv = &(*argv)[1]; 165 break; 166 case PICK_SEQ: 167 ids = imap_str2ids(cfgn, mailbox, (*argv)[0]); 168 idss = imap_ids2str(ids); 169 estr = smprintf("%s %s", estr, idss); 170 free(idss); 171 llist_free(ids); 172 *argv = &(*argv)[1]; 173 break; 174 case PICK_NUM: 175 case PICK_KEY: 176 case PICK_DATE: 177 estr = smprintf("%s %s", estr, (*argv)[0]); 178 *argv = &(*argv)[1]; 179 break; 180 case PICK_EXPR: 181 rstr = pick_mksearchstring(cfgn, mailbox, argv); 182 estr = smprintf("%s %s", estr, rstr); 183 free(rstr); 184 free(astr); 185 continue; 186 default: 187 continue; 188 } 189 free(astr); 190 } 191 } while((*argv)[0] != NULL); 192 193 return estr; 194 } 195 196 llist_t * 197 pick_sanitizesort(llist_t *sortl) 198 { 199 llistelem_t *elem; 200 int i, found, isreverse; 201 202 isreverse = 0; 203 forllist(sortl, elem) { 204 for (i = 0; i < strlen(elem->key); i++) { 205 if (islower(elem->key[i])) 206 elem->key[i] = toupper(elem->key[i]) & 0xFF; 207 } 208 209 found = 0; 210 for (i = 0; i < nelem(sortcriteria); i++) { 211 if (!strcmp(elem->key, sortcriteria[i])) 212 found = 1; 213 } 214 if (!found) 215 die("Sort criterion '%s' not supported.\n", elem->key); 216 217 if (!strcasecmp(elem->key, "REVERSE")) { 218 if (isreverse) { 219 die("You want to 'REVERSE REVERSE', which" 220 " is not possible.\n"); 221 } 222 isreverse = 1; 223 } else { 224 isreverse = 0; 225 } 226 } 227 228 if (isreverse) { 229 die("Your last search criterion is 'REVERSE', which is" 230 " not possible.\n"); 231 } 232 233 return sortl; 234 } 235 236 char * 237 pick_threadalgorithm(imap_t *imap) 238 { 239 llistelem_t *cap; 240 int i; 241 242 forllist(imap->caps, cap) { 243 if (!strncmp(cap->key, "THREAD", 6)) { 244 for (i = 0; i < nelem(threadalgorithms); i++) { 245 if (!strcmp(cap->key+7, threadalgorithms[i])) 246 return threadalgorithms[i]; 247 } 248 } 249 } 250 251 return NULL; 252 } 253 254 void 255 pick_printsearchsyntax(void) 256 { 257 int i, j; 258 259 printf("SYNTAX:\n" 260 "The search syntax is a small layer above the " 261 "specification in IMAP4v1 (RFC3501).\n" 262 "Every expression begins with the search keyword, " 263 "prepended by a colon.\n" 264 "\n" 265 "Example: :or :text \"Hello World\" :unseen\n" 266 "\n" 267 "ARGUMENTS:\n" 268 "String: UTF-8 string\n" 269 "Date: e.g. \"5-JUL-2010\" RFC 2822 Section 3.3\n" 270 "Number: decimal number\n" 271 "Expression: an expression\n" 272 "Keyword: String\n" 273 "Sequence: e.g. 1,2,3,5:7,300:320\n" 274 "\n" 275 "EXPRESSIONS:\n" 276 "Expression: arguments\n"); 277 for (i = 0; i < nelem(expressions); i++) { 278 printf("%s:", expressions[i].expr); 279 for (j = 0; expressions[i].syntax[j] != PICK_END; j++) { 280 if (expressions[i].syntax[j] == PICK_HEADERPREP) 281 continue; 282 printf(" %s", desc[(int)expressions[i].syntax[j]]); 283 } 284 printf("\n"); 285 } 286 } 287 288 void 289 pick_printsortsyntax(void) 290 { 291 int i; 292 293 printf("\nSORT:\n" 294 "The -o flag is taking as an argument a list of " 295 "criteria.\n" 296 "They can be specified as upper- or lowercase.\n" 297 "\n" 298 "CRITERIA:\n"); 299 for (i = 0; i < nelem(sortcriteria); i++) 300 printf("%s\n", sortcriteria[i]); 301 } 302 303 void 304 pick_printthreadsyntax(void) 305 { 306 int i; 307 308 printf("\nTHREAD:\n" 309 "The -t flag will trigger the thread structure of " 310 "the given search results to be written linearly " 311 "into the results sequence.\n" 312 "Following thread algorithms are defined in various " 313 "RFCs. The list given is the order in which on an " 314 "algorithm is decided.\n\n"); 315 for (i = 0; i < nelem(threadalgorithms); i++) 316 printf("%s\n", threadalgorithms[i]); 317 } 318 319 void 320 pick_printsyntax(void) 321 { 322 pick_printsearchsyntax(); 323 pick_printsortsyntax(); 324 pick_printthreadsyntax(); 325 } 326 327 void 328 pickusage(char *argv0) 329 { 330 die("usage: %s [-sqdht] [-c cfg] [-m folder] [-o sort criteria]" 331 " [-e seq] [search syntax]\n" 332 "See -s for syntax help.\n", argv0); 333 } 334 335 int 336 pickmain(int argc, char *argv[]) 337 { 338 config_t *cfg; 339 imap_t *imap; 340 int status; 341 char *user, *pass, *netspec, *addseq, *sstr, *pstr, *selected, 342 *sorts, *talg, *cfgn, *argv0; 343 llist_t *results, *sortl; 344 mark_t *marks; 345 346 enum { 347 BEQUIET = 0x01, 348 DRYRUN = 0x02, 349 PRINTSYNTAX = 0x04, 350 DOTHREAD = 0x08, 351 352 NOARGS = 0x10 353 }; 354 355 status = 0; 356 addseq = "p"; 357 sorts = NULL; 358 selected = NULL; 359 cfgn = NULL; 360 361 ARGBEGIN(argv0) { 362 case 'c': 363 cfgn = EARGF(pickusage(argv0)); 364 break; 365 case 'd': 366 status |= DRYRUN; 367 break; 368 case 'e': 369 addseq = EARGF(pickusage(argv0)); 370 break; 371 case 'm': 372 selected = EARGF(pickusage(argv0)); 373 break; 374 case 'o': 375 sorts = EARGF(pickusage(argv0)); 376 break; 377 case 'q': 378 status |= BEQUIET; 379 break; 380 case 's': 381 status |= PRINTSYNTAX; 382 break; 383 case 't': 384 status |= DOTHREAD; 385 break; 386 default: 387 pickusage(argv0); 388 } ARGEND; 389 390 if (status & PRINTSYNTAX) { 391 pick_printsyntax(); 392 return 0; 393 } 394 395 if (argc < 1) 396 pickusage(argv0); 397 398 cfg = config_init(cfgn); 399 user = config_checkgetstr(cfg, "imapuser"); 400 pass = config_checkgetstr(cfg, "imappass"); 401 netspec = config_checkgetstr(cfg, "imapnet"); 402 if (selected == NULL) { 403 selected = config_checkgetstr(cfg, "selected"); 404 } else { 405 selected = memdups(selected); 406 } 407 if (cfg->name != NULL) { 408 cfgn = memdups(cfg->name); 409 } else { 410 cfgn = memdups(cfgn); 411 } 412 413 marks = mark_init(cfg->name, selected); 414 if (marks == NULL) 415 die("Could not initialize marks for %s.\n", addseq); 416 417 imap = imap_new(netspec, user, pass); 418 419 if (imap_init(imap)) 420 imap_die(imap, "imap_init"); 421 if (imap_select(imap, selected)) 422 imap_die(imap, "imap_select"); 423 config_free(cfg); 424 425 sstr = pick_mksearchstring(cfgn, selected, &argv); 426 free(cfgn); 427 if (status & DOTHREAD) { 428 talg = pick_threadalgorithm(imap); 429 if (talg == NULL) { 430 die("Could not find a supported threading " 431 "algorithm.\n"); 432 } 433 434 results = imap_thread(imap, talg, sstr); 435 } else { 436 if (sorts != NULL) { 437 sortl = llist_splitstr(sorts, " ,"); 438 sortl = pick_sanitizesort(sortl); 439 sorts = llist_joinstr(sortl, " "); 440 llist_free(sortl); 441 results = imap_sort(imap, sorts, sstr); 442 free(sorts); 443 } else { 444 results = imap_search(imap, sstr); 445 } 446 } 447 free(sstr); 448 449 if (results == NULL) { 450 printf("No results found.\n"); 451 mark_stop(marks); 452 imap_close(imap); 453 imap_free(imap); 454 return 1; 455 } 456 457 pstr = llist_joinstr(results, " "); 458 llist_free(results); 459 mark_set(marks, addseq, pstr); 460 if (!(status & BEQUIET)) 461 printf("%s\n", pstr); 462 free(pstr); 463 464 mark_stop(marks); 465 imap_close(imap); 466 imap_free(imap); 467 return 0; 468 } 469