39main(int argc, char **argv) 40 { 41 char *delta_file; 42 char *mail_alias; 43 long max_msg_size = DEF_MAX_MSG; 44 long max_ctm_size = 0; 45 char *log_file = NULL; 46 struct stat sb; 47 48 err_prog_name(argv[0]); 49 50 OPTIONS("[-l log] [-m maxmsgsize] [-c maxctmsize] ctm-delta mail-alias") 51 NUMBER('m', max_msg_size) 52 NUMBER('c', max_ctm_size) 53 STRING('l', log_file) 54 ENDOPTS 55 56 if (argc != 3) 57 usage(); 58 59 if (log_file != NULL) 60 err_set_log(log_file); 61 62 delta_file = argv[1]; 63 mail_alias = argv[2]; 64 65 if (stat(delta_file, &sb) < 0) 66 { 67 err("%s: %s", delta_file, strerror(errno)); 68 exit(1); 69 } 70 71 if (max_ctm_size != 0 && sb.st_size > max_ctm_size) 72 apologise(delta_file, sb.st_size, max_ctm_size, mail_alias); 73 else 74 chop_and_send(delta_file, sb.st_size, max_msg_size, mail_alias); 75 76 return 0; 77 } 78 79 80/* 81 * Carve our CTM delta into pieces, encode them, and send them. 82 */ 83void 84chop_and_send(char *delta, off_t ctm_size, long max_msg_size, char *mail_alias) 85 { 86 int npieces; 87 long msg_size; 88 long exp_size; 89 int pce; 90 FILE *sfp; 91 FILE *dfp; 92 unsigned sum; 93 94#define howmany(x,y) (((x)+((y)-1))/(y)) 95 96 /* 97 * Work out how many pieces we need, bearing in mind that each piece 98 * grows by 4/3 when encoded. We count the newlines too, but ignore 99 * all mail headers and piece headers. They are a "small" (almost 100 * constant) per message overhead that we make the user worry about. :-) 101 */ 102 exp_size = ctm_size * 4 / 3; 103 exp_size += howmany(exp_size, LINE_LENGTH); 104 npieces = howmany(exp_size, max_msg_size); 105 msg_size = howmany(ctm_size, npieces); 106 107#undef howmany 108 109 if ((dfp = fopen(delta, "r")) == NULL) 110 { 111 err("cannot open '%s' for reading.", delta); 112 exit(1); 113 } 114 115 for (pce = 1; pce <= npieces; pce++) 116 { 117 sfp = open_sendmail(); 118 if (sfp == NULL) 119 exit(1); 120 write_header(sfp, mail_alias, delta, pce, npieces); 121 sum = encode_body(sfp, dfp, msg_size); 122 write_trailer(sfp, sum); 123 if (!close_sendmail(sfp)) 124 exit(1); 125 err("%s %d/%d sent to %s", delta, pce, npieces, mail_alias); 126 } 127 128 fclose(dfp); 129 } 130 131 132/* 133 * MIME BASE64 encode table. 134 */ 135static char to_b64[0x40] = 136 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 137 138/* 139 * This cheap plastic checksum effectively rotates our checksum-so-far 140 * left one, then adds the character. We only want 16 bits of it, and 141 * don't care what happens to the rest. It ain't much, but it's small. 142 */ 143#define add_ck(sum,x) \ 144 ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0)) 145 146/* 147 * Encode the body. Use an encoding almost the same as MIME BASE64. 148 * 149 * Characters are read from delta_fp and encoded characters are written 150 * to sm_fp. At most 'msg_size' characters should be read from delta_fp. 151 * 152 * The body consists of lines of up to LINE_LENGTH characters. Each group 153 * of 4 characters encodes 3 input characters. Each output character encodes 154 * 6 bits. Thus 64 different characters are needed in this representation. 155 */ 156unsigned 157encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size) 158 { 159 unsigned short cksum = 0xffff; 160 unsigned char *ip; 161 char *op; 162 int want, n, i; 163 unsigned char inbuf[LINE_LENGTH*3/4]; 164 char outbuf[LINE_LENGTH+1]; 165 166 /* 167 * Round up to the nearest line boundary, for the tiniest of gains, 168 * and lots of neatness. :-) 169 */ 170 msg_size += (LINE_LENGTH*3/4) - 1; 171 msg_size -= msg_size % (LINE_LENGTH*3/4); 172 173 while (msg_size > 0) 174 { 175 want = (msg_size < sizeof(inbuf)) ? msg_size : sizeof(inbuf); 176 if ((n = fread(inbuf, sizeof(char), want, delta_fp)) == 0) 177 break; 178 msg_size -= n; 179 180 for (i = 0; i < n; i++) 181 add_ck(cksum, inbuf[i]); 182 183 /* 184 * Produce a line of encoded data. Every line length will be a 185 * multiple of 4, except for, perhaps, the last line. 186 */ 187 ip = inbuf; 188 op = outbuf; 189 while (n >= 3) 190 { 191 *op++ = to_b64[ip[0] >> 2]; 192 *op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4]; 193 *op++ = to_b64[(ip[1] << 2 & 0x3f) | ip[2] >> 6]; 194 *op++ = to_b64[ip[2] & 0x3f]; 195 ip += 3; 196 n -= 3; 197 } 198 if (n > 0) 199 { 200 *op++ = to_b64[ip[0] >> 2]; 201 *op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4]; 202 if (n >= 2) 203 *op++ = to_b64[ip[1] << 2 & 0x3f]; 204 } 205 *op++ = '\n'; 206 fwrite(outbuf, sizeof(char), op - outbuf, sm_fp); 207 } 208 209 if (ferror(delta_fp)) 210 { 211 err("error reading input file."); 212 exit(1); 213 } 214 215 if (ferror(sm_fp)) 216 { 217 err("error writing to sendmail"); 218 exit(1); 219 } 220 221 return cksum; 222 } 223 224 225/* 226 * Write the mail header and data header. 227 */ 228void 229write_header(FILE *sfp, char *mail_alias, char *delta, int pce, int npieces) 230 { 231 char *sn; 232 233 if ((sn = strrchr(delta, '/')) == NULL) 234 sn = delta; 235 else 236 sn++; 237 238 fprintf(sfp, "From: %s-owner\n", mail_alias); 239 fprintf(sfp, "To: %s\n", mail_alias); 240 fprintf(sfp, "Subject: ctm-mail %s %d/%d\n\n", sn, pce, npieces); 241 242 fprintf(sfp, "CTM_MAIL BEGIN %s %d %d\n", sn, pce, npieces); 243 } 244 245 246/* 247 * Write the data trailer. 248 */ 249void 250write_trailer(FILE *sfp, unsigned sum) 251 { 252 fprintf(sfp, "CTM_MAIL END %ld\n", (long)sum); 253 } 254 255 256/* 257 * We're terribly sorry, but the delta is too big to send. 258 */ 259void 260apologise(char *delta, off_t ctm_size, long max_ctm_size, char *mail_alias) 261 { 262 FILE *sfp; 263 char *sn; 264 265 sfp = open_sendmail(); 266 if (sfp == NULL) 267 exit(1); 268 269 if ((sn = strrchr(delta, '/')) == NULL) 270 sn = delta; 271 else 272 sn++; 273 274 fprintf(sfp, "From: %s-owner\n", mail_alias); 275 fprintf(sfp, "To: %s\n", mail_alias); 276 fprintf(sfp, "Subject: ctm-notice %s\n\n", sn); 277 278 fprintf(sfp, "%s is %ld bytes. The limit is %ld bytes.\n\n", sn, 279 (long)ctm_size, max_ctm_size); 280 fprintf(sfp, "You can retrieve this delta via ftpmail, or your good mate at the university.\n"); 281 282 if (!close_sendmail(sfp)) 283 exit(1); 284 } 285 286 287/* 288 * Start a pipe to sendmail. Sendmail will decode the destination 289 * from the message contents. 290 */ 291FILE * 292open_sendmail() 293 { 294 FILE *fp; 295 char buf[100]; 296 297 sprintf(buf, "%s -t", _PATH_SENDMAIL); 298 if ((fp = popen(buf, "w")) == NULL) 299 err("cannot start sendmail"); 300 return fp; 301 } 302 303 304/* 305 * Close a pipe to sendmail. Sendmail will then do its bit. 306 * Return 1 on success, 0 on failure. 307 */ 308int 309close_sendmail(FILE *fp) 310 { 311 int status; 312 313 fflush(fp); 314 if (ferror(fp)) 315 { 316 err("error writing to sendmail"); 317 return 0; 318 } 319 320 if ((status = pclose(fp)) != 0) 321 err("sendmail failed with status %d", status); 322 323 return (status == 0); 324 }
| 40main(int argc, char **argv) 41 { 42 char *delta_file; 43 char *mail_alias; 44 long max_msg_size = DEF_MAX_MSG; 45 long max_ctm_size = 0; 46 char *log_file = NULL; 47 struct stat sb; 48 49 err_prog_name(argv[0]); 50 51 OPTIONS("[-l log] [-m maxmsgsize] [-c maxctmsize] ctm-delta mail-alias") 52 NUMBER('m', max_msg_size) 53 NUMBER('c', max_ctm_size) 54 STRING('l', log_file) 55 ENDOPTS 56 57 if (argc != 3) 58 usage(); 59 60 if (log_file != NULL) 61 err_set_log(log_file); 62 63 delta_file = argv[1]; 64 mail_alias = argv[2]; 65 66 if (stat(delta_file, &sb) < 0) 67 { 68 err("%s: %s", delta_file, strerror(errno)); 69 exit(1); 70 } 71 72 if (max_ctm_size != 0 && sb.st_size > max_ctm_size) 73 apologise(delta_file, sb.st_size, max_ctm_size, mail_alias); 74 else 75 chop_and_send(delta_file, sb.st_size, max_msg_size, mail_alias); 76 77 return 0; 78 } 79 80 81/* 82 * Carve our CTM delta into pieces, encode them, and send them. 83 */ 84void 85chop_and_send(char *delta, off_t ctm_size, long max_msg_size, char *mail_alias) 86 { 87 int npieces; 88 long msg_size; 89 long exp_size; 90 int pce; 91 FILE *sfp; 92 FILE *dfp; 93 unsigned sum; 94 95#define howmany(x,y) (((x)+((y)-1))/(y)) 96 97 /* 98 * Work out how many pieces we need, bearing in mind that each piece 99 * grows by 4/3 when encoded. We count the newlines too, but ignore 100 * all mail headers and piece headers. They are a "small" (almost 101 * constant) per message overhead that we make the user worry about. :-) 102 */ 103 exp_size = ctm_size * 4 / 3; 104 exp_size += howmany(exp_size, LINE_LENGTH); 105 npieces = howmany(exp_size, max_msg_size); 106 msg_size = howmany(ctm_size, npieces); 107 108#undef howmany 109 110 if ((dfp = fopen(delta, "r")) == NULL) 111 { 112 err("cannot open '%s' for reading.", delta); 113 exit(1); 114 } 115 116 for (pce = 1; pce <= npieces; pce++) 117 { 118 sfp = open_sendmail(); 119 if (sfp == NULL) 120 exit(1); 121 write_header(sfp, mail_alias, delta, pce, npieces); 122 sum = encode_body(sfp, dfp, msg_size); 123 write_trailer(sfp, sum); 124 if (!close_sendmail(sfp)) 125 exit(1); 126 err("%s %d/%d sent to %s", delta, pce, npieces, mail_alias); 127 } 128 129 fclose(dfp); 130 } 131 132 133/* 134 * MIME BASE64 encode table. 135 */ 136static char to_b64[0x40] = 137 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 138 139/* 140 * This cheap plastic checksum effectively rotates our checksum-so-far 141 * left one, then adds the character. We only want 16 bits of it, and 142 * don't care what happens to the rest. It ain't much, but it's small. 143 */ 144#define add_ck(sum,x) \ 145 ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0)) 146 147/* 148 * Encode the body. Use an encoding almost the same as MIME BASE64. 149 * 150 * Characters are read from delta_fp and encoded characters are written 151 * to sm_fp. At most 'msg_size' characters should be read from delta_fp. 152 * 153 * The body consists of lines of up to LINE_LENGTH characters. Each group 154 * of 4 characters encodes 3 input characters. Each output character encodes 155 * 6 bits. Thus 64 different characters are needed in this representation. 156 */ 157unsigned 158encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size) 159 { 160 unsigned short cksum = 0xffff; 161 unsigned char *ip; 162 char *op; 163 int want, n, i; 164 unsigned char inbuf[LINE_LENGTH*3/4]; 165 char outbuf[LINE_LENGTH+1]; 166 167 /* 168 * Round up to the nearest line boundary, for the tiniest of gains, 169 * and lots of neatness. :-) 170 */ 171 msg_size += (LINE_LENGTH*3/4) - 1; 172 msg_size -= msg_size % (LINE_LENGTH*3/4); 173 174 while (msg_size > 0) 175 { 176 want = (msg_size < sizeof(inbuf)) ? msg_size : sizeof(inbuf); 177 if ((n = fread(inbuf, sizeof(char), want, delta_fp)) == 0) 178 break; 179 msg_size -= n; 180 181 for (i = 0; i < n; i++) 182 add_ck(cksum, inbuf[i]); 183 184 /* 185 * Produce a line of encoded data. Every line length will be a 186 * multiple of 4, except for, perhaps, the last line. 187 */ 188 ip = inbuf; 189 op = outbuf; 190 while (n >= 3) 191 { 192 *op++ = to_b64[ip[0] >> 2]; 193 *op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4]; 194 *op++ = to_b64[(ip[1] << 2 & 0x3f) | ip[2] >> 6]; 195 *op++ = to_b64[ip[2] & 0x3f]; 196 ip += 3; 197 n -= 3; 198 } 199 if (n > 0) 200 { 201 *op++ = to_b64[ip[0] >> 2]; 202 *op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4]; 203 if (n >= 2) 204 *op++ = to_b64[ip[1] << 2 & 0x3f]; 205 } 206 *op++ = '\n'; 207 fwrite(outbuf, sizeof(char), op - outbuf, sm_fp); 208 } 209 210 if (ferror(delta_fp)) 211 { 212 err("error reading input file."); 213 exit(1); 214 } 215 216 if (ferror(sm_fp)) 217 { 218 err("error writing to sendmail"); 219 exit(1); 220 } 221 222 return cksum; 223 } 224 225 226/* 227 * Write the mail header and data header. 228 */ 229void 230write_header(FILE *sfp, char *mail_alias, char *delta, int pce, int npieces) 231 { 232 char *sn; 233 234 if ((sn = strrchr(delta, '/')) == NULL) 235 sn = delta; 236 else 237 sn++; 238 239 fprintf(sfp, "From: %s-owner\n", mail_alias); 240 fprintf(sfp, "To: %s\n", mail_alias); 241 fprintf(sfp, "Subject: ctm-mail %s %d/%d\n\n", sn, pce, npieces); 242 243 fprintf(sfp, "CTM_MAIL BEGIN %s %d %d\n", sn, pce, npieces); 244 } 245 246 247/* 248 * Write the data trailer. 249 */ 250void 251write_trailer(FILE *sfp, unsigned sum) 252 { 253 fprintf(sfp, "CTM_MAIL END %ld\n", (long)sum); 254 } 255 256 257/* 258 * We're terribly sorry, but the delta is too big to send. 259 */ 260void 261apologise(char *delta, off_t ctm_size, long max_ctm_size, char *mail_alias) 262 { 263 FILE *sfp; 264 char *sn; 265 266 sfp = open_sendmail(); 267 if (sfp == NULL) 268 exit(1); 269 270 if ((sn = strrchr(delta, '/')) == NULL) 271 sn = delta; 272 else 273 sn++; 274 275 fprintf(sfp, "From: %s-owner\n", mail_alias); 276 fprintf(sfp, "To: %s\n", mail_alias); 277 fprintf(sfp, "Subject: ctm-notice %s\n\n", sn); 278 279 fprintf(sfp, "%s is %ld bytes. The limit is %ld bytes.\n\n", sn, 280 (long)ctm_size, max_ctm_size); 281 fprintf(sfp, "You can retrieve this delta via ftpmail, or your good mate at the university.\n"); 282 283 if (!close_sendmail(sfp)) 284 exit(1); 285 } 286 287 288/* 289 * Start a pipe to sendmail. Sendmail will decode the destination 290 * from the message contents. 291 */ 292FILE * 293open_sendmail() 294 { 295 FILE *fp; 296 char buf[100]; 297 298 sprintf(buf, "%s -t", _PATH_SENDMAIL); 299 if ((fp = popen(buf, "w")) == NULL) 300 err("cannot start sendmail"); 301 return fp; 302 } 303 304 305/* 306 * Close a pipe to sendmail. Sendmail will then do its bit. 307 * Return 1 on success, 0 on failure. 308 */ 309int 310close_sendmail(FILE *fp) 311 { 312 int status; 313 314 fflush(fp); 315 if (ferror(fp)) 316 { 317 err("error writing to sendmail"); 318 return 0; 319 } 320 321 if ((status = pclose(fp)) != 0) 322 err("sendmail failed with status %d", status); 323 324 return (status == 0); 325 }
|