depot.cc (15142B)
1 /* -*- Mode: c++; -*- */ 2 /* -------------------------------------------------------------------- 3 * Filename: 4 * depot.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 #include <map> 35 #include <string> 36 37 #include "depot.h" 38 #include "mailbox.h" 39 #include "status.h" 40 #include "convert.h" 41 #include "io.h" 42 43 using namespace ::std; 44 using namespace Binc; 45 46 //-------------------------------------------------------------------- 47 DepotFactory::DepotFactory(void) 48 { 49 } 50 51 //-------------------------------------------------------------------- 52 DepotFactory::~DepotFactory(void) 53 { 54 for (vector<Depot *>::iterator i = depots.begin(); i != depots.end(); 55 ++i) 56 delete *i; 57 } 58 59 //-------------------------------------------------------------------- 60 Depot *DepotFactory::get(const string &name) const 61 { 62 for (vector<Depot *>::const_iterator i = depots.begin(); i != depots.end(); 63 ++i) 64 if ((*i)->getName() == name) 65 return *i; 66 67 return 0; 68 } 69 70 //-------------------------------------------------------------------- 71 void DepotFactory::assign(Depot *depot) 72 { 73 depots.push_back(depot); 74 } 75 76 //-------------------------------------------------------------------- 77 DepotFactory &DepotFactory::getInstance(void) 78 { 79 static DepotFactory depotfactory; 80 return depotfactory; 81 } 82 83 //-------------------------------------------------------------------- 84 Depot::Depot(void) : enditerator(0, 0) 85 { 86 defaultmailbox = 0; 87 selectedmailbox = 0; 88 89 delimiter = '/'; 90 } 91 92 //-------------------------------------------------------------------- 93 Depot::Depot(const string &name) : enditerator(0, 0) 94 { 95 defaultmailbox = 0; 96 selectedmailbox = 0; 97 98 delimiter = '/'; 99 100 this->name = name; 101 } 102 103 //-------------------------------------------------------------------- 104 Depot::~Depot(void) 105 { 106 } 107 108 //-------------------------------------------------------------------- 109 const string &Depot::getLastError(void) const 110 { 111 return lastError; 112 } 113 114 //-------------------------------------------------------------------- 115 void Depot::setLastError(const string &error) const 116 { 117 lastError = error; 118 } 119 120 //-------------------------------------------------------------------- 121 void Depot::assign(Mailbox *m) 122 { 123 for (vector<Mailbox *>::const_iterator i = backends.begin(); 124 i != backends.end(); ++i) 125 if (*i == m) break; 126 127 backends.push_back(m); 128 } 129 130 //-------------------------------------------------------------------- 131 Mailbox *Depot::get(const string &s_in) const 132 { 133 for (vector<Mailbox *>::const_iterator i = backends.begin(); 134 i != backends.end(); ++i) 135 if ((*i)->isMailbox(mailboxToFilename(s_in))) 136 return *i; 137 138 setLastError("No such mailbox " + toImapString(s_in)); 139 return 0; 140 } 141 142 //-------------------------------------------------------------------- 143 bool Depot::setSelected(Mailbox *m) 144 { 145 for (vector<Mailbox *>::const_iterator i = backends.begin(); 146 i != backends.end(); ++i) 147 if (*i == m) { 148 selectedmailbox = m; 149 return true; 150 } 151 152 setLastError("Attempted to select unregistered Mailbox type in Depot"); 153 return false; 154 } 155 156 //-------------------------------------------------------------------- 157 const string &Depot::getName(void) const 158 { 159 return name; 160 } 161 162 //-------------------------------------------------------------------- 163 void Depot::setDelimiter(char c) 164 { 165 delimiter = c; 166 } 167 168 //-------------------------------------------------------------------- 169 const char Depot::getDelimiter(void) const 170 { 171 return delimiter; 172 } 173 174 //-------------------------------------------------------------------- 175 bool Depot::setDefaultType(const string &name) 176 { 177 for (vector<Mailbox *>::const_iterator i = backends.begin(); 178 i != backends.end(); ++i) 179 if ((*i)->getTypeName() == name) { 180 defaultmailbox = *i; 181 return true; 182 } 183 184 setLastError("attempt to default to unregistered Mailbox type " + name); 185 return false; 186 } 187 188 //-------------------------------------------------------------------- 189 Mailbox *Depot::getSelected(void) const 190 { 191 return selectedmailbox; 192 } 193 194 //-------------------------------------------------------------------- 195 void Depot::resetSelected(void) 196 { 197 selectedmailbox = 0; 198 } 199 200 //-------------------------------------------------------------------- 201 Mailbox *Depot::getDefault(void) const 202 { 203 return defaultmailbox; 204 } 205 206 //-------------------------------------------------------------------- 207 bool Depot::createMailbox(const string &s_in) const 208 { 209 const string &mailboxname = mailboxToFilename(toCanonMailbox(s_in)); 210 if (mailboxname == "") 211 return false; 212 213 Mailbox *mailbox = getDefault(); 214 if (mailbox == 0) { 215 setLastError("no default mailbox defined"); 216 return false; 217 } 218 219 bool result = mailbox->createMailbox(mailboxname, 0777); 220 if (result) 221 return true; 222 else { 223 setLastError(mailbox->getLastError()); 224 return false; 225 } 226 } 227 228 //-------------------------------------------------------------------- 229 bool Depot::deleteMailbox(const string &s_in) const 230 { 231 const string &mailboxname = mailboxToFilename(toCanonMailbox(s_in)); 232 if (mailboxname == "") 233 return false; 234 235 Mailbox *mailbox = get(s_in); 236 if (mailbox == 0) { 237 setLastError(s_in + ": no such mailbox"); 238 return false; 239 } 240 241 bool result = mailbox->deleteMailbox(mailboxname); 242 if (result) 243 return true; 244 else { 245 setLastError(mailbox->getLastError()); 246 return false; 247 } 248 } 249 250 //-------------------------------------------------------------------- 251 bool Depot::renameMailbox(const string &s_in, const string &t_in) const 252 { 253 IO &logger = IOFactory::getInstance().get(2); 254 255 const string &source = mailboxToFilename(s_in).c_str(); 256 const string &dest = mailboxToFilename(t_in).c_str(); 257 if (source == "" || dest == "") 258 return false; 259 260 int nrenamed = 0; 261 const iterator e = end(); 262 for (iterator i = begin("."); i != e; ++i) { 263 string entry = *i; 264 265 if (entry.substr(0, source.length()) == source) { 266 string sourcename, destname; 267 268 if (entry.length() == source.length()) { 269 sourcename = source; 270 destname = dest; 271 272 } else if (entry.length() > source.length() 273 && entry[source.length()] == '.') { 274 sourcename = entry; 275 destname = dest + entry.substr(source.length()); 276 } else continue; 277 278 if (rename(sourcename.c_str(), destname.c_str()) != 0) { 279 logger << "error renaming " << sourcename << " to " 280 << destname << ": " << strerror(errno) << endl; 281 } else 282 nrenamed++; 283 284 Mailbox *mailbox; 285 if ((mailbox = get(filenameToMailbox(sourcename))) != 0) 286 mailbox->bumpUidValidity(filenameToMailbox(sourcename)); 287 if ((mailbox = get(filenameToMailbox(destname))) != 0) 288 mailbox->bumpUidValidity(filenameToMailbox(destname)); 289 } 290 } 291 292 if (nrenamed == 0) { 293 setLastError("An error occurred when renaming " 294 + toImapString(s_in) 295 + " to " + toImapString(t_in) 296 + ". Try creating a new mailbox," 297 " then copy over all messages." 298 " Finally, delete the original mailbox"); 299 return false; 300 } else 301 return true; 302 } 303 304 //-------------------------------------------------------------------- 305 bool Depot::getStatus(const std::string &s_in, Status &dest) const 306 { 307 const string mailbox = toCanonMailbox(s_in); 308 if (mailbox == "") { 309 setLastError("Unrecognized mailbox: " + toImapString(s_in)); 310 return false; 311 } 312 313 string mailboxFilename = mailboxToFilename(mailbox); 314 if (mailboxFilename == "") 315 return false; 316 317 Mailbox *m = get(mailbox); 318 if (m == 0) { 319 setLastError("Unrecognized mailbox: " + toImapString(s_in)); 320 return false; 321 } 322 323 int statusid = m->getStatusID(mailboxFilename); 324 if (mailboxstatuses.find(mailbox) != mailboxstatuses.end()) { 325 dest = mailboxstatuses[mailbox]; 326 if (dest.getStatusID() == statusid) 327 return true; 328 } 329 330 if (!m->getStatus(mailboxFilename, dest)) { 331 setLastError(m->getLastError()); 332 return false; 333 } 334 335 dest.setStatusID(statusid); 336 mailboxstatuses[mailbox] = dest; 337 return true; 338 } 339 340 //-------------------------------------------------------------------- 341 Depot::iterator::iterator(void) 342 { 343 dirp = 0; 344 ref = new int; 345 *ref = 1; 346 } 347 348 //-------------------------------------------------------------------- 349 Depot::iterator::iterator(DIR *dp, struct dirent *sp) 350 { 351 dirp = dp; 352 direntp = sp; 353 354 ref = new int; 355 *ref = 1; 356 } 357 358 //-------------------------------------------------------------------- 359 Depot::iterator::iterator(const iterator ©) 360 { 361 if (*copy.ref != 0) 362 ++(*copy.ref); 363 364 ref = copy.ref; 365 dirp = copy.dirp; 366 direntp = copy.direntp; 367 } 368 369 //-------------------------------------------------------------------- 370 Depot::iterator::~iterator(void) 371 { 372 deref(); 373 } 374 375 //-------------------------------------------------------------------- 376 Depot::iterator &Depot::iterator::operator =(const iterator ©) 377 { 378 if (*copy.ref != 0) 379 ++(*copy.ref); 380 381 deref(); 382 383 ref = copy.ref; 384 dirp = copy.dirp; 385 direntp = copy.direntp; 386 387 return *this; 388 } 389 390 //-------------------------------------------------------------------- 391 void Depot::iterator::deref(void) 392 { 393 // decrease existing copy ref if there is one 394 if (*ref != 0 && --(*ref) == 0) { 395 if (dirp) { 396 closedir(dirp); 397 dirp = 0; 398 } 399 400 delete ref; 401 ref = 0; 402 } 403 } 404 405 //-------------------------------------------------------------------- 406 string Depot::iterator::operator * (void) const 407 { 408 if (direntp == 0) 409 return ""; 410 411 return direntp->d_name; 412 } 413 414 //-------------------------------------------------------------------- 415 void Depot::iterator::operator ++ (void) 416 { 417 direntp = readdir(dirp); 418 } 419 420 //-------------------------------------------------------------------- 421 bool Depot::iterator::operator == (Depot::iterator i) const 422 { 423 return direntp == i.direntp; 424 } 425 426 //-------------------------------------------------------------------- 427 bool Depot::iterator::operator != (Depot::iterator i) const 428 { 429 return direntp != i.direntp; 430 } 431 432 //-------------------------------------------------------------------- 433 Depot::iterator Depot::begin(const string &path) const 434 { 435 Depot::iterator i; 436 437 if ((i.dirp = opendir(path.c_str())) == 0) { 438 IO &logger = IOFactory::getInstance().get(2); 439 440 logger << "opendir on " + path + " failed" << endl; 441 setLastError("opendir on " + path + " failed"); 442 return end(); 443 } 444 445 ++i; 446 return i; 447 } 448 449 //-------------------------------------------------------------------- 450 const Depot::iterator &Depot::end(void) const 451 { 452 return enditerator; 453 } 454 455 //-------------------------------------------------------------------- 456 MaildirPPDepot::MaildirPPDepot(void) : Depot("Maildir++") 457 { 458 } 459 460 //-------------------------------------------------------------------- 461 MaildirPPDepot::~MaildirPPDepot(void) 462 { 463 } 464 465 //-------------------------------------------------------------------- 466 string MaildirPPDepot::mailboxToFilename(const string &m) const 467 { 468 string prefix = "INBOX"; prefix += delimiter; 469 470 string mm = m; 471 trim(mm, string(&delimiter, 1)); 472 string tmp = mm; 473 uppercase(tmp); 474 if (tmp != "INBOX" && tmp.substr(0, 6) != "INBOX/") { 475 setLastError("With a Maildir++ depot, you must create all" 476 " mailboxes under INBOX. Try creating" 477 " INBOX/" + mm + "."); 478 return ""; 479 } 480 481 string twodelim; 482 twodelim += delimiter; 483 twodelim += delimiter; 484 485 if (mm == "INBOX") return "."; 486 else if (mm.length() <= 6) { 487 setLastError("With a Maildir++ depot, you must create all" 488 " mailboxes under INBOX."); 489 return ""; 490 } else if (mm.substr(0, 6) != prefix) { 491 setLastError("With a Maildir++ depot, you must create all" 492 " mailboxes under INBOX."); 493 return ""; 494 } else if (mm.find(twodelim) != string::npos) { 495 setLastError("Invalid character combination " 496 + twodelim + " in mailbox name"); 497 return ""; 498 } else if (mm != "" && mm.substr(1).find('.') != string::npos) { 499 setLastError("Invalid character '.' in mailbox name"); 500 return ""; 501 } else { 502 string tmp = mm.substr(6); 503 for (string::iterator i = tmp.begin(); i != tmp.end(); ++i) 504 if (*i == '/') *i = '.'; 505 506 return "." + tmp; 507 } 508 } 509 510 //-------------------------------------------------------------------- 511 string MaildirPPDepot::filenameToMailbox(const string &m) const 512 { 513 if (m == "." || m == "..") 514 return "INBOX"; 515 else if (m.find(delimiter) != string::npos) 516 return ""; 517 else if (m != "" && m[0] == '.') { 518 string tmp = m; 519 for (string::iterator i = tmp.begin(); i != tmp.end(); ++i) 520 if (*i == '.') *i = delimiter; 521 522 return "INBOX" + tmp; 523 } else 524 return ""; 525 } 526 527 //-------------------------------------------------------------------- 528 IMAPdirDepot::IMAPdirDepot(void) : Depot("IMAPdir") 529 { 530 } 531 532 //-------------------------------------------------------------------- 533 IMAPdirDepot::~IMAPdirDepot(void) 534 { 535 } 536 537 //-------------------------------------------------------------------- 538 string IMAPdirDepot::mailboxToFilename(const string &m) const 539 { 540 string tmp; 541 string mm = m; 542 trim(mm, string(&delimiter, 1)); 543 544 string twodelim; 545 twodelim += delimiter; 546 twodelim += delimiter; 547 548 if (mm.find(twodelim) != string::npos) { 549 setLastError("Invalid character combination " 550 + twodelim + " in mailbox name"); 551 return ""; 552 } 553 554 string::const_iterator i = mm.begin(); 555 while (i != mm.end()) { 556 if (*i == delimiter) { 557 tmp += '.'; 558 } else if (*i == '\\') { 559 tmp += "\\\\"; 560 } else if (*i == '.') { 561 if (i == mm.begin()) 562 tmp += "."; 563 else 564 tmp += "\\."; 565 } else { 566 tmp += *i; 567 } 568 569 ++i; 570 } 571 572 return tmp; 573 } 574 575 //-------------------------------------------------------------------- 576 string IMAPdirDepot::filenameToMailbox(const string &m) const 577 { 578 string tmp; 579 bool escape = false; 580 581 // hide the magic "." mailbox. 582 if (m == "." || m == "..") 583 return ""; 584 585 string::const_iterator i = m.begin(); 586 while (i != m.end()) { 587 if (*i == '.') { 588 if (i != m.begin() && !escape) tmp += delimiter; 589 else if (i == m.begin() || escape) tmp += '.'; 590 escape = false; 591 } else if (*i == '\\') { 592 if (!escape) escape = true; else { 593 tmp += '\\'; 594 escape = false; 595 } 596 } else if (*i == delimiter) { 597 return ""; 598 } else { 599 if (escape) return ""; 600 else { 601 tmp += *i; 602 escape = false; 603 } 604 } 605 606 ++i; 607 } 608 609 return tmp; 610 }