summaryrefslogtreecommitdiffstats
path: root/libmail/mboxopen.C
diff options
context:
space:
mode:
Diffstat (limited to 'libmail/mboxopen.C')
-rw-r--r--libmail/mboxopen.C282
1 files changed, 282 insertions, 0 deletions
diff --git a/libmail/mboxopen.C b/libmail/mboxopen.C
new file mode 100644
index 0000000..54e108f
--- /dev/null
+++ b/libmail/mboxopen.C
@@ -0,0 +1,282 @@
+/*
+** Copyright 2002-2008, Double Precision Inc.
+**
+** See COPYING for distribution information.
+*/
+#include "libmail_config.h"
+#include <cstring>
+
+#include "mbox.H"
+#include "mboxopen.H"
+#include "mboxsighandler.H"
+#include "mboxmagictag.H"
+#include "file.H"
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <utime.h>
+
+using namespace std;
+
+mail::mbox::OpenTask::OpenTask(mail::mbox &mboxAccount,
+ string folderArg, mail::callback &callbackArg,
+ mail::callback::folder *openCallbackArg)
+ : TimedTask(mboxAccount, callbackArg),
+ folder(folderArg),
+ openCallback(openCallbackArg)
+{
+}
+
+mail::mbox::OpenTask::~OpenTask()
+{
+}
+
+bool mail::mbox::OpenTask::doit()
+{
+ bool isInbox= folder == "INBOX";
+
+ if (folder == "")
+ {
+ fail(strerror(ENOENT));
+ return true;
+ }
+
+ mail::mbox::lock mailbox_lock(isInbox ? mboxAccount.inboxMailboxPath:folder);
+
+ mail::mbox::lock inbox_lock(mboxAccount.inboxSpoolPath); // Just in case
+
+ struct stat stat_buf;
+
+ if (isInbox)
+ {
+ // We must have a r/w lock on the mail spool file.
+
+ if (!inbox_lock(false))
+ {
+ if (errno == EAGAIN)
+ return false; // Deferral, try again later.
+
+ // ENOENT is ok, the spool file does not exist.
+
+ if (errno != ENOENT)
+ {
+ fail(strerror(errno));
+ return true;
+ }
+ isInbox=false;
+
+ // spool file not created yet, new account probably.
+ }
+
+ // Make sure $HOME/mboxAccount exists.
+ int createMbox=::open(mboxAccount.inboxMailboxPath.c_str(),
+ O_RDWR|O_CREAT, 0600);
+
+ if (createMbox < 0)
+ {
+ fail(strerror(errno));
+ return true;
+ }
+ close(createMbox);
+ }
+
+ bool hasExistingFolder= openCallback == NULL;
+
+ // Opening an existing folder, in read-only mode. A read-only lock
+ // will be fine.
+
+ bool ro=false;
+
+ if (hasExistingFolder && mboxAccount.currentFolderReadOnly)
+ {
+ if (!mailbox_lock(true))
+ {
+ if (errno != EAGAIN && errno != EEXIST)
+ {
+ fail(strerror(errno));
+ return true;
+ }
+ return false;
+ }
+ }
+
+ // Try for a read/write lock
+
+ else if (!mailbox_lock(false))
+ {
+ // If not permission denied, we're in trouble.
+
+ if (errno != EPERM && errno != EACCES)
+ {
+ if (errno != EAGAIN && errno != EEXIST)
+ {
+ fail(strerror(errno));
+ return true;
+ }
+
+ // EAGAIN means try again
+
+ return false;
+
+ }
+
+ if (isInbox) // Must have r/w to inbox
+ {
+ if (access(mboxAccount.inboxSpoolPath.c_str(), W_OK))
+ {
+ fail("Invalid permissions on system mailbox.");
+ return true;
+ }
+ return false;
+ }
+
+ // Try a read-only lock, then.
+
+ if (!mailbox_lock(true))
+ {
+ if (errno != EAGAIN)
+ {
+ fail(strerror(errno));
+ return true;
+ }
+ return false;
+ }
+ ro=true;
+ }
+
+ // Lock-n-load
+
+ int mboxfd=mailbox_lock.getfd();
+
+ mail::file mboxFp(mboxfd, isInbox ? "r+":"r");
+
+ if (!mboxAccount.scan(mboxFp, NULL, openCallback == NULL,
+ NULL,
+ false, &callback))
+ {
+ fail(errno == EINVAL
+ ? "File does not appear to be a mail folder."
+ : strerror(errno));
+ return true;
+ }
+
+ if (isInbox)
+ {
+ // Copy new mail.
+
+ mail::mbox::sighandler updating(mboxfd); // Critical section
+
+ mail::file spoolFp(inbox_lock.getfd(), "r");
+
+ if (!mboxAccount.scan(spoolFp, &mboxFp, openCallback == NULL,
+ NULL,
+ false, &callback))
+ {
+ updating.rollback();
+
+ fail(errno == EINVAL
+ ? "File does not appear to be a mail folder."
+ : strerror(errno));
+ return true;
+ }
+
+ updating.block();
+ if (ftruncate(inbox_lock.getfd(), 0) < 0)
+ ; // Ignore gcc warning
+
+ struct utimbuf ut;
+
+ if (fstat(mboxfd, &stat_buf) < 0)
+ {
+ fail(strerror(errno));
+ return true;
+ }
+
+ ut.modtime=--stat_buf.st_mtime;
+ ut.actime= ut.modtime;
+
+ if (utime(mboxAccount.inboxMailboxPath.c_str(), &ut) < 0)
+ {
+ fail(strerror(errno));
+ return true;
+ }
+ }
+ else if (fstat(mboxfd, &stat_buf) < 0)
+ {
+ fail(strerror(errno));
+ return true;
+ }
+
+ // Save folder particulars, to detect someone else's changes.
+
+ mboxAccount.folderSavedTimestamp= stat_buf.st_mtime;
+ mboxAccount.folderSavedSize=stat_buf.st_size;
+
+ const char *okmsg="OK";
+
+ if (openCallback) // We're doing mail::folder::open()
+ {
+ mboxAccount.currentFolderCallback=openCallback;
+ mboxAccount.currentFolderReadOnly=ro;
+
+ mboxAccount.index.clear();
+
+ vector<mboxMessageIndex>::iterator b, e;
+
+ b=mboxAccount.folderMessageIndex.begin();
+ e=mboxAccount.folderMessageIndex.end();
+
+ while (b != e)
+ mboxAccount.index.push_back( (*b++).tag.getMessageInfo());
+
+ mboxAccount.currentFolder=folder;
+
+ if (ro)
+ okmsg="WARNING: Folder opened in read-only mode.";
+ }
+
+ opened(okmsg, callback);
+ return true;
+}
+
+void mail::mbox::OpenTask::opened(const char *okmsg, mail::callback &callback)
+{
+ done();
+ callback.success("Folder opened");
+}
+
+//
+// Slight variation on the above - explicit new mail check.
+//
+
+
+mail::mbox::CheckNewMailTask::CheckNewMailTask(mail::mbox &mboxAccount,
+ string folderArg,
+ mail::callback &callbackArg,
+ mail::callback::folder
+ *openCallbackArg)
+ : OpenTask(mboxAccount, folderArg, callbackArg, openCallbackArg)
+{
+}
+
+mail::mbox::CheckNewMailTask::~CheckNewMailTask()
+{
+}
+
+void mail::mbox::CheckNewMailTask::opened(const char *okmsg,
+ mail::callback &callback)
+{
+ mboxAccount.checkNewMail();
+
+ if (mboxAccount.newMessages)
+ {
+ mboxAccount.newMessages=false;
+ if (mboxAccount.currentFolderCallback)
+ mboxAccount.currentFolderCallback->newMessages();
+ }
+ OpenTask::opened(okmsg, callback);
+}
+
+