void CursesAttachmentDisplay::createMsgAtt(mail::mimestruct *mimePtr, mail::envelope *env, int &rowNum, int nestingLevel) { std::string name; std::string filename; CursesMessage::getDescriptionOf(mimePtr, env, name, filename, true); Attachment *a=new Attachment(this, mimePtr, env, name); if (!a) LIBMAIL_THROW("Out of memory."); try { children.push_back(a); } catch (...) { delete a; LIBMAIL_THROW(LIBMAIL_THROW_EMPTY); } a->setRow(rowNum++); a->setCol(nestingLevel * 2); attachment_list.push_back(a); }
bool CtrlCHandler::processKey(const Curses::Key &key) { if (key == '\x03') { if (loggingOut) // If logout stuck, we don't care LIBMAIL_THROW(_("Have a nice day.")); if (myServer::cmdcount > 0) // Server command pending { time_t t=time(NULL); // Two Ctrl-Cs at least 5 seconds apart will do the // trick. if (lastCtrlC == 0 || t < lastCtrlC + 5) { lastCtrlC=t; statusBar->beepError(); } else { LIBMAIL_THROW(_("Have a nice day.")); } } } return false; }
void CursesAttachmentDisplay::createAttList(mail::mimestruct *mimePtr, int &rowNum, int nestingLevel) { if (mimePtr == NULL || mimePtr->messagerfc822()) { // Top level msg, or a message/rfc822 attachment. createMsgAtt(mimePtr, mimePtr ? &mimePtr->getEnvelope(): &messageInfoPtr->envelope, rowNum, nestingLevel); if (mimePtr) { if (mimePtr->getNumChildren() == 0) return; mimePtr=mimePtr->getChild(0); } else mimePtr= &messageInfoPtr->structure; ++nestingLevel; } if (mimePtr->getNumChildren() > 0) { // A multipart attachment. CursesLabel *l=new CursesLabel(this, mimePtr->type + "/" + mimePtr->subtype); if (!l) LIBMAIL_THROW("Out of memory"); try { children.push_back(l); } catch (...) { delete l; LIBMAIL_THROW(LIBMAIL_THROW_EMPTY); } l->setRow(rowNum++); l->setCol(nestingLevel * 2); ++nestingLevel; size_t i; for (i=0; i<mimePtr->getNumChildren(); i++) createAttList(mimePtr->getChild(i), rowNum, nestingLevel+1); return; } createMsgAtt(mimePtr, NULL, rowNum, nestingLevel); }
mail::rfc2047::encode::encode(std::string txt, std::string charset) { char *p=rfc2047_encode_str(txt.c_str(), charset.c_str(), rfc2047_qp_allow_any); if (!p) LIBMAIL_THROW("Out of memory."); try { encodedString=p; } catch (...) { free(p); LIBMAIL_THROW(LIBMAIL_THROW_EMPTY); } free(p); }
bool CursesAttachmentDisplay::processKey(const Curses::Key &key) { CursesMessage *messageInfo=messageInfoPtr; if (!messageInfo) return false; if (GlobalKeys::processKey(key, GlobalKeys::ATTACHMENTSCREEN, messageInfo->myfolder->getServer())) return true; if (key == key_FOLDERINDEX || key == key_LESSTHAN) { keepgoing=false; myServer::nextScreen=&folderIndexScreen; myServer::nextScreenArg=messageInfo->myfolder; return true; } if (key == key_EXPUNGE) { std::set<std::string> removeAttachments; std::list<Attachment *>::iterator b=attachment_list.begin(), e=attachment_list.end(); while (b != e) { if ((*b)->deleted && (*b)->mime) removeAttachments.insert((*b)->mime->mime_id); ++b; } if (removeAttachments.empty()) { statusBar->clearstatus(); statusBar->status(_("No attachments to expunge.")); statusBar->beepError(); } myServer::Callback callback; mail::addMessage *add=messageInfo->myfolder->getFolder() ->addMessage(callback); if (!add) { statusBar->clearstatus(); statusBar->status(strerror(errno)); statusBar->beepError(); return true; } myServer::Callback copy_callback; size_t dummy; add->assembleRemoveAttachmentsFrom(dummy, messageInfo->myfolder ->getServer()->server, messageInfo->myfolder ->getIndex(messageInfo->messagesortednum).uid, messageInfo->structure, removeAttachments, copy_callback); if (!myServer::eventloop(copy_callback)) { add->fail(copy_callback.msg); myServer::eventloop(callback); return true; } if (!add->assemble()) { add->fail(strerror(errno)); myServer::eventloop(callback); return true; } myServer *s=messageInfo->myfolder->getServer(); try { s->currentFolder->isExpungingDrafts=true; add->go(); if (!myServer::eventloop(callback)) { if (s->currentFolder) s->currentFolder-> isExpungingDrafts=false; return true; } if (s->server) s->checkNewMail(); // Make sure newly-added message is in the folder index. if (!messageInfoPtr.isDestroyed() && s->currentFolder) { // Delete the older message. size_t messageNum=messageInfoPtr->messagenum; if (s->currentFolder->mymessage) delete s->currentFolder->mymessage; std::vector<size_t> msgList; msgList.push_back(messageNum); myServer::Callback removeCallback; s->server->removeMessages(msgList, removeCallback); myServer::eventloop(removeCallback); } if (s->currentFolder) s->currentFolder->isExpungingDrafts=false; } catch (...) { if (s->currentFolder) s->currentFolder->isExpungingDrafts=false; LIBMAIL_THROW(LIBMAIL_THROW_EMPTY); } keepgoing=false; if (myServer::nextScreen == NULL) { if (s->currentFolder) { myServer::nextScreen=&folderIndexScreen; myServer::nextScreenArg=s->currentFolder; } else { myServer::nextScreen= &hierarchyScreen; myServer::nextScreenArg=NULL; } } return true; } return false; }
bool CertificatesScreen::processKey(const Curses::Key &key) { if (key == key_ABORT) { keepgoing=false; myServer::nextScreen= &mainMenu; myServer::nextScreenArg=NULL; PreviousScreen::previousScreen(); return true; } if (key == key_IMPORTCERTIFICATE) { std::string filename; { OpenDialog open_dialog; open_dialog.requestFocus(); myServer::eventloop(); std::vector<std::string> &filenameList= open_dialog.getFilenameList(); if (filenameList.size()) filename=filenameList[0]; } mainScreen->erase(); mainScreen->draw(); requestFocus(); if (filename.size() == 0) return true; std::string cert; { std::stringstream ss; std::ifstream ifs(filename.c_str()); if (!ifs.is_open()) { statusBar->clearstatus(); statusBar->status(strerror(errno), statusBar->SYSERROR); statusBar->beepError(); mainScreen->draw(); return true; } ss << ifs.rdbuf(); cert=ss.str(); } if (cert.size() >= 0xFFF0) { statusBar->clearstatus(); statusBar->status(_("Certificate file too large"), statusBar->SYSERROR); statusBar->beepError(); return true; } if (!tls_validate_pem_cert(cert.c_str(), cert.size())) { statusBar->clearstatus(); statusBar->status(_("Cannot parse certificate file"), statusBar->SYSERROR); statusBar->beepError(); return true; } std::string name="XXX"; char *s=tls_cert_name(cert.c_str(), cert.size()); try { if (s) { name=s; free(s); } } catch (...) { if (s) free(s); LIBMAIL_THROW(LIBMAIL_THROW_EMPTY); } std::map<std::string, Certificates::cert>::iterator i; for (i=myServer::certs->certs.begin(); i != myServer::certs->certs.end(); ++i) { std::string name2="YYY"; s=tls_cert_name(i->second.cert.c_str(), i->second.cert.size()); try { if (s) { name2=s; free(s); if (name2 == name) break; } } catch (...) { if (s) free(s); LIBMAIL_THROW(LIBMAIL_THROW_EMPTY); } } // Most names are very long if (name.size() > 60) { std::string::iterator first=name.begin(); while (first != name.end()) { if (*first == '\\') { if (++first == name.end()) break; ++first; continue; } if (*first == ',') break; ++first; } std::string::iterator comma_sep=first; if (comma_sep != name.end()) ++comma_sep; size_t l=first-name.begin(); if (l < 57) { l=57-l; if (l < (size_t)(name.end() - comma_sep)) { while (comma_sep != name.end()) { if (*comma_sep == '\\') { if (++comma_sep == name.end()) break; ++comma_sep; continue; } if (*comma_sep == ',' && comma_sep >= name.end()-l) break; ++comma_sep; } if (comma_sep != name.end()) { name=std::string(name.begin(), first) + "..." + std::string(comma_sep, name.end() ); } } } } std::string tryid; Certificates::cert c; if (i != myServer::certs->certs.end()) { myServer::promptInfo ask(_("Replace existing cert? (Y/N) ")); ask=myServer::prompt(ask.yesno()); if (ask.abortflag) return true; if ((std::string)ask != "Y") return true; tryid=i->first; c=i->second; name=c.name; } else { size_t n=0; time_t t=time(NULL); do { std::ostringstream o; o << t << "-" << std::setw(3) << std::setfill('0') << ++n; tryid=o.str(); } while (myServer::certs->certs.find(tryid) != myServer::certs->certs.end()); } c.name=name; c.cert=cert; myServer::certs->certs[tryid]=c; PasswordList::passwordList.save(); keepgoing=false; myServer::nextScreen= &certificatesScreenInternal; myServer::nextScreenArg=NULL; jumpto=tryid; return true; } return GlobalKeys::processKey(key, GlobalKeys::CERTIFICATESCREEN, NULL); }
bool CursesMessageDisplay::processKeyInFocus(const Curses::Key &key) { CursesMessage *messageInfo=messageInfoPtr; if (!messageInfo) return false; if (key == key.ENTER) { size_t row; size_t col; std::string url; if (!messageInfo->getCurrentLink(row, col, url)) return true; if (url.substr(0, 7) == "mailto:") { if (myMessage::checkInterrupted(false) && (messageInfo=messageInfoPtr) != NULL) myMessage::newMessage(messageInfo->myfolder ->getFolder(), messageInfo->myfolder ->getServer(), url.substr(7)); return true; } std::string handler; size_t p=url.find(':'); if (p != std::string::npos) { handler=myServer::getConfigDir() + "/" + url.substr(0, p) + ".handler"; if (access(handler.c_str(), X_OK)) { handler=FILTERDIR "/" + url.substr(0, p) + ".handler"; if (access(handler.c_str(), X_OK)) handler=""; } } if (handler.size() == 0) { statusBar->clearstatus(); statusBar-> status(Gettext(_("Cannot find handler for %1%")) << url); statusBar->beepError(); return true; } pid_t pp=fork(); if (pp < 0) { statusBar->clearstatus(); statusBar->status(strerror(errno)); statusBar->beepError(); return true; } if (pp == 0) { close(1); open("/dev/null", O_WRONLY); dup2(1, 2); dup2(1, 0); execl(handler.c_str(), handler.c_str(), url.c_str(), (char *)NULL); exit(1); } pid_t p2; int waitstat; while ((p2=wait(&waitstat)) != pp) { if (p2 < 0 && errno != EINTR) break; } if (p2 == pp && WIFEXITED(waitstat) && WEXITSTATUS(waitstat) == 0) { statusBar->status(_("Started external handler.")); return true; } statusBar->clearstatus(); statusBar->status(_("External handler terminated with a non-zero exit code."), statusBar->SYSERROR); return true; } if (key == key_TAKEADDR) { mail::envelope *e=messageInfo->getEnvelope(); std::vector<mail::address> addrList; addrList.reserve(e->from.size() + e->to.size() + e->cc.size() + e->bcc.size() + e->sender.size() + e->replyto.size()); addrList.insert(addrList.end(), e->from.begin(), e->from.end()); addrList.insert(addrList.end(), e->to.begin(), e->to.end()); addrList.insert(addrList.end(), e->cc.begin(), e->cc.end()); addrList.insert(addrList.end(), e->bcc.begin(), e->bcc.end()); addrList.insert(addrList.end(), e->sender.begin(), e->sender.end()); addrList.insert(addrList.end(), e->replyto.begin(), e->replyto.end()); AddressBook::take(addrList); return true; } if (key == key_FOLDERINDEX || key == key_LESSTHAN) { keepgoing=false; myServer::nextScreen=&folderIndexScreen; myServer::nextScreenArg=messageInfo->myfolder; return true; } if (key == key_DELETE) messageInfo->myfolder->markDeleted( messageInfo->messagesortednum, true, false); if (key == key_UNDELETE) messageInfo->myfolder->markDeleted( messageInfo->messagesortednum, false, false); if (key == key_NEXTMESSAGE || key == key_DELETE || key == key_UNDELETE) { size_t dummy; if (messageInfo->myfolder->getNextMessage(dummy)) { Curses::keepgoing=false; myServer::nextScreen= &goNextMessage; myServer::nextScreenArg=messageInfo->myfolder; return true; } return true; } if (key == key_PREVMESSAGE) { size_t dummy; if (messageInfo->myfolder->getPrevMessage(dummy)) { Curses::keepgoing=false; myServer::nextScreen= &goPrevMessage; myServer::nextScreenArg=messageInfo->myfolder; } return true; } if (key == ' ' || key == Key::RIGHT || key == Key::PGDN || key == Key::DOWN) { if ((key == Key::DOWN || key == Key::RIGHT) && messageInfo->nextLink()) return true; // Went to next link instead size_t nLines=messageInfo->nLines(); size_t h=getHeight(); size_t lastLine= nLines > h ? nLines-h:0; size_t firstLineSaved=getFirstLineShown(); if (key == Key::DOWN || key == Key::RIGHT) setFirstLineShown(getFirstLineShown()+1); else setFirstLineShown(getFirstLineShown()+h); if (getFirstLineShown() > lastLine) setFirstLineShown(lastLine); if (firstLineSaved == getFirstLineShown() && key == ' ') { size_t dummy; if (messageInfo->myfolder-> getNextUnreadMessage(dummy)) { Curses::keepgoing=false; myServer::nextScreen= &goNextUnreadMessage; myServer::nextScreenArg=messageInfo->myfolder; return true; } statusBar->clearstatus(); statusBar->status(Gettext(_("No more unread mail in %1%")) << messageInfo->myfolder ->getFolder()->getName()); return true; } folderUpdated(); draw2(); messageInfo->toLastLink(); return true; } if (key == Key::LEFT || key == Key::PGUP || key == Key::UP) { if (key != Key::PGUP && messageInfo->prevLink()) return true; size_t h=key != Key::PGUP ? 1:getHeight(); setFirstLineShown(getFirstLineShown() < h ? 0: getFirstLineShown()-h); folderUpdated(); draw2(); return true; } if (key == key_VIEWATT) { Curses::keepgoing=false; myServer::nextScreen=showAttachments; myServer::nextScreenArg=messageInfo; return true; } if (key == key_SAVE) { std::string filename; { SaveDialog save_dialog(filename); save_dialog.requestFocus(); myServer::eventloop(); filename=save_dialog; mainScreen->erase(); } mainScreen->draw(); requestFocus(); if (messageInfoPtr.isDestroyed() || filename == "") return true; CursesAttachmentDisplay::downloadTo(messageInfoPtr, messageInfoPtr->shownMimeId != "" ? messageInfoPtr->structure .find(messageInfoPtr-> shownMimeId) :NULL, filename); return true; } if (key == key_BOUNCE) { if (messageInfo->shownMimeId.size()) { statusBar->clearstatus(); statusBar->status(_("Cannot forward only an attachment, just the whole message.")); statusBar->beepError(); return true; } mail::smtpInfo sendInfo; std::string from; std::string replyto; std::string fcc; std::string customheaders; if (!myMessage::getDefaultHeaders(messageInfo->myfolder ->getFolder(), messageInfo->myfolder ->getServer(), from, replyto, fcc, customheaders)) { return true; } { std::vector<mail::address> addrList; size_t errIndex; if (mail::address::fromString(from, addrList, errIndex) && addrList.size() > 0) sendInfo.sender=addrList[0].getAddr(); } mail::ptr<myFolder> folderPtr=messageInfo->myfolder; if (!CursesMessage::getBounceTo(sendInfo) || !CursesMessage::getSendInfo(Gettext(_("Blind-forward message to %1% address(es)? (Y/N) ")) << sendInfo.recipients .size(), "", sendInfo, NULL)) return true; disconnectCallbackStub disconnectStub; myServer::Callback sendCallback; mail::account *smtpServer; mail::folder *folder= CursesMessage::getSendFolder(sendInfo, smtpServer, NULL, disconnectStub); if (!folder) return true; if (folderPtr.isDestroyed() || messageInfoPtr.isDestroyed()) { delete folder; if (smtpServer) delete smtpServer; return true; } try { messageInfo->copyContentsTo(folder, sendCallback); bool rc=myServer::eventloop(sendCallback); delete folder; folder=NULL; if (smtpServer) { if (rc) { myServer::Callback disconnectCallback; disconnectCallback.noreport=true; smtpServer->logout(disconnectCallback); myServer:: eventloop(disconnectCallback); } delete smtpServer; smtpServer=NULL; } } catch (...) { if (folder) delete folder; if (smtpServer) delete smtpServer; LIBMAIL_THROW(LIBMAIL_THROW_EMPTY); } return true; } if (key == key_REPLY) { mail::ptr<myFolder> folder=messageInfo->myfolder; if (myMessage::checkInterrupted() && !folder.isDestroyed() && !messageInfoPtr.isDestroyed()) messageInfo->reply(); return true; } if (key == key_FWD) { mail::ptr<myFolder> folder=messageInfo->myfolder; if (myMessage::checkInterrupted() && !folder.isDestroyed() && !messageInfoPtr.isDestroyed()) messageInfo->forward(); return true; } if (key == key_HEADERS) { erase(); messageInfo->fullEnvelopeHeaders= !messageInfo->fullEnvelopeHeaders; setFirstLineShown(0); messageInfo->beginReformat(getWidth()); folderUpdated(); return true; } if (key == key_PRINT) { mail::ptr<CursesMessage> ptr=messageInfo; int pipefd[2]; pid_t pid1; myServer::promptInfo printPrompt(_("Print to: ")); if (printCommand.size() == 0) printCommand="lpr"; printPrompt=myServer::prompt(printPrompt .initialValue(printCommand)); if (printPrompt.abortflag || ptr.isDestroyed()) return true; std::vector<const char *> args; { const char *p=getenv("SHELL"); if (!p || !*p) p="/bin/sh"; args.push_back(p); } args.push_back("-c"); printCommand=printPrompt; args.push_back(printCommand.c_str()); args.push_back(0); // // Fork a child process which writes the text image of the // message to a pipe. // // The parent process runs the print command, piping the text // into it. signal(SIGCHLD, SIG_DFL); if (pipe(pipefd) < 0) { statusBar->clearstatus(); statusBar->status(strerror(errno)); statusBar->beepError(); return true; } if ((pid1=fork()) == 0) { FILE *fp; // First child exits, the second child generates the // text. if (fork()) exit(0); signal(SIGPIPE, SIG_DFL); close(pipefd[0]); fp=fdopen(pipefd[1], "w"); if (fp) { size_t nLines=messageInfo->nLines(); size_t i; std::vector<std::pair<textAttributes, std::string> > line; std::vector<std::pair<textAttributes, std::string> > ::iterator b, e; for (i=0; i<nLines; i++) { messageInfo->getLineImage(i, line); for (b=line.begin(), e=line.end(); b != e; ++b) { fprintf(fp, "%s", b->second.c_str()); } fprintf(fp, "\n"); } fflush(fp); } exit(0); } // Wait for the first child to exit. if (pid1 < 0) { close(pipefd[0]); close(pipefd[1]); statusBar->clearstatus(); statusBar->status(strerror(errno)); statusBar->beepError(); return true; } close(pipefd[1]); while (wait(NULL) != pid1) ; Curses::runCommand(args, pipefd[0], ""); close(pipefd[0]); return true; } if (key == key_ROT13) { erase(); messageInfo->rot13=!messageInfo->rot13; setFirstLineShown(0); messageInfo->beginReformat(getWidth()); folderUpdated(); return true; } if ((key == key_UNENCRYPT) && (messageInfo->isSigned() || messageInfo->isEncrypted()) && GPG::gpg.gpg_installed()) { mail::ptr<CursesMessage> ptr=messageInfo; std::string passphrase; std::vector<std::string> options; bool wasEncrypted=messageInfo->isEncrypted(); if (wasEncrypted) { if (!PasswordList::passwordList.check("decrypt:", passphrase)) { myServer::promptInfo passPrompt(_("Passphrase (if" " required): ")); passPrompt=myServer::prompt(passPrompt .password()); if (passPrompt.abortflag || ptr.isDestroyed()) return true; passphrase=passPrompt; } } std::string::iterator b=GPG::gpg.extraDecryptVerifyOptions.begin(); while (b != GPG::gpg.extraDecryptVerifyOptions.end()) { if (unicode_isspace((unsigned char)*b)) { b++; continue; } std::string::iterator s=b; while (b != GPG::gpg.extraDecryptVerifyOptions.end()) { if (unicode_isspace((unsigned char)*b)) break; b++; } options.push_back(std::string(s, b)); } bool decryptFailed; if (messageInfo->decrypt(passphrase, options, decryptFailed)) { if (wasEncrypted && !decryptFailed) PasswordList::passwordList .save("decrypt:", passphrase); erase(); setFirstLineShown(0); messageInfo->beginReformat(getWidth()); folderUpdated(); if (wasEncrypted && decryptFailed) { statusBar->clearstatus(); statusBar->status(_("ERROR: Decryption failed," " passphrase forgotten.")); statusBar->beepError(); } } return true; } if (key == key_MSGSEARCH) { mail::ptr<CursesMessage> ptr=messageInfo; myServer::promptInfo searchPrompt(_("Search: ")); searchPrompt=myServer::prompt(searchPrompt .initialValue(searchString)); if (searchPrompt.abortflag || ptr.isDestroyed()) return true; searchString=searchPrompt; if (searchString.size() == 0) return true; mail::Search searchEngine; if (!searchEngine.setString(searchString, unicode_default_chset())) { statusBar->clearstatus(); statusBar->status(strerror(errno)); statusBar->beepError(); return true; } searchEngine.reset(); statusBar->clearstatus(); statusBar->status(_("Searching...")); statusBar->flush(); size_t n=messageInfo->nLines(); std::vector<std::pair<textAttributes, std::string> > line; while (firstLineToSearch < n) { messageInfo->getLineImage(firstLineToSearch, line); std::string s; std::vector<std::pair<textAttributes, std::string> > ::iterator b, e; for (b=line.begin(), e=line.end(); b != e; ++b) s += Gettext::toutf8(b->second); unicode_char *uc; size_t ucsize; if (unicode_convert_tou_tobuf(s.c_str(), s.size(), "utf-8", &uc, &ucsize, NULL)) { statusBar->clearstatus(); statusBar->status(strerror(errno)); statusBar->beepError(); return true; } if (ucsize == 0) { free(uc); ++firstLineToSearch; continue; } size_t i; for (i=0; i<ucsize; ++i) { searchEngine << uc[i]; } free(uc); searchEngine << (unicode_char)' '; // EOL if (searchEngine) { size_t nLines=messageInfo->nLines(); size_t h=getHeight(); size_t lastLine= nLines > h ? nLines-h:0; size_t foundAt=firstLineToSearch; size_t n=foundAt > 0 ? foundAt-1:0; if (n > lastLine) n=lastLine; setFirstLineShown(n); firstLineToSearch=foundAt+1; statusBar->clearstatus(); statusBar->status(Gettext(_("Text located," " displayed on" " line #%1%")) << (firstLineToSearch-n)); draw(); return true; } ++firstLineToSearch; } statusBar->clearstatus(); statusBar->status(_("Not found.")); return true; } if (key.fkey()) { std::string local_cmd; if (!CursesIndexDisplay::FilterMessageCallback ::getFilterCmd(key.fkeynum(), local_cmd)) return true; CursesIndexDisplay::FilterMessageCallback cb(local_cmd); std::vector<size_t> msgvec; msgvec.push_back(messageInfo->myfolder->getServerIndex (messageInfo->myfolder->getCurrentMessage())); messageInfo->myfolder->getServer() ->server->readMessageContent(msgvec, true, mail::readBoth, cb); if (myServer::eventloop(cb)) { cb.finish(); if (myServer::nextScreen) return true; if (cb.errmsg.size()) { statusBar->clearstatus(); statusBar->status(cb.errmsg); statusBar->beepError(); } } return true; } return false; }