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(§ion, &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 }