Bincimap 2.0.16
Easy Imapping
Loading...
Searching...
No Matches
depot.cc
Go to the documentation of this file.
1
7#include <map>
8#include <string>
9#include <unistd.h>
10#include <errno.h>
11
12#include "depot.h"
13#include "mailbox.h"
14#include "status.h"
15#include "convert.h"
16#include "iodevice.h"
17#include "iofactory.h"
18
19using namespace ::std;
20using namespace Binc;
21
22//--------------------------------------------------------------------
23DepotFactory::DepotFactory(void)
24{
25}
26
27//--------------------------------------------------------------------
29{
30 for (vector<Depot *>::iterator i = depots.begin(); i != depots.end();
31 ++i)
32 delete *i;
33}
34
35//--------------------------------------------------------------------
36Depot *DepotFactory::get(const string &name) const
37{
38 for (vector<Depot *>::const_iterator i = depots.begin(); i != depots.end();
39 ++i)
40 if ((*i)->getName() == name)
41 return *i;
42
43 return 0;
44}
45
46//--------------------------------------------------------------------
48{
49 depots.push_back(depot);
50}
51
52//--------------------------------------------------------------------
54{
55 static DepotFactory depotfactory;
56 return depotfactory;
57}
58
59//--------------------------------------------------------------------
60Depot::Depot(void) : enditerator(0, 0)
61{
62 defaultmailbox = 0;
63 selectedmailbox = 0;
64
65 delimiter = '/';
66}
67
68//--------------------------------------------------------------------
69Depot::Depot(const string &name) : enditerator(0, 0)
70{
71 defaultmailbox = 0;
72 selectedmailbox = 0;
73
74 delimiter = '/';
75
76 this->name = name;
77}
78
79//--------------------------------------------------------------------
81{
82}
83
84//--------------------------------------------------------------------
85const string &Depot::getLastError(void) const
86{
87 return lastError;
88}
89
90//--------------------------------------------------------------------
91void Depot::setLastError(const string &error) const
92{
93 lastError = error;
94}
95
96//--------------------------------------------------------------------
98{
99 for (vector<Mailbox *>::const_iterator i = backends.begin();
100 i != backends.end(); ++i)
101 if (*i == m) break;
102
103 backends.push_back(m);
104}
105
106//--------------------------------------------------------------------
107Mailbox *Depot::get(const string &s_in) const
108{
109 for (vector<Mailbox *>::const_iterator i = backends.begin();
110 i != backends.end(); ++i)
111 if ((*i)->isMailbox(mailboxToFilename(s_in)))
112 return *i;
113
114 setLastError("No such mailbox " + toImapString(s_in));
115 return 0;
116}
117
118//--------------------------------------------------------------------
120{
121 for (vector<Mailbox *>::const_iterator i = backends.begin();
122 i != backends.end(); ++i)
123 if (*i == m) {
124 selectedmailbox = m;
125 return true;
126 }
127
128 setLastError("Attempted to select unregistered Mailbox type in Depot");
129 return false;
130}
131
132//--------------------------------------------------------------------
133const string &Depot::getName(void) const
134{
135 return name;
136}
137
138//--------------------------------------------------------------------
139const string &Depot::getPersonalNamespace(void) const
140{
141 return personalNamespace;
142}
143
144//--------------------------------------------------------------------
145const string &Depot::getOthersNamespace(void) const
146{
147 return othersNamespace;
148}
149
150//--------------------------------------------------------------------
151const string &Depot::getSharedNamespace(void) const
152{
153 return sharedNamespace;
154}
155
156//--------------------------------------------------------------------
158{
159 delimiter = c;
160}
161
162//--------------------------------------------------------------------
163const char Depot::getDelimiter(void) const
164{
165 return delimiter;
166}
167
168//--------------------------------------------------------------------
169bool Depot::setDefaultType(const string &name)
170{
171 for (vector<Mailbox *>::const_iterator i = backends.begin();
172 i != backends.end(); ++i)
173 if ((*i)->getTypeName() == name) {
174 defaultmailbox = *i;
175 return true;
176 }
177
178 setLastError("attempt to default to unregistered Mailbox type " + name);
179 return false;
180}
181
182//--------------------------------------------------------------------
184{
185 return selectedmailbox;
186}
187
188//--------------------------------------------------------------------
190{
191 selectedmailbox = 0;
192}
193
194//--------------------------------------------------------------------
196{
197 return defaultmailbox;
198}
199
200//--------------------------------------------------------------------
201bool Depot::createMailbox(const string &s_in) const
202{
203 const string &mailboxname = mailboxToFilename(toCanonMailbox(s_in));
204 if (mailboxname == "") {
205 setLastError("invalid mailbox name");
206 return false;
207 }
208
209 Mailbox *mailbox = getDefault();
210 if (mailbox == 0) {
211 setLastError("no default mailbox defined");
212 return false;
213 }
214
215 bool result = mailbox->createMailbox(mailboxname, 0777);
216 if (result)
217 return true;
218 else {
219 setLastError(mailbox->getLastError());
220 return false;
221 }
222}
223
224//--------------------------------------------------------------------
225bool Depot::deleteMailbox(const string &s_in) const
226{
227 const string &mailboxname = mailboxToFilename(toCanonMailbox(s_in));
228
229 Mailbox *mailbox = get(s_in);
230 if (mailbox == 0) {
231 setLastError(s_in + ": no such mailbox");
232 return false;
233 }
234
235 bool result = mailbox->deleteMailbox(mailboxname);
236 if (result)
237 return true;
238 else {
239 setLastError(mailbox->getLastError());
240 return false;
241 }
242}
243
244//--------------------------------------------------------------------
245bool Depot::renameMailbox(const string &s_in, const string &t_in) const
246{
247 const string &source = mailboxToFilename(s_in).c_str();
248 const string &dest = mailboxToFilename(t_in).c_str();
249
250 int nrenamed = 0;
251 const iterator e = end();
252 for (iterator i = begin("."); i != e; ++i) {
253 string entry = *i;
254
255 if (entry.substr(0, source.length()) == source) {
256 string sourcename, destname;
257
258 if (entry.length() == source.length()) {
259 sourcename = source;
260 destname = dest;
261
262 } else if (entry.length() > source.length()
263 && entry[source.length()] == '.') {
264 sourcename = entry;
265 destname = dest + entry.substr(source.length());
266 } else continue;
267
268 if (rename(sourcename.c_str(), destname.c_str()) != 0) {
269 bincWarning << "error renaming " << sourcename << " to "
270 << destname << ": " << strerror(errno) << endl;
271 } else
272 nrenamed++;
273
274 Mailbox *mailbox;
275 if ((mailbox = get(filenameToMailbox(sourcename))) != 0)
276 mailbox->bumpUidValidity(filenameToMailbox(sourcename));
277 if ((mailbox = get(filenameToMailbox(destname))) != 0)
278 mailbox->bumpUidValidity(filenameToMailbox(destname));
279 }
280 }
281
282 if (nrenamed == 0) {
283 setLastError("An error occurred when renaming "
284 + toImapString(s_in)
285 + " to " + toImapString(t_in)
286 + ". Try creating a new mailbox,"
287 " then copy over all messages."
288 " Finally, delete the original mailbox");
289 return false;
290 } else
291 return true;
292}
293
294//--------------------------------------------------------------------
295bool Depot::getStatus(const std::string &s_in, Status &dest) const
296{
297 const string mailbox = toCanonMailbox(s_in);
298 Mailbox *m = get(mailbox);
299 if (m == 0) {
300 setLastError("Unrecognized mailbox: " + toImapString(s_in));
301 return false;
302}
303
304 int statusid = m->getStatusID(mailboxToFilename(mailbox));
305 if (mailboxstatuses.find(mailbox) != mailboxstatuses.end()) {
306 dest = mailboxstatuses[mailbox];
307 if (dest.getStatusID() == statusid)
308 return true;
309 }
310
311 if (!m->getStatus(mailboxToFilename(mailbox), dest)) {
313 return false;
314 }
315
316 dest.setStatusID(statusid);
317 mailboxstatuses[mailbox] = dest;
318 return true;
319}
320
321//----------------------------------------------------------------------
322vector<string> Depot::getSubscriptions(void) const
323{
324 return subscribed;
325}
326
327//----------------------------------------------------------------------
328void Depot::subscribeTo(const std::string mailbox)
329{
330 for (vector<string>::iterator i = subscribed.begin();
331 i != subscribed.end(); ++i) {
332 if (*i == mailbox)
333 return;
334 }
335
336 subscribed.push_back(mailbox);
337}
338
339//----------------------------------------------------------------------
340bool Depot::unsubscribeTo(const std::string mailbox)
341{
342 for (vector<string>::iterator i = subscribed.begin();
343 i != subscribed.end(); ++i) {
344 if (*i == mailbox) {
345 subscribed.erase(i);
346 return true;
347 }
348 }
349
350 return false;
351}
352
353//----------------------------------------------------------------------
355{
356 // drop all existing subscribed folders.
357 subscribed.clear();
358
359 // try loading the .subscribed file
360 bool ok = false;
361 FILE *fp = fopen(".subscribed", "r");
362 map<string, bool> addedEntries;
363 if (fp) {
364 int c;
365 string current;
366 while ((c = fgetc(fp)) != EOF) {
367 if (c == '\n') {
368 if (current != "") {
369 if (current == "INBOX")
370 current = ".";
371
372 if (current.substr(0, 5) == "INBOX")
373 current = current.substr(5);
374
375 if (addedEntries.find(current) == addedEntries.end()) {
376 subscribed.push_back(filenameToMailbox(current));
377 addedEntries[current] = true;
378 }
379 current = "";
380 }
381 } else
382 current += c;
383 }
384
385 fclose(fp);
386 ok = true;
387 }
388
389 if (!ok) {
390 subscribed.push_back("INBOX");
392 }
393}
394
395//----------------------------------------------------------------------
396bool Depot::saveSubscribes(void) const
397{
398 // create a safe file name
399 string tpl = ".subscribed-tmp-XXXXXX";
400 char *ftemplate = new char[tpl.length() + 1];
401
402 strcpy(ftemplate, tpl.c_str());
403 int fd = mkstemp(ftemplate);
404 if (fd == -1) {
405 bincWarning << "unable to create temporary file \""
406 << tpl << "\"" << endl;
407 delete[] ftemplate;
408 return false;
409 }
410
411 map<string, bool> addedEntries;
412 for (vector<string>::const_iterator i = subscribed.begin();
413 i != subscribed.end(); ++i) {
414 if (addedEntries.find(*i) == addedEntries.end()) {
415 addedEntries[*i] = true;
416 string w = mailboxToFilename(*i) + "\n";
417 if (write(fd, w.c_str(), w.length()) != (ssize_t) w.length()) {
418 bincWarning << "failed to write to " << tpl << ": "
419 << strerror(errno) << endl;
420 break;
421 }
422 }
423 }
424
425 if ((fsync(fd) && (errno != EROFS || errno != EINVAL)) || close(fd)) {
426 bincWarning << "failed to close " << ftemplate
427 << ": " << strerror(errno) << endl;
428 delete[] ftemplate;
429 return false;
430 }
431
432 if (rename(ftemplate, ".subscribed") != 0) {
433 bincWarning << "failed to rename " << ftemplate
434 << " to .subscribed: "
435 << strerror(errno) << endl;
436 delete[] ftemplate;
437 return false;
438 }
439
440 delete[] ftemplate;
441 return true;
442}
443
444//--------------------------------------------------------------------
446{
447 dirp = 0;
448 ref = new int;
449 *ref = 1;
450}
451
452//--------------------------------------------------------------------
453Depot::iterator::iterator(DIR *dp, struct dirent *sp)
454{
455 dirp = dp;
456 direntp = sp;
457
458 ref = new int;
459 *ref = 1;
460}
461
462//--------------------------------------------------------------------
464{
465 if (*copy.ref != 0)
466 ++(*copy.ref);
467
468 ref = copy.ref;
469 dirp = copy.dirp;
470 direntp = copy.direntp;
471}
472
473//--------------------------------------------------------------------
475{
476 deref();
477}
478
479//--------------------------------------------------------------------
481{
482 if (*copy.ref != 0)
483 ++(*copy.ref);
484
485 deref();
486
487 ref = copy.ref;
488 dirp = copy.dirp;
489 direntp = copy.direntp;
490
491 return *this;
492}
493
494//--------------------------------------------------------------------
496{
497 // decrease existing copy ref if there is one
498 if (*ref != 0 && --(*ref) == 0) {
499 if (dirp) {
500 closedir(dirp);
501 dirp = 0;
502 }
503
504 delete ref;
505 ref = 0;
506 }
507}
508
509//--------------------------------------------------------------------
511{
512 if (direntp == 0)
513 return "";
514
515 return direntp->d_name;
516}
517
518//--------------------------------------------------------------------
520{
521 direntp = readdir(dirp);
522}
523
524//--------------------------------------------------------------------
526{
527 return direntp == i.direntp;
528}
529
530//--------------------------------------------------------------------
532{
533 return direntp != i.direntp;
534}
535
536//--------------------------------------------------------------------
537Depot::iterator Depot::begin(const string &path) const
538{
540
541 if ((i.dirp = opendir(path.c_str())) == 0) {
542 bincWarning << "opendir on " << path << " failed" << endl;
543 setLastError("opendir on " + path + " failed");
544 return end();
545 }
546
547 ++i;
548 return i;
549}
550
551//--------------------------------------------------------------------
552const Depot::iterator &Depot::end(void) const
553{
554 return enditerator;
555}
556
557//--------------------------------------------------------------------
559{
560 privateNamespace = "INBOX";
561 privateNamespace += getDelimiter();
562}
563
564//--------------------------------------------------------------------
566{
567}
568
569//--------------------------------------------------------------------
571{
572 return privateNamespace;
573}
574
575//--------------------------------------------------------------------
576string MaildirPPDepot::mailboxToFilename(const string &m) const
577{
578 string prefix = "INBOX"; prefix += delimiter;
579
580 string mm = m;
581 trim(mm, string(&delimiter, 1));
582 string tmp = mm;
583 uppercase(tmp);
584 if (tmp != "INBOX" && tmp.substr(0, 6) != prefix) {
585 setLastError("With a Maildir++ depot, you must create all"
586 " mailboxes under INBOX. Try creating"
587 " " + prefix + mm + " .");
588 return "";
589 }
590
591 string twodelim;
592 twodelim += delimiter;
593 twodelim += delimiter;
594
595 if (mm == "INBOX") return ".";
596 else if (mm.length() <= 6) {
597 setLastError("With a Maildir++ depot, you must create all"
598 " mailboxes under INBOX.");
599 return "";
600 } else if (mm.substr(0, 6) != prefix) {
601 setLastError("With a Maildir++ depot, you must create all"
602 " mailboxes under INBOX.");
603 return "";
604 } else if (mm.find(twodelim) != string::npos) {
605 setLastError("Invalid character combination "
606 + twodelim + " in mailbox name");
607 return "";
608 } else if (mm != "" && delimiter != '.' && mm.substr(1).find('.') != string::npos) {
609 setLastError("Invalid character '.' in mailbox name");
610 return "";
611 } else {
612 string tmp = mm.substr(6);
613 for (string::iterator i = tmp.begin(); i != tmp.end(); ++i)
614 if (*i == delimiter) *i = '.';
615
616 return "." + tmp;
617 }
618}
619
620//--------------------------------------------------------------------
621string MaildirPPDepot::filenameToMailbox(const string &m) const
622{
623 if (m == ".") return "INBOX";
624 else if (delimiter != '.' && m.find(delimiter) != string::npos) return "";
625 else if (m != "" && m[0] == '.') {
626 string tmp = m;
627 for (string::iterator i = tmp.begin(); i != tmp.end(); ++i)
628 if (*i == '.') *i = delimiter;
629
630 return "INBOX" + tmp;
631 } else return "";
632}
633
634//--------------------------------------------------------------------
636{
637}
638
639//--------------------------------------------------------------------
641{
642}
643
644//--------------------------------------------------------------------
645string IMAPdirDepot::mailboxToFilename(const string &m) const
646{
647 string tmp;
648 string mm = m;
649 trim(mm, string(&delimiter, 1));
650
651 string twodelim;
652 twodelim += delimiter;
653 twodelim += delimiter;
654
655 if (mm.find(twodelim) != string::npos) {
656 setLastError("Invalid character combination "
657 + twodelim + " in mailbox name");
658 return "";
659 }
660
661 string::const_iterator i = mm.begin();
662 while (i != mm.end()) {
663 if (*i == delimiter) {
664 tmp += '.';
665 } else if (*i == '\\') {
666 tmp += "\\\\";
667 } else if (*i == '.') {
668 if (i == mm.begin())
669 tmp += ".";
670 else
671 tmp += "\\.";
672 } else {
673 tmp += *i;
674 }
675
676 ++i;
677 }
678
679 return tmp;
680}
681
682//--------------------------------------------------------------------
683string IMAPdirDepot::filenameToMailbox(const string &m) const
684{
685 string tmp;
686 bool escape = false;
687
688 // hide the magic "." mailbox.
689 if (m == "." || m == "..")
690 return "";
691
692 string::const_iterator i = m.begin();
693 while (i != m.end()) {
694 if (*i == '.') {
695 if (i != m.begin() && !escape) tmp += delimiter;
696 else if (i == m.begin() || escape) tmp += '.';
697 escape = false;
698 } else if (*i == '\\') {
699 if (!escape) escape = true; else {
700 tmp += '\\';
701 escape = false;
702 }
703 } else if (*i == delimiter) {
704 return "";
705 } else {
706 if (escape) return "";
707 else {
708 tmp += *i;
709 escape = false;
710 }
711 }
712
713 ++i;
714 }
715
716 return tmp;
717}
void operator++(void)
Definition: depot.cc:519
std::string operator*(void) const
Definition: depot.cc:510
iterator & operator=(const iterator &copy)
Definition: depot.cc:480
bool operator==(iterator) const
Definition: depot.cc:525
bool operator!=(iterator) const
Definition: depot.cc:531
void deref(void)
Definition: depot.cc:495
static DepotFactory & getInstance(void)
Definition: depot.cc:53
void assign(Depot *)
Definition: depot.cc:47
Depot * get(const std::string &name) const
Definition: depot.cc:36
~DepotFactory(void)
Definition: depot.cc:28
virtual Mailbox * get(const std::string &path) const
Definition: depot.cc:107
virtual iterator begin(const std::string &) const
Definition: depot.cc:537
const std::string & getName(void) const
Definition: depot.cc:133
std::map< std::string, Status > mailboxstatuses
Definition: depot.h:77
void resetSelected(void)
Definition: depot.cc:189
Depot(void)
Definition: depot.cc:60
virtual void loadSubscribes(void)
Definition: depot.cc:354
virtual const std::string & getOthersNamespace(void) const
Definition: depot.cc:145
void setLastError(const std::string &error) const
Definition: depot.cc:91
virtual ~Depot(void)
Definition: depot.cc:80
virtual bool setSelected(Mailbox *)
Definition: depot.cc:119
char delimiter
Definition: depot.h:76
virtual bool saveSubscribes(void) const
Definition: depot.cc:396
virtual const iterator & end(void) const
Definition: depot.cc:552
const std::string & getLastError(void) const
Definition: depot.cc:85
virtual void assign(Mailbox *)
Definition: depot.cc:97
Mailbox * getDefault(void) const
Definition: depot.cc:195
virtual void subscribeTo(const std::string mailbox)
Definition: depot.cc:328
virtual bool deleteMailbox(const std::string &m) const
Definition: depot.cc:225
virtual std::string mailboxToFilename(const std::string &m) const =0
virtual bool createMailbox(const std::string &m) const
Definition: depot.cc:201
virtual bool renameMailbox(const std::string &m, const std::string &n) const
Definition: depot.cc:245
virtual const std::string & getPersonalNamespace(void) const
Definition: depot.cc:139
virtual const std::string & getSharedNamespace(void) const
Definition: depot.cc:151
std::string name
Definition: depot.h:75
bool getStatus(const std::string &s_in, Status &dest) const
Definition: depot.cc:295
bool setDefaultType(const std::string &n)
Definition: depot.cc:169
virtual Mailbox * getSelected(void) const
Definition: depot.cc:183
void setDelimiter(char)
Definition: depot.cc:157
const char getDelimiter(void) const
Definition: depot.cc:163
virtual std::string filenameToMailbox(const std::string &m) const =0
std::string lastError
Definition: depot.h:74
virtual std::vector< std::string > getSubscriptions(void) const
Definition: depot.cc:322
virtual bool unsubscribeTo(const std::string mailbox)
Definition: depot.cc:340
std::string filenameToMailbox(const std::string &m) const
Definition: depot.cc:683
std::string mailboxToFilename(const std::string &m) const
Definition: depot.cc:645
const std::string & getLastError(void) const
Definition: mailbox.cc:103
virtual bool createMailbox(const std::string &s, mode_t mode, uid_t owner=0, gid_t group=0, bool root=false)=0
virtual bool deleteMailbox(const std::string &s)=0
virtual void bumpUidValidity(const std::string &) const =0
virtual unsigned int getStatusID(const std::string &) const =0
virtual bool getStatus(const std::string &, Status &) const =0
std::string filenameToMailbox(const std::string &m) const
Definition: depot.cc:621
std::string mailboxToFilename(const std::string &m) const
Definition: depot.cc:576
const std::string & getPersonalNamespace(void) const
Definition: depot.cc:570
int getStatusID(void) const
Definition: status.h:38
void setStatusID(int i)
Definition: status.h:30
Declaration of miscellaneous convertion functions.
Declaration of the IODevice class.
Declaration of the IOFactory class.
#define bincWarning
Definition: iofactory.h:44
Declaration of the Mailbox class (Mailbox is logical container)
Definition: bincimapd.cc:9
std::string toImapString(const std::string &s_in)
Definition: convert.h:103
std::string toCanonMailbox(const std::string &s_in)
Definition: convert.h:216
void uppercase(std::string &input)
Definition: convert.h:115
void trim(std::string &s_in, const std::string &chars=" \t\r\n")
Definition: convert.h:137
Declaration of the Status class.