init.c

Go to the documentation of this file.
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

 

 


Jason Armstrong <ja@riverdrums.com>