sieve.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
       ---
       sieve.c (12425B)
       ---
            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 <stdarg.h>
           10 #include <string.h>
           11 #include <strings.h>
           12 #include <errno.h>
           13 
           14 #include "ind.h"
           15 #include "arg.h"
           16 #include "cfg.h"
           17 #include "llist.h"
           18 #include "sieve.h"
           19 #include "net.h"
           20 #include "base64.h"
           21 #include "pager.h"
           22 
           23 char *sieverror = NULL;
           24 
           25 sieve_t *
           26 sieve_new(char *netspec, char *user, char *pass)
           27 {
           28         sieve_t *sieve;
           29 
           30         sieve = mallocz(sizeof(sieve_t), 2);
           31         sieve->netspec = memdup(netspec, strlen(netspec)+1);
           32         sieve->user = memdup(user, strlen(user)+1);
           33         sieve->pass = memdup(pass, strlen(pass)+1);
           34 
           35         return sieve;
           36 }
           37 
           38 void
           39 sieve_free(sieve_t *sieve)
           40 {
           41         if (sieve->netspec != NULL)
           42                 free(sieve->netspec);
           43         if (sieve->user != NULL)
           44                 free(sieve->user);
           45         if (sieve->pass != NULL)
           46                 free(sieve->pass);
           47         if (sieve->fd != NULL)
           48                 net_free(sieve->fd);
           49         if (sieve->caps != NULL)
           50                 llist_free(sieve->caps);
           51         if (sieve->parser != NULL)
           52                 parser_free(sieve->parser);
           53         free(sieve);
           54 }
           55 
           56 void
           57 sieve_die(char *fmt, ...)
           58 {
           59         va_list fmtargs;
           60 
           61         va_start(fmtargs, fmt);
           62         vfprintf(stderr, fmt, fmtargs);
           63         va_end(fmtargs);
           64 
           65         if (sieverror != NULL) {
           66                 fprintf(stderr, ": %s\n", sieverror);
           67                 free(sieverror);
           68         } else
           69                 fprintf(stderr, "\n");
           70 
           71         exit(1);
           72 }
           73 
           74 int
           75 sieve_getstatus(sieve_t *sieve, char *line)
           76 {
           77         char *nline, *tb;
           78 
           79         if (sieverror != NULL) {
           80                 free(sieverror);
           81                 sieverror = NULL;
           82         }
           83 
           84         if (line == NULL)
           85                 nline = net_gets(sieve->fd);
           86         else
           87                 nline = line;
           88 
           89         if (!strncmp(nline, "OK", 2)) {
           90                 if (line == NULL)
           91                         free(nline);
           92                 return 0;
           93         }
           94 
           95         tb = nline+3;
           96         if (*tb == '(') {
           97                 tb = strchr(&tb[1], ')');
           98                 if (tb == NULL)
           99                         tb = nline+3;
          100                 else
          101                         tb += 2;
          102         }
          103         sieverror = parser_parsesieve(sieve->parser, tb);
          104 
          105         return 1;
          106 }
          107 
          108 enum {
          109         CAPABILITY = 0x1,
          110         LOGGEDIN
          111 };
          112 
          113 int
          114 sieve_connect(sieve_t *sieve)
          115 {
          116         sieve->fd = net_new(sieve->netspec);
          117         if (sieve->fd == NULL)
          118                 return 1;
          119 
          120         if (net_connect(sieve->fd)) {
          121                 net_free(sieve->fd);
          122                 sieve->fd = NULL;
          123                 return 1;
          124         }
          125         sieve->parser = parser_new("net", sieve->fd);
          126         sieve->state = CAPABILITY;
          127 
          128         return 0;
          129 }
          130 
          131 void
          132 sieve_close(sieve_t *sieve)
          133 {
          134         if (sieve->fd != NULL) {
          135                 net_close(sieve->fd);
          136                 net_free(sieve->fd);
          137                 sieve->fd = NULL;
          138         }
          139 }
          140 
          141 int
          142 sieve_capabilities(sieve_t *sieve)
          143 {
          144         char *line, *key, *value;
          145         int p;
          146 
          147         if (sieve->caps != NULL)
          148                 llist_free(sieve->caps);
          149         sieve->caps = llist_new();
          150 
          151         if (sieve->state != CAPABILITY)
          152                 net_printf(sieve->fd, "CAPABILITIY\r\n");
          153         for (; (line = net_gets(sieve->fd)); free(line)) {
          154                 if (line[0] != '"')
          155                         break;
          156 
          157                 key = NULL;
          158                 value = NULL;
          159                 sscanf(line, "\"%32m[^\"]\" \"%1024m[^\"]\"",
          160                                 &key, &value);
          161                 if (key != NULL && value != NULL) {
          162                         llist_add(sieve->caps, key, value,
          163                                         strlen(value)+1);
          164                 } else {
          165                         if (key != NULL)
          166                                 llist_add(sieve->caps, key, NULL, 0);
          167                 }
          168 
          169                 if (key != NULL)
          170                         free(key);
          171                 if (value != NULL)
          172                         free(value);
          173         }
          174 
          175         p = sieve_getstatus(sieve, line);
          176         free(line);
          177 
          178         return p;
          179 }
          180 
          181 int
          182 sieve_starttls(sieve_t *sieve)
          183 {
          184         int ret;
          185 
          186         net_printf(sieve->fd, "STARTTLS\r\n");
          187         ret = sieve_getstatus(sieve, NULL);
          188         if (ret)
          189                 return 1;
          190 
          191         if (net_addssl(sieve->fd))
          192                 return 1;
          193         return 0;
          194 }
          195 
          196 int
          197 sieve_logout(sieve_t *sieve)
          198 {
          199         net_printf(sieve->fd, "LOGOUT\r\n");
          200         return sieve_getstatus(sieve, NULL);
          201 }
          202 
          203 int
          204 sieve_havespace(sieve_t *sieve, char *script, int size)
          205 {
          206         char *sn;
          207 
          208         sn = parser_encodestring(script);
          209         net_printf(sieve->fd, "HAVESPACE %s %d\r\n", sn, size);
          210         free(sn);
          211 
          212         return sieve_getstatus(sieve, NULL);
          213 }
          214 
          215 int
          216 sieve_authenticate(sieve_t *sieve)
          217 {
          218         llistelem_t *result;
          219         char *authstr;
          220 
          221         result = llist_get(sieve->caps, "SASL");
          222         if (!strstr((char *)result->data, "PLAIN"))
          223                 return 1;
          224 
          225         authstr = parser_encodeplainlogin(sieve->user, sieve->pass);
          226         net_printf(sieve->fd, "AUTHENTICATE \"PLAIN\" \"%s\"\r\n", authstr);
          227         free(authstr);
          228 
          229         return sieve_getstatus(sieve, NULL);
          230 }
          231 
          232 int
          233 sieve_upscript(sieve_t *sieve, char *cmd, char *name, char *script)
          234 {
          235         char *sn, *esc;
          236 
          237         if (name != NULL)
          238                 sn = parser_encodestring(name);
          239         else
          240                 sn = memdup("", 1);
          241         esc = parser_encodestring(script);
          242         net_printf(sieve->fd, "%s %s%s%s\r\n", cmd, sn,
          243                         (name != NULL)? " ": "", esc);
          244         free(esc);
          245         free(sn);
          246 
          247         return sieve_getstatus(sieve, NULL);
          248 }
          249 
          250 int
          251 sieve_putscript(sieve_t *sieve, char *name, char *script)
          252 {
          253         return sieve_upscript(sieve, "PUTSCRIPT", name, script);
          254 }
          255 
          256 int
          257 sieve_checkscript(sieve_t *sieve, char *script)
          258 {
          259         return sieve_upscript(sieve, "CHECKSCRIPT", NULL, script);
          260 }
          261 
          262 int
          263 sieve_noop(sieve_t *sieve)
          264 {
          265         net_printf(sieve->fd, "NOOP\r\n");
          266         return sieve_getstatus(sieve, NULL);
          267 }
          268 
          269 int
          270 sieve_unauthenticate(sieve_t *sieve)
          271 {
          272         net_printf(sieve->fd, "UNAUTHENTICATE\r\n");
          273         return sieve_getstatus(sieve, NULL);
          274 }
          275 
          276 llist_t *
          277 sieve_listscripts(sieve_t *sieve)
          278 {
          279         char *line, *name;
          280         llist_t *scripts;
          281         int isactive, ret;
          282 
          283         scripts = llist_new();
          284         net_printf(sieve->fd, "LISTSCRIPTS\r\n");
          285 
          286         while((line = net_gets(sieve->fd))) {
          287                 name = NULL;
          288                 isactive = 0;
          289 
          290                 if (line[0] != '"' && line[0] != '{')
          291                         break;
          292                 name = parser_parsesieve(sieve->parser, line);
          293                 if (line[0] == '{') {
          294                         free(line);
          295                         line = net_gets(sieve->fd);
          296                 }
          297                 if (name == NULL) {
          298                         free(line);
          299                         continue;
          300                 }
          301                 if (strstr(line, " ACTIVE"))
          302                         isactive = 1;
          303                 if (strstr(line, " active"))
          304                         isactive = 1;
          305                 free(line);
          306 
          307                 llist_add(scripts, name, &isactive, sizeof(isactive));
          308                 free(name);
          309         }
          310 
          311         ret = sieve_getstatus(sieve, line);
          312         free(line);
          313         if (ret || scripts->len < 1) {
          314                 llist_free(scripts);
          315                 return NULL;
          316         }
          317 
          318         return scripts;
          319 }
          320 
          321 int
          322 sieve_setactive(sieve_t *sieve, char *script)
          323 {
          324         char *sn;
          325 
          326         sn = parser_encodestring(script);
          327         net_printf(sieve->fd, "SETACTIVE %s\r\n", sn);
          328         free(sn);
          329 
          330         return sieve_getstatus(sieve, NULL);
          331 }
          332 
          333 char *
          334 sieve_getscript(sieve_t *sieve, char *script)
          335 {
          336         char *line, *ret;
          337 
          338         ret = parser_encodestring(script);
          339         net_printf(sieve->fd, "GETSCRIPT %s\r\n", ret);
          340         free(ret);
          341 
          342         line = net_gets(sieve->fd);
          343         if (line[0] != '{') {
          344                 sieve_getstatus(sieve, line);
          345                 return NULL;
          346         }
          347 
          348         ret = parser_parsesieve(sieve->parser, line);
          349         free(line);
          350 
          351         line = net_gets(sieve->fd);
          352         free(line);
          353 
          354         if (sieve_getstatus(sieve, NULL)) {
          355                 free(ret);
          356                 return NULL;
          357         }
          358 
          359         return ret;
          360 }
          361 
          362 int
          363 sieve_deletescript(sieve_t *sieve, char *script)
          364 {
          365         char *sn;
          366 
          367         sn = parser_encodestring(script);
          368         net_printf(sieve->fd, "DELETESCRIPT %s\r\n", sn);
          369         free(sn);
          370 
          371         return sieve_getstatus(sieve, NULL);
          372 }
          373 
          374 int
          375 sieve_renamescript(sieve_t *sieve, char *old, char *new)
          376 {
          377         char *sno, *snn;
          378 
          379         sno = parser_encodestring(old);
          380         snn = parser_encodestring(new);
          381         net_printf(sieve->fd, "RENAMESCRIPT %s %s\r\n", sno, snn);
          382         free(sno);
          383         free(snn);
          384 
          385         return sieve_getstatus(sieve, NULL);
          386 }
          387 
          388 void
          389 sieve_init(sieve_t *sieve)
          390 {
          391         llistelem_t *result;
          392 
          393         if (sieve_connect(sieve))
          394                 die("sieve_connect: Netspec or credentials invalid.\n");
          395 
          396         if (sieve_capabilities(sieve))
          397                 die("sieve_capabilities: Could not get capabilities.\n");
          398 
          399         result = llist_get(sieve->caps, "STARTTLS");
          400         if (result != NULL) {
          401                 if (sieve_starttls(sieve)) {
          402                         die("sieve_starttls: Could not setupt STARTTLS.\n");
          403                 } else {
          404                         if (sieve_capabilities(sieve)) {
          405                                 die("sieve_capabilities: Could not get "\
          406                                                 "capabilities after "\
          407                                                 "STARTTLS\n");
          408                         }
          409                 }
          410         }
          411 
          412         if (sieve_authenticate(sieve))
          413                 sieve_die("sieve_authenticate");
          414 
          415         sieve->state = LOGGEDIN;
          416 }
          417 
          418 void
          419 sieveusage(char *argv0)
          420 {
          421         die("usage: %s [-h] [-c cfg] [-b|-l|-d|-v script|-p script [file]|"
          422                         "-g script [file]|-e script|-t [file]|-a script|"
          423                         "-s script [name [space]]|-r old new]\n", argv0);
          424 }
          425 
          426 int
          427 sievemain(int argc, char *argv[])
          428 {
          429         int status, len;
          430         char *script, *file, *netspec, *user, *pass, *data, *cfgn, *argv0;
          431         config_t *cfg;
          432         llistelem_t *elem;
          433         llist_t *results;
          434         sieve_t *sieve;
          435 
          436         enum {
          437                 DOLIST = 1 << 0,
          438                 DOPUT = 1 << 1,
          439                 DOGET = 1 << 2,
          440                 DOEDIT = 1 << 3,
          441                 DOCHECK = 1 << 4,
          442                 DODELETE = 1 << 5,
          443                 DORENAME = 1 << 6,
          444                 DOACTIVATE = 1 << 7,
          445                 DODEACTIVATE = 1 << 8,
          446                 DOHAVESPACE = 1 << 10,
          447                 DOSHOWCAPABILITIES = 1 << 11,
          448         };
          449 
          450         status = 0;
          451         script = NULL;
          452         file = NULL;
          453         results = NULL;
          454         cfgn = NULL;
          455 
          456         ARGBEGIN(argv0) {
          457         case 'a':
          458                 status |= DOACTIVATE;
          459                 break;
          460         case 'b':
          461                 status |= DOSHOWCAPABILITIES;
          462                 break;
          463         case 'c':
          464                 cfgn = EARGF(sieveusage(argv0));
          465                 break;
          466         case 'd':
          467                 status |= DODELETE;
          468                 break;
          469         case 'e':
          470                 status |= DOEDIT;
          471                 break;
          472         case 'g':
          473                 status |= DOGET;
          474                 break;
          475         case 'l':
          476                 status |= DOLIST;
          477                 break;
          478         case 'p':
          479                 status |= DOPUT;
          480                 break;
          481         case 'r':
          482                 status |= DORENAME;
          483                 break;
          484         case 's':
          485                 status |= DOHAVESPACE;
          486                 break;
          487         case 't':
          488                 status |= DOCHECK;
          489                 break;
          490         case 'v':
          491                 status |= DODEACTIVATE;
          492                 break;
          493         default:
          494                 sieveusage(argv0);
          495         } ARGEND;
          496 
          497         if (!status)
          498                 sieveusage(argv0);
          499 
          500         cfg = config_init(cfgn);
          501         netspec = (config_checkget(cfg, "sievenet"))->data;
          502         user = (config_checkget(cfg, "sieveuser"))->data;
          503         pass = (config_checkget(cfg, "sievepass"))->data;
          504 
          505         sieve = sieve_new(netspec, user, pass);
          506         config_free(cfg);
          507 
          508         sieve_init(sieve);
          509 
          510         if (status & DOSHOWCAPABILITIES) {
          511                 forllist(sieve->caps, elem) {
          512                         if (elem->data != NULL) {
          513                                 printf("%s = %s\n", elem->key,
          514                                                 (char *)elem->data);
          515                         } else {
          516                                 printf("%s\n", elem->key);
          517                         }
          518                 }
          519                 goto goodsieveending;
          520         }
          521 
          522         if (status & DOLIST) {
          523                 results = sieve_listscripts(sieve);
          524                 if (results == NULL)
          525                         die("No script is there.\n");
          526                 forllist(results, elem) {
          527                         if (*(int *)elem->data) {
          528                                 printf("%s <- active\n", elem->key);
          529                         } else {
          530                                 printf("%s\n", elem->key);
          531                         }
          532                 }
          533                 goto goodsieveending;
          534         }
          535 
          536         if (status & DODEACTIVATE) {
          537                 if (sieve_setactive(sieve, ""))
          538                         sieve_die("sieve_deactivate");
          539                 printf("All scripts deactivated.\n");
          540                 goto goodsieveending;
          541         }
          542 
          543         if (status & DOCHECK) {
          544                 if (argc > 0) {
          545                         data = readfile(argv[0], &len);
          546                         if (data == NULL)
          547                                 edie("readfile");
          548                 } else {
          549                         data = readstdin(&len);
          550                         if (data == NULL)
          551                                 edie("readstdin");
          552                 }
          553                 if (len < 1)
          554                         die("Script has a length of zero.\n");
          555 
          556                 if (sieve_checkscript(sieve, data))
          557                         sieve_die("sieve_checkscript");
          558                 printf("Script is ok.\n");
          559                 goto goodsieveending;
          560         }
          561 
          562         if (argc < 1)
          563                 die("Script name needs to be given.\n");
          564         script = argv[0];
          565 
          566         if (status & DOPUT) {
          567                 if (argc > 1) {
          568                         data = readfile(argv[1], &len);
          569                         if (data == NULL)
          570                                 edie("readfile");
          571                 } else {
          572                         data = readstdin(&len);
          573                         if (data == NULL)
          574                                 edie("readstdin");
          575                 }
          576                 if (len < 1)
          577                         die("Script has a length of zero.\n");
          578 
          579                 if (sieve_putscript(sieve, script, data))
          580                         sieve_die("sieve_putscript");
          581                 printf("Script %s was written.\n", argv[0]);
          582                 goto goodsieveending;
          583         }
          584 
          585         if (status & DOGET) {
          586                 data = sieve_getscript(sieve, script);
          587                 if (data == NULL)
          588                         sieve_die("sieve_getscript");
          589 
          590                 if (argc > 1) {
          591                         if (writefile(argv[1], data, strlen(data), "w+"))
          592                                 edie("writefile");
          593                         free(data);
          594                         return 0;
          595                 }
          596                 if (writeall(stdout, data, strlen(data)))
          597                         edie("writeall");
          598                 free(data);
          599                 goto goodsieveending;
          600         }
          601 
          602         if (status & DOEDIT) {
          603                 data = sieve_getscript(sieve, script);
          604                 if (data == NULL)
          605                         sieve_die("sieve_getscript");
          606 
          607                 sieve_close(sieve);
          608                 file = editstringext(data, "siv");
          609                 free(data);
          610 
          611                 if (file != NULL) {
          612                         sieve_init(sieve);
          613                         if (sieve_putscript(sieve, script, file))
          614                                 sieve_die("sieve_putscript");
          615                         free(file);
          616                         printf("Script %s was changed and uploaded.\n",
          617                                         script);
          618                 } else {
          619                         printf("Script %s was not changed. Will do nothing.\n",
          620                                         script);
          621                 }
          622                 goto goodsieveending;
          623         }
          624 
          625         if (status & DODELETE) {
          626                 if (sieve_deletescript(sieve, script))
          627                         sieve_die("sieve_deletescript");
          628                 printf("Script %s was removed.\n", script);
          629                 goto goodsieveending;
          630         }
          631 
          632         if (status & DORENAME) {
          633                 if (argc < 2)
          634                         die("Pleace specify the new name of the script.\n");
          635                 if (sieve_renamescript(sieve, script, argv[1]))
          636                         sieve_die("sieve_renamescript");
          637                 printf("Script %s was renamed to %s.\n", script, argv[1]);
          638                 goto goodsieveending;
          639         }
          640 
          641         if (status & DOACTIVATE) {
          642                 if (sieve_setactive(sieve, script))
          643                         sieve_die("sieve_activate");
          644                 printf("Script %s is now active.\n", script);
          645                 goto goodsieveending;
          646         }
          647 
          648         if (status & DOHAVESPACE) {
          649                 if (argc < 2)
          650                         die("You need to specify the size.\n");
          651                 len = atoi(argv[1]);
          652                 if (len < 1)
          653                         die("Size should be bigger than zero.\n");
          654 
          655                 if (sieve_havespace(sieve, script, len))
          656                         sieve_die("sieve_checksize");
          657                 printf("%d bytes are available for script %s.\n", len, script);
          658                 goto goodsieveending;
          659         }
          660 
          661 goodsieveending:
          662         if (results != NULL)
          663                 llist_free(results);
          664         sieve_close(sieve);
          665         sieve_free(sieve);
          666 
          667         return 0;
          668 }
          669