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