diff options
Diffstat (limited to 'libmail/nntp.C')
| -rw-r--r-- | libmail/nntp.C | 1489 |
1 files changed, 1489 insertions, 0 deletions
diff --git a/libmail/nntp.C b/libmail/nntp.C new file mode 100644 index 0000000..b7a51c5 --- /dev/null +++ b/libmail/nntp.C @@ -0,0 +1,1489 @@ +/* +** Copyright 2003-2008, Double Precision Inc. +** +** See COPYING for distribution information. +*/ +#include "libmail_config.h" +#include "mail.H" +#include "misc.H" +#include "driver.H" +#include "nntp.H" +#include "nntpnewsrc.H" +#include "nntpxpat.H" +#include "copymessage.H" +#include "search.H" +#include "smtpinfo.H" +#include "objectmonitor.H" +#include "libcouriertls.h" +#include "expungelist.H" +#include <unicode/unicode.h> +#include <sstream> +#include <iostream> +#include <iomanip> +#include <time.h> +#include <errno.h> +#include <unistd.h> +#include <algorithm> +#include <fstream> +#include <vector> +#include <signal.h> +#include <set> +#include "nntplogin2.H" +#include "nntpfolder.H" +#include "nntplogout.H" +#include "nntpfetch.H" +#include "nntpcache.H" +#include "nntpxover.H" +#include "nntpchecknew.H" +#include "rfc2045/rfc2045.h" + +#define TIMEOUTINACTIVITY 15 + +using namespace std; + +mail::nntp::nntpMessageInfo::nntpMessageInfo() + : msgNum(0), msgFlag(0) +{ +} + +mail::nntp::nntpMessageInfo::~nntpMessageInfo() +{ +} + +///////////////////////////////////////////////////////////////////////////// + +LIBMAIL_START + +static bool open_nntp(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 != "nntp" && + nntpLoginInfo.method != "nntps") + return false; + + accountRet=new mail::nntp(oi.url, oi.pwd, oi.certificates, + oi.extraString, + oi.loginCallbackObj, + callback, + disconnectCallback); + return true; +} + +static bool nntp_remote(string url, bool &flag) +{ + mail::loginInfo nntpLoginInfo; + + if (!mail::loginUrlDecode(url, nntpLoginInfo)) + return false; + + if (nntpLoginInfo.method != "nntp" && + nntpLoginInfo.method != "nntps") + return false; + + flag=true; + return true; +} + +driver nntp_driver= { &open_nntp, &nntp_remote }; + +LIBMAIL_END + +///////////////////////////////////////////////////////////////////////////// +// +// Service this account. +// + +void mail::nntp::handler(vector<pollfd> &fds, int &ioTimeout) +{ + int fd_save=getfd(); + + // Disconnect from the server, after a period of inactivity. + + if (inactivityTimeout) + { + time_t now=time(NULL); + + if (now + autologoutSetting < inactivityTimeout) + // Time reset?? + { + inactivityTimeout = now + autologoutSetting; + } + + if (now >= inactivityTimeout) + { + installTask(new mail::nntp::LogoutTask(NULL, + *this, + true)); + } + else + { + now = inactivityTimeout - now; + + if (now * 1000 <= ioTimeout) + { + ioTimeout = now * 1000; + } + } + } + + if (!tasks.empty()) + { + int t=(*tasks.begin())->getTimeout(); + + if (t * 1000 <= ioTimeout) + { + ioTimeout=t; + } + } + + MONITOR(mail::nntp); + + process(fds, ioTimeout); + + if (DESTROYED()) + ioTimeout=0; + + if (DESTROYED() || 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; + } + } + } + + return; +} + +void mail::nntp::resumed() +{ + if (!tasks.empty()) + (*tasks.begin())->resetTimeout(); +} + +// +// There's something to read. NNTP tasks read one line at a time. + +int mail::nntp::socketRead(const std::string &readbuffer) +{ + size_t n; + + if (!tasks.empty() && (n=readbuffer.find('\n')) != std::string::npos) + { + size_t i=n; + + if (i > 0 && readbuffer[i-1] == '\r') + --i; + + string line=readbuffer.substr(0, i); + + Task *t= *tasks.begin(); + + t->resetTimeout(); // Command is alive + t->serverResponse(line.c_str()); + return n+1; + } + + return 0; +} + +// +// Let the first task handle disconnection events. +// + +void mail::nntp::disconnect(const char *reason) +{ + string errmsg=reason ? reason:""; + + if (getfd() >= 0) + { + string errmsg2=socketDisconnect(); + + if (errmsg2.size() > 0) + errmsg=errmsg2; + } + + if (!tasks.empty()) + (*tasks.begin())->disconnected(errmsg.c_str()); +} + +mail::nntp::nntp(string url, string passwd, + std::vector<std::string> &certificates, + string newsrcFilenameArg, + mail::loginCallback *loginCallbackFunc, + callback &callback, + callback::disconnect &disconnectCallbackArg) + : mail::fd(disconnectCallbackArg, certificates), + inactivityTimeout(0), + folderCallback(NULL), + hasNewgroups(false), didCacheNewsrc(false), + disconnectCallback(&disconnectCallbackArg), + genericTmpFp(NULL), genericTmpRfcp(NULL) +{ + if (newsrcFilenameArg.size() == 0) + { + callback.fail("Invalid NNTP login (.newsrc unspecified)"); + return; + } + + newsrcFilename=newsrcFilenameArg; + + if (!mail::loginUrlDecode(url, savedLoginInfo)) + { + callback.fail("Invalid NNTP URL."); + return; + } + timeoutSetting=getTimeoutSetting(savedLoginInfo, "timeout", 60, + 30, 600); + autologoutSetting=getTimeoutSetting(savedLoginInfo, "autologout", 300, + 5, 1800); + + savedLoginInfo.loginCallbackFunc=loginCallbackFunc; + savedLoginInfo.use_ssl=savedLoginInfo.method == "nntps"; + if (passwd.size() > 0) + savedLoginInfo.pwd=passwd; + + try { + installTask(new LoginTask(&callback, *this)); + } catch (...) { + callback.fail(strerror(errno)); + } +} + +void mail::nntp::cleartmp() +{ + if (genericTmpFp != NULL) + { + fclose(genericTmpFp); + genericTmpFp=NULL; + } + + if (genericTmpRfcp != NULL) + { + rfc2045_free(genericTmpRfcp); + genericTmpRfcp=NULL; + } +} + +mail::nntp::~nntp() +{ + cleartmp(); + while (!tasks.empty()) + (*tasks.begin())->disconnected("NNTP connection aborted."); + + if (disconnectCallback) + { + callback::disconnect *d=disconnectCallback; + + disconnectCallback=NULL; + + d->disconnected("Connection aborted."); + } + index.clear(); +} + +// Cache newsrc into a map + +void mail::nntp::cacheNewsrc() +{ + if (didCacheNewsrc) + return; + + cachedNewsrc.clear(); + ifstream i(newsrcFilename.c_str()); + + string line; + + while (i.is_open() && !getline(i, line).fail()) + { + newsrc parseLine(line); + + if (parseLine.newsgroupname.size() == 0) + continue; + + cachedNewsrc.insert(make_pair(parseLine.newsgroupname, + parseLine)); + } + didCacheNewsrc=true; +} + +// Save an updated newsrc line + +bool mail::nntp::updateOpenedNewsrc(newsrc &n) +{ + updateCachedNewsrc(); + discardCachedNewsrc(); + + bool updated=false; + + string newNewsrcFilename=newsrcFilename + ".tmp"; + + ofstream o(newNewsrcFilename.c_str()); + + if (o.is_open()) + { + ifstream i(newsrcFilename.c_str()); + + string line; + + while (i.is_open() && !getline(i, line).fail()) + { + newsrc parseLine(line); + + if (parseLine.newsgroupname == n.newsgroupname) + { + o << (string)n << endl; + updated=true; + } + else + o << line << endl; + } + + if (!updated) + o << (string)n << endl; + + o << flush; + o.close(); + + if (!o.fail() && !o.bad() && + rename(newNewsrcFilename.c_str(), + newsrcFilename.c_str()) == 0) + return true; + } + return false; +} + +// For speed, we sort the cachedNewsrc by ptrs + +class mail::nntp::cachedNewsrcSort { +public: + bool operator()(newsrc *a, newsrc *b) + { + return a->newsgroupname < b->newsgroupname; + } +}; + +// Save updated newsrc cache + +bool mail::nntp::updateCachedNewsrc() +{ + if (!didCacheNewsrc) + return true; + + vector<newsrc *> vec; + + vec.reserve(cachedNewsrc.size()); + + map<string, newsrc>::iterator b=cachedNewsrc.begin(), + e=cachedNewsrc.end(); + + while (b != e) + { + vec.push_back(&b->second); + b++; + } + + sort(vec.begin(), vec.end(), cachedNewsrcSort()); + + string newNewsrcFilename=newsrcFilename + ".tmp"; + + ofstream o(newNewsrcFilename.c_str()); + + if (o.is_open()) + { + // Copy any extra lines... + + ifstream i(newsrcFilename.c_str()); + + string line; + + while (i.is_open() && !getline(i, line).fail()) + { + if (line[0] == '#') + { + o << line << endl; + continue; + } + } + i.close(); + + vector<newsrc *>::iterator b=vec.begin(), e=vec.end(); + + while (b != e) + { + o << (string)**b << endl; + b++; + } + o << flush; + o.close(); + + if (!o.fail() && !o.bad() && + rename(newNewsrcFilename.c_str(), + newsrcFilename.c_str()) == 0) + return true; + } + return false; +} + +// Discard cached newsrc + +void mail::nntp::discardCachedNewsrc() +{ + cachedNewsrc.clear(); + didCacheNewsrc=false; +} + +// Create a newsrc line based on the currently open folder index + +void mail::nntp::createNewsrc(newsrc &n) +{ + n.msglist.clear(); + + vector<nntpMessageInfo>::iterator + b=index.begin(), e=index.end(); + + if (b != e) + { + bool first=true; + + msgnum_t lastNum=0; + + while (b != e) + { + if (first) + { + if (b->msgNum > 1) + n.msglist.push_back(make_pair(1, + b->msgNum-1) + ); + } + else if (lastNum+1 != b->msgNum) + n.msglist.push_back(make_pair(lastNum+1, + b->msgNum-1)); + first=false; + lastNum= b->msgNum; + b++; + } + } + else if (hiWatermark > 1) + { + n.msglist.push_back(make_pair(1, hiWatermark-1)); + } +} + +void mail::nntp::installTask(Task *taskp) +{ + if (taskp == NULL) + LIBMAIL_THROW((strerror(errno))); + + bool wasEmpty=tasks.empty(); + + tasks.insert(tasks.end(), taskp); + inactivityTimeout=0; // We're active now + + if (wasEmpty) + { + Task *p= *tasks.begin(); + + p->resetTimeout(); + p->installedTask(); + } +} + +void mail::nntp::logout(callback &callback) +{ + installTask(new mail::nntp::LogoutTask(&callback, *this, false)); +} + +void mail::nntp::checkNewMail(callback &callback) +{ + if (openedGroup.size() > 0) + installTask(new mail::nntp::CheckNewTask(&callback, *this, + openedGroup)); + else + callback.success("Ok"); +} + +bool mail::nntp::hasCapability(string capability) +{ + if (capability == LIBMAIL_CHEAPSTATUS) + return true; + + return false; +} + +string mail::nntp::getCapability(string capability) +{ + if (capability == LIBMAIL_SERVERTYPE) + return "nntp"; + + if (capability == LIBMAIL_SERVERDESCR) + return "Usenet server"; + + return ""; +} + +mail::folder *mail::nntp::folderFromString(string s) +{ + string words[3]; + + words[0]=""; + words[1]=""; + words[2]=""; + + int i=0; + + string::iterator b=s.begin(), e=s.end(); + + while (b != e) + { + if (*b == ':') + { + b++; + ++i; + continue; + } + + if (*b == '\\') + { + b++; + if (b == e) + break; + } + + if (i < 3) + words[i].append(&*b, 1); + b++; + } + + return new mail::nntp::folder(this, words[2], + strchr(words[0].c_str(), 'F') != NULL, + strchr(words[0].c_str(), 'D') != NULL); +} + +void mail::nntp::readTopLevelFolders(callback::folderList &callback1, + callback &callback2) +{ + mail::nntp::folder subscribed(this, FOLDER_SUBSCRIBED, + false, true); + mail::nntp::folder all(this, FOLDER_NEWSRC, + false, true); + mail::nntp::folder newnews(this, FOLDER_CHECKNEW, + false, true); + mail::nntp::folder refresh(this, FOLDER_REFRESH, + false, true); + + vector<const mail::folder *> v; + + v.push_back(&subscribed); + v.push_back(&all); + v.push_back(&newnews); + v.push_back(&refresh); + + callback1.success(v); + callback2.success("OK"); +} + +void mail::nntp::findFolder(string folder, + callback::folderList &callback1, + callback &callback2) +{ + if (folder.size() == 0 || + (folder[0] == '/' && folder.find('.') == std::string::npos)) + { + mail::nntp::folder dummy(this, folder, false, true); + + vector<const mail::folder *> vec; + + vec.push_back(&dummy); + + callback1.success(vec); + callback2.success("OK"); + return; + } + + if (folder.size() > sizeof(FOLDER_SUBSCRIBED) && + folder.substr(0, sizeof(FOLDER_SUBSCRIBED)) == + FOLDER_SUBSCRIBED ".") + { + mail::nntp::folder dummy(this, folder, true, false); + + vector<const mail::folder *> vec; + + vec.push_back(&dummy); + + callback1.success(vec); + callback2.success("OK"); + return; + } + + bool foundFolder=false; + bool foundSubFolders=false; + + { + ifstream i(newsrcFilename.c_str()); + + if (i.is_open()) + { + string line; + + while (i.is_open() && !getline(i, line).fail()) + { + newsrc parseLine(line); + + if (parseLine.newsgroupname.size() == 0) + continue; + + string s=FOLDER_NEWSRC "." + parseLine.newsgroupname; + + if (s == folder) + foundFolder=true; + + if (s.size() > folder.size() && + s.substr(0, folder.size()) == folder && + s[folder.size()] == '.') + foundSubFolders = true; + } + } + } + + if (!foundFolder) + foundSubFolders=true; + + mail::nntp::folder dummy(this, folder, foundFolder, + foundSubFolders); + + vector<const mail::folder *> vec; + + vec.push_back(&dummy); + + callback1.success(vec); + callback2.success("OK"); +} + +string mail::nntp::translatePath(string path) +{ + // Evil trick: "" gets as the fake top-level hierarchy, + // readTopLevelFolders. Otherwise, unless the path already starts with + // a ".", prepend the SUBSCRIBED trunc. + + if (path.size() > 0 && path[0] != '/') + return FOLDER_SUBSCRIBED "." + path; + + return path; +} + +mail::folder *mail::nntp::getSendFolder(const smtpInfo &info, + const mail::folder *folder, + std::string &errmsg) +{ + if (info.recipients.size() == 0 && + folder == NULL && + info.options.find("POST") != info.options.end()) + { + // Sanity check + + + mail::folder *f=new mail::nntp::folder(this, "", false, + true); + // Any of our folders will do the trick. + + if (!f) + errmsg=strerror(errno); + return f; + } + + return mail::account::getSendFolder(info, folder, errmsg); +} + +void mail::nntp::readMessageAttributes(const vector<size_t> &messages, + MessageAttributes attributes, + callback::message &callback) +{ + bool useXover; + + if (attributes & MESSAGESIZE) + useXover=true; + else if (attributes & MIMESTRUCTURE) + useXover=false; + else if (attributes & (ENVELOPE | ARRIVALDATE)) + useXover=true; + else + useXover=false; + + if (useXover) + { + installTask(new XoverTask(&callback, *this, + openedGroup, messages, attributes)); + return; + } + + genericAttributes(this, this, messages, attributes, callback); +} + +bool mail::nntp::fixGenericMessageNumber(std::string uid, size_t &msgNum) +{ + msgnum_t n; + + istringstream i(uid); + + i >> n; + + if (i.bad() || i.fail()) + { + return false; + } + + // Need to fix up the message # + + size_t cnt=getFolderIndexSize(); + + if (cnt <= msgNum) + { + msgNum=cnt; + if (msgNum == 0) + { + return false; + } + --msgNum; + } + + while (n > index[msgNum].msgNum) + if (++msgNum >= cnt) + { + return false; + } + + while (msgNum > 0 && n < index[msgNum].msgNum) + --msgNum; + + if (n != index[msgNum].msgNum) + { + return false; + } + + return true; +} + +void mail::nntp::readMessageContent(const vector<size_t> &messages, + bool peek, + enum mail::readMode readType, + callback::message &callback) +{ + genericReadMessageContent(this, this, messages, peek, + readType, callback); +} + +void mail::nntp::readMessageContent(size_t messageNum, + bool peek, + const mimestruct &msginfo, + enum mail::readMode readType, + callback::message &callback) +{ + genericReadMessageContent(this, this, messageNum, peek, msginfo, + readType, callback); +} + +void mail::nntp::readMessageContentDecoded(size_t messageNum, + bool peek, + const mimestruct &msginfo, + callback::message &callback) +{ + genericReadMessageContentDecoded(this, this, messageNum, peek, + msginfo, callback); +} + +size_t mail::nntp::getFolderIndexSize() +{ + return openedGroup.size() == 0 ? 0:index.size(); +} + +mail::messageInfo mail::nntp::getFolderIndexInfo(size_t msgNum) +{ + if (msgNum < getFolderIndexSize()) + { + messageInfo dummy; + + unsigned char n=index[msgNum].msgFlag; + + dummy.marked= !!(n & IDX_FLAGGED); + dummy.deleted=!!(n & IDX_DELETED); + + ostringstream o; + + o << index[msgNum].msgNum; + + dummy.uid=o.str(); + return dummy; + } + + return messageInfo(); +} + +void mail::nntp::saveFolderIndexInfo(size_t msgNum, + const messageInfo &msgInfo, + callback &callback) +{ + if (msgNum < getFolderIndexSize()) + { + unsigned char n=0; + + if (msgInfo.marked) + n |= IDX_FLAGGED; + + if (msgInfo.deleted) + n |= IDX_DELETED; + + index[msgNum].msgFlag=n; + if (openedGroup.size() > 0) + folderCallback->messageChanged(msgNum); + } + callback.success("Message status updated."); +} + +void mail::nntp::genericMarkRead(size_t messageNumber) +{ +} + +void mail::nntp::updateFolderIndexFlags(const vector<size_t> &messages, + bool doFlip, + bool enableDisable, + const messageInfo &flags, + callback &callback) +{ + vector<size_t>::const_iterator b, e; + + b=messages.begin(); + e=messages.end(); + + size_t n=getFolderIndexSize(); + + while (b != e) + { + size_t i= *b++; + + if (i < n) + { + messageInfo dummy; + + unsigned char n=index[i].msgFlag; + + dummy.marked= !!(n & IDX_FLAGGED); + dummy.deleted=!!(n & IDX_DELETED); + +#define DOFLAG(d, field, dummy2) \ + if (flags.field) \ + { \ + dummy.field=\ + doFlip ? !dummy.field\ + : enableDisable; \ + } + + LIBMAIL_MSGFLAGS; +#undef DOFLAG + n=0; + + if (dummy.marked) + n |= IDX_FLAGGED; + + if (dummy.deleted) + n |= IDX_DELETED; + index[i].msgFlag=n; + } + } + + b=messages.begin(); + e=messages.end(); + + MONITOR(mail::nntp); + + while (!DESTROYED() && b != e) + { + size_t i= *b++; + + if (i < n && folderCallback) + folderCallback->messageChanged(i); + } + + saveSnapshot(); + callback.success("OK"); +} + +void mail::nntp::getFolderKeywordInfo(size_t msgNum, + set<string> &keywords) +{ + keywords.clear(); + if (msgNum >= index.size()) + return; + index[msgNum].keywords.getFlags(keywords); +} + +bool mail::nntp::genericProcessKeyword(size_t msgNum, + updateKeywordHelper &helper) +{ + if (msgNum >= index.size()) + return true; + helper.doUpdateKeyword(index[msgNum].keywords, keywordHashtable); + return true; +} + + +void mail::nntp::updateKeywords(const std::vector<size_t> &messages, + const std::set<std::string> &keywords, + bool setOrChange, + // false: set, true: see changeTo + bool changeTo, + callback &cb) +{ + MONITOR(mail::nntp); + + genericUpdateKeywords(messages, keywords, setOrChange, changeTo, + folderCallback, this, cb); + + if (!DESTROYED()) + saveSnapshot(); +} + + +void mail::nntp::updateFolderIndexInfo(callback &callback) +{ + vector<size_t> deletedMsgs; + + size_t n=getFolderIndexSize(); + size_t i; + + for (i=0; i<n; i++) + if (index[i].msgFlag & IDX_DELETED) + deletedMsgs.push_back(i); + + removeMessages(deletedMsgs, callback); +} + +void mail::nntp::removeMessages(const vector<size_t> &messages, + callback &callback) +{ + if (openedGroup.size() == 0) + { + callback.success("Ok."); + return; + } + + set<size_t> msgNumSet; + + msgNumSet.insert(messages.begin(), messages.end()); + + list< pair<size_t, size_t> > removedList; + size_t n=getFolderIndexSize(); + + MONITOR(mail::nntp); + + while (!DESTROYED() && n) + { + --n; + if (msgNumSet.find(n) == msgNumSet.end()) + continue; + + index.erase(index.begin() + n); + + if (!removedList.empty() && + removedList.begin()->first == n+1) + { + removedList.begin()->first--; + continue; + } + + if (removedList.size() >= 100) + { + vector< pair<size_t, size_t> > vec; + + vec.insert(vec.end(), removedList.begin(), + removedList.end()); + removedList.clear(); + + if (folderCallback) + folderCallback->messagesRemoved(vec); + } + + removedList.push_front(make_pair(n, n)); + } + + if (!DESTROYED() && removedList.size() > 0) + { + vector< pair<size_t, size_t> > vec; + + vec.insert(vec.end(), removedList.begin(), + removedList.end()); + removedList.clear(); + + if (folderCallback) + folderCallback->messagesRemoved(vec); + } + + if (!DESTROYED()) + { + updateCachedNewsrc(); + discardCachedNewsrc(); + + newsrc newNewsrc; + + createNewsrc(newNewsrc); + newNewsrc.newsgroupname=openedGroup; + newNewsrc.subscribed=true; + updateOpenedNewsrc(newNewsrc); + + saveSnapshot(); + } + + callback.success("Group updated."); +} + +void mail::nntp::saveSnapshot() +{ + if (folderCallback) + { + ostringstream o; + + o << loWatermark << '-' << hiWatermark; + + folderCallback->saveSnapshot(o.str()); + } +} + +void mail::nntp::copyMessagesTo(const vector<size_t> &messages, + mail::folder *copyTo, + callback &callback) +{ + copyMessages::copy(this, messages, copyTo, callback); +} + +void mail::nntp::searchMessages(const searchParams &searchInfo, + searchCallback &callback) +{ + string hdr; + switch (searchInfo.criteria) { + case searchParams::from: + hdr="FROM"; + break; + case searchParams::to: + hdr="TO"; + break; + case searchParams::cc: + hdr="CC"; + break; + case searchParams::bcc: + hdr="BCC"; + break; + case searchParams::subject: + hdr="SUBJECT"; + break; + case searchParams::header: + hdr=searchInfo.param1; + break; + default: + break; + } + + if (hdr.size() > 0) + { + unicode_char *uc; + size_t ucsize; + libmail_u_convert_handle_t + h(libmail_u_convert_tou_init(searchInfo.charset.c_str(), + &uc, &ucsize, 0)); + + if (h) + { + libmail_u_convert(h, searchInfo.param2.c_str(), + searchInfo.param2.size()); + + if (libmail_u_convert_deinit(h, NULL)) + uc=NULL; + } + else + { + uc=NULL; + } + + if (uc) + { + size_t i; + + for (i=0; i<ucsize; i++) + if (uc[i] < 32 || uc[i] >= 127) + break; + + if (i < ucsize) + { + free(uc); + } + else + { + char *p; + size_t psize; + + h=libmail_u_convert_fromu_init("iso-8859-1", + &p, &psize, 1); + + if (h) + { + libmail_u_convert_uc(h, uc, ucsize); + if (libmail_u_convert_deinit(h, NULL)) + p=NULL; + } + else + p=NULL; + + free(uc); + + if (!p) + { + callback.fail(strerror(errno)); + return; + } + + try { + string s=p; + searchMessagesXpat(hdr, s, + searchInfo + .searchNot, + searchInfo.scope, + searchInfo.rangeLo, + searchInfo.rangeHi, + callback); + free(p); + } catch (...) { + free(p); + callback.fail(strerror(errno)); + } + return; + } + } + } + + switch (searchInfo.criteria) { + case searchParams::replied: + case searchParams::deleted: + case searchParams::draft: + case searchParams::unread: + break; + default: + callback.fail("Not implemented."); + return; + } + + searchMessages::search(callback, searchInfo, this); +} + +void mail::nntp::searchMessagesXpat(string hdr, string srch, + bool searchNot, + searchParams::Scope searchScope, + size_t rangeLo, + size_t rangeHi, + searchCallback &callback) +{ + + string::iterator b, e; + + b=hdr.begin(); + e=hdr.end(); + + while (b != e) + { + if (*b < ' ' || *b >= 127) + { + errno=EINVAL; + callback.fail(strerror(errno)); + } + ++b; + } + + b=srch.begin(); + e=srch.end(); + + while (b != e) + { + if (*b < ' ' || *b >= 127) + { + errno=EINVAL; + callback.fail(strerror(errno)); + } + ++b; + } + + XpatTaskCallback *cb=new XpatTaskCallback(&callback); + + if (!cb) + { + callback.fail(strerror(errno)); + return; + } + + try { + installTask(new XpatTask(cb, *this, + openedGroup, + hdr, srch, searchNot, + searchScope, rangeLo, rangeHi)); + } catch (...) { + delete cb; + throw; + } +} + +void mail::nntp::genericMessageRead(string uid, + size_t messageNumber, + bool peek, + mail::readMode getType, + callback::message &callback) +{ + if (openedGroup.size() == 0) + { + callback.fail("Invalid message number."); + return; + } + + if (fixGenericMessageNumber(uid, messageNumber) && + genericCachedUid(uid)) + { + // We can optimize by taking this path + + vector<size_t> vec; + + vec.push_back(messageNumber); + readMessageContent(vec, peek, getType, callback); + return; + } + + installTask(new FetchTask(&callback, *this, openedGroup, + messageNumber, uid, getType)); +} + +void mail::nntp::genericMessageSize(string uid, + size_t messageNumber, + callback::message &callback) +{ + if (!fixGenericMessageNumber(uid, messageNumber)) + { + callback.fail("Invalid message number."); + return; + } + + callback.messageSizeCallback(messageNumber, 0); + + callback.success("OK"); // TODO +} + +void mail::nntp::genericGetMessageFd(string uid, + size_t messageNumber, + bool peek, + int &fdRet, + callback &callback) +{ + if (openedGroup.size() == 0) + { + callback.fail("Invalid message number."); + return; + } + + if (genericTmpFp && uid == cachedUid) + { + fdRet=fileno(genericTmpFp); + callback.success("OK"); + return; + } + + installTask(new CacheTask(&callback, *this, openedGroup, + messageNumber, uid, + &fdRet, NULL)); +} + +bool mail::nntp::genericCachedUid(string uid) +{ + return genericTmpFp && uid == cachedUid; +} + +void mail::nntp::genericGetMessageStruct(string uid, + size_t messageNumber, + struct rfc2045 *&structRet, + callback &callback) +{ + if (openedGroup.size() == 0) + { + callback.fail("Invalid message number."); + return; + } + + if (genericTmpRfcp && uid == cachedUid) + { + structRet=genericTmpRfcp; + callback.success("OK"); + return; + } + + installTask(new CacheTask(&callback, *this, openedGroup, + messageNumber, uid, + NULL, &structRet)); +} + +///////////////////////////////////////////////////////////////////////////// +// +// The basic NNTP task + +mail::nntp::Task::Task(callback *callbackArg, + nntp &myserverArg) + : callbackPtr(callbackArg), myserver( &myserverArg), + defaultTimeout( time(NULL) + myserver->timeoutSetting) +{ +} + +mail::nntp::Task::~Task() +{ + done(); // Make sure that any callback gets invoked +} + +void mail::nntp::Task::resetTimeout() +{ + defaultTimeout=time(NULL) + myserver->timeoutSetting; +} + +void mail::nntp::Task::done() +{ + callback *cb=callbackPtr; + + callbackPtr=NULL; + + if (myserver != NULL) // Kilroy was here + { + if ((*myserver->tasks.begin()) != this) + { + cerr << "INTERNAL ERROR: mail::nntp::Task::done()" + << endl; + abort(); + } + + myserver->tasks.erase(myserver->tasks.begin()); + + try { + if (!myserver->tasks.empty()) + { + Task *p= *myserver->tasks.begin(); + + p->resetTimeout(); + p->installedTask(); + } + else + emptyQueue(); + + } catch (...) { + myserver=NULL; + delete this; + if (cb) + cb->fail("Operation aborted."); + throw; + } + + + myserver=NULL; + delete this; + } + + if (cb) + cb->fail("Operation aborted."); +} + +void mail::nntp::Task::emptyQueue() +{ + myserver->inactivityTimeout=time(NULL) + myserver->autologoutSetting; +} + +// Subclass succeeded + +void mail::nntp::Task::success(string message) +{ + callback *cb=callbackPtr; + + callbackPtr=NULL; + done(); + if (cb) + cb->success(message); +} + +// Subclass failed + +void mail::nntp::Task::fail(string message) +{ + callback *cb=callbackPtr; + + callbackPtr=NULL; + done(); + if (cb) + cb->fail(message); +} + +// Check for task timeout. + +int mail::nntp::Task::getTimeout() +{ + time_t now; + + time(&now); + + if (now < defaultTimeout) + return defaultTimeout - now; + + string errmsg=myserver->socketDisconnect(); + + if (errmsg.size() == 0) + errmsg="Server timed out."; + + disconnected(errmsg.c_str()); + return 0; +} + +// Server has disconnected. +// The default implementation takes this task off the queue, +// calls the next Task's disconnect method, then deletes +// itself. + +void mail::nntp::Task::disconnected(const char *reason) +{ + callback *cb=callbackPtr; + + callbackPtr=NULL; + + if ((*myserver->tasks.begin()) != this) + { + cerr << "INTERNAL ERROR: mail::nntp::Task::done()" << endl; + abort(); + } + + nntp *s=myserver; + + myserver=NULL; + + s->tasks.erase(s->tasks.begin()); + delete this; + + if (!s->tasks.empty()) + (*s->tasks.begin())->disconnected(reason); + + if (cb) + cb->fail(reason); +} + +void mail::nntp::Task::installedTask() +{ +} |
