rohrpost

A commandline mail client to change the world as we see it.
git clone git://r-36.net/rohrpost
Log | Files | Refs | LICENSE

sieve.c (12401B)


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