maildir.cc (22130B)
1 /* -*- Mode: c++; -*- */ 2 /* -------------------------------------------------------------------- 3 * Filename: 4 * maildir.cc 5 * 6 * Description: 7 * Implementation of the Maildir class. 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 <iostream> 39 #include <iomanip> 40 #include <algorithm> 41 42 #include <ctype.h> 43 #include <dirent.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <stdio.h> 47 #include <sys/stat.h> 48 #include <sys/types.h> 49 #include <unistd.h> 50 51 #include "io.h" 52 #include "session.h" 53 #include "status.h" 54 #include "storage.h" 55 #include "convert.h" 56 #include "maildir.h" 57 #include "maildirmessage.h" 58 #include "pendingupdates.h" 59 60 using namespace ::std; 61 using namespace Binc; 62 63 // Used to generate the unique names for Maildir delivery 64 static int numDeliveries = 0; 65 66 //------------------------------------------------------------------------ 67 Maildir::iterator::iterator(void) 68 { 69 } 70 71 //------------------------------------------------------------------------ 72 Maildir::iterator::iterator(Maildir *home, 73 MessageMap::iterator it, 74 const SequenceSet &_bset, 75 unsigned int _mod) 76 : BaseIterator(1), mailbox(home), bset(_bset), mod(_mod), i(it) 77 { 78 uidmax = home->getMaxUid(); 79 sqnrmax = home->getMaxSqnr(); 80 } 81 82 //------------------------------------------------------------------------ 83 Maildir::iterator::iterator(const iterator ©) 84 : BaseIterator(copy.sqnr), mailbox(copy.mailbox), 85 bset(copy.bset), mod(copy.mod), i(copy.i), uidmax(copy.uidmax), 86 sqnrmax(copy.sqnrmax) 87 { 88 } 89 90 //------------------------------------------------------------------------ 91 Maildir::iterator &Maildir::iterator::operator =(const iterator ©) 92 { 93 sqnr = copy.sqnr; 94 mailbox = copy.mailbox; 95 bset = copy.bset; 96 mod = copy.mod; 97 i = copy.i; 98 uidmax = copy.uidmax; 99 sqnrmax = copy.sqnrmax; 100 return *this; 101 } 102 103 //------------------------------------------------------------------------ 104 Maildir::iterator::~iterator(void) 105 { 106 } 107 108 //------------------------------------------------------------------------ 109 MaildirMessage &Maildir::iterator::curMessage(void) 110 { 111 return i->second; 112 } 113 114 //------------------------------------------------------------------------ 115 Message &Maildir::iterator::operator *(void) 116 { 117 return curMessage(); 118 } 119 120 //------------------------------------------------------------------------ 121 void Maildir::iterator::operator ++(void) 122 { 123 ++i; 124 ++sqnr; 125 reposition(); 126 } 127 128 //------------------------------------------------------------------------ 129 bool Maildir::iterator::operator ==(const BaseIterator &a) const 130 { 131 const iterator *b = dynamic_cast<const iterator *>(&a); 132 return b ? (i == b->i) : false; 133 } 134 135 //------------------------------------------------------------------------ 136 bool Maildir::iterator::operator !=(const BaseIterator &a) const 137 { 138 return !((*this) == a); 139 } 140 141 //------------------------------------------------------------------------ 142 void Maildir::iterator::reposition(void) 143 { 144 for (;;) { 145 if (i == mailbox->messages.end()) 146 break; 147 148 Message &message = curMessage(); 149 if ((mod & SKIP_EXPUNGED) && message.isExpunged()) { 150 ++i; 151 continue; 152 } 153 154 bool inset = false; 155 if (mod & SQNR_MODE) { 156 if (bset.isInSet(sqnr) || (!bset.isLimited() && sqnr == sqnrmax)) 157 inset = true; 158 } else { 159 if (bset.isInSet(message.getUID()) || (!bset.isLimited() && message.getUID() == uidmax)) 160 inset = true; 161 } 162 163 if (!inset) { 164 ++i; 165 if (!message.isExpunged()) 166 ++sqnr; 167 continue; 168 } 169 170 break; 171 } 172 } 173 174 //------------------------------------------------------------------------ 175 Mailbox::iterator Maildir::begin(const SequenceSet &bset, 176 unsigned int mod) const 177 { 178 beginIterator = iterator((Maildir *)this, messages.begin(), bset, mod); 179 beginIterator.reposition(); 180 181 return Mailbox::iterator(beginIterator); 182 } 183 184 //------------------------------------------------------------------------ 185 Mailbox::iterator Maildir::end(void) const 186 { 187 endIterator = iterator((Maildir *)this, messages.end(), 188 endIterator.bset, endIterator.mod); 189 return Mailbox::iterator(endIterator); 190 } 191 192 //------------------------------------------------------------------------ 193 void Maildir::iterator::erase(void) 194 { 195 MessageMap::iterator iter = i; 196 ++iter; 197 198 MaildirMessageCache::getInstance().removeStatus(&curMessage()); 199 mailbox->index.remove(i->second.getUnique()); 200 mailbox->messages.erase(i); 201 202 i = iter; 203 reposition(); 204 } 205 206 //------------------------------------------------------------------------ 207 Maildir::Maildir(void) : Mailbox() 208 { 209 firstscan = true; 210 cacheRead = false; 211 uidvalidity = 0; 212 uidnext = 1; 213 selected = false; 214 oldrecent = 0; 215 oldexists = 0; 216 } 217 218 //------------------------------------------------------------------------ 219 Maildir::~Maildir(void) 220 { 221 } 222 223 //------------------------------------------------------------------------ 224 void Maildir::setPath(const string &path_in) 225 { 226 path = path_in; 227 } 228 229 //------------------------------------------------------------------------ 230 bool Maildir::getUpdates(bool doscan, unsigned int type, 231 PendingUpdates &updates, bool forceScan) 232 { 233 if (doscan && scan(forceScan) != Success) 234 return false; 235 236 unsigned int exists = 0; 237 unsigned int recent = 0; 238 bool displayExists = false; 239 240 // count messages, find recent 241 if (!readOnly && (type & PendingUpdates::EXPUNGE)) { 242 Mailbox::iterator i = begin(SequenceSet::all(), 243 INCLUDE_EXPUNGED | SQNR_MODE); 244 245 while (i != end()) { 246 Message &message = *i; 247 248 if (message.isExpunged()) { 249 updates.addExpunged(i.getSqnr()); 250 i.erase(); 251 mailboxchanged = true; 252 displayExists = true; 253 } else 254 ++i; 255 } 256 } 257 258 Mailbox::iterator i = begin(SequenceSet::all(), 259 INCLUDE_EXPUNGED | SQNR_MODE); 260 for (; i != end(); ++i) { 261 Message & message = *i; 262 // at this point, there is a message that is not expunged 263 ++exists; 264 if (message.getStdFlags() & Message::F_RECENT) ++recent; 265 } 266 267 if (displayExists || exists != oldexists) 268 updates.setExists(oldexists = exists); 269 270 if (recent != oldrecent) 271 updates.setRecent(oldrecent = recent); 272 273 if (type & PendingUpdates::FLAGS) { 274 Mailbox::iterator i = begin(SequenceSet::all(), SQNR_MODE); 275 for (; i != end(); ++i) { 276 Message &message = *i; 277 if (message.hasFlagsChanged()) { 278 int flags = message.getStdFlags(); 279 280 updates.addFlagUpdates(i.getSqnr(), message.getUID(), flags); 281 282 message.setFlagsUnchanged(); 283 } 284 } 285 } 286 287 return true; 288 } 289 290 //------------------------------------------------------------------------ 291 bool Maildir::isMailbox(const std::string &s_in) const 292 { 293 if (s_in == "") 294 return false; 295 296 struct stat mystat; 297 298 return ((stat((s_in + "/cur").c_str(), &mystat) == 0 299 && S_ISDIR(mystat.st_mode)) 300 && (stat((s_in + "/new").c_str(), &mystat) == 0 301 && S_ISDIR(mystat.st_mode)) 302 && (stat((s_in + "/tmp").c_str(), &mystat) == 0 303 && S_ISDIR(mystat.st_mode))); 304 } 305 306 //------------------------------------------------------------------------ 307 const std::string Maildir::getTypeName(void) const 308 { 309 return "Maildir"; 310 } 311 312 //------------------------------------------------------------------------ 313 void Maildir::bumpUidValidity(const string &s_in) const 314 { 315 unlink((s_in + "/bincimap-uidvalidity").c_str()); 316 unlink((s_in + "/bincimap-cache").c_str()); 317 } 318 319 //------------------------------------------------------------------------ 320 bool Maildir::isMarked(const std::string &s_in) const 321 { 322 DIR *dirp = opendir((s_in + "/new").c_str()); 323 if (dirp == 0) return false; 324 325 struct dirent *direntp; 326 while ((direntp = readdir(dirp)) != 0) { 327 string s = direntp->d_name; 328 if (s[0] != '.' 329 && s.find('/') == string::npos 330 && s.find(':') == string::npos) { 331 closedir(dirp); 332 return true; 333 } 334 } 335 336 closedir(dirp); 337 return false; 338 } 339 340 //------------------------------------------------------------------------ 341 unsigned int Maildir::getStatusID(const string &path) const 342 { 343 if (path == "") 344 return 0; 345 346 unsigned int statusid = 0; 347 struct stat mystat; 348 if (stat((path + "/new").c_str(), &mystat) == 0) 349 statusid = mystat.st_ctime; 350 351 if (stat((path + "/cur").c_str(), &mystat) == 0) 352 statusid += mystat.st_ctime; 353 354 if (stat((path + "/bincimap-cache").c_str(), &mystat) == 0) 355 statusid += mystat.st_ctime; 356 357 return statusid; 358 } 359 360 //------------------------------------------------------------------------ 361 bool Maildir::getStatus(const string &path, Status &s) const 362 { 363 unsigned int messages = 0; 364 unsigned int unseen = 0; 365 unsigned int recent = 0; 366 367 const string cachefilename = path + "/bincimap-cache"; 368 const string uidvalfilename = path + "/bincimap-uidvalidity"; 369 370 Storage cache(cachefilename, Storage::ReadOnly); 371 Storage uidvalfile(uidvalfilename, Storage::ReadOnly); 372 373 string section, key, value; 374 map<string, bool> mincache; 375 while (cache.get(§ion, &key, &value)) 376 if (isdigit(section[0]) && key == "_ID") 377 mincache[value] = true; 378 379 unsigned int uidvalidity = 0; 380 unsigned int uidnext = 0; 381 while (uidvalfile.get(§ion, &key, &value)) 382 if (section == "depot" && key == "_uidvalidity") 383 uidvalidity = (unsigned int) atoi(value); 384 else if (section == "depot" && key == "_uidnext") 385 uidnext = (unsigned int) atoi(value); 386 387 s.setUidValidity(uidvalidity < 1 ? time(0) : uidvalidity); 388 389 // Scan new 390 DIR *dirp = opendir((path + "/new").c_str()); 391 if (dirp == 0) return false; 392 393 struct dirent *direntp; 394 while ((direntp = readdir(dirp)) != 0) { 395 const string filename = direntp->d_name; 396 if (filename[0] == '.' 397 || filename.find(':') != string::npos 398 || filename.find('/') != string::npos) 399 continue; 400 401 ++recent; 402 ++uidnext; 403 ++unseen; 404 ++messages; 405 } 406 407 closedir(dirp); 408 409 // Scan cur 410 if ((dirp = opendir((path + "/cur").c_str())) == 0) 411 return false; 412 413 while ((direntp = readdir(dirp)) != 0) { 414 const string dname = direntp->d_name; 415 if (dname[0] == '.') 416 continue; 417 418 ++messages; 419 420 // Add to unseen if it doesn't have the seen flag or if it has no 421 // flags. 422 const string::size_type pos = dname.find(':'); 423 if (pos != string::npos) { 424 if (mincache.find(dname.substr(0, pos)) == mincache.end()) { 425 ++recent; 426 ++uidnext; 427 } 428 429 if (dname.substr(pos).find('S') == string::npos) 430 ++unseen; 431 } else { 432 if (mincache.find(dname) == mincache.end()) { 433 ++recent; 434 ++uidnext; 435 } 436 437 ++unseen; 438 } 439 } 440 441 closedir(dirp); 442 443 s.setRecent(recent); 444 s.setMessages(messages); 445 s.setUnseen(unseen); 446 s.setUidNext(uidnext); 447 448 return true; 449 } 450 451 //------------------------------------------------------------------------ 452 unsigned int Maildir::getMaxUid(void) const 453 { 454 MessageMap::const_iterator i = messages.end(); 455 if (i == messages.begin()) 456 return 0; 457 458 --i; 459 for (;;) { 460 const MaildirMessage &message = i->second; 461 if (message.isExpunged()) { 462 if (i == messages.begin()) 463 return 0; 464 --i; 465 } else { 466 return message.getUID(); 467 } 468 } 469 470 return 0; 471 } 472 473 //------------------------------------------------------------------------ 474 unsigned int Maildir::getMaxSqnr(void) const 475 { 476 int sqnr = messages.size(); 477 MessageMap::const_iterator i = messages.end(); 478 if (i == messages.begin()) 479 return 0; 480 481 --i; 482 for (;;) { 483 const MaildirMessage &message = i->second; 484 if (message.isExpunged()) { 485 if (i == messages.begin()) 486 return 0; 487 --sqnr; 488 --i; 489 } else { 490 return sqnr; 491 } 492 } 493 494 return 0; 495 } 496 497 //------------------------------------------------------------------------ 498 unsigned int Maildir::getUidValidity(void) const 499 { 500 return uidvalidity; 501 } 502 503 //------------------------------------------------------------------------ 504 unsigned int Maildir::getUidNext(void) const 505 { 506 return uidnext; 507 } 508 509 //------------------------------------------------------------------------ 510 Message *Maildir::createMessage(const string &mbox, time_t idate) 511 { 512 string sname = mbox + "/tmp/bincimap-XXXXXX"; 513 char *safename = strdup(sname.c_str()); 514 515 int fd = mkstemp(safename); 516 if (fd == -1) { 517 setLastError("Unable to create safe name."); 518 return 0; 519 } 520 521 string safenameStr = safename; 522 delete safename; 523 524 MaildirMessage message(*this); 525 526 message.setFile(fd); 527 message.setSafeName(safenameStr); 528 message.setInternalDate(idate); 529 530 newMessages.push_back(message); 531 vector<MaildirMessage>::iterator i = newMessages.end(); 532 --i; 533 return &(*i); 534 } 535 536 //------------------------------------------------------------------------ 537 bool Maildir::commitNewMessages(const string &mbox) 538 { 539 Session &session = Session::getInstance(); 540 IO &logger = IOFactory::getInstance().get(2); 541 542 vector<MaildirMessage>::iterator i = newMessages.begin(); 543 map<MaildirMessage *, string> committedMessages; 544 545 struct timeval youngestFile = {0, 0}; 546 547 bool abort = false; 548 while (!abort && i != newMessages.end()) { 549 MaildirMessage &m = *i; 550 551 if (m.getInternalFlags() & MaildirMessage::Committed) { 552 ++i; 553 continue; 554 } 555 556 m.setInternalFlag(MaildirMessage::Committed); 557 558 string safeName = m.getSafeName(); 559 560 for (int attempt = 0; !abort && attempt < 1000; ++attempt) { 561 struct timeval tv; 562 gettimeofday(&tv, 0); 563 youngestFile = tv; 564 565 // Generate Maildir conformant file name 566 BincStream ssid; 567 ssid << (int) tv.tv_sec << "." 568 << "R" << (int) rand() 569 << "M" << (int) tv.tv_usec 570 << "P" << (int) session.getPid() 571 << "Q" << numDeliveries++ 572 << "." << session.getHostname(); 573 574 BincStream ss; 575 ss << mbox << "/new/" << ssid.str(); 576 577 string fileName = ss.str(); 578 579 if (link(safeName.c_str(), fileName.c_str()) == 0) { 580 unlink(safeName.c_str()); 581 m.setInternalDate(tv.tv_sec); 582 m.setUnique(ssid.str()); 583 m.setUID(0); 584 committedMessages[&m] = fileName; 585 break; 586 } 587 588 if (errno == EEXIST) 589 continue; 590 591 logger << "Warning: link(" << toImapString(safeName) << ", " 592 << toImapString(fileName) << ") failed: " 593 << strerror(errno) << endl; 594 595 session.setResponseCode("TRYCREATE"); 596 session.setLastError("failed, internal error."); 597 abort = true; 598 break; 599 } 600 601 ++i; 602 } 603 604 // abort means to make an attempt to restore the mailbox to 605 // its original state. 606 if (abort) { 607 // Fixme: Messages that are in committedMessages should be skipped 608 // here. 609 for (i = newMessages.begin(); i != newMessages.end(); ++i) 610 unlink((*i).getSafeName().c_str()); 611 612 map<MaildirMessage *, string>::const_iterator j 613 = committedMessages.begin(); 614 while (j != committedMessages.end()) { 615 if (unlink(j->second.c_str()) != 0) { 616 if (errno == ENOENT) { 617 // FIXME: The message was probably moves away from new/ by 618 // another IMAP session. 619 logger << "error rollbacking after failed commit to " 620 << toImapString(mbox) << ", failed to unlink " 621 << toImapString(j->second) 622 << ": " << strerror(errno) << endl; 623 } else { 624 logger << "error rollbacking after failed commit to " 625 << toImapString(mbox) << ", failed to unlink " 626 << toImapString(j->second) 627 << ": " << strerror(errno) << endl; 628 newMessages.clear(); 629 return false; 630 } 631 } 632 633 ++j; 634 } 635 636 newMessages.clear(); 637 return false; 638 } 639 640 // cover the extremely unlikely event that another concurrent 641 // Maildir accessor has just made a file with the same timestamp and 642 // random number by spinning until the timestamp has changed before 643 // moving the message into cur. 644 struct timeval tv; 645 gettimeofday(&tv, 0); 646 while (tv.tv_sec == youngestFile.tv_sec 647 && tv.tv_usec == youngestFile.tv_usec) { 648 gettimeofday(&tv, 0); 649 } 650 651 map<MaildirMessage *, string>::const_iterator j 652 = committedMessages.begin(); 653 for (;j != committedMessages.end(); ++j) { 654 string basename = j->second.substr(j->second.rfind('/') + 1); 655 656 int flags = j->first->getStdFlags(); 657 string flagStr; 658 if (flags & Message::F_DRAFT) flagStr += "D"; 659 if (flags & Message::F_FLAGGED) flagStr += "F"; 660 if (flags & Message::F_ANSWERED) flagStr += "R"; 661 if (flags & Message::F_SEEN) flagStr += "S"; 662 if (flags & Message::F_DELETED) flagStr += "T"; 663 664 string dest = mbox + "/cur/" + basename + ":2," + flagStr; 665 if (rename(j->second.c_str(), dest.c_str()) == 0) 666 continue; 667 668 if (errno != ENOENT) 669 logger << "when setting flags on: " << j->second 670 << ": " << strerror(errno) << endl; 671 } 672 673 committedMessages.clear(); 674 return true; 675 } 676 677 //------------------------------------------------------------------------ 678 bool Maildir::rollBackNewMessages(void) 679 { 680 vector<MaildirMessage>::const_iterator i = newMessages.begin(); 681 // Fixme: Messages that are in committedMessages should be skipped 682 // here. 683 for (; i != newMessages.end(); ++i) 684 unlink((*i).getSafeName().c_str()); 685 686 newMessages.clear(); 687 return true; 688 } 689 690 //------------------------------------------------------------------------ 691 bool Maildir::fastCopy(Message &m, Mailbox &desttype, 692 const std::string &destname) 693 { 694 // At this point, fastCopy is broken because the creation time is 695 // equal for the two clones. The new clone must have a new creation 696 // time. Symlinks are a possibility, but they break if other Maildir 697 // accessors rename mailboxes. 698 // return false; 699 700 Session &session = Session::getInstance(); 701 IO &logger = IOFactory::getInstance().get(2); 702 703 MaildirMessage *message = dynamic_cast<MaildirMessage *>(&m); 704 if (!message) 705 return false; 706 707 string mfilename = message->getFileName(); 708 if (mfilename == "") 709 return false; 710 711 Maildir *mailbox = dynamic_cast<Maildir *>(&desttype); 712 if (!mailbox) 713 return false; 714 715 for (int attempt = 0; attempt < 1000; ++attempt) { 716 717 struct timeval tv; 718 gettimeofday(&tv, 0); 719 720 // Generate Maildir conformant file name 721 BincStream ssid; 722 ssid << (int) tv.tv_sec << "." 723 << "R" << (int) rand() 724 << "M" << (int) tv.tv_usec 725 << "P" << (int) session.getPid() 726 << "Q" << numDeliveries++ 727 << "." << session.getHostname(); 728 729 BincStream ss; 730 ss << destname << "/tmp/" << ssid.str(); 731 732 string fileName = ss.str(); 733 734 if (link((path + "/cur/" + mfilename).c_str(), fileName.c_str()) == 0) { 735 MaildirMessage newmess = *message; 736 newmess.setSafeName(fileName); 737 newmess.setUnique(ssid.str()); 738 newmess.setInternalDate((time_t) tv.tv_sec); 739 newmess.setUID(0); 740 newMessages.push_back(newmess); 741 break; 742 } 743 744 if (errno == EEXIST) 745 continue; 746 747 logger << "Warning: link(" << toImapString(path + "/cur/" + mfilename) 748 << ", " << toImapString(fileName) << ") failed: " 749 << strerror(errno) << endl; 750 751 session.setResponseCode("TRYCREATE"); 752 session.setLastError("failed, internal error."); 753 return false; 754 } 755 756 return true; 757 } 758 759 //------------------------------------------------------------------------ 760 MaildirMessage *Maildir::get(const std::string &id) 761 { 762 MaildirIndexItem *item = index.find(id); 763 if (!item) 764 return 0; 765 766 unsigned int uid = item->uid; 767 if (messages.find(uid) == messages.end()) 768 return 0; 769 770 return &messages.find(uid)->second; 771 } 772 773 //------------------------------------------------------------------------ 774 void Maildir::add(MaildirMessage &m) 775 { 776 MessageMap::iterator it = messages.find(m.getUID()); 777 if (it != messages.end()) 778 messages.erase(it); 779 messages.insert(make_pair(m.getUID(), m)); 780 index.insert(m.getUnique(), m.getUID()); 781 } 782 783 //------------------------------------------------------------------------ 784 unsigned int MaildirIndex::getSize(void) const 785 { 786 return idx.size(); 787 } 788 789 //------------------------------------------------------------------------ 790 void MaildirIndex::insert(const string &unique, unsigned int uid, 791 const string &fileName) 792 { 793 if (idx.find(unique) == idx.end()) { 794 MaildirIndexItem item; 795 item.uid = uid; 796 item.fileName = fileName; 797 idx[unique] = item; 798 } else { 799 MaildirIndexItem &item = idx[unique]; 800 if (uid != 0) item.uid = uid; 801 if (fileName != "") item.fileName = fileName; 802 } 803 } 804 805 //------------------------------------------------------------------------ 806 void MaildirIndex::remove(const string &unique) 807 { 808 map<string, MaildirIndexItem>::iterator it = idx.find(unique); 809 if (it != idx.end()) 810 idx.erase(it); 811 } 812 813 //------------------------------------------------------------------------ 814 MaildirIndexItem *MaildirIndex::find(const string &unique) 815 { 816 map<string, MaildirIndexItem>::iterator it = idx.find(unique); 817 if (it != idx.end()) 818 return &it->second; 819 820 return 0; 821 } 822 823 //------------------------------------------------------------------------ 824 void MaildirIndex::clear(void) 825 { 826 idx.clear(); 827 } 828 829 //------------------------------------------------------------------------ 830 void MaildirIndex::clearUids(void) 831 { 832 map<string, MaildirIndexItem>::iterator it = idx.begin(); 833 for (; it != idx.end(); ++it) 834 it->second.uid = 0; 835 } 836 837 //------------------------------------------------------------------------ 838 void MaildirIndex::clearFileNames(void) 839 { 840 map<string, MaildirIndexItem>::iterator it = idx.begin(); 841 for (; it != idx.end(); ++it) 842 it->second.fileName = ""; 843 }