ctm_smail.c (14707) | ctm_smail.c (16880) |
---|---|
1/* 2 * Send a compressed CTM delta to a recipient mailing list by encoding it 3 * in safe ASCII characters, in mailer-friendly chunks, and passing it 4 * to sendmail. The encoding is almost the same as MIME BASE64, and is 5 * protected by a simple checksum. 6 * 7 * Author: Stephen McKay 8 * 9 * NOTICE: This is free software. I hope you get some use from this program. 10 * In return you should think about all the nice people who give away software. 11 * Maybe you should write some free software too. | 1/* 2 * Send a compressed CTM delta to a recipient mailing list by encoding it 3 * in safe ASCII characters, in mailer-friendly chunks, and passing it 4 * to sendmail. The encoding is almost the same as MIME BASE64, and is 5 * protected by a simple checksum. 6 * 7 * Author: Stephen McKay 8 * 9 * NOTICE: This is free software. I hope you get some use from this program. 10 * In return you should think about all the nice people who give away software. 11 * Maybe you should write some free software too. |
12 * 13 * $Id$ |
|
12 */ 13 14#include <stdio.h> | 14 */ 15 16#include <stdio.h> |
17#include <stdlib.h> |
|
15#include <string.h> 16#include <unistd.h> | 18#include <string.h> 19#include <unistd.h> |
20#include <fcntl.h> |
|
17#include <sys/types.h> 18#include <sys/stat.h> 19#include <errno.h> 20#include <paths.h> 21#include "error.h" 22#include "options.h" 23 24#define DEF_MAX_MSG 64000 /* Default maximum mail msg minus headers. */ 25 26#define LINE_LENGTH 76 /* Chars per encode line. Divisible by 4. */ 27 28void chop_and_send(char *delta, off_t ctm_size, long max_msg_size, 29 char *mail_alias); | 21#include <sys/types.h> 22#include <sys/stat.h> 23#include <errno.h> 24#include <paths.h> 25#include "error.h" 26#include "options.h" 27 28#define DEF_MAX_MSG 64000 /* Default maximum mail msg minus headers. */ 29 30#define LINE_LENGTH 76 /* Chars per encode line. Divisible by 4. */ 31 32void chop_and_send(char *delta, off_t ctm_size, long max_msg_size, 33 char *mail_alias); |
34void chop_and_queue(char *delta, off_t ctm_size, long max_msg_size, 35 char *queue_dir, char *mail_alias); |
|
30unsigned encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size); 31void write_header(FILE *sfp, char *mail_alias, char *delta, int pce, 32 int npieces); 33void write_trailer(FILE *sfp, unsigned sum); 34void apologise(char *delta, off_t ctm_size, long max_ctm_size, 35 char *mail_alias); 36FILE *open_sendmail(void); 37int close_sendmail(FILE *fp); | 36unsigned encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size); 37void write_header(FILE *sfp, char *mail_alias, char *delta, int pce, 38 int npieces); 39void write_trailer(FILE *sfp, unsigned sum); 40void apologise(char *delta, off_t ctm_size, long max_ctm_size, 41 char *mail_alias); 42FILE *open_sendmail(void); 43int close_sendmail(FILE *fp); |
44int lock_queuedir(char *queue_dir); 45void free_lock(int lockf, char *queue_dir); 46void add_to_queue(char *queue_dir, char *mail_alias, char *delta, int npieces, char **tempnames); |
|
38 | 47 |
39 | |
40int 41main(int argc, char **argv) 42 { 43 char *delta_file; 44 char *mail_alias; 45 long max_msg_size = DEF_MAX_MSG; 46 long max_ctm_size = 0; 47 char *log_file = NULL; | 48int 49main(int argc, char **argv) 50 { 51 char *delta_file; 52 char *mail_alias; 53 long max_msg_size = DEF_MAX_MSG; 54 long max_ctm_size = 0; 55 char *log_file = NULL; |
56 char *queue_dir = NULL; |
|
48 struct stat sb; 49 50 err_prog_name(argv[0]); 51 | 57 struct stat sb; 58 59 err_prog_name(argv[0]); 60 |
52 OPTIONS("[-l log] [-m maxmsgsize] [-c maxctmsize] ctm-delta mail-alias") | 61 OPTIONS("[-l log] [-m maxmsgsize] [-c maxctmsize] [-q queuedir] ctm-delta mail-alias") |
53 NUMBER('m', max_msg_size) 54 NUMBER('c', max_ctm_size) 55 STRING('l', log_file) | 62 NUMBER('m', max_msg_size) 63 NUMBER('c', max_ctm_size) 64 STRING('l', log_file) |
65 STRING('q', queue_dir) |
|
56 ENDOPTS 57 58 if (argc != 3) 59 usage(); 60 61 if (log_file != NULL) 62 err_set_log(log_file); 63 64 delta_file = argv[1]; 65 mail_alias = argv[2]; 66 67 if (stat(delta_file, &sb) < 0) 68 { 69 err("%s: %s", delta_file, strerror(errno)); 70 exit(1); 71 } 72 73 if (max_ctm_size != 0 && sb.st_size > max_ctm_size) 74 apologise(delta_file, sb.st_size, max_ctm_size, mail_alias); | 66 ENDOPTS 67 68 if (argc != 3) 69 usage(); 70 71 if (log_file != NULL) 72 err_set_log(log_file); 73 74 delta_file = argv[1]; 75 mail_alias = argv[2]; 76 77 if (stat(delta_file, &sb) < 0) 78 { 79 err("%s: %s", delta_file, strerror(errno)); 80 exit(1); 81 } 82 83 if (max_ctm_size != 0 && sb.st_size > max_ctm_size) 84 apologise(delta_file, sb.st_size, max_ctm_size, mail_alias); |
75 else | 85 else if (queue_dir == NULL) |
76 chop_and_send(delta_file, sb.st_size, max_msg_size, mail_alias); | 86 chop_and_send(delta_file, sb.st_size, max_msg_size, mail_alias); |
87 else 88 chop_and_queue(delta_file, sb.st_size, max_msg_size, queue_dir, mail_alias); |
|
77 78 return 0; 79 } 80 81 82/* 83 * Carve our CTM delta into pieces, encode them, and send them. 84 */ 85void 86chop_and_send(char *delta, off_t ctm_size, long max_msg_size, char *mail_alias) 87 { 88 int npieces; 89 long msg_size; 90 long exp_size; 91 int pce; 92 FILE *sfp; 93 FILE *dfp; 94 unsigned sum; 95 | 89 90 return 0; 91 } 92 93 94/* 95 * Carve our CTM delta into pieces, encode them, and send them. 96 */ 97void 98chop_and_send(char *delta, off_t ctm_size, long max_msg_size, char *mail_alias) 99 { 100 int npieces; 101 long msg_size; 102 long exp_size; 103 int pce; 104 FILE *sfp; 105 FILE *dfp; 106 unsigned sum; 107 |
108#ifdef howmany 109#undef howmany 110#endif 111 |
|
96#define howmany(x, y) (((x) + ((y) - 1)) / (y)) 97 98 /* 99 * Work out how many pieces we need, bearing in mind that each piece 100 * grows by 4/3 when encoded. We count the newlines too, but ignore 101 * all mail headers and piece headers. They are a "small" (almost 102 * constant) per message overhead that we make the user worry about. :-) 103 */ --- 21 unchanged lines hidden (view full) --- 125 if (!close_sendmail(sfp)) 126 exit(1); 127 err("%s %d/%d sent to %s", delta, pce, npieces, mail_alias); 128 } 129 130 fclose(dfp); 131 } 132 | 112#define howmany(x, y) (((x) + ((y) - 1)) / (y)) 113 114 /* 115 * Work out how many pieces we need, bearing in mind that each piece 116 * grows by 4/3 when encoded. We count the newlines too, but ignore 117 * all mail headers and piece headers. They are a "small" (almost 118 * constant) per message overhead that we make the user worry about. :-) 119 */ --- 21 unchanged lines hidden (view full) --- 141 if (!close_sendmail(sfp)) 142 exit(1); 143 err("%s %d/%d sent to %s", delta, pce, npieces, mail_alias); 144 } 145 146 fclose(dfp); 147 } 148 |
149/* 150 * Carve our CTM delta into pieces, encode them, and drop them in the 151 * queue dir. 152 * 153 * Basic algorythm: 154 * 155 * - for (each piece) 156 * - gen. temp. file name (one which the de-queuer will ignore) 157 * - record in array 158 * - open temp. file 159 * - encode delta (including headers) into the temp file 160 * - close temp. file 161 * - end 162 * - lock queue directory 163 * - foreach (temp. file) 164 * - rename to the proper filename 165 * - end 166 * - unlock queue directory 167 * 168 * This is probably overkill, but it means that incomplete deltas 169 * don't get mailed, and also reduces the window for lock races 170 * between ctm_smail and the de-queueing process. 171 */ |
|
133 | 172 |
173void 174chop_and_queue(char *delta, off_t ctm_size, long max_msg_size, char *queue_dir, char *mail_alias) 175{ 176 int npieces, pce, len; 177 long msg_size, exp_size; 178 FILE *sfp, *dfp; 179 unsigned sum; 180 char **tempnames, *tempnam, *sn; 181 182#define howmany(x, y) (((x) + ((y) - 1)) / (y)) 183 184 /* 185 * Work out how many pieces we need, bearing in mind that each piece 186 * grows by 4/3 when encoded. We count the newlines too, but ignore 187 * all mail headers and piece headers. They are a "small" (almost 188 * constant) per message overhead that we make the user worry about. :-) 189 */ 190 exp_size = ctm_size * 4 / 3; 191 exp_size += howmany(exp_size, LINE_LENGTH); 192 npieces = howmany(exp_size, max_msg_size); 193 msg_size = howmany(ctm_size, npieces); 194 195#undef howmany 196 197 /* 198 * allocate space for the array of filenames. Try to be portable 199 * by not assuming anything to do with sizeof(char *) 200 */ 201 tempnames = malloc(npieces * sizeof(char *)); 202 if (tempnames == NULL) 203 { 204 err("malloc for tempnames failed"); 205 exit(1); 206 } 207 208 len = strlen(queue_dir) + 16; 209 tempnam = malloc(len); 210 if (tempnam == NULL) 211 { 212 err("malloc for tempnames failed"); 213 exit(1); 214 } 215 216 if ((dfp = fopen(delta, "r")) == NULL) 217 { 218 err("cannot open '%s' for reading.", delta); 219 exit(1); 220 } 221 222 if ((sn = strrchr(delta, '/')) == NULL) 223 sn = delta; 224 else 225 sn++; 226 227 for (pce = 1; pce <= npieces; pce++) 228 { 229 if (snprintf(tempnam, len, "%s/.%08d-%03d", queue_dir, getpid(), pce) >= len) 230 err("Whoops! tempnam isn't long enough"); 231 232 tempnames[pce - 1] = strdup(tempnam); 233 if (tempnames[pce - 1] == NULL) 234 { 235 err("strdup failed for temp. filename"); 236 exit(1); 237 } 238 239 sfp = fopen(tempnam, "w"); 240 if (sfp == NULL) 241 exit(1); 242 243 write_header(sfp, mail_alias, delta, pce, npieces); 244 sum = encode_body(sfp, dfp, msg_size); 245 write_trailer(sfp, sum); 246 247 if (fclose(sfp) != 0) 248 exit(1); 249 250 err("%s %d/%d created succesfully", sn, pce, npieces); 251 } 252 253 add_to_queue(queue_dir, mail_alias, delta, npieces, tempnames); 254 255 fclose(dfp); 256 257} 258 259 |
|
134/* 135 * MIME BASE64 encode table. 136 */ 137static char to_b64[0x40] = 138 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 139 140/* 141 * This cheap plastic checksum effectively rotates our checksum-so-far --- 69 unchanged lines hidden (view full) --- 211 if (ferror(delta_fp)) 212 { 213 err("error reading input file."); 214 exit(1); 215 } 216 217 if (ferror(sm_fp)) 218 { | 260/* 261 * MIME BASE64 encode table. 262 */ 263static char to_b64[0x40] = 264 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 265 266/* 267 * This cheap plastic checksum effectively rotates our checksum-so-far --- 69 unchanged lines hidden (view full) --- 337 if (ferror(delta_fp)) 338 { 339 err("error reading input file."); 340 exit(1); 341 } 342 343 if (ferror(sm_fp)) 344 { |
219 err("error writing to sendmail"); | 345 err("error writing encoded file"); |
220 exit(1); 221 } 222 223 return cksum; 224 } 225 226 227/* --- 91 unchanged lines hidden (view full) --- 319 return 0; 320 } 321 322 if ((status = pclose(fp)) != 0) 323 err("sendmail failed with status %d", status); 324 325 return (status == 0); 326 } | 346 exit(1); 347 } 348 349 return cksum; 350 } 351 352 353/* --- 91 unchanged lines hidden (view full) --- 445 return 0; 446 } 447 448 if ((status = pclose(fp)) != 0) 449 err("sendmail failed with status %d", status); 450 451 return (status == 0); 452 } |
453 454/* 455 * Lock the queuedir so we're the only guy messing about in there. 456 */ 457int 458lock_queuedir(char *queue_dir) 459{ 460 int fp, len; 461 char *buffer; 462 struct stat sb; 463 464 len = strlen(queue_dir) + 8; 465 466 buffer = malloc(len); 467 if (buffer == NULL) 468 { 469 err("malloc failed in lock_queuedir"); 470 exit(1); 471 } 472 473 if (snprintf(buffer, len, "%s/.lock", queue_dir) >= len) 474 err("Whoops. lock buffer too small in lock_queuedir"); 475 476 /* 477 * We do our own lockfile scanning to avoid unlink races. 60 478 * seconds should be enough to ensure that we won't get more races 479 * happening between the stat and the open/flock. 480 */ 481 482 while (stat(buffer, &sb) == 0) 483 sleep(60); 484 485 if ((fp = open(buffer, O_WRONLY | O_CREAT | O_EXLOCK, 0600)) < 0) 486 { 487 err("can't open `%s' in lock_queuedir", buffer); 488 exit(1); 489 } 490 491 snprintf(buffer, len, "%8ld", getpid()); 492 write(fp, buffer, 8); 493 494 free(buffer); 495 496 return(fp); 497} 498 499/* 500 * Lock the queuedir so we're the only guy messing about in there. 501 */ 502void 503free_lock(int lockf, char *queue_dir) 504{ 505 int len; 506 char *path; 507 508 /* 509 * Most important: free the lock before we do anything else! 510 */ 511 512 close(lockf); 513 514 len = strlen(queue_dir) + 7; 515 516 path = malloc(len); 517 if (path == NULL) 518 { 519 err("malloc failed in free_lock"); 520 exit(1); 521 } 522 523 if (snprintf(path, len, "%s/.lock", queue_dir) >= len) 524 err("lock path buffer too small in free_lock"); 525 526 if (unlink(path) != 0) 527 { 528 err("can't unlink lockfile `%s'", path); 529 exit(1); 530 } 531 532 free(path); 533} 534 535/* move everything into the queue directory. */ 536 537void 538add_to_queue(char *queue_dir, char *mail_alias, char *delta, int npieces, char **tempnames) 539{ 540 char *queuefile, *sn; 541 int pce, len, lockf; 542 543 if ((sn = strrchr(delta, '/')) == NULL) 544 sn = delta; 545 else 546 sn++; 547 548 /* try to malloc all we need BEFORE entering the lock loop */ 549 550 len = strlen(queue_dir) + strlen(sn) + 7; 551 queuefile = malloc(len); 552 if (queuefile == NULL) 553 { 554 err("can't malloc for queuefile"); 555 exit(1); 556 } 557 558 /* 559 * We should be the only process mucking around in the queue 560 * directory while we add the new queue files ... it could be 561 * awkward if the de-queue process starts it's job while we're 562 * adding files ... 563 */ 564 565 lockf = lock_queuedir(queue_dir); 566 for (pce = 0; pce < npieces; pce++) 567 { 568 struct stat sb; 569 570 if (snprintf(queuefile, len, "%s/%s+%03d", queue_dir, sn, pce + 1) >= len) 571 err("whoops, queuefile buffer is too small"); 572 573 if (stat(queuefile, &sb) == 0) 574 { 575 err("WOAH! Queue file `%s' already exists! Bailing out.", queuefile); 576 free_lock(lockf, queue_dir); 577 exit(1); 578 } 579 580 rename(tempnames[pce], queuefile); 581 err("Queue file %s now exists", queuefile); 582 } 583 584 free_lock(lockf, queue_dir); 585 586 free(queuefile); 587} |
|