Logo Search packages:      
Sourcecode: mailfilter version File versions  Download package

mailfilter.cc

// mailfilter.cc - source file for the mailfilter program
// Copyright (c) 2000 - 2004  Andreas Bauer <baueran@in.tum.de>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.

#include <string>
#include <iostream>
#include <fstream>
#include <csignal>
#include <stdexcept>
#include <FlexLexer.h>

extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <netdb.h>
#include <setjmp.h>
}

#ifdef HAVE_GETOPT_H
extern "C" {
#include <getopt.h>
}
#else
extern "C" {
#include <stdlib.h>
#include "getopt.h"
}
#endif

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "mailfilter.hh"
#include "Preferences.hh"
#include "Feedback.hh"
#include "PopAccount.hh"
#include "Account.hh"
#include "RFC822.hh"
#include "i18n.hh"
#include "rcparser.h"

using namespace std;

// C-ish declarations, brr...
extern "C"
{
  int yyparse(void*);
}
void connectSigint (int signo);

FlexLexer* lexer;

int main (int argc, char *argv[]) {
  int error = 0;
  int c = 0;
  int option_index = 0;
  int verboseMode = -1;
  int returnValue = 0;
  struct timeval tv;
  struct timezone tz;
  string mailfilterrc, logfile;
  time_t now;
  string prefsFile;
  extern char *optarg;
  struct sigaction sigact;
  bool testMode = false;
  bool doReturn = false;
  
  static struct option long_options[] =
    {
      {"help", 0, NULL, VALUE_HELP},
      {"verbose", 1, NULL, VALUE_VERBOSE},
      {"mailfilterrc", 1, NULL, VALUE_MAILFILTERRC},
      {"logfile", 1, NULL, VALUE_LOGFILE},
      {"version", 0, NULL, VALUE_VERSION},
      {"test", 0, NULL, VALUE_TEST},
      {"return-value", 0, NULL, VALUE_RETURN},
      {0, 0, 0, 0}
    };

#ifdef HAVE_GETTEXT
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE_NAME, LOCALEDIR);
  textdomain (PACKAGE_NAME);
#endif

  // Scan command line parameters and options
  while ((c = getopt_long (argc, 
                     argv, 
                     "hL:M:Vv:tr",
                     long_options, 
                     &option_index))
       != -1)
    {
      switch (c)
      {
      case 'h':
      case VALUE_HELP:
        cout << _("Mailfilter filters e-mail and removes spam in one or many POP accounts.") << endl;
        cout << endl;
        cout << _("Usage: ") << PACKAGE_NAME << _(" [OPTION]...") << endl;
        cout << endl;
        cout << _("If a long option shows an argument as mandatory, then it is mandatory") << endl;
        cout << _("for the equivalent short option also.") << endl;
        cout << endl;
        cout << _("Options:") << endl;
        cout << _("  -h, --help                 Display this help information") << endl;
        cout << _("  -L, --logfile=FILE         Specify logfile location") << endl;
        cout << _("  -M, --mailfilterrc=FILE    Specify rcfile location") << endl;
        cout << _("  -r, --return-value         Enable additional return values") << endl;
        cout << _("  -t, --test                 Simulate deletes") << endl;
        cout << _("  -v, --verbose=LEVEL        Specify level of verbosity") << endl;
        cout << _("  -V, --version              Display version information") << endl;
        cout << endl;
        cout << _("Report bugs to <baueran@users.sourceforge.net>.") << endl;
        return 0;
        break;
      case 't':
      case VALUE_TEST:
        testMode = true;
        break;
      case 'r':
      case VALUE_RETURN:
        doReturn = true;
        break;
      case 'v':
      case VALUE_VERBOSE:
        verboseMode = atoi (optarg);
        break;
      case 'V':
      case VALUE_VERSION:
        cout << PACKAGE_NAME << " " << PACKAGE_VERSION << endl;
        cout << endl;
        cout << _("Copyright (c) 2000 - 2004  Andreas Bauer  <baueran@users.sourceforge.net>") << endl;
        cout << endl;
        cout << _("This is free software; see the source for copying conditions.  There is NO") << endl;
        cout << _("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl;
        return 0;
        break;
      case 'M':
      case VALUE_MAILFILTERRC:
        mailfilterrc = optarg;
        break;
      case 'L':
      case VALUE_LOGFILE:
        logfile = optarg;
        break;
      default:
        // Unrecognized option
        cerr << _("Try '") << argv[0] << _(" --help' for more information.") << endl;
        return -1;
      }
    }
  
  try {
    // Locate .mailfilterrc depending on whether the user gave one on the command line or not
    if (!mailfilterrc.length ()) {
      if (getenv ("HOME")) {
      ifstream checkRcfile(((string) (prefsFile = 
                              (string) getenv ("HOME") + 
                              (string) "/.mailfilterrc")).c_str ());
      
      // Windows people have trouble with the leading dot
      // so here's an extra check, in case .mailfilterrc
      // can't be located in the user's home directory
      if (!checkRcfile.is_open ())
        prefsFile = (string) getenv ("HOME") + (string) "/_mailfilterrc";
      
      checkRcfile.close ();
      }
      else
      throw pref::IOException ();
    }
    else
      prefsFile = mailfilterrc.c_str ();
    
    // Set sigint signal handler
    sigact.sa_handler = connectSigint;
    sigemptyset (&sigact.sa_mask);
    sigact.sa_flags = 0;
    
    if (sigaction (SIGINT, &sigact, NULL) < 0) {
      cerr << (string)PACKAGE_NAME << (string)_(": Error: Signal handler could not be installed.") << endl;
      exit(-1);
    }

    // Open the rcfile and set some preferences
    ifstream rcfile(prefsFile.c_str ());
    pref::Preferences prefs (prefsFile);

    if ( rcfile.is_open() ) {
      // Was a logfile specified on the command line?
      if (logfile.length ())
      prefs.setLogfile (logfile.c_str ());

      // Start the scanner + parser
      lexer = new yyFlexLexer;
      lexer->switch_streams (&rcfile);
      yyparse ((void*) &prefs);
      rcfile.close ();
      delete lexer;

      // Eventually override level of verbosity, before mailfilter starts
      if (verboseMode >= 0) {
      if (verboseMode >= MIN_VERBOSE && verboseMode <= MAX_VERBOSE)
        prefs.setVerboseLevel(verboseMode);
      else {
        cerr << (string)PACKAGE_NAME << (string)_(": Error: The level of verbosity must contain values between ");
        cerr << MIN_VERBOSE;
        cerr << (string)_(" and ");
        cerr << MAX_VERBOSE;
        cerr << "." << endl;
        return -1;
      }
      }
    }
    else
      throw pref::IOException();

    // Eventually override testing-flag, before mailfilter starts
    if (testMode)
      prefs.setTestMode (true);

    // Precompile expressions (filters, friends lists)
    if (prefs.precompileExpressions () != 0)
      throw pref::RegExException ();
    else
      prefs.setCompiled (true);

    // Prepare the mailfilter Feedback module for error messages and general output
    fb::Feedback report(prefs.getLogfile(), prefs.getVerboseLevel());
    vector <pref :: serverInfo> :: const_iterator curServer = prefs.getServers ().begin();
    vector <string> messageIDs;

    // Create separate account objects that can also be checked separately
    while (curServer != prefs.getServers ().end ()) {
      acc::PopAccount curAccount (curServer->name,
                          curServer->user,
                          curServer->pass,
                          curServer->protocol,
                          curServer->port,
                          &prefs,
                          &report);
      gettimeofday (&tv, &tz);
      now = tv.tv_sec;
      
      if (prefs.getVerboseLevel () <= 3) {
      report.message(3, (string) PACKAGE_NAME + (string) ": " + (string) PACKAGE_VERSION + (string) _(" querying ") +
                   (string)curServer->name.c_str() + (string)_(" on ") + (string)ctime(&now));
      }      
      else {
      report.message(4, (string)PACKAGE_NAME + (string)": " + (string)PACKAGE_VERSION + (string)_(" querying ") + (string)curServer->user.c_str() + (string)"@" +
                   (string)curServer->name.c_str() + (string)_(" on ") + (string)ctime(&now));
      }
      
      // Add previous message IDs to the list in case we want to check for duplicate mails
      curAccount.addMessageIDs(messageIDs);

      // Now log into account and kill all the spam and set the program's return value:
      // 0 if no messages were scanned, a positive integer otherwise.
      if ((error = curAccount.check()))
      returnValue = error;

      // Try to get all the message IDs of mails stored in curAccount
      messageIDs = curAccount.getMessageIDs();

      if (error == -1)
      report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Could not login to server ") +
                   (string)curServer->name.c_str() + (string)_(". Unknown error.\n"));
      else if (error == DNS_LOOKUP_FAILURE)
      report.message(1, (string)PACKAGE_NAME + (string)_(": Error: DNS lookup failure for ") + (string)curServer->name.c_str() + (string)_(". Login failed.\n"));
      else if (error == SOCKET_CONNECTION_FAILURE)
      report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Network connection could not be established.\n"));
      else if (error == AUTHENTICATION_FAILURE)
      report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Authentication failed. Login canceled.\n"));
      else if (error == NO_REPLY_FAILURE)
      report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Mail server not responding. Login canceled.\n"));      
      else if (error == SOCKET_COMMUNICATION_FAILURE)
      report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Could not establish mail server connection.\n"));
      else if (error == CMD_STAT_FAILED)
      report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Sent STAT, but server responded with an error.\n"));
      else if (error == CMD_TOP_FAILED)
      report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Sent TOP, but server responded with an error.\n"));
      else if (error == CMD_LIST_FAILED)
      report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Sent LIST, but server responded with an error.\n"));
      else if (error == CMD_DELE_FAILED)
      report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Sent DELE, but server responded with an error. Delete failed.\n"));
      else if (error == MALFORMED_HEADER_FAILURE) {
      report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Encountered a malformed e-mail header which could not be handled by Mailfilter. ") +
                   (string)_("Program aborted.\n"));
      }
      else if (error == ALARM_FAILURE)
            report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Operation timed out.\n"));
      else if (error == SIGNAL_FAILURE)
            report.message(1, (string)PACKAGE_NAME + (string)_(": Error: Signal handler could not be installed.\n"));
      
      curServer++;
      error = 0;
    }
  }
  catch (pref::MalformedPrefsFile) {
    cerr << PACKAGE_NAME << _(": Error: The rcfile for preferences has wrong number of arguments or malformed syntax.") << endl;
    return -1;
  }
  catch (pref::IOException) {
    cerr << PACKAGE_NAME << _(": Error: The rcfile for preferences (usually $HOME/.mailfilterrc) could not be read.") << endl;
    return -1;
  }
  catch (pref::RegExException) {
    cerr << PACKAGE_NAME << _(": Error: Aborted pre-compilation of Regular Expressions. Check the syntax of your filters and rules.") << endl;
    return -1;
  }  
  catch (fb::LogfileAccess) {
    cerr << PACKAGE_NAME << _(": Error: Could not access the logfile. Check the file permissions of your logfile and make sure the rcfile contains the correct path.") << endl;
    return -1;
  }
  catch (conn::IOException) {
    cerr << PACKAGE_NAME << _(": Error: Communication failure. Either the server closed the connection due to a time-out problem, or the entire network connection has dropped.") << endl;
    return -1;
  }
  catch (conn::ConnectionTimeOut) {
    cerr << PACKAGE_NAME << _(": Error: Connection to the mail server has timed out.") << endl;
    return -1;
  }
  catch (acc::UnknownError) {
    cerr << PACKAGE_NAME << _(": Error: Unknown communication error occured. Please consider reporting a bug. Thank you.") << endl;
    return -1;
  }
  catch (rfc::MalformedHeader) {
    cerr << PACKAGE_NAME << _(": Error: Malformed e-mail header.") << endl;
    return -1;
  }
  catch (runtime_error &re) {
    cerr << PACKAGE_NAME << _(": Error: ") << re.what() << "." << endl;
    return -1;
  }
  catch (...) {
    cerr << PACKAGE_NAME << _(": Error: This is an unknown error. Please consider reporting a bug. Thank you.") << endl;
    return -1;
  }

  return doReturn? returnValue : 0;
}


//! Sigup signal call-back function
void connectSigint(int signo) {
  sigset_t maskSet;
  sigset_t oldSet;
  
  // Mask other signals while we prompt to cerr and finally quit
  signal(SIGINT, connectSigint);
  sigfillset(&maskSet);
  sigprocmask(SIG_SETMASK, &maskSet, &oldSet);

  // Prompt error
  cerr << PACKAGE_NAME << _(": Signal SIGINT caught. Terminating with signal ") << signo << (string)"." << endl;
  
  // Quit program
  exit(0);
}

Generated by  Doxygen 1.6.0   Back to index