pick.c - rohrpost - A commandline mail client to change the world as we see it.
 (HTM) git clone git://r-36.net/rohrpost
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       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