rohrpost

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