bincimap

Log | Files | Refs | LICENSE

session.cc (17524B)


      1 /* -*- Mode: c++; -*- */
      2 /*  --------------------------------------------------------------------
      3  *  Filename:
      4  *    session.cc
      5  *  
      6  *  Description:
      7  *    <--->
      8  *
      9  *  Authors:
     10  *    Andreas Aardal Hanssen <andreas-binc curly bincimap spot org>
     11  *
     12  *  Bugs:
     13  *
     14  *  ChangeLog:
     15  *
     16  *  --------------------------------------------------------------------
     17  *  Copyright 2002-2005 Andreas Aardal Hanssen
     18  *
     19  *  This program is free software; you can redistribute it and/or modify
     20  *  it under the terms of the GNU General Public License as published by
     21  *  the Free Software Foundation; either version 2 of the License, or
     22  *  (at your option) any later version.
     23  *
     24  *  This program is distributed in the hope that it will be useful,
     25  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     26  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     27  *  GNU General Public License for more details.
     28  *
     29  *  You should have received a copy of the GNU General Public License
     30  *  along with this program; if not, write to the Free Software
     31  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
     32  *  --------------------------------------------------------------------
     33  */
     34 #ifdef HAVE_CONFIG_H
     35 #include <config.h>
     36 #endif
     37 
     38 #include <syslog.h>
     39 
     40 #include "argparser.h"
     41 #include "io.h"
     42 #include "session.h"
     43 #include "storage.h"
     44 #include "tools.h"
     45 #include "convert.h"
     46 #include <string>
     47 #include <map>
     48 
     49 using namespace ::std;
     50 using namespace Binc;
     51 
     52 extern char **environ;
     53 
     54 //----------------------------------------------------------------------
     55 Session::Session(void)
     56 {
     57   readbytes = 0;
     58   writebytes = 0;
     59   statements = 0;
     60   bodies = 0;
     61   idletimeout = 0;
     62   authtimeout = 0;
     63   mailboxchanges = true;
     64   logfacility = LOG_DAEMON;
     65 }
     66 
     67 //----------------------------------------------------------------------
     68 Session &Session::getInstance(void)
     69 {
     70   static Session session;
     71   return session;
     72 }
     73 
     74 //----------------------------------------------------------------------
     75 void Session::initConfig(void)
     76 {
     77 }
     78 
     79 //----------------------------------------------------------------------
     80 const int Session::getState(void) const
     81 {
     82   return state;
     83 }
     84 
     85 //----------------------------------------------------------------------
     86 void Session::setState(int n)
     87 {
     88   state = n;
     89 }
     90 
     91 //----------------------------------------------------------------------
     92 const string &Session::getUserID(void) const
     93 {
     94   return userid;
     95 }
     96 
     97 //----------------------------------------------------------------------
     98 void Session::setUserID(const string &s)
     99 {
    100   userid = s;
    101 }
    102 
    103 //----------------------------------------------------------------------
    104 const string &Session::getIP(void) const
    105 {
    106   return ip;
    107 }
    108 
    109 //----------------------------------------------------------------------
    110 void Session::setIP(const string &s)
    111 {
    112   ip = s;
    113 }
    114 
    115 //----------------------------------------------------------------------
    116 void Session::setLogFacility(int facility)
    117 {
    118   logfacility = facility;
    119 }
    120 
    121 //----------------------------------------------------------------------
    122 int Session::getLogFacility(void) const
    123 {
    124   return logfacility;
    125 }
    126 
    127 //----------------------------------------------------------------------
    128 void Session::addBody(void)
    129 {
    130   ++bodies;
    131 }
    132 
    133 //----------------------------------------------------------------------
    134 void Session::addStatement(void)
    135 {
    136   ++statements;
    137 }
    138 
    139 //----------------------------------------------------------------------
    140 void Session::addReadBytes(int i)
    141 {
    142   readbytes += i;
    143 }
    144 
    145 //----------------------------------------------------------------------
    146 void Session::addWriteBytes(int i)
    147 {
    148   writebytes += i;
    149 }
    150 
    151 //----------------------------------------------------------------------
    152 int Session::getBodies(void) const
    153 {
    154   return bodies;
    155 }
    156 
    157 //----------------------------------------------------------------------
    158 int Session::getStatements(void) const
    159 {
    160   return statements;
    161 }
    162 
    163 //----------------------------------------------------------------------
    164 int Session::getWriteBytes(void) const
    165 {
    166   return writebytes;
    167 }
    168 
    169 //----------------------------------------------------------------------
    170 int Session::getReadBytes(void) const
    171 {
    172   return readbytes;
    173 }
    174 
    175 //----------------------------------------------------------------------
    176 bool Session::parseRequestLine(int argc, char * argv[])
    177 {
    178   args.addOptional("h|?|help", "Display this help screen", true);
    179   args.addOptional("v|version", "Display the version of Binc IMAP", true);
    180   args.addOptional("s|ssl", "Toggle enabling of SSL", true);
    181   args.addOptional("c|conf", "Sets the path to the config file", false);
    182   args.addOptional("a|allow-plain", "Allow authentication when not in SSL", true);
    183   args.addOptional("auth-penalty", "Sets the auth penalty", false);
    184   args.addOptional("d|disable-starttls", "Toggles disabling of STARTTLS", false);
    185   args.addOptional("L|logtype", "Sets the method used for logging", false);
    186   args.addOptional("I|ip-variable", "Sets the env variable that contains the remote IP", false);
    187   args.addOptional("d|depot", "Sets the depot type", false);
    188   args.addOptional("M|mailbox-type", "Sets the mailbox tyoe", false);
    189   args.addOptional("m|mailbox-path", "Sets the mailbox path", false);
    190   args.addOptional("C|create-inbox", "Toggles auto-creating INBOX", false);
    191   args.addOptional("S|subscribe-mailboxes", "CSV list of mailboxes to subscribe to", false);
    192   args.addOptional("u|umask", "Sets the default umask", false);
    193   args.addOptional("J|jail-path", "Sets the jail path", false);
    194   args.addOptional("x|jail-user", "Sets the jail user", false);
    195   args.addOptional("X|jail-group", "Sets the jail group", false);
    196   args.addOptional("i|idle-timeout", "Sets the idle timeout", false); 
    197   args.addOptional("t|auth-timeout", "Sets the auth timeout", false); 
    198   args.addOptional("T|transfer-timeout", "Sets the transfer timeout", false); 
    199   args.addOptional("b|transfer-buffersize", "Sets the transfer buffer size", false); 
    200   args.addOptional("p|pem-file", "Sets the path to the SSL PEM file", false);
    201   args.addOptional("P|ca-path", "Sets the path to the CA cert file", false);
    202   args.addOptional("f|ca-file", "Sets the path to the CA cert directory", false);
    203   args.addOptional("l|cipher-list", "Sets the SSL cipher list", false);
    204   args.addOptional("V|verify-peer", "Toggles peer verificatin", true);
    205 
    206   if (!args.parse(argc, argv)) {
    207     setLastError("Command line error, " + args.errorString());
    208     return false;
    209   }
    210 
    211   command.help = args["help"] == "yes" ? true : false;
    212   command.version = args["version"] == "yes" ? true : false;
    213   command.ssl = args["ssl"] == "yes" ? true : false;
    214   command.configfile = args["conf"];
    215 
    216   unparsedArgs = argv + args.argc();
    217 
    218   return true;
    219 }
    220 
    221 //----------------------------------------------------------------------
    222 void Session::assignCommandLineArgs(void)
    223 {
    224   if (args.hasArg("allow-plain"))
    225     globalconfig["Authentication"]["allow plain auth in non ssl"] = args["allow-plain"];
    226 
    227   if (args.hasArg("auth-penalty"))
    228     globalconfig["Authentication"]["auth penalty"] = args["auth-penalty"];
    229 
    230   if (args.hasArg("disable-starttls"))
    231     globalconfig["Authentication"]["disable starttls"] = args["disable-starttls"];
    232 
    233   if (args.hasArg("logtype"))
    234     globalconfig["Log"]["type"] = args["logtype"];
    235 
    236   if (args.hasArg("ip-variable"))
    237     globalconfig["Log"]["environment ip variable"] = args["ip-variable"];
    238 
    239   if (args.hasArg("depot"))
    240     globalconfig["Mailbox"]["depot"] = args["depot"];
    241 
    242   if (args.hasArg("mailbox-type"))
    243     globalconfig["Mailbox"]["type"] = args["mailbox-type"];
    244 
    245   if (args.hasArg("mailbox-path"))
    246     globalconfig["Mailbox"]["path"] = args["mailbox-path"];
    247 
    248   if (args.hasArg("create-inbox"))
    249     globalconfig["Mailbox"]["auto create inbox"] = args["create-inbox"];
    250 
    251   if (args.hasArg("subscribe-mailboxes"))
    252     globalconfig["Mailbox"]["auto subscribe mailboxes"] = args["subscribe-mailboxes"];
    253 
    254   if (args.hasArg("umask"))
    255     globalconfig["Mailbox"]["umask"] = args["umask"];
    256 
    257   if (args.hasArg("jail-path"))
    258     globalconfig["Security"]["jail path"] = args["jail-path"];
    259 
    260   if (args.hasArg("jail-user"))
    261     globalconfig["Security"]["jail user"] = args["jail-user"];
    262 
    263   if (args.hasArg("jail-group"))
    264     globalconfig["Security"]["jail group"] = args["jail-group"];
    265 
    266   if (args.hasArg("idle-timeout"))
    267     globalconfig["Session"]["idle timeout"] = args["idle-timeout"];
    268 
    269   if (args.hasArg("auth-timeout"))
    270     globalconfig["Session"]["auth timeout"] = args["auth-timeout"];
    271 
    272   if (args.hasArg("transfer-timeout"))
    273     globalconfig["Session"]["transfer timeout"] = args["transfer-timeout"];
    274 
    275   if (args.hasArg("transfer-buffersize"))
    276     globalconfig["Session"]["transfer buffer size"] = args["transfer-buffersize"];
    277 
    278   if (args.hasArg("pem-file"))
    279     globalconfig["SSL"]["pem file"] = args["pem-file"];
    280 
    281   if (args.hasArg("ca-path"))
    282     globalconfig["SSL"]["ca path"] = args["ca-path"];
    283 
    284   if (args.hasArg("ca-file"))
    285     globalconfig["SSL"]["ca file"] = args["ca-file"];
    286 
    287   if (args.hasArg("cipher-list"))
    288     globalconfig["SSL"]["cipher list"] = args["cipher-list"];
    289 
    290   if (args.hasArg("verify-peer"))
    291     globalconfig["SSL"]["verify peer"] = args["verify-peer"];
    292 }
    293 
    294 //----------------------------------------------------------------------
    295 void Session::add(const string &a, const string &b)
    296 {
    297   attrs[a] = b;
    298 }
    299 
    300 //----------------------------------------------------------------------
    301 const string &Session::operator [] (const string &s_in) const
    302 {
    303   static const string NIL = "";
    304   if (attrs.find(s_in) == attrs.end())
    305     return NIL;
    306   else
    307     return attrs.find(s_in)->second;
    308 }
    309 
    310 //----------------------------------------------------------------------
    311 void Session::exportToEnv(void)
    312 {
    313   Tools &tools = Tools::getInstance();
    314 
    315   tools.setenv("BINCIMAP_STATE", toString(state));
    316   tools.setenv("BINCIMAP_USERID", userid);
    317   tools.setenv("BINCIMAP_IP", ip);
    318 
    319   for (map<string, string>::const_iterator i = attrs.begin();
    320        i != attrs.end(); ++i)
    321     tools.setenv("BINCIMAP_CONF_" + i->first, i->second);
    322 
    323   int n = 0;
    324   for (vector<string>::const_iterator i = subscribed.begin();
    325        i != subscribed.end(); ++i, ++n)
    326     tools.setenv("BINCIMAP_SUBSCRIBED_" + toString(n), *i);
    327 
    328   map<string, map<string, string> >::const_iterator gi = globalconfig.begin();
    329   for (; gi != globalconfig.end(); ++gi) {
    330     map<string, string>::const_iterator ji = gi->second.begin();
    331     for (; ji != gi->second.end(); ++ji)
    332       tools.setenv("BINCIMAP_GLOBALCONFIG_" 
    333 		   + toHex(gi->first) + "::" + toHex(ji->first),
    334 		   toHex(ji->second));
    335   }
    336 
    337   map<string, map<string, string> >::const_iterator li = localconfig.begin();
    338   for (; li != localconfig.end(); ++li) {
    339     map<string, string>::const_iterator ji = li->second.begin();
    340     for (; ji != li->second.end(); ++ji) {
    341       tools.setenv("BINCIMAP_GLOBALCONFIG_" 
    342 		   + toHex(li->first) + "::" + toHex(ji->first),
    343 		   toHex(ji->second));
    344     }
    345   }
    346 }
    347 
    348 //----------------------------------------------------------------------
    349 void Session::importFromEnv(void)
    350 {
    351   char *c;
    352   int cnt = 0;
    353   while ((c = environ[cnt]) != 0) {
    354     string s = c;
    355 
    356     if (s.substr(0, 14) == "BINCIMAP_STATE") state = atoi(s.substr(15));
    357     else if (s.substr(0, 15) == "BINCIMAP_USERID") userid = s.substr(16);
    358     else if (s.substr(0, 11) == "BINCIMAP_IP") ip = s.substr(12);
    359     else if (s.substr(0, 21) == "BINCIMAP_GLOBALCONFIG") {
    360       const string config = s.substr(22);
    361       if (config.find("::") != string::npos) {
    362 	const string section = fromHex(config.substr(0, config.find("::")));
    363 	const string data = config.substr(config.find("::") + 2);
    364 	
    365 	if (data.find("=") != string::npos) {
    366 	  const string key = fromHex(data.substr(0, data.find("=")));
    367 	  const string value = fromHex(data.substr(data.find("=") + 1));
    368 
    369 	  globalconfig[section][key] = value;
    370 	}
    371       }
    372     } else if (s.substr(0, 20) == "BINCIMAP_LOCALCONFIG") {
    373       const string config = s.substr(21);
    374       if (config.find("::") != string::npos) {
    375 	const string section = fromHex(config.substr(0, config.find("::")));
    376 	const string data = config.substr(config.find("::") + 2);
    377 	
    378 	if (data.find("=") != string::npos) {
    379 	  const string key = fromHex(data.substr(0, data.find("=")));
    380 	  const string value = fromHex(data.substr(data.find("=") + 1));
    381 
    382 	  localconfig[section][key] = value;
    383 	}
    384       }
    385     }
    386 
    387     ++cnt;
    388   }
    389 }
    390 
    391 //----------------------------------------------------------------------
    392 const string &Session::getLastError(void) const
    393 {
    394   return lastError;
    395 }
    396 
    397 //----------------------------------------------------------------------
    398 void Session::setLastError(const string &error) const
    399 {
    400   lastError = error;
    401 }
    402 
    403 //----------------------------------------------------------------------
    404 const string &Session::getResponseCode(void) const
    405 {
    406   return responseCode;
    407 }
    408 
    409 //----------------------------------------------------------------------
    410 void Session::setResponseCode(const string &code) const
    411 {
    412   responseCode = "[" + code + "] ";
    413 }
    414 
    415 //----------------------------------------------------------------------
    416 void Session::clearResponseCode(void) const
    417 {
    418   responseCode = "";
    419 }
    420 
    421 //----------------------------------------------------------------------
    422 pid_t Session::getPid(void)
    423 {
    424   if (pid == 0)
    425     pid = getpid();
    426 
    427   return pid;
    428 }
    429 
    430 //----------------------------------------------------------------------
    431 const std::string &Session::getHostname(void)
    432 {
    433   if (hostname == "") {
    434     char hostnamec[512];
    435     int hostnamelen = gethostname(hostnamec, sizeof(hostnamec));
    436     if (hostnamelen == -1 || hostnamelen == sizeof(hostnamec))
    437       strcpy(hostnamec, "localhost");
    438     hostnamec[511] = '\0';
    439 
    440     char *c;
    441     while ((c = strchr(hostnamec, '/')) != 0) *c = '\057';
    442     while ((c = strchr(hostnamec, ':')) != 0) *c = '\072';
    443     
    444     hostname = hostnamec;
    445   }
    446 
    447   return hostname;
    448 }
    449 
    450 //----------------------------------------------------------------------
    451 void Session::subscribeTo(const std::string mailbox)
    452 {
    453   for (vector<string>::iterator i = subscribed.begin();
    454        i != subscribed.end(); ++i) {
    455     if (*i == mailbox)
    456       return;
    457   }
    458 
    459   subscribed.push_back(mailbox);
    460 }
    461 
    462 //----------------------------------------------------------------------
    463 bool Session::unsubscribeTo(const std::string mailbox)
    464 {
    465   for (vector<string>::iterator i = subscribed.begin();
    466        i != subscribed.end(); ++i) {
    467     if (*i == mailbox) {
    468       subscribed.erase(i);
    469       return true;
    470     }
    471   }
    472 
    473   return false;
    474 }
    475 
    476 //----------------------------------------------------------------------
    477 void Session::loadSubscribes(void)
    478 {
    479   // drop all existing subscribed folders.
    480   subscribed.clear();
    481 
    482   // try loading the .bincimap-subscribed file
    483   bool ok = false;
    484   FILE *fp = fopen(".bincimap-subscribed", "r");
    485   map<string, bool> addedEntries;
    486   if (fp) {
    487     int c;
    488     string current;
    489     while ((c = fgetc(fp)) != EOF) {
    490       if (c == '\n') {
    491 	if (current != "") {
    492 	  if (addedEntries.find(current) == addedEntries.end()) {
    493 	    subscribed.push_back(current);
    494 	    addedEntries[current] = true;
    495 	  }
    496 
    497 	  current = "";
    498 	}
    499       } else
    500 	current += c;
    501     }
    502 
    503     fclose(fp);
    504     ok = true;
    505   }
    506 
    507   bool deleteOld = false;
    508   if (!ok) {
    509     // load subscription list. if it doesn't exist - initialize it with
    510     // the values from the auto subscribe list.
    511     Storage subs("bincimap-subscribed", Storage::ReadOnly);
    512     string section, key, value;
    513    
    514     while (subs.get(&section, &key, &value))
    515       if (section == "subscribed")
    516 	subscribed.push_back(value);
    517   
    518     if (subs.eof()) {
    519       ok = true;
    520       deleteOld = true;
    521     }
    522   }
    523 
    524   if (!ok) {
    525     string autosubmailboxes = globalconfig["Mailbox"]["auto subscribe mailboxes"];
    526     vector<string> mboxes;
    527     split(autosubmailboxes, ",", mboxes);
    528     
    529     for (vector<string>::const_iterator i = mboxes.begin(); i != mboxes.end();
    530 	 ++i) {
    531       string tmp = *i;
    532       trim(tmp);
    533       subscribed.push_back(tmp);
    534     }
    535     
    536     saveSubscribes();
    537   }
    538 
    539   if (deleteOld) {
    540     if (saveSubscribes())
    541       unlink("bincimap-subscribed");
    542   }
    543 }
    544 
    545 //----------------------------------------------------------------------
    546 bool Session::saveSubscribes(void) const
    547 {
    548   IO &logger = IOFactory::getInstance().get(2);
    549 
    550   // create a safe file name
    551   string tpl = ".bincimap-subscribed-tmp-XXXXXX";
    552   char *ftemplate = new char[tpl.length() + 1];
    553 
    554   strcpy(ftemplate, tpl.c_str());
    555   int fd = mkstemp(ftemplate);
    556   if (fd == -1) {
    557     logger << "unable to create temporary file \""
    558 	   << tpl << "\"" << endl;
    559     delete [] ftemplate;
    560     return false;
    561   }
    562 
    563   map<string, bool> addedEntries;
    564   for (vector<string>::const_iterator i = subscribed.begin();
    565        i != subscribed.end(); ++i) {
    566     if (addedEntries.find(*i) == addedEntries.end()) {
    567       addedEntries[*i] = true;
    568       string w = (*i) + "\n";
    569       if (write(fd, w.c_str(), w.length()) != (ssize_t) w.length()) {
    570 	logger << "failed to write to " << tpl << ": "
    571 	     << strerror(errno) << endl;
    572 	break;
    573       }
    574     }
    575   }
    576   
    577   if (fsync(fd) != 0 && errno != EINVAL || close(fd) != 0) {
    578       logger << "failed to close " << ftemplate 
    579 	     << ": " << strerror(errno) << endl;
    580       delete [] ftemplate;
    581       return false;
    582   }
    583  
    584   if (rename(ftemplate, ".bincimap-subscribed") != 0) {
    585       logger << "failed to rename " << ftemplate 
    586 	     << " to .bincimap-subscribed: "
    587 	     << strerror(errno) << endl;
    588       delete [] ftemplate;
    589       return false;
    590   }
    591 
    592   delete [] ftemplate;
    593   return true;
    594 }
    595 
    596 //----------------------------------------------------------------------
    597 int Session::timeout() const
    598 {
    599   return state == NONAUTHENTICATED ? authtimeout : idletimeout;
    600 }