commands/vpopbull.cc
changeset 0 6f7a81934006
equal deleted inserted replaced
-1:000000000000 0:6f7a81934006
       
     1 // Copyright (C) 1999,2000 Bruce Guenter <bruceg@em.ca>
       
     2 //
       
     3 // This program is free software; you can redistribute it and/or modify
       
     4 // it under the terms of the GNU General Public License as published by
       
     5 // the Free Software Foundation; either version 2 of the License, or
       
     6 // (at your option) any later version.
       
     7 //
       
     8 // This program is distributed in the hope that it will be useful,
       
     9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    11 // GNU General Public License for more details.
       
    12 //
       
    13 // You should have received a copy of the GNU General Public License
       
    14 // along with this program; if not, write to the Free Software
       
    15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    16 
       
    17 #include <config.h>
       
    18 #include "ac/dirent.h"
       
    19 #include <errno.h>
       
    20 #include <fcntl.h>
       
    21 #include <stdlib.h>
       
    22 #include <sys/stat.h>
       
    23 #include <sys/types.h>
       
    24 #include "ac/time.h"
       
    25 #include <unistd.h>
       
    26 #include "misc/itoa.h"
       
    27 #include "mystring/mystring.h"
       
    28 #include "config/configrc.h"
       
    29 //#include "misc/debug.h"
       
    30 #include "cli/cli.h"
       
    31 #include "fdbuf/fdbuf.h"
       
    32 #include "vcommand.h"
       
    33 
       
    34 const char* cli_program = "vpopbull";
       
    35 const char* cli_help_prefix = "Delivers pop bulletins to virtual users\n";
       
    36 const char* cli_help_suffix = "";
       
    37 const char* cli_args_usage = "";
       
    38 const int cli_args_min = 0;
       
    39 const int cli_args_max = 0;
       
    40 
       
    41 static int o_quiet = false;
       
    42 
       
    43 // Scans bulletin directories for any which are newer than the
       
    44 // F<.timestamp> file in the specified maildir.
       
    45 // For each bulletin that it finds, it adds a symlink to that bulletin to
       
    46 // the specified maildir.
       
    47 // Since no reformatting is done, these bulletins must be fully formatted
       
    48 // email messages, including full headers.
       
    49 //
       
    50 // This program is designed to be run from C<checkvpw-postsetuid>.
       
    51 
       
    52 cli_option cli_options[] = {
       
    53   { 0, "quiet", cli_option::flag, true, &o_quiet,
       
    54     "Suppress all status messages", 0 },
       
    55   {0}
       
    56 };
       
    57 
       
    58 // RETURN VALUE
       
    59 //
       
    60 // Exits false if an error occurred during startup, true otherwise.
       
    61 
       
    62 // ENVIRONMENT
       
    63 //
       
    64 // This program expects the environment variable C<HOME> to be set, and
       
    65 // executes a change directory to the contents of it before starting.
       
    66 //
       
    67 // This program expects C<MAILDIR> to be set, and delivers any bulletins
       
    68 // that it finds into this maildir.
       
    69 //
       
    70 // If C<VUSER> is set, a local bulletin directory is searched as above.
       
    71 
       
    72 // FILES
       
    73 //
       
    74 // The following control files are used:
       
    75 //
       
    76 // =over 8
       
    77 //
       
    78 // =item F<global-bulletin-dir>
       
    79 //
       
    80 // This specifies the bulletin directory for all domains.
       
    81 //
       
    82 // =item F<bulletin-dir>
       
    83 //
       
    84 // This specifies the bulletin (sub)directory for virtual domains.
       
    85 //
       
    86 // =back
       
    87 
       
    88 // SEE ALSO
       
    89 //
       
    90 // vmailmgr(7),
       
    91 // checkvpw(8),
       
    92 // configuration.html
       
    93 
       
    94 // NOTES
       
    95 //
       
    96 // If either the global or local bulletin directories do not exist, they
       
    97 // are silently ignored without failing.
       
    98 
       
    99 #ifndef HAVE_GETHOSTNAME
       
   100 int gethostname(char *name, size_t len);
       
   101 #endif
       
   102 
       
   103 #define FATAL_FAILURES 0
       
   104 
       
   105 #if FATAL_FAILURES
       
   106 
       
   107 static void log(const mystring& msg)
       
   108 {
       
   109   if(!o_quiet)
       
   110     ferr << "vpopbull: " msg << endl;
       
   111 }
       
   112 
       
   113 #define FAIL(X) do{ log(X); return false; }while(0)
       
   114 
       
   115 #else
       
   116 
       
   117 #define FAIL(X) do{ return false; }while(0)
       
   118 
       
   119 #endif
       
   120 
       
   121 static bool make_link(const mystring& linkname, mystring dest)
       
   122 {
       
   123   char host[128];
       
   124   gethostname(host, sizeof host);
       
   125   pid_t pid = getpid();
       
   126   mystring destname;
       
   127   dest += "/";
       
   128   for(;;) {
       
   129     time_t t = time(0);
       
   130     destname = dest + itoa(t) + ".";
       
   131     destname = destname + itoa(pid) + "." + host;
       
   132     struct stat buf;
       
   133     if(stat(destname.c_str(), &buf) == -1 && errno == ENOENT)
       
   134       break;
       
   135     sleep(2);
       
   136   }
       
   137   if(symlink(linkname.c_str(), destname.c_str()) == -1)
       
   138     FAIL("Could not make symbolic link.");
       
   139   return true;
       
   140 }
       
   141 
       
   142 static bool link_file(const mystring& bulldir,
       
   143 		      const mystring& filename,
       
   144 		      const mystring& destdir)
       
   145 {
       
   146   mystring src;
       
   147   if(bulldir[0] == '/')
       
   148     // simple symbolic link
       
   149     src = bulldir + "/" + filename;
       
   150   else {
       
   151     int i = -1;
       
   152     while((i = destdir.find_first('/', i+1)) > 0)
       
   153       src += "../";
       
   154     src = src + bulldir + filename;
       
   155   }
       
   156   return make_link(src, destdir);
       
   157 }
       
   158 
       
   159 static time_t maildir_time;
       
   160 
       
   161 static time_t stat_mtime(const mystring& path)
       
   162 {
       
   163   struct stat statbuf;
       
   164   if(stat(path.c_str(), &statbuf) == -1)
       
   165     return 0;
       
   166   else
       
   167     return statbuf.st_mtime;
       
   168 }
       
   169 
       
   170 static bool scan_file(const mystring& bulldir,
       
   171 		      const mystring& filename,
       
   172 		      const mystring& destdir)
       
   173 {
       
   174   mystring fullname = bulldir + "/" + filename;
       
   175   time_t mtime = stat_mtime(fullname);
       
   176   if(!mtime)
       
   177     FAIL("Can't stat bulletin '" + fullname + "'.");
       
   178   if(maildir_time < mtime)
       
   179     return link_file(bulldir, filename, destdir);
       
   180   return true;
       
   181 }
       
   182 
       
   183 static bool scan_bulletins(const mystring& destdir, const mystring& bulldir)
       
   184 {
       
   185   DIR* dir = opendir(bulldir.c_str());
       
   186   // Do not fail if the directory does not exist.
       
   187   if(!dir)
       
   188     FAIL("Can't open bulletin directory '" + bulldir + "'.");
       
   189   dirent* entry;
       
   190   while((entry = readdir(dir)) != 0) {
       
   191     if(entry->d_name[0] == '.')
       
   192       continue;
       
   193     if(!scan_file(bulldir, entry->d_name, destdir))
       
   194       return false;
       
   195   }
       
   196   closedir(dir);
       
   197   return true;
       
   198 }
       
   199 
       
   200 static void stat_maildir(const mystring& maildir)
       
   201 {
       
   202   mystring timestamp = maildir + "/.timestamp";
       
   203   maildir_time = stat_mtime(timestamp);
       
   204   int fd = open(timestamp.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0600);
       
   205   close(fd);
       
   206 }
       
   207   
       
   208 static bool scan_bulletins(const mystring& maildir, bool non_virtual)
       
   209 {
       
   210   stat_maildir(maildir);
       
   211   mystring dir = maildir + "/new";
       
   212 #if FATAL_FAILURES
       
   213   return scan_bulletins(dir, config->global_bulletin_dir()) &&
       
   214     (non_virtual || scan_bulletins(dir, config->bulletin_dir()));
       
   215 #else
       
   216   scan_bulletins(dir, config->global_bulletin_dir());
       
   217   if(!non_virtual)
       
   218     scan_bulletins(dir, config->bulletin_dir());
       
   219   return true;
       
   220 #endif
       
   221 }
       
   222 
       
   223 int cli_main(int, char*[])
       
   224 {
       
   225   if(!go_home())
       
   226     return 1;
       
   227   mystring maildir = getenv("MAILDIR");
       
   228   if(!maildir) {
       
   229     if(!o_quiet)
       
   230       ferr << "vpopbull: MAILDIR is not set." << endl;
       
   231     return 1;
       
   232   }
       
   233   mystring vuser = getenv("VUSER");
       
   234   return scan_bulletins(maildir, !vuser) ? 0 : 1;
       
   235 }