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