home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / com / utils / elm / sources / arepdaem.c < prev    next >
C/C++ Source or Header  |  1992-10-04  |  18KB  |  677 lines

  1.  
  2. static char rcsid[] = "@(#)$Id: arepdaem.c,v 4.1.1.4 90/12/05 15:12:52 syd Exp $";
  3.  
  4. /*******************************************************************************
  5.  *  The Elm Mail System  -  $Revision: 4.1.1.4 $   $State: Exp $
  6.  *
  7.  *             Copyright (c) 1986, 1987 Dave Taylor
  8.  *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  9.  *******************************************************************************
  10.  * Bug reports, patches, comments, suggestions should be sent to:
  11.  *
  12.  *    Syd Weinstein, Elm Coordinator
  13.  *    elm@DSI.COM            dsinc!elm
  14.  *
  15.  *******************************************************************************
  16.  * $Log:    arepdaem.c,v $
  17.  * Revision 4.1.1.4  90/12/05  15:12:52  syd
  18.  * Fix lock file flags, wrong mode flag used
  19.  * From: Syd via Terry Furman
  20.  *
  21.  * Revision 4.1.1.3  90/10/07  20:39:31  syd
  22.  * Added missing parens to an imbedded assignment.
  23.  * From: Phil Hochstetler <phil@sequent.com>
  24.  *
  25.  * Revision 4.1.1.2  90/08/15  22:50:14  syd
  26.  * Fix last size to time call
  27.  * From: Syd
  28.  *
  29.  * Revision 4.1.1.1  90/08/15  22:33:54  syd
  30.  * Fix to use time instead of bytes for changes to file and to process
  31.  * each entry on delete properly
  32.  * From: Denis Lambot <d241s016!lde@swn.siemens.be>
  33.  *
  34.  * Revision 4.1  90/04/28  22:44:33  syd
  35.  * checkin of Elm 2.3 as of Release PL0
  36.  *
  37.  *
  38.  ******************************************************************************/
  39.  
  40. /** Keep track of mail as it arrives, and respond by sending a 'recording'
  41.     file to the sender as new mail is received.
  42.  
  43.     Note: the user program that interacts with this program is the
  44.     'autoreply' program and that should be consulted for further
  45.     usage information.
  46.  
  47.     This program is part of the 'autoreply' system, and is designed
  48.     to run every hour and check all mailboxes listed in the file
  49.     "/etc/autoreply.data", where the data is in the form:
  50.  
  51.     username    replyfile    current-mailfile-size
  52.  
  53.     To avoid a flood of autoreplies, this program will NOT reply to mail
  54.     that contains header "X-Mailer: fastmail".  Further, each time the
  55.     program responds to mail, the 'mailfile size' entry is updated in
  56.     the file /etc/autoreply.data to allow the system to be brought
  57.     down and rebooted without any loss of data or duplicate messages.
  58.  
  59.     This daemon also uses a lock semaphore file, /usr/spool/uucp/LCK..arep,
  60.     to ensure that more than one copy of itself is never running.  For this
  61.     reason, it is recommended that this daemon be started up each morning
  62.     from cron, since it will either start since it's needed or simply see
  63.     that the file is there and disappear.
  64.  
  65.     Since this particular program is the main daemon answering any
  66.     number of different users, it must be run with uid root.
  67. **/
  68.  
  69. #include <stdio.h>
  70. #include "defs.h"
  71.  
  72. #ifdef BSD
  73. # include <sys/time.h>
  74. #else
  75. # include <time.h>
  76. #endif
  77.  
  78. #include <sys/types.h>
  79. #include <sys/stat.h>
  80. #include <signal.h>
  81. #include <fcntl.h>
  82. #include <errno.h>
  83.  
  84. static char ident[] = { WHAT_STRING };
  85.  
  86. char     arep_lock_file[SLEN];  /* autoreply lock file  */
  87. char     autoreply_file[SLEN];  /* autoreply data file  */
  88. char     logfile[SLEN];         /* first choice   */
  89. #define logfile2    ("/" AUTOREP_LOG)    /* second choice  */
  90.  
  91. #define BEGINNING    0        /* see fseek(3S) for info */
  92. #define SLEEP_TIME    1800 /* 3600        /* run once an hour       */
  93. #define MAX_PEOPLE    20        /* max number in program  */
  94.  
  95. #define EXISTS        00        /* lock file exists??     */
  96.  
  97. #define remove_return(s)    if (strlen(s) > 0) { \
  98.                       if (s[strlen(s)-1] == '\n') \
  99.                     s[strlen(s)-1] = '\0'; \
  100.                         }
  101.  
  102. struct replyrec {
  103.     char     username[NLEN];        /* login name of user */
  104.     char    mailfile[SLEN];        /* name of mail file  */
  105.     char    replyfile[SLEN];    /* name of reply file */
  106.     long    mailsize;        /* mail file size     */
  107.     int     in_list;        /* for new replies    */
  108.       } reply_table[MAX_PEOPLE];
  109.  
  110. FILE  *logfd;                /* logfile (log action)   */
  111. time_t autoreply_time = 0L;        /* modif date of autoreply file */
  112. int   active = 0;            /* # of people 'enrolled' */
  113.  
  114. FILE  *open_logfile();            /* forward declaration    */
  115.  
  116. long  bytes();                /*       ditto           */
  117. time_t ModTime();            /*       ditto          */
  118.  
  119. #ifdef VOIDSIG
  120. void    term_signal();
  121. #else
  122. int    term_signal();
  123. #endif
  124.  
  125. main()
  126. {
  127.     long size;
  128.     int  person, data_changed;
  129.     time_t time;
  130.  
  131.         initpaths();
  132.         sprintf(autoreply_file, "%s/%s", elmhome, AUTOREP_FILE);
  133.         sprintf(logfile,        "%s/%s", elmhome, AUTOREP_LOG);
  134.         sprintf(arep_lock_file, "%s/%s", elmhome, AUTOREP_LOCK);
  135.  
  136. #ifndef OS2
  137.     if (fork()) exit(0);
  138. #endif
  139.  
  140.     if (! lock())
  141.       exit(0);    /* already running! */
  142.  
  143.     signal(SIGINT, term_signal);     /* Terminate signal         */
  144.     signal(SIGTERM, term_signal);     /* Terminate signal         */
  145.  
  146. /*
  147.  *    note the usage of the BSD style setpgrp wont hurt
  148.  *    system V as its calling sequence is no arguments.
  149.  *    The idea is to disassociate from the terminal to
  150.  *    prevent signals.
  151.  */
  152. #ifndef OS2
  153.     person = getpid();
  154.     setpgrp(person, person);
  155. #endif
  156.  
  157.     while (1) {
  158.  
  159.       logfd = open_logfile();    /* open the log */
  160.  
  161.       /* 1. check to see if autoreply table has changed.. */
  162.  
  163.       if ((time = ModTime(autoreply_file)) != autoreply_time) {
  164.         read_autoreply_file();
  165.         autoreply_time = time;
  166.       }
  167.  
  168.       /* 2. now for each active person... */
  169.  
  170.       data_changed = 0;
  171.  
  172.       for (person = 0; person < active; person++) {
  173.         if ((size = bytes(reply_table[person].mailfile)) !=
  174.         reply_table[person].mailsize) {
  175.           if (size > reply_table[person].mailsize)
  176.             read_newmail(person);
  177.           /* else mail removed - resync */
  178.           reply_table[person].mailsize = size;
  179.           data_changed++;
  180.         }
  181.       }
  182.  
  183.       /* 3. if data changed, update autoreply file */
  184.  
  185.       if (data_changed)
  186.         update_autoreply_file();
  187.  
  188.       close_logfile();        /* close the logfile again */
  189.  
  190.       /* 4. Go to sleep...  */
  191.  
  192.       sleep(SLEEP_TIME);
  193.     }
  194. }
  195.  
  196. int
  197. read_autoreply_file()
  198. {
  199.     /** We're here because the autoreply file has changed!!  It
  200.         could either be because someone has been added or because
  201.         someone has been removed...since the list will always be in
  202.         order (nice, eh?) we should have a pretty easy time of it...
  203.     **/
  204.  
  205.     FILE *file;
  206.     char username[SLEN],     replyfile[SLEN];
  207.     int  person;
  208.      long size;
  209.  
  210.     log("Autoreply data file has changed!  Reading...");
  211.  
  212. /*
  213.  * clear old entries prior to reread
  214.  */
  215.     for (person = 0; person < active; person++)
  216.       reply_table[person].in_list = 0;
  217.  
  218.     if ((file = fopen(autoreply_file,"r")) == NULL) {
  219.       log("No-one is using autoreply...");
  220.     } else {
  221.       while (fscanf(file, "%s %s %ld", username, replyfile, &size) != EOF) {
  222.         /* check to see if this person is already in the list */
  223.         if ((person = in_list(username)) != -1) {
  224.           reply_table[person].in_list = 1;
  225.           reply_table[person].mailsize = size;     /* sync */
  226.         }
  227.         else {     /* if not, add them */
  228.           if (active == MAX_PEOPLE) {
  229.         unlock();
  230.         exit(log("Couldn't add %s - already at max people!",
  231.                username));
  232.           }
  233.           log("adding %s to the active list", username);
  234.           strcpy(reply_table[active].username, username);
  235.           sprintf(reply_table[active].mailfile, "%s%s", mailhome, username);
  236.           strcpy(reply_table[active].replyfile, replyfile);
  237.           reply_table[active].mailsize = size;
  238.           reply_table[active].in_list = 1;    /* obviously! */
  239.           active++;
  240.         }
  241.       }
  242.       fclose(file);
  243.     }
  244.  
  245.     /** now check to see if anyone has been removed... **/
  246.  
  247.     person = 0;
  248.     while (person < active)
  249.       if (reply_table[person].in_list) {
  250.         person++;
  251.       }
  252.       else {
  253.         log("removing %s from the active list",
  254.            reply_table[person].username);
  255.         strcpy(reply_table[person].username,
  256.            reply_table[active-1].username);
  257.         strcpy(reply_table[person].mailfile,
  258.            reply_table[active-1].mailfile);
  259.         strcpy(reply_table[person].replyfile,
  260.            reply_table[active-1].replyfile);
  261.         reply_table[person].mailsize = reply_table[active-1].mailsize;
  262.         active--;
  263.       }
  264. }
  265.  
  266. update_autoreply_file()
  267. {
  268.     /** update the entries in the autoreply file... **/
  269.  
  270.     FILE *file;
  271.     register int person;
  272.  
  273.     if ((file = fopen(autoreply_file,"w")) == NULL) {
  274.           log("Couldn't update autoreply file!");
  275.       return;
  276.     }
  277.  
  278.     for (person = 0; person < active; person++)
  279.       fprintf(file, "%s %s %ld\n",
  280.           reply_table[person].username,
  281.           reply_table[person].replyfile,
  282.           reply_table[person].mailsize);
  283.  
  284.     fclose(file);
  285.  
  286. /*    printf("updated autoreply file\n"); */
  287.     autoreply_time = ModTime(autoreply_file);
  288. }
  289.  
  290. int
  291. in_list(name)
  292. char *name;
  293. {
  294.     /** search the current active reply list for the specified username.
  295.         return the index if found, or '-1' if not. **/
  296.  
  297.     register int iindex;
  298.  
  299.     for (iindex = 0; iindex < active; iindex++)
  300.       if (strcmp(name, reply_table[iindex].username) == 0)
  301.         return(iindex);
  302.  
  303.     return(-1);
  304. }
  305.  
  306. read_newmail(person)
  307. int person;
  308. {
  309.     /** Read the new mail for the specified person. **/
  310.  
  311.  
  312.     FILE *mailfile;
  313.     char from_whom[SLEN], subject[SLEN];
  314.     int  sendit;
  315.  
  316.     log("New mail for %s", reply_table[person].username);
  317.  
  318.         if ((mailfile = fopen(reply_table[person].mailfile,"rb")) == NULL)
  319.            return(log("can't open mailfile for user %s",
  320.             reply_table[person].username));
  321.  
  322.         if (fseek(mailfile, reply_table[person].mailsize, BEGINNING) == -1)
  323.            return(log("couldn't seek to %ld in mail file!",
  324.                    reply_table[person].mailsize));
  325.  
  326.     while (get_return(mailfile, person, from_whom, subject, &sendit) != -1)
  327.       if (sendit)
  328.         reply_to_mail(person, from_whom, subject);
  329.  
  330.     return;
  331. }
  332.  
  333. int
  334. get_return(file, person, from, subject, sendit)
  335. FILE *file;
  336. int  person, *sendit;
  337. char *from, *subject;
  338. {
  339.     /** Reads the new message and return the from and subject lines.
  340.         sendit is set to true iff it isn't a machine generated msg
  341.     **/
  342.  
  343.     char name1[SLEN], name2[SLEN], lastname[SLEN];
  344.     char buffer[SLEN], hold_return[NLEN];
  345.     int done = 0, in_header = 0;
  346.  
  347.     from[0] = '\0';
  348.     name1[0] = '\0';
  349.     name2[0] = '\0';
  350.     lastname[0] = '\0';
  351.     *sendit = 1;
  352.  
  353.     while (! done) {
  354.  
  355.       if (fgets(buffer, SLEN, file) == NULL)
  356.     return(-1);
  357.  
  358.       fixline(buffer);
  359.  
  360.       if (first_word(buffer, "From ")) {
  361.     in_header++;
  362.     sscanf(buffer, "%*s %s", hold_return);
  363.       }
  364.       else if (in_header) {
  365.         if (first_word(buffer, ">From")) {
  366.       sscanf(buffer,"%*s %s %*s %*s %*s %*s %*s %*s %*s %s", name1, name2);
  367.       add_site(from, name2, lastname);
  368.         }
  369.         else if (first_word(buffer,"Subject:")) {
  370.       remove_return(buffer);
  371.       strcpy(subject, (char *) (buffer + 8));
  372.         }
  373.         else if (first_word(buffer,"X-Mailer: fastmail"))
  374.       *sendit = 0;
  375.         else if (strlen(buffer) == 1)
  376.       done = 1;
  377.       }
  378.     }
  379.  
  380.     if (from[0] == '\0')
  381.       strcpy(from, hold_return); /* default address! */
  382.     else
  383.       add_site(from, name1, lastname);    /* get the user name too! */
  384.  
  385.     return(0);
  386. }
  387.  
  388. add_site(buffer, site, lastsite)
  389. char *buffer, *site, *lastsite;
  390. {
  391.     /** add site to buffer, unless site is 'uucp', or the same as
  392.         lastsite.   If not, set lastsite to site.
  393.     **/
  394.  
  395.     char local_buffer[SLEN], *strip_parens();
  396.  
  397.     if (strcmp(site, "uucp") != 0)
  398.       if (strcmp(site, lastsite) != 0) {
  399.           if (buffer[0] == '\0')
  400.             strcpy(buffer, strip_parens(site));         /* first in list! */
  401.           else {
  402.             sprintf(local_buffer,"%s!%s", buffer, strip_parens(site));
  403.             strcpy(buffer, local_buffer);
  404.           }
  405.           strcpy(lastsite, strip_parens(site)); /* don't want THIS twice! */
  406.        }
  407. }
  408.  
  409. remove_first_word(string)
  410. char *string;
  411. {    /** removes first word of string, ie up to first non-white space
  412.         following a white space! **/
  413.  
  414.     register int loc;
  415.  
  416.     for (loc = 0; string[loc] != ' ' && string[loc] != '\0'; loc++)
  417.         ;
  418.  
  419.     while (string[loc] == ' ' || string[loc] == '\t')
  420.       loc++;
  421.  
  422.     move_left(string, loc);
  423. }
  424.  
  425. move_left(string, chars)
  426. char string[];
  427. int  chars;
  428. {
  429.     /** moves string chars characters to the left DESTRUCTIVELY **/
  430.  
  431.     register int i;
  432.  
  433.     chars--; /* index starting at zero! */
  434.  
  435.     for (i=chars; string[i] != '\0' && string[i] != '\n'; i++)
  436.       string[i-chars] = string[i];
  437.  
  438.     string[i-chars] = '\0';
  439. }
  440.  
  441. reply_to_mail(person, from, subject)
  442. int   person;
  443. char *from, *subject;
  444. {
  445.     /** Respond to the message from the specified person with the
  446.         specified subject... **/
  447.  
  448.     char buffer[SLEN];
  449.  
  450.     if (strlen(subject) == 0)
  451.       strcpy(subject, "Auto-reply Mail");
  452.     else if (! first_word(subject,"Auto-reply")) {
  453.       sprintf(buffer, "Auto-reply to:%s", subject);
  454.       strcpy(subject, buffer);
  455.     }
  456.  
  457.     log("auto-replying to '%s'", from);
  458.  
  459.     mail(from, subject, reply_table[person].replyfile, person);
  460. }
  461.  
  462. reverse(string)
  463. char *string;
  464. {
  465.     /** reverse string... pretty trivial routine, actually! **/
  466.  
  467.     char buffer[SLEN];
  468.     register int i, j = 0;
  469.  
  470.     for (i = strlen(string)-1; i >= 0; i--)
  471.       buffer[j++] = string[i];
  472.  
  473.     buffer[j] = '\0';
  474.  
  475.     strcpy(string, buffer);
  476. }
  477.  
  478. long
  479. bytes(name)
  480. char *name;
  481. {
  482.     /** return the number of bytes in the specified file.  This
  483.         is to check to see if new mail has arrived....  **/
  484.  
  485.     int ok = 1;
  486.     struct stat buffer;
  487.  
  488.     if (stat(name, &buffer) != 0)
  489.       if (errno != 2) {
  490.        unlock();
  491.        exit(fprintf(stderr,"Error %d attempting fstat on %s", errno, name));
  492.       }
  493.       else
  494.         ok = 0;
  495.  
  496.     return(ok ? buffer.st_size : 0);
  497. }
  498.  
  499. time_t
  500. ModTime(name)
  501. char *name;
  502. {
  503.     /** return the modification time in the specified file.
  504.         This is to check to see if autoreply has changed....  **/
  505.  
  506.     int ok = 1;
  507.     struct stat buffer;
  508.  
  509.     if (stat(name, &buffer) != 0)
  510.       if (errno != 2) {
  511.        unlock();
  512.        exit(fprintf(stderr,"Error %d attempting fstat on %s", errno, name));
  513.       }
  514.       else
  515.         ok = 0;
  516.  
  517.     return(ok ? buffer.st_mtime : (time_t) 0);
  518. }
  519.  
  520. mail(to, subject, filename, person)
  521. char *to, *subject, *filename;
  522. int   person;
  523. {
  524.     /** Mail 'file' to the user from person... **/
  525.  
  526.     char buffer[VERY_LONG_STRING];
  527.  
  528.     sprintf(buffer, "fastmail -f \"%s [autoreply]\" -s \"%s\" %s %s",
  529.         reply_table[person].username, subject, filename, to);
  530.  
  531.     system(buffer);
  532. }
  533.  
  534. log(message, arg)
  535. char *message;
  536. char *arg;
  537. {
  538.     /** Put log entry into log file.  Use the format:
  539.           date-time: <message>
  540.     **/
  541.  
  542.     struct tm *thetime;
  543.     long      clock;
  544. #ifndef    _POSIX_SOURCE
  545.     struct tm *localtime();
  546.     time_t time();
  547. #endif
  548.     char      buffer[SLEN];
  549.  
  550.     /** first off, get the time and date **/
  551.  
  552.     clock = time((long *) 0);       /* seconds since ???   */
  553.     thetime = localtime(&clock);    /* and NOW the time... */
  554.  
  555.     /** then put the message out! **/
  556.  
  557.     sprintf(buffer, message, arg);
  558.  
  559.     fprintf(logfd,"%d/%d-%d:%02d: %s\n",
  560.         thetime->tm_mon+1, thetime->tm_mday,
  561.             thetime->tm_hour,  thetime->tm_min,
  562.             buffer);
  563. }
  564.  
  565. FILE *open_logfile()
  566. {
  567.     /** open the logfile.  returns a valid file descriptor **/
  568.  
  569.     FILE *fd;
  570.  
  571.     if ((fd = fopen(logfile, "a")) == NULL)
  572.       if ((fd = fopen(logfile2, "a")) == NULL) {
  573.         unlock();
  574.         exit(1);    /* give up! */
  575.       }
  576.  
  577.     return( (FILE *) fd);
  578. }
  579.  
  580. close_logfile()
  581. {
  582.     /** Close the logfile until needed again. **/
  583.  
  584.     fclose(logfd);
  585. }
  586.  
  587. char *strip_parens(string)
  588. char *string;
  589. {
  590.     /** Return string with all parenthesized information removed.
  591.         This is a non-destructive algorithm... **/
  592.  
  593.     static char  buffer[SLEN];
  594.     register int depth = 0, buffer_index = 0;
  595.  
  596.     for (; *string; string++) {
  597.       if (*string == '(')
  598.         depth++;
  599.       else if (*string == ')')
  600.         depth--;
  601.       else if (depth == 0)
  602.         buffer[buffer_index++] = *string;
  603.     }
  604.  
  605.     buffer[buffer_index] = '\0';
  606.  
  607.     return( (char *) buffer);
  608. }
  609.  
  610. /*** LOCK and UNLOCK - ensure only one copy of this daemon running at any
  611.      given time by using a file existance semaphore (wonderful stuff!) ***/
  612.  
  613. lock()
  614. {
  615.     char lock_name[SLEN];        /* name of lock file  */
  616.     char pid_buffer[SHORT];
  617.     int pid, create_fd;
  618.  
  619.     strcpy(lock_name, arep_lock_file);
  620. #ifdef PIDCHECK
  621.       /** first, try to read the lock file, and if possible, check the pid.
  622.       If we can validate that the pid is no longer active, then remove
  623.       the lock file.
  624.        **/
  625.     if((create_fd=open(lock_name,O_RDONLY)) != -1) {
  626.       if (read(create_fd, pid_buffer, SHORT) > 0) {
  627.         pid = atoi(pid_buffer);
  628.         if (pid) {
  629.           if (kill(pid, 0)) {
  630.             close(create_fd);
  631.             if (unlink(lock_name) != 0) {
  632.             printf("Error %s (%s)\n\ttrying to unlink file %s (%s)\n",
  633.             error_name(errno), error_description(errno), lock_name, "lock");
  634.             return(0);
  635.             }
  636.           } else /* kill pid check succeeded */
  637.             return(0);
  638.         } else /* pid was zero */
  639.           return(0);
  640.       } else /* read failed */
  641.         return(0);
  642.     }
  643.     /* ok, either the open failed or we unlinked it, now recreate it. */
  644. #else
  645.     if (access(lock_name, EXISTS) == 0)
  646.       return(0);    /* file already exists */
  647. #endif
  648.  
  649.     if ((create_fd=creat(lock_name, 0444)) == -1)
  650.       return(0);    /* can't create file!!   */
  651.  
  652.     sprintf(pid_buffer,"%d\n", getpid() );        /* write the current pid to the file */
  653.     write(create_fd, pid_buffer, strlen(pid_buffer));
  654.     close(create_fd);                /* no need to keep it open */
  655.  
  656.     return(1);
  657.  
  658. }
  659.  
  660. unlock()
  661. {
  662.     /** remove lock file if it's there! **/
  663.  
  664.         chmod(arep_lock_file, 0666);
  665.     (void) unlink(arep_lock_file);
  666. }
  667.  
  668. #ifdef VOIDSIG
  669. void    term_signal()
  670. #else
  671. int    term_signal()
  672. #endif
  673. {
  674.     unlock();
  675.     exit(1);    /* give up! */
  676. }
  677.