/* ** Copyright 2002-2011, Double Precision Inc. ** ** See COPYING for distribution information. */ #include "libmail_config.h" #include "misc.H" #include "smtp.H" #include "driver.H" #include "search.H" #include "imaphmac.H" #include "base64.H" #include "smtpfolder.H" #include "tcpd/spipe.h" #include "unicode/courier-unicode.h" #include "libcouriertls.h" #include #include #include #include #include #if HAVE_SYS_WAIT_H #include #endif #ifndef WEXITSTATUS #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED #define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif using namespace std; #define SMTPTIMEOUT 60 #define INACTIVITY_TIMEOUT 30 #define DIGIT(c) (strchr("0123456789", (c))) ///////////////////////////////////////////////////////////////////////////// LIBMAIL_START static bool open_smtp(mail::account *&accountRet, mail::account::openInfo &oi, mail::callback &callback, mail::callback::disconnect &disconnectCallback) { mail::loginInfo nntpLoginInfo; if (!mail::loginUrlDecode(oi.url, nntpLoginInfo)) return false; if (nntpLoginInfo.method != "smtp" && nntpLoginInfo.method != "smtps" && nntpLoginInfo.method != "sendmail") return false; accountRet=new mail::smtp(oi.url, oi.pwd, oi.certificates, callback, disconnectCallback, oi.loginCallbackObj); return true; } static bool smtp_remote(string url, bool &flag) { mail::loginInfo nntpLoginInfo; if (!mail::loginUrlDecode(url, nntpLoginInfo)) return false; if (nntpLoginInfo.method != "smtp" && nntpLoginInfo.method != "smtps") return false; flag=true; return true; } driver smtp_driver= { &open_smtp, &smtp_remote }; LIBMAIL_END /////////////////////////////////////////////////////////////////////////// // // Helper class sends outbound message // /////////////////////////////////////////////////////////////////////////// mail::smtp::Blast::Blast(mail::smtp *smtpArg) : eofSent(false), mySmtp(smtpArg), lastChar('\n'), bytesTotal(0), bytesDone(0) { } mail::smtp::Blast::~Blast() { if (mySmtp) mySmtp->myBlaster=NULL; } bool mail::smtp::Blast::fillWriteBuffer() { if (!mySmtp) { if (!eofSent) { eofSent=true; writebuffer += "\r\n.\r\n"; return true; } return false; } mySmtp->installHandler(mySmtp->responseHandler); // Reset the timeout every time the write buffer gets filled char buffer[BUFSIZ]; size_t i; FILE *fp=mySmtp->sendQueue.front().message; for (i=0; isendQueue.front().callback; if (c) c->reportProgress(bytesTotal, bytesTotal, 0, 1); mySmtp->myBlaster=NULL; mySmtp=NULL; eofSent=true; return true; } ++bytesDone; if (lastChar == '\n' && ch == '.') { ungetc(ch, fp); // Leading .s are doubled. --bytesDone; } if (ch == '\n' && lastChar != '\r') { ungetc(ch, fp); --bytesDone; ch='\r'; } buffer[i]=lastChar=ch; } mail::callback *c=mySmtp->sendQueue.front().callback; if (c) c->reportProgress(bytesDone, bytesTotal, 0, 1); writebuffer += string(buffer, buffer+sizeof(buffer)); return true; } // Something arrived from the server. int mail::smtp::socketRead(const string &readbuffer) { size_t n=readbuffer.find('\n'); if (n == std::string::npos) return 0; string l=readbuffer.substr(0, n++); size_t cr; while ((cr=l.find('\r')) != std::string::npos) l=l.substr(0, cr) + l.substr(cr+1); // Collect multiline SMTP replies. Keep only the last 30 lines // received. smtpResponse.push_back(l); if (smtpResponse.size() > 30) smtpResponse.erase(smtpResponse.begin()); string::iterator b=l.begin(), e=l.end(); int numCode=0; while (b != e && isdigit((int)(unsigned char)*b)) numCode=numCode * 10 + (*b++ - '0'); if (b == l.begin()+3) { if (b == e || unicode_isspace((unsigned char)*b)) { string s=""; list::iterator b, e; b=smtpResponse.begin(); e=smtpResponse.end(); while (b != e) { if (s.size() > 0) s += "\n"; s += *b++; } smtpResponse.clear(); if (responseHandler != 0) (this->*responseHandler)(numCode, s); else fatalError=true; // SMTP msg when no handler installed, something's // gone wrong. } } return n; } void mail::smtp::installHandler(void (mail::smtp::*handler)(int, string)) { responseHandler=handler; responseTimeout=0; if (handler != 0) { time(&responseTimeout); responseTimeout += SMTPTIMEOUT; } } void mail::smtp::disconnect(const char *reason) { while (!sendQueue.empty()) { struct messageQueueInfo &info=sendQueue.front(); info.callback->fail(reason); fclose(info.message); sendQueue.pop(); } pingTimeout=0; if (getfd() >= 0) { string errmsg=socketDisconnect(); if (errmsg.size() == 0) errmsg=reason; if (orderlyShutdown) success(errmsg); else if (smtpLoginInfo.callbackPtr) error(errmsg); else disconnect_callback.disconnected(errmsg.c_str()); } } void mail::smtp::resumed() { if (responseHandler != 0) { time(&responseTimeout); responseTimeout += SMTPTIMEOUT; } } void mail::smtp::handler(vector &fds, int &ioTimeout) { if (fatalError) return; int fd_save=getfd(); time_t now; time(&now); if (responseHandler != 0) { if (now >= responseTimeout) { fatalError=true; ioTimeout=0; (this->*responseHandler)(500, "500 Server timed out."); return; } if (ioTimeout >= (responseTimeout - now) * 1000) { ioTimeout=(responseTimeout - now)*1000; } } if (pingTimeout) { if (pingTimeout < now) { mail::smtpInfo dummy; send(NULL, dummy, NULL, false); } else { if (ioTimeout >= (pingTimeout - now)*1000) { ioTimeout=(pingTimeout - now)*1000; } } } mail::fd::process(fds, ioTimeout); if (getfd() < 0) { size_t i; for (i=fds.size(); i; ) { --i; if (fds[i].fd == fd_save) { fds.erase(fds.begin()+i, fds.begin()+i+1); break; } } } } mail::smtp::smtp(string url, string passwd, std::vector &certificates, mail::callback &callback, mail::callback::disconnect &disconnectCallback, loginCallback *loginCallbackFunc) : mail::fd(disconnectCallback, certificates), orderlyShutdown(false), ready2send(false), fatalError(true), pingTimeout(0) { responseHandler=NULL; smtpLoginInfo.callbackPtr= &callback; smtpLoginInfo.loginCallbackFunc=loginCallbackFunc; if (!loginUrlDecode(url, smtpLoginInfo)) { smtpLoginInfo.callbackPtr->fail("Invalid SMTP URL."); return; } if (smtpLoginInfo.method == "sendmail") // Local sendmail { smtpLoginInfo.use_ssl=false; smtpLoginInfo.uid=""; smtpLoginInfo.pwd=""; string errmsg; int pipefd[2]; if (libmail_streampipe(pipefd) < 0) { smtpLoginInfo.callbackPtr->fail(strerror(errno)); return; } pid_t pid1=fork(); if (pid1 < 0) { close(pipefd[0]); close(pipefd[1]); smtpLoginInfo.callbackPtr->fail(strerror(errno)); return; } if (pid1 == 0) { close(0); close(1); errno=EIO; if (dup(pipefd[1]) != 0 || dup(pipefd[1]) != 1) { perror("500 dup"); exit(1); } close(pipefd[0]); close(pipefd[1]); pid1=fork(); if (pid1 < 0) { printf("500 fork() failed.\r\n"); exit(1); } if (pid1) exit(0); execl(SENDMAIL, SENDMAIL, "-bs", (char *)NULL); printf("500 %s: %s\n", SENDMAIL, strerror(errno)); exit(0); } close(pipefd[1]); int waitstat; pid_t pid2; while ((pid2=wait(&waitstat)) != pid1) { if (kill(pid1, 0)) break; } try { errmsg=socketAttach(pipefd[0]); } catch (...) { close(pipefd[0]); LIBMAIL_THROW(LIBMAIL_THROW_EMPTY); } } else { smtpLoginInfo.use_ssl= smtpLoginInfo.method == "smtps"; if (passwd.size() > 0) smtpLoginInfo.pwd=passwd; string errmsg=socketConnect(smtpLoginInfo, "smtp", "smtps"); if (errmsg.size() > 0) { smtpLoginInfo.callbackPtr->fail(errmsg); return; } } fatalError=false; hmac_method_index=0; installHandler(&mail::smtp::greetingResponse); } // Login failed void mail::smtp::error(string errMsg) { mail::callback *c=smtpLoginInfo.callbackPtr; smtpLoginInfo.callbackPtr=NULL; if (c) c->fail(errMsg); } // Login succeeded void mail::smtp::success(string errMsg) { mail::callback *c=smtpLoginInfo.callbackPtr; smtpLoginInfo.callbackPtr=NULL; if (c) c->success(errMsg); } // Send EHLO/HELO void mail::smtp::howdy(const char *hi) { char buffer[512]; buffer[sizeof(buffer)-1]=0; if (gethostname(buffer, sizeof(buffer)-1) < 0) strcpy(buffer, "localhost"); socketWrite(string(hi) + buffer + "\r\n"); } void mail::smtp::greetingResponse(int n, string s) { switch (n / 100) { case 1: case 2: case 3: break; default: error(s); return; } installHandler(&mail::smtp::ehloResponse); howdy("EHLO "); } void mail::smtp::ehloResponse(int n, string s) { switch (n / 100) { case 1: case 2: case 3: break; default: // Maybe HELO will work installHandler(&mail::smtp::heloResponse); howdy("HELO "); return; } // Parse EHLO capability list bool firstLine=true; while (s.size() > 0) { string line; size_t p=s.find('\n'); if (p == std::string::npos) { line=s; s=""; } else { line=s.substr(0, p); s=s.substr(p+1); } if (firstLine) { firstLine=false; continue; } while ((p=s.find('\r')) != std::string::npos) s=s.substr(0, p) + s.substr(p+1); for (p=0; p 0) { capabilities.insert(capa + "=" + line.substr(0, p)); } if (p < line.size()) p++; line=line.substr(p); } } else capabilities.insert(capa); } starttls(); } void mail::smtp::heloResponse(int n, string s) { switch (n / 100) { case 1: case 2: case 3: starttls(); return; } error(s); } void mail::smtp::starttls() { #if HAVE_LIBCOURIERTLS if (smtpLoginInfo.use_ssl // Already SSL-encrypted || smtpLoginInfo.options.count("notls") > 0 || !hasCapability("STARTTLS") || socketEncrypted()) { begin_auth(); return; } installHandler(&mail::smtp::starttlsResponse); socketWrite("STARTTLS\r\n"); #else begin_auth(); return; #endif } void mail::smtp::starttlsResponse(int n, string s) { switch (n / 100) { case 1: case 2: if (!socketBeginEncryption(smtpLoginInfo)) { smtpLoginInfo.callbackPtr=NULL; return; } capabilities.clear(); greetingResponse(n, s); return; } error(s); } void mail::smtp::begin_auth() { if (!hasCapability("AUTH=EXTERNAL")) { begin_auth_nonexternal(); return; } installHandler(&mail::smtp::auth_external_response); socketWrite("AUTH EXTERNAL =\r\n"); } void mail::smtp::auth_external_response(int n, string s) { switch (n / 100) { case 1: case 2: case 3: authenticated(); return; } begin_auth_nonexternal(); } void mail::smtp::begin_auth_nonexternal() { int i; if (smtpLoginInfo.uid.size() == 0) { authenticate_hmac(); return; } for (i=0; mail::imaphmac::hmac_methods[i]; ++i) { if (hasCapability(string("AUTH=CRAM-") + mail::imaphmac ::hmac_methods[hmac_method_index]->getName() )) break; } if (mail::imaphmac::hmac_methods[i] || hasCapability("AUTH=LOGIN")) { if (smtpLoginInfo.pwd.size() == 0) { currentCallback=smtpLoginInfo.loginCallbackFunc; if (currentCallback) { currentCallback->target=this; currentCallback->getPassword(); return; } } } authenticate_hmac(); } void mail::smtp::loginInfoCallback(std::string str) { currentCallback=NULL; smtpLoginInfo.pwd=str; authenticate_hmac(); } void mail::smtp::loginInfoCallbackCancel() { currentCallback=NULL; error("Login cancelled"); } void mail::smtp::authenticate_hmac() { for (;;) { if (mail::imaphmac::hmac_methods[hmac_method_index] == NULL || smtpLoginInfo.uid.size() == 0) { authenticate_login(); // Exhausted SASL, forget it. return; } if (hasCapability(string("AUTH=CRAM-") + mail::imaphmac ::hmac_methods[hmac_method_index]->getName() )) break; hmac_method_index++; } installHandler(&mail::smtp::hmacResponse); socketWrite(string("AUTH CRAM-") + mail::imaphmac::hmac_methods[hmac_method_index]->getName() + "\r\n"); } void mail::smtp::hmacResponse(int n, string s) { switch (n / 100) { case 1: case 2: authenticated(); return; } if ((n / 100) != 3 || s.size() < 4) { ++hmac_method_index; authenticate_hmac(); return; } s=mail::decodebase64str(s.substr(4)); s=(*mail::imaphmac::hmac_methods[hmac_method_index])(smtpLoginInfo .pwd, s); string sHex; { ostringstream hexEncode; hexEncode << hex; string::iterator b=s.begin(); string::iterator e=s.end(); while (b != e) hexEncode << setw(2) << setfill('0') << (int)(unsigned char)*b++; sHex=hexEncode.str(); } s=mail::encodebase64str(smtpLoginInfo.uid + " " + sHex); socketWrite(s + "\r\n"); } void mail::smtp::authenticate_login() { if (!hasCapability("AUTH=LOGIN") || smtpLoginInfo.uid.size() == 0 || smtpLoginInfo.options.count("cram")) { authenticated(); return; } installHandler(&mail::smtp::loginUseridResponse); socketWrite("AUTH LOGIN\r\n"); } void mail::smtp::loginUseridResponse(int n, string msg) { if ((n / 100) != 3) { error(msg); return; } installHandler(&mail::smtp::loginPasswdResponse); socketWrite(string(mail::encodebase64str(smtpLoginInfo.uid)) + "\r\n"); } void mail::smtp::loginPasswdResponse(int n, string msg) { if ((n / 100) != 3) { error(msg); return; } installHandler(&mail::smtp::loginResponse); socketWrite(string(mail::encodebase64str(smtpLoginInfo.pwd)) + "\r\n"); } void mail::smtp::loginResponse(int n, string msg) { if ((n / 100) != 2) { error(msg); return; } authenticated(); } void mail::smtp::authenticated() { ready2send=true; if (!sendQueue.empty()) startNextMessage(); // Already something's queued up. else pingTimeout=time(NULL) + INACTIVITY_TIMEOUT; success("Connected to " + smtpLoginInfo.server); } mail::smtp::~smtp() { disconnect("Connection forcibly aborted."); } void mail::smtp::logout(mail::callback &callback) { if (fatalError) { callback.success("Server timed out."); return; } smtpLoginInfo.callbackPtr= &callback; orderlyShutdown=true; installHandler(&mail::smtp::quitResponse); socketWrite("QUIT\r\n"); } void mail::smtp::quitResponse(int n, string s) { if (!socketEndEncryption()) { socketDisconnect(); success(s); } } void mail::smtp::checkNewMail(mail::callback &callback) // STUB { callback.success("OK"); } bool mail::smtp::hasCapability(string capability) { if (capability == LIBMAIL_SINGLEFOLDER) return false; return capabilities.count(capability) > 0; } string mail::smtp::getCapability(string name) { upper(name); if (name == LIBMAIL_SERVERTYPE) { return "smtp"; } if (name == LIBMAIL_SERVERDESCR) { return "ESMTP server"; } if (name == LIBMAIL_SINGLEFOLDER) return ""; return capabilities.count(name) > 0 ? "1":""; } // Not implemented mail::folder *mail::smtp::folderFromString(string) { errno=EINVAL; return NULL; } // Not implemented void mail::smtp::readTopLevelFolders(mail::callback::folderList &callback1, mail::callback &callback2) { vector dummy; callback1.success(dummy); callback2.success("OK"); } // Not implemented void mail::smtp::findFolder(string folder, class mail::callback::folderList &callback1, class mail::callback &callback2) { callback2.fail("Folder not found."); } // This is why we're here: mail::folder *mail::smtp::getSendFolder(const mail::smtpInfo &info, const mail::folder *folderArg, string &errmsg) { if (folderArg) { return mail::account::getSendFolder(info, folderArg, errmsg); } if (info.recipients.size() == 0) { errno=ENOENT; errmsg="Empty recipient list."; return NULL; } mail::smtpFolder *folder=new mail::smtpFolder(this, info); if (!folder) { errmsg=strerror(errno); return NULL; } return folder; } void mail::smtp::readMessageAttributes(const vector &messages, MessageAttributes attributes, mail::callback::message &callback) { callback.fail("Invalid message number."); } void mail::smtp::readMessageContent(const vector &messages, bool peek, enum mail::readMode readType, mail::callback::message &callback) { callback.fail("Invalid message number."); } void mail::smtp::readMessageContent(size_t messageNum, bool peek, const mail::mimestruct &msginfo, enum mail::readMode readType, mail::callback::message &callback) { callback.fail("Invalid message number."); } void mail::smtp::readMessageContentDecoded(size_t messageNum, bool peek, const mail::mimestruct &msginfo, mail::callback::message &callback) { callback.fail("Invalid message number."); } size_t mail::smtp::getFolderIndexSize() { return 0; } mail::messageInfo mail::smtp::getFolderIndexInfo(size_t messageNum) { return mail::messageInfo(); } void mail::smtp::saveFolderIndexInfo(size_t messageNumber, const mail::messageInfo &info, mail::callback &callback) { callback.fail("Invalid message number."); } void mail::smtp::updateFolderIndexFlags(const vector &messages, bool doFlip, bool enableDisable, const mail::messageInfo &flags, mail::callback &callback) { callback.fail("Invalid message number."); } void mail::smtp::updateFolderIndexInfo(mail::callback &callback) { callback.success("OK"); } void mail::smtp::removeMessages(const std::vector &messages, mail::callback &cb) { updateFolderIndexInfo(cb); } void mail::smtp::copyMessagesTo(const vector &messages, mail::folder *copyTo, mail::callback &callback) { callback.fail("Invalid message number."); } void mail::smtp::searchMessages(const mail::searchParams &searchInfo, mail::searchCallback &callback) { vector empty; callback.success(empty); } ////////////////////////////////////////////////////////////////////////////// // // Another message is ready to go out. Called by mail::smtpFolder::go, // and one other place. void mail::smtp::send(FILE *tmpfile, mail::smtpInfo &info, mail::callback *callback, bool flag8bit) { pingTimeout=0; struct messageQueueInfo msgInfo; msgInfo.message=tmpfile; msgInfo.messageInfo=info; msgInfo.callback=callback; msgInfo.flag8bit=flag8bit; bool wasEmpty=sendQueue.empty(); sendQueue.push(msgInfo); if (wasEmpty && ready2send) startNextMessage(); } void mail::smtp::startNextMessage() { if (fatalError) { messageProcessed(500, "500 A fatal error occurred while connected to remote server."); return; } socketWrite("RSET\r\n"); installHandler(&mail::smtp::rsetResponse); } void mail::smtp::rsetResponse(int result, string message) { struct messageQueueInfo &info=sendQueue.front(); if (info.message == NULL) // Just a NO-OP ping. { sendQueue.pop(); if (!sendQueue.empty()) startNextMessage(); else pingTimeout=time(NULL) + INACTIVITY_TIMEOUT; return; } switch (result / 100) { case 1: case 2: case 3: break; default: messageProcessed(result, message); return; } if (!validString(info.messageInfo.sender)) return; { vector::iterator b=info.messageInfo.recipients.begin(), e=info.messageInfo.recipients.end(); while (b != e) if (!validString(*b++)) return; } { map::iterator b=info.messageInfo.options.begin(), e=info.messageInfo.options.end(); while (b != e) if (!validString((*b++).second)) return; } string mailfrom="MAIL FROM:<" + info.messageInfo.sender + ">"; if (hasCapability("8BITMIME")) mailfrom += info.flag8bit ? " BODY=8BITMIME":" BODY=7BIT"; if (hasCapability("SIZE")) { long pos; if (fseek(info.message, 0L, SEEK_END) < 0 || (pos=ftell(info.message)) < 0) { messageProcessed(500, strerror(errno)); return; } pos= pos < 1024 * 1024 ? pos * 70 / 68: pos/68 * 70; string buffer; { ostringstream o; o << pos; buffer=o.str(); } mailfrom += " SIZE="; mailfrom += buffer; } if (info.messageInfo.options.count("DSN") > 0 || info.messageInfo.options.count("RET") > 0) { if (!hasCapability("DSN")) { messageProcessed(500, "500 This mail server does not support delivery status notifications."); return; } } if (hasCapability("DSN")) { if (info.messageInfo.options.count("RET") > 0) mailfrom += " RET=" + info.messageInfo.options .find("RET")->second; } if (hasCapability("XVERP=COURIER")) { if (info.messageInfo.options.count("VERP") > 0) mailfrom += " VERP"; } if (info.messageInfo.options.count("SECURITY") > 0) { string sec=info.messageInfo.options.find("SECURITY")->second; if (info.messageInfo.options.count("XSECURITY=" + sec) == 0) { messageProcessed(500, "500 requested security not available."); return; } mailfrom += " SECURITY=" + sec; } pipelineCmd=mailfrom; mailfrom += "\r\n"; pipelineCount=0; installHandler(&mail::smtp::mailfromResponse); socketWrite(mailfrom); } // RCPT TO handler in pipeline mode. void mail::smtp::pipelinedResponse(int result, string message) { switch (result / 100) { case 1: case 2: case 3: break; default: if (pipelineError.size() == 0) pipelineError=sendQueue.front().messageInfo .recipients[rcptCount] + ": " + message; break; } ++rcptCount; if (--pipelineCount || fatalError) return; // More on the way if (pipelineError.size() > 0) messageProcessed(500, pipelineError); else { installHandler(&mail::smtp::dataResponse); socketWrite("DATA\r\n"); } } // MAIL FROM response. RCPT TO response in non-pipelined mode. void mail::smtp::mailfromResponse(int result, string message) { switch (result / 100) { case 1: case 2: case 3: break; default: messageProcessed(result, pipelineCmd + ": " + message); return; } struct messageQueueInfo &info=sendQueue.front(); rcptCount=0; if (pipelineCount == 0 && hasCapability("PIPELINING") && smtpLoginInfo.options.count("nopipelining") == 0) { string rcptto=""; vector::iterator b=info.messageInfo.recipients.begin(), e=info.messageInfo.recipients.end(); pipelineError=""; while (b != e) { rcptto += "RCPT TO:<" + *b++ + ">"; if (info.messageInfo.options.count("DSN") > 0) rcptto += " NOTIFY=" + info.messageInfo.options.find("DSN") ->second; rcptto += "\r\n"; ++pipelineCount; } installHandler(&mail::smtp::pipelinedResponse); socketWrite(rcptto); return; } if (pipelineCount < info.messageInfo.recipients.size()) { pipelineCmd=info.messageInfo.recipients[pipelineCount++]; string rcptto= "RCPT TO:<" + pipelineCmd + ">"; if (info.messageInfo.options.count("DSN") > 0) rcptto += " NOTIFY=" + info.messageInfo.options.find("DSN") ->second; rcptto += "\r\n"; socketWrite(rcptto); return; } installHandler(&mail::smtp::dataResponse); socketWrite("DATA\r\n"); } // 1st DATA response void mail::smtp::dataResponse(int result, string message) { switch (result / 100) { case 1: case 2: case 3: break; default: messageProcessed(result, message); break; } long fpos; if ( fseek(sendQueue.front().message, 0L, SEEK_END) < 0 || (fpos=ftell(sendQueue.front().message)) < 0 || fseek(sendQueue.front().message, 0L, SEEK_SET) < 0 || (myBlaster=new Blast(this)) == NULL) { fatalError=true; messageProcessed(500, strerror(errno)); return; } myBlaster->bytesTotal=fpos; installHandler(&mail::smtp::data2Response); socketWrite(myBlaster); } void mail::smtp::data2Response(int result, string message) { if (myBlaster) myBlaster->mySmtp=NULL; installHandler(NULL); // Nothing more. messageProcessed(result, message); } bool mail::smtp::validString(string s) { string::iterator b=s.begin(), e=s.end(); while (b != e) { if ((int)(unsigned char)*b <= ' ' || *b == '>' || *b == '<') { messageProcessed(500, "500 Invalid character in message sender, recipient, or options."); return false; } b++; } return true; } void mail::smtp::messageProcessed(int result, string message) { struct messageQueueInfo &info=sendQueue.front(); mail::callback *callback=info.callback; fclose(info.message); try { sendQueue.pop(); if (!sendQueue.empty()) startNextMessage(); else pingTimeout=time(NULL) + INACTIVITY_TIMEOUT; switch (result / 100) { case 1: case 2: case 3: callback->success(message); break; default: callback->fail(message); break; } } catch (...) { callback->fail("An exception has occurred while processing message."); } } string mail::smtp::translatePath(string path) { return "INBOX"; // NOOP }