00001 00012 /* 00013 COPYRIGHT (C) 2005 RIVERDRUMS 00014 00015 This program is free software; you can redistribute it and/or 00016 modify it under the terms of the GNU General Public License 00017 as published by the Free Software Foundation; either version 2 00018 of the License, or (at your option) any later version. 00019 00020 This program is distributed in the hope that it will be useful, 00021 but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 GNU General Public License for more details. 00024 00025 You should have received a copy of the GNU General Public License 00026 along with this program; if not, write to the Free Software 00027 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00028 00029 */ 00030 00031 00032 #include "init.h" 00033 00034 /* Riverdrums Library */ 00035 #include <rd/conf.h> 00036 #include <rd/log.h> 00037 00038 #define VF_VERSION "0.3.3" 00039 #define VF_READ_BUFSIZE 1024 00043 #define VF_DEFAULT_HOST "localhost" 00044 #define VF_DEFAULT_PORT 25 00045 #define VF_DEFAULT_LOGFILE "/var/log/veldfire.log" 00046 #define VF_DEFAULT_LOGLEVEL 3 00047 #define VF_DEFAULT_CONFIGFILE "/etc/veldfire/veldfire.conf" 00048 #define VF_DEFAULT_SECTION "veldfire" 00049 #define VF_DEFAULT_TIMEOUT 60 00050 #define VF_DEFAULT_MAXCONNECTION 128 00052 00053 #define VF_USER_CONFIGFILE ".veldfirerc" 00057 #define VF_ENV_FROM "VELDFIRE_FROM" 00058 #define VF_ENV_SUBJECT "VELDFIRE_SUBJECT" 00059 #define VF_ENV_HOST "VELDFIRE_HOST" 00060 #define VF_ENV_PORT "VELDFIRE_PORT" 00061 #define VF_ENV_LOGFILE "VELDFIRE_LOGFILE" 00062 #define VF_ENV_LOGLEVEL "VELDFIRE_LOGLEVEL" 00063 #define VF_ENV_CONFIGFILE "VELDFIRE_CONFIGFILE" 00064 #define VF_ENV_SECTION "VELDFIRE_SECTION" 00065 #define VF_ENV_REPORT "VELDFIRE_REPORT" 00066 #define VF_ENV_SENDTIMEOUT "VELDFIRE_SENDTIMEOUT" 00067 #define VF_ENV_RECVTIMEOUT "VELDFIRE_RECVTIMEOUT" 00068 #define VF_ENV_TIMEOUT "VELDFIRE_TIMEOUT" 00069 #define VF_ENV_MAXCONNECTION "VELDFIRE_MAXCONNECTION" 00070 #define VF_ENV_FOREGROUND "VELDFIRE_FOREGROUND" 00071 #define VF_ENV_PRINTLOG "VELDFIRE_PRINTLOG" 00073 00074 00076 #define VF_CONF_TO "to" 00077 #define VF_CONF_FROM "from" 00078 #define VF_CONF_SUBJECT "subject" 00079 #define VF_CONF_HOST "host" 00080 #define VF_CONF_PORT "port" 00081 #define VF_CONF_LOGFILE "logfile" 00082 #define VF_CONF_LOGLEVEL "loglevel" 00083 #define VF_CONF_REPORT "report" 00084 #define VF_CONF_MAXCONNECTION "maxconnection" 00085 #define VF_CONF_SENDTIMEOUT "sendtimeout" 00086 #define VF_CONF_RECVTIMEOUT "recvtimeout" 00087 #define VF_CONF_TIMEOUT "timeout" 00088 #define VF_CONF_BULK "bulk" 00089 #define VF_CONF_NOMESSAGE "nomessage" 00090 #define VF_CONF_DEBUG "debug" 00091 #define VF_CONF_FOREGROUND "foreground" 00092 #define VF_CONF_HEADER "header" 00093 #define VF_CONF_PRINTLOG "printlog" 00095 00096 #define VF_USERAGENT "User-Agent: Veldfire-" VF_VERSION 00099 /* Private function declarations */ 00100 veldfire * __veldfire_init(void); 00101 void __veldfire_defaults(veldfire *v); 00102 void __veldfire_env(veldfire *v); 00103 void __veldfire_configfile(veldfire *v); 00104 void __veldfire_commandline(veldfire *v, int ac, char *av[]); 00105 char ** __veldfire_add_header(veldfire *v, char *header); 00106 void __veldfire_cleanup(veldfire *v); 00107 00116 veldfire * 00117 VF__init(int ac, char *av[]) 00118 { 00119 veldfire *v = NULL; 00120 00121 if ( (v = __veldfire_init()) == NULL) { 00122 return NULL; 00123 } 00124 00125 __veldfire_defaults(v); 00126 __veldfire_env(v); 00127 __veldfire_commandline(v, ac, av); 00128 __veldfire_configfile(v); 00129 __veldfire_commandline(v, ac, av); 00130 00131 if (VF__check_config(v) < 0) { 00132 __veldfire_cleanup(v); 00133 v = NULL; 00134 } else { 00135 v->log = RD__log_init(v->logfile, v->loglevel, 0, 1, 0, 1, 1, v->printlog); 00136 } 00137 00138 return v; 00139 } 00140 00146 void VF__cleanup(veldfire *v) { __veldfire_cleanup(v); } 00147 00154 veldfire * 00155 __veldfire_init(void) 00156 { 00157 veldfire *v = NULL; 00158 00159 if ( (v = (veldfire *) malloc(sizeof(veldfire))) ) { 00160 memset(v, 0, sizeof(veldfire)); 00161 } 00162 00163 return v; 00164 } 00165 00166 00172 void 00173 __veldfire_defaults(veldfire *v) 00174 { 00175 char *p = getenv("HOME"); 00176 00177 v->host = VF_DEFAULT_HOST; 00178 v->port = VF_DEFAULT_PORT; 00179 v->logfile = VF_DEFAULT_LOGFILE; 00180 v->loglevel = VF_DEFAULT_LOGLEVEL; 00181 v->configfile = VF_DEFAULT_CONFIGFILE; 00182 v->section = VF_DEFAULT_SECTION; 00183 v->maxconnection = VF_DEFAULT_MAXCONNECTION; 00184 v->sendtimeout = VF_DEFAULT_TIMEOUT; 00185 v->recvtimeout = VF_DEFAULT_TIMEOUT; 00186 00187 /* 00188 * A bit of advertising 00189 */ 00190 __veldfire_add_header(v, VF_USERAGENT); 00191 00192 /* 00193 * If there is a $HOME/.veldfirerc, then use that instead 00194 * of the system one 00195 */ 00196 00197 if (p) { 00198 int l = strlen(p) + strlen(VF_USER_CONFIGFILE) + 2; 00199 char *cf = (char *) malloc(l); 00200 struct stat st; 00201 00202 if (cf == NULL) { 00203 /* Tough luck */ 00204 return; 00205 } 00206 00207 snprintf(cf, l, "%s/%s", p, VF_USER_CONFIGFILE); 00208 if (stat(cf, &st) == 0) { 00209 /* This is one malloc that can't be freed ... */ 00211 v->configfile = cf; 00212 } else { 00213 free(cf); 00214 } 00215 } 00216 } 00217 00218 00224 void 00225 __veldfire_env(veldfire *v) 00226 { 00227 char *p; 00228 00229 if ( (p = getenv(VF_ENV_FROM)) ) v->from = p; 00230 if ( (p = getenv(VF_ENV_SUBJECT)) ) v->subject = p; 00231 if ( (p = getenv(VF_ENV_HOST)) ) v->host = p; 00232 if ( (p = getenv(VF_ENV_PORT)) ) v->port = atoi(p); 00233 if ( (p = getenv(VF_ENV_LOGFILE)) ) v->logfile = p; 00234 if ( (p = getenv(VF_ENV_LOGLEVEL)) ) v->loglevel = atoi(p); 00235 if ( (p = getenv(VF_ENV_CONFIGFILE)) ) v->configfile = p; 00236 if ( (p = getenv(VF_ENV_SECTION)) ) v->section = p; 00237 if ( (p = getenv(VF_ENV_REPORT)) ) v->report = p; 00238 if ( (p = getenv(VF_ENV_MAXCONNECTION)) ) v->maxconnection = atoi(p); 00239 if ( (p = getenv(VF_ENV_TIMEOUT)) ) { 00240 v->sendtimeout = atoi(p); 00241 v->recvtimeout = atoi(p); 00242 } 00243 if ( (p = getenv(VF_ENV_SENDTIMEOUT)) ) v->sendtimeout = atoi(p); 00244 if ( (p = getenv(VF_ENV_RECVTIMEOUT)) ) v->recvtimeout = atoi(p); 00245 if ( (p = getenv(VF_ENV_FOREGROUND)) ) v->foreground = atoi(p); 00246 if ( (p = getenv(VF_ENV_PRINTLOG)) ) v->printlog = atoi(p); 00247 } 00248 00249 00255 void 00256 __veldfire_configfile(veldfire *v) 00257 { 00258 int i; 00259 char *p; 00260 char **cp; 00261 00262 v->c = RD__conf_read_section(v->configfile, v->section); 00263 00264 if (v->c == NULL) { 00265 return; 00266 } 00267 00268 if ( (p = RD__conf_get_val(v->c, v->section, VF_CONF_TO))) { 00269 v->to = p; 00270 } 00271 00272 if ( (p = RD__conf_get_val(v->c, v->section, VF_CONF_FROM))) { 00273 v->from = p; 00274 } 00275 00276 if ( (p = RD__conf_get_val(v->c, v->section, VF_CONF_SUBJECT))) { 00277 v->subject = p; 00278 } 00279 00280 if ( (p = RD__conf_get_val(v->c, v->section, VF_CONF_HOST))) { 00281 v->host = p; 00282 } 00283 00284 if ( (i = RD__conf_get_int(v->c, v->section, VF_CONF_PORT)) > 0) { 00285 v->port = i; 00286 } 00287 00288 if ( (p = RD__conf_get_val(v->c, v->section, VF_CONF_LOGFILE))) { 00289 v->logfile = p; 00290 } 00291 00292 if ( (i = RD__conf_get_int(v->c, v->section, VF_CONF_LOGLEVEL)) > 0) { 00293 v->loglevel = i; 00294 } 00295 00296 if ( (p = RD__conf_get_val(v->c, v->section, VF_CONF_REPORT))) { 00297 v->report = p; 00298 } 00299 00300 if ( (i = RD__conf_get_int(v->c, v->section, VF_CONF_MAXCONNECTION)) > 0) { 00301 v->maxconnection = i; 00302 } 00303 00304 if ( (i = RD__conf_get_int(v->c, v->section, VF_CONF_TIMEOUT)) > 0) { 00305 v->sendtimeout = i; 00306 v->recvtimeout = i; 00307 } 00308 00309 if ( (i = RD__conf_get_int(v->c, v->section, VF_CONF_SENDTIMEOUT)) > 0) { 00310 v->sendtimeout = i; 00311 } 00312 00313 if ( (i = RD__conf_get_int(v->c, v->section, VF_CONF_RECVTIMEOUT)) > 0) { 00314 v->recvtimeout = i; 00315 } 00316 00317 if ( (i = RD__conf_get_int(v->c, v->section, VF_CONF_BULK)) > 0) { 00318 v->bulk = i; 00319 } 00320 00321 if ( (i = RD__conf_get_int(v->c, v->section, VF_CONF_NOMESSAGE)) > 0) { 00322 v->nomessage = i; 00323 } 00324 00325 if ( (i = RD__conf_get_int(v->c, v->section, VF_CONF_DEBUG)) > 0) { 00326 v->debug = i; 00327 } 00328 00329 if ( (i = RD__conf_get_int(v->c, v->section, VF_CONF_FOREGROUND)) > 0) { 00330 v->foreground = i; 00331 } 00332 00333 if ( (i = RD__conf_get_int(v->c, v->section, VF_CONF_PRINTLOG)) > 0) { 00334 v->printlog = i; 00335 } 00336 00337 00338 /* This array of pointers does not get freed by RD__conf_free(). If 00339 * it needs to be freed, it has to happen on its own. 00340 */ 00341 if ( (cp = RD__conf_get_vals(v->c, v->section, VF_CONF_HEADER))) { 00342 int s = 0; 00343 /* 00344 * There might already be headers from the command line, so 00345 * we need to add these ones to that array 00346 */ 00347 00348 while (cp[s]) { 00349 __veldfire_add_header(v, cp[s]); 00350 s++; 00351 } 00352 } 00353 00354 } 00355 00356 00364 void 00365 __veldfire_commandline(veldfire *v, int ac, char *av[]) 00366 { 00367 int i = 1; 00368 char *p; 00369 00370 v->prog = ( (p = strrchr(av[0], '/')) && *(p + 1)) ? p + 1 : av[0]; 00371 00372 /* H */ 00373 /* b c d e f g h i l m n o p r s t v x y z */ 00374 /* a jk q u w */ 00375 00376 for (i = 1; i < ac; i++) { 00377 if (av[i][0] == '-') { 00378 if ( ++i >= ac && 00379 av[i - 1][1] != 'x' && 00380 av[i - 1][1] != 'd' && 00381 av[i - 1][1] != 'g' && 00382 av[i - 1][1] != 'i' && 00383 av[i - 1][1] != 'v' && 00384 av[i - 1][1] != 'h') { 00385 VF__help(v); 00386 } 00387 00388 switch (av[i - 1][1]) { 00389 case 'f' : v->from = av[i]; break; 00390 case 't' : v->to = av[i]; break; 00391 case 's' : v->subject = av[i]; break; 00392 case 'H' : __veldfire_add_header(v, av[i]); break; 00393 case 'm' : v->host = av[i]; break; 00394 case 'p' : v->port = atoi(av[i]); break; 00395 case 'l' : v->logfile = av[i]; break; 00396 case 'e' : v->loglevel = atoi(av[i]); break; 00397 case 'c' : v->configfile = av[i]; break; 00398 case 'n' : v->section = av[i]; break; 00399 case 'r' : v->report = av[i]; break; 00400 case 'o' : v->maxconnection = atoi(av[i]); break; 00401 case 'y' : v->sendtimeout = atoi(av[i]); break; 00402 case 'z' : v->recvtimeout = atoi(av[i]); break; 00403 case 'b' : v->bulk = 1; i--; break; 00404 case 'x' : v->nomessage = 1; i--; break; 00405 case 'd' : v->debug = 1; i--; break; 00406 case 'g' : v->foreground = 1; i--; break; 00407 case 'i' : v->printlog = 1; i--; break; 00408 case 'v' : VF__version(v); break; 00409 case 'h' : 00410 case '?' : 00411 default : VF__help(v); 00412 } 00413 } else { 00414 /* Options are over, files follow */ 00415 v->files = &av[i]; 00416 break; 00417 } 00418 } 00419 } 00420 00430 char ** 00431 __veldfire_add_header(veldfire *v, char *header) 00432 { 00433 char **tmp = v->headers; 00434 int s = 0; 00435 00436 /* 00437 * Count existing headers 00438 */ 00439 00440 if (v->headers) { 00441 while (v->headers[s]) { 00442 /* Don't add it twice */ 00443 if (strcmp(v->headers[s], header) == 0) { 00444 return v->headers; 00445 } 00446 s++; 00447 } 00448 } 00449 00450 if ( (v->headers = (char **) 00451 realloc(v->headers, 00452 (s + 2) * sizeof(char *))) == NULL) { 00453 /* Well, who is going to notice ? */ 00454 v->headers = tmp; 00455 return NULL; 00456 } 00457 00458 /* Memory allocation is up to the calling function, we just point to 00459 * what they've passed us. If it disappears, we're in trouble. */ 00460 v->headers[s++] = header; 00461 v->headers[s] = NULL; 00462 00463 return v->headers; 00464 } 00465 00466 00474 void 00475 VF__version(veldfire *v) 00476 { 00477 printf("Veldfire-%s by Jason Armstrong <ja@riverdrums.com>\n", VF_VERSION); 00478 __veldfire_cleanup(v); 00479 exit(0); 00480 } 00481 00487 void 00488 VF__help(veldfire *v) 00489 { 00490 printf( 00491 "usage: %s [options] [files to attach]\n" 00492 "\n" 00493 " -f FROM From address\n" 00494 " -t TO To address, or addresses separated by one of: ,:;\\t\\n\n" 00495 " or file containing one address per line\n" 00496 " -s SUBJECT Subject of the email\n" 00497 " -H HEADER Add custom header field (multiple values possible)\n" 00498 " -m HOST Mail host (default %s)\n" 00499 " -p PORT Port on mail host (default %d)\n" 00500 " -l LOGFILE Log File (default %s)\n" 00501 " -e LOGLEVEL Log Level (1-6) (default %d)\n" 00502 " -c CONFIGFILE Config file (default %s)\n" 00503 " -n SECTION Section in config file (default %s)\n" 00504 " -r REPORT Email address to send a report to\n" 00505 " -o MAXCONNECTION Maximum number of emails in one connection (default %d)\n" 00506 " -y SENDTIMEOUT Timeout for sending data\n" 00507 " -z RECVTIMEOUT Timeout for reading data\n" 00508 //" -b Bulk mail (bundle recipients)\n" 00509 " -x No message (don't wait for a msg on stdin)\n" 00510 " -g Foreground (don't fork to the background)\n" 00511 " -i Log to stdout as well as to the log file\n" 00512 //" -d Debug (messages not sent)\n" 00513 " -h Help\n" 00514 " -v Version\n" 00515 "\n" 00516 "Email format: J Smith <j@smith.com> | j@smith.com | <j@smith.com>\n" 00517 "Messages are read from stdin, and can be already formatted multi-part messages\n" 00518 "\n", v->prog, 00519 VF_DEFAULT_HOST, VF_DEFAULT_PORT, 00520 VF_DEFAULT_LOGFILE, VF_DEFAULT_LOGLEVEL, 00521 VF_DEFAULT_CONFIGFILE, VF_DEFAULT_SECTION, 00522 VF_DEFAULT_MAXCONNECTION); 00523 00524 __veldfire_cleanup(v); 00525 exit(0); 00526 } 00527 00533 void 00534 __veldfire_cleanup(veldfire *v) 00535 { 00536 if (v) { 00537 if (v->c) RD__conf_free(v->c); 00538 if (v->msg && v->freemsg) free(v->msg); 00539 00540 /* We just free the array, we don't know what it points to */ 00541 if (v->headers) free(v->headers); 00542 if (v->log) RD__log_free(v->log); 00543 free(v); 00544 } 00545 } 00546 00557 int 00558 VF__check_config(veldfire *v) 00559 { 00560 int rc = 0; 00561 00562 if (v->to == NULL) { 00563 rc = -1; 00564 fprintf(stderr, "Missing 'to' address\n"); 00565 } 00566 00567 /* Check that all files referred to are really files and 00568 * can be opened by us 00569 */ 00570 00571 if (v->files) { 00572 int s = 0; 00573 struct stat st; 00574 00575 while (v->files[s]) { 00576 if (stat(v->files[s], &st) < 0) { 00577 fprintf(stderr, "%s - %s\n", v->files[s], strerror(errno)); 00578 rc = -1; 00579 } else { 00580 if ( !(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) ) { 00581 fprintf(stderr, "%s must be a file or a link to a file\n", v->files[s]); 00582 rc = -1; 00583 } else { 00584 FILE *fp = fopen(v->files[s], "r"); 00585 00586 /* Try to open and close the file read-only */ 00587 if (fp == NULL) { 00588 fprintf(stderr, "%s - %s\n", v->files[s], strerror(errno)); 00589 rc = -1; 00590 } else { 00591 fclose(fp); 00592 } 00593 } 00594 } 00595 s++; 00596 } 00597 } 00598 00599 if (rc < 0) { 00600 VF__help(v); 00601 } 00602 00603 return rc; 00604 } 00605 00612 char * 00613 VF__get_msg(veldfire *v) 00614 { 00615 char *r = NULL, *t = NULL; 00616 int c, s = 0, l = 0; 00617 00618 if (v->nomessage) { 00619 v->freemsg = 1; 00620 return (strdup("")); 00621 } 00622 00623 if (isatty(fileno(stdin))) { 00624 printf("Your message please, end with EOF (^D):\n"); 00625 fflush(stdout); 00626 } 00627 00628 while ( (c = fgetc(stdin)) != EOF) { 00629 if (l >= s) { 00630 t = r; 00631 if ( (r = realloc(r, s + VF_READ_BUFSIZE + 3)) == NULL) { 00632 v->freemsg = 0; 00633 if (t) free(t); 00634 return NULL; 00635 } 00636 s += VF_READ_BUFSIZE + 1; 00637 } 00638 00639 /* Auto detect a single dot on a line on its own ... */ 00640 if ( (c == '\r' || c == '\n') && l > 2 && 00641 r[l - 1] == '.' && r[l - 2] == '\n') { 00642 r[l++] = '.'; 00643 } 00644 00645 r[l++] = c; 00646 } 00647 00648 if (r && l) { 00649 r[l] = 0; 00650 } 00651 00652 v->freemsg = 1; 00653 00654 return r; 00655 } 00656 00657 #ifdef INITTEST 00658 00659 void 00660 _print_vf(veldfire *v) 00661 { 00662 if (v == NULL) { 00663 return; 00664 } 00665 printf("Name : %s\n", v->prog ? v->prog : "(none)"); 00666 printf("Config File : %s\n", v->configfile ? v->configfile : "(none)"); 00667 printf("Section : %s\n", v->section ? v->section : "(none)"); 00668 printf("LogFile : %s\n", v->logfile ? v->logfile : "(none)"); 00669 printf("LogLevel : %d\n", v->loglevel); 00670 printf("Mail host : %s\n", v->host ? v->host : "(none)"); 00671 printf("Mailport : %d\n", v->port); 00672 printf("From : %s\n", v->from ? v->from : "(none)"); 00673 printf("To : %s\n", v->to ? v->to : "(none)"); 00674 printf("Report : %s\n", v->report ? v->report : "(none)"); 00675 printf("Subject : %s\n", v->subject ? v->subject : "(none)"); 00676 printf("Bulk : %d\n", v->bulk); 00677 printf("No Message : %d\n", v->nomessage); 00678 printf("Debug : %d\n", v->debug); 00679 printf("Foreground : %d\n", v->foreground); 00680 printf("Printlog : %d\n", v->printlog); 00681 printf("MaxConnection : %d\n", v->maxconnection); 00682 printf("Timeout Snd : %d\n", v->sendtimeout); 00683 printf("Timeout Rcv : %d\n", v->recvtimeout); 00684 printf("Msg : %s\n", v->msg ? v->msg : "(none)"); 00685 00686 if (v->files) { 00687 int s = 0; 00688 while (v->files[s]) { 00689 printf("File : %s\n", v->files[s++]); 00690 } 00691 } 00692 00693 if (v->headers) { 00694 int s = 0; 00695 while (v->headers[s]) { 00696 printf("Header: %s\n", v->headers[s++]); 00697 } 00698 } 00699 } 00700 00701 int main(int argc, char *argv[]) { 00702 veldfire *v = VF__init(argc, argv); 00703 if (v) { 00704 v->msg = VF__get_msg(v); 00705 _print_vf(v); 00706 } 00707 VF__cleanup(v); 00708 00709 return 0; 00710 } 00711 00712 00713 #endif