Attempt to fix several flaws that prevented me from backing up my 650MB of mails from my BeOS partition:

- Select() didn't actually reselect even when asked to force things from AddMessage(), making the uploaded mail get an off-by-one unique id assigned and a second copy downloaded on subsequent fetching, though this would really need proofreading as I'm not really sure how it all works,
- the allocated buffer wasn't freed, making mail_daemon allocate 650MB, which obviously crashed when out of physical ram, now it only uses 15MB :p,
- try to find workable IMAP flags for sent and pending mails, or use custom ones when the server allows arbitrary flags,
- the LIST command wasn't checked for correct response, making subsequent commands like CREATE mailbox fail from the OK answer of previous ones when syncing on a new accound,
- try to read the creating time (actually modification time since creation time is reset when copying files around) and pass it to APPEND command so I won't get the whole 10000 mails all received as of today.
Now I can put all my old mails on an imap server (tested on dovecot) to read it from other OSes.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36995 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
François Revol 2010-06-01 19:04:00 +00:00
parent a5bc5bb66d
commit 0c538753e2

View File

@ -114,6 +114,8 @@ class IMAP4Client : public BRemoteMailStorageProtocol {
bool force_reselect;
int32 fLastExists;
BString fSentFlag;
BString fPendingFlag;
};
class NoopWorker : public BHandler {
@ -386,6 +388,7 @@ void IMAP4Client::InitializeMailboxes() {
hierarchy_delimiter = dem[2]();
if (hierarchy_delimiter == "" || hierarchy_delimiter == "NIL")
hierarchy_delimiter = "/";
WasCommandOkay(tag);
}
if (mb_root.ByteAt(mb_root.Length() - 1) != hierarchy_delimiter.ByteAt(0)) {
@ -433,17 +436,33 @@ status_t IMAP4Client::AddMessage(const char *mailbox, BPositionIO *data, BString
if (node != NULL) {
BString status;
node->ReadAttrString(B_MAIL_ATTR_STATUS,&status);
/*if (status == "Sent")
attributes += " \\Sent";
if (status == "Pending")
attributes += " \\Sent";*/
if (status == "Sent" && fSentFlag.Length())
attributes << " " << fSentFlag;
if (status == "Pending" && fPendingFlag.Length())
attributes << " " << fPendingFlag;
if (status == "Replied")
attributes += " \\Answered";
}
}
BString receivedDate;
BFile *file = dynamic_cast<BFile *> (data);
time_t creationTime;
// actually creation time is reset on file copy, modification time seems better...
if (file && file->GetModificationTime(&creationTime) == B_OK) {
struct tm tm;
localtime_r(&creationTime, &tm);
char *buffer = receivedDate.LockBuffer(256);
command << ((struct mailbox_info *)(box_info.ItemAt(box_index)))->server_mb_name << "\" (" << attributes << ") {" << size << '}';
strftime(buffer, 256, "%e-%b-%Y %H:%M:%S %z", &tm);
receivedDate.UnlockBuffer();
receivedDate.Prepend('"', 1);
receivedDate << "\" ";
}
command << ((struct mailbox_info *)(box_info.ItemAt(box_index)))->server_mb_name
<< "\" (" << attributes << ") " << receivedDate << "{" << size << '}';
SendCommand(command.String());
status_t err = ReceiveLine(command);
if (err < B_OK)
@ -461,7 +480,10 @@ status_t IMAP4Client::AddMessage(const char *mailbox, BPositionIO *data, BString
send(net,buffer,size,0);
send(net,"\r\n",2,0);
}
Select(mailbox,false,false,false,true);
delete [] buffer;
force_reselect = true;
Select(mailbox,true,false,false,false,false);
if (((struct mailbox_info *)(box_info.ItemAt(box_index)))->next_uid <= 0) {
command = "FETCH ";
@ -679,7 +701,7 @@ status_t IMAP4Client::Select(const char *mb, bool reselect, bool queue_new_messa
const char *real_mb = info->server_mb_name.String();
if ((selected_mb != real_mb) || (noop) || (no_command)) {
if ((selected_mb != real_mb) || (noop) || (no_command) || (reselect)) {
if ((selected_mb != "") && (selected_mb != real_mb)){
BString trash;
if (SendCommand("CLOSE") < B_OK)
@ -689,7 +711,7 @@ status_t IMAP4Client::Select(const char *mb, bool reselect, bool queue_new_messa
WasCommandOkay(trash);
}
BString cmd;
if (selected_mb == real_mb)
if (selected_mb == real_mb && !reselect)
cmd = "NOOP";
else
cmd << "SELECT \"" << real_mb << '\"';
@ -720,6 +742,32 @@ status_t IMAP4Client::Select(const char *mb, bool reselect, bool queue_new_messa
if ((response.CountItems() > 1) && (strcasecmp(response[1](),"RECENT") == 0))
recent = atoi(response[0]());
// search a workable Sent flag.
// cf.
// http://serverfault.com/questions/115769/which-imap-flags-are-reliably-supported-across-most-mail-servers
if (response[0].CountItems() == 2 && strcasecmp(response[0][0](),"PERMANENTFLAGS") == 0) {
//PRINT(("PERM FLAGS: '%s'\n", response[0][1]()));
for (int i = 0; i < response[0][1].CountItems(); i++) {
BString flag(response[0][1][i]());
//PRINT(("PERM FLAGS[%d]: '%s'\n", i, response[0][1][i]()));
// lucky you
if (flag == "\\Sent")
fSentFlag = "\\Sent";
// the server supports arbitrary flags, define our own;
else if (flag == "\\*")
fSentFlag = "$Sent";
// note $MDNSent is not the same, it's used for delivery notifications...
if (flag == "\\Pending")
fPendingFlag = "\\Pending";
// the server supports arbitrary flags, define our own;
else if (flag == "\\*")
fPendingFlag = "$Pending";
}
//PRINT(("Sent FLAG: '%s'\n", fSentFlag.String()));
//PRINT(("Pending FLAG: '%s'\n", fPendingFlag.String()));
}
}
// Whenever the mailbox is updated, only EXISTS is sent so we have no
@ -871,12 +919,18 @@ status_t IMAP4Client::GetMessage(const char *mailbox, const char *message, BPosi
if (strcmp(response[2][1][i](),"\\Seen") == 0) {
headers->AddString("STATUS","Read");
}
if (strcmp(response[2][1][i](),"\\Sent") == 0) {
if (fSentFlag.Length() && strcmp(response[2][1][i](),fSentFlag.String()) == 0) {
if (headers->HasString("STATUS"))
headers->ReplaceString("STATUS","Sent");
else
headers->AddString("STATUS","Sent");
}
if (fPendingFlag.Length() && strcmp(response[2][1][i](),fPendingFlag.String()) == 0) {
if (headers->HasString("STATUS"))
headers->ReplaceString("STATUS","Pending");
else
headers->AddString("STATUS","Pending");
}
if (strcmp(response[2][1][i](),"\\Answered") == 0) {
if (headers->HasString("STATUS"))
headers->ReplaceString("STATUS","Replied");