// // VMime library (http://www.vmime.org) // Copyright (C) 2002 Vincent Richard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 3 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // Linking this library statically or dynamically with other modules is making // a combined work based on this library. Thus, the terms and conditions of // the GNU General Public License cover the whole combination. // #include #include #include #include #include #include #include "vmime/vmime.hpp" #include "vmime/platforms/posix/posixHandler.hpp" #include "example6_tracer.hpp" #include "example6_authenticator.hpp" #include "example6_certificateVerifier.hpp" #include "example6_timeoutHandler.hpp" // Global session object static vmime::shared_ptr g_session = vmime::net::session::create(); /** Returns the messaging protocols supported by VMime. * * @param type service type (vmime::net::service::TYPE_STORE or * vmime::net::service::TYPE_TRANSPORT) */ static const std::string findAvailableProtocols(const vmime::net::service::Type type) { vmime::shared_ptr sf = vmime::net::serviceFactory::getInstance(); std::ostringstream res; size_t count = 0; for (size_t i = 0 ; i < sf->getServiceCount() ; ++i) { const vmime::net::serviceFactory::registeredService& serv = *sf->getServiceAt(i); if (serv.getType() == type) { if (count != 0) { res << ", "; } res << serv.getName(); ++count; } } return res.str(); } // Exception helper static std::ostream& operator<<(std::ostream& os, const vmime::exception& e) { os << "* vmime::exceptions::" << e.name() << std::endl; os << " what = " << e.what() << std::endl; // More information for special exceptions if (dynamic_cast (&e)) { const vmime::exceptions::command_error& cee = dynamic_cast (e); os << " command = " << cee.command() << std::endl; os << " response = " << cee.response() << std::endl; } if (dynamic_cast (&e)) { const vmime::exceptions::invalid_response& ir = dynamic_cast (e); os << " response = " << ir.response() << std::endl; } if (dynamic_cast (&e)) { const vmime::exceptions::connection_greeting_error& cgee = dynamic_cast (e); os << " response = " << cgee.response() << std::endl; } if (dynamic_cast (&e)) { const vmime::exceptions::authentication_error& aee = dynamic_cast (e); os << " response = " << aee.response() << std::endl; } if (dynamic_cast (&e)) { const vmime::exceptions::filesystem_exception& fse = dynamic_cast (e); os << " path = " << vmime::platform::getHandler()-> getFileSystemFactory()->pathToString(fse.path()) << std::endl; } if (e.other()) { os << *e.other(); } return os; } /** Print the MIME structure of a message on the standard output. * * @param s structure object * @param level current depth */ static void printStructure( vmime::shared_ptr s, const int level = 0 ) { for (size_t i = 0 ; i < s->getPartCount() ; ++i) { vmime::shared_ptr part = s->getPartAt(i); for (int j = 0 ; j < level * 2 ; ++j) { std::cout << " "; } std::cout << (part->getNumber() + 1) << ". " << part->getType().generate() << " [" << part->getSize() << " byte(s)]" << std::endl; printStructure(part->getStructure(), level + 1); } } static const vmime::string getFolderPathString(vmime::shared_ptr f) { const vmime::string n = f->getName().getBuffer(); if (n.empty()) { // root folder return "/"; } else { vmime::shared_ptr p = f->getParent(); return getFolderPathString(p) + n + "/"; } } /** Print folders and sub-folders on the standard output. * * @param folder current folder */ static void printFolders(vmime::shared_ptr folder, const int level = 0) { for (int j = 0 ; j < level * 2 ; ++j) { std::cout << " "; } const vmime::net::folderAttributes attr = folder->getAttributes(); std::ostringstream attrStr; if (attr.getSpecialUse() == vmime::net::folderAttributes::SPECIALUSE_ALL) { attrStr << " \\use:All"; } else if (attr.getSpecialUse() == vmime::net::folderAttributes::SPECIALUSE_ARCHIVE) { attrStr << " \\use:Archive"; } else if (attr.getSpecialUse() == vmime::net::folderAttributes::SPECIALUSE_DRAFTS) { attrStr << " \\use:Drafts"; } else if (attr.getSpecialUse() == vmime::net::folderAttributes::SPECIALUSE_FLAGGED) { attrStr << " \\use:Flagged"; } else if (attr.getSpecialUse() == vmime::net::folderAttributes::SPECIALUSE_JUNK) { attrStr << " \\use:Junk"; } else if (attr.getSpecialUse() == vmime::net::folderAttributes::SPECIALUSE_SENT) { attrStr << " \\use:Sent"; } else if (attr.getSpecialUse() == vmime::net::folderAttributes::SPECIALUSE_TRASH) { attrStr << " \\use:Trash"; } else if (attr.getSpecialUse() == vmime::net::folderAttributes::SPECIALUSE_IMPORTANT) { attrStr << " \\use:Important"; } if (attr.getFlags() & vmime::net::folderAttributes::FLAG_HAS_CHILDREN) { attrStr << " \\flag:HasChildren"; } if (attr.getFlags() & vmime::net::folderAttributes::FLAG_NO_OPEN) { attrStr << " \\flag:NoOpen"; } for (size_t i = 0, n = attr.getUserFlags().size() ; i < n ; ++i) { attrStr << " \\" << attr.getUserFlags()[i]; } std::cout << getFolderPathString(folder); std::cout << " " << attrStr.str(); std::cout << std::endl; std::vector > subFolders = folder->getFolders(false); for (unsigned int i = 0 ; i < subFolders.size() ; ++i) { printFolders(subFolders[i], level + 1); } } /** Print a menu on the standard output. * * @param choices menu choices */ static unsigned int printMenu(const std::vector & choices) { std::cout << std::endl; for (unsigned int i = 0 ; i < choices.size() ; ++i) { std::cout << " " << (i + 1) << ". " << choices[i] << std::endl; } std::cout << std::endl; std::cout << " Your choice? [1-" << choices.size() << "] "; std::cout.flush(); std::string line; std::getline(std::cin, line); std::istringstream iss(line); unsigned int choice = 0; iss >> choice; std::cout << std::endl; if (choice < 1 || choice > choices.size()) { return 0; } else { return choice; } } /** Send a message interactively. */ static void sendMessage() { try { // Request user to enter an URL std::cout << "Enter an URL to connect to transport service." << std::endl; std::cout << "Available protocols: " << findAvailableProtocols(vmime::net::service::TYPE_TRANSPORT) << std::endl; std::cout << "(eg. smtp://myserver.com, sendmail://localhost)" << std::endl; std::cout << "> "; std::cout.flush(); vmime::string urlString; std::getline(std::cin, urlString); vmime::utility::url url(urlString); vmime::shared_ptr tr; if (url.getUsername().empty() || url.getPassword().empty()) { tr = g_session->getTransport(url, vmime::make_shared ()); } else { tr = g_session->getTransport(url); } #if VMIME_HAVE_TLS_SUPPORT // Enable TLS support if available tr->setProperty("connection.tls", true); // Set the time out handler tr->setTimeoutHandlerFactory(vmime::make_shared ()); // Set the object responsible for verifying certificates, in the // case a secured connection is used (TLS/SSL) tr->setCertificateVerifier( vmime::make_shared () ); #endif // VMIME_HAVE_TLS_SUPPORT // You can also set some properties (see example7 to know the properties // available for each service). For example, for SMTP: if (!url.getUsername().empty() || !url.getPassword().empty()) { tr->setProperty("options.need-authentication", true); } // Trace communication between client and server vmime::shared_ptr traceStream = vmime::make_shared (); tr->setTracerFactory(vmime::make_shared (traceStream)); // Information about the mail std::cout << "Enter email of the expeditor (eg. me@somewhere.com): "; std::cout.flush(); vmime::string fromString; std::getline(std::cin, fromString); vmime::mailbox from(fromString); vmime::mailboxList to; for (bool cont = true ; cont ; ) { std::cout << "Enter email of the recipient (empty to stop): "; std::cout.flush(); vmime::string toString; std::getline(std::cin, toString); cont = (toString.size() != 0); if (cont) { to.appendMailbox(vmime::make_shared (toString)); } } std::cout << "Enter message data, including headers (end with '.' on a single line):" << std::endl; std::ostringstream data; for (bool cont = true ; cont ; ) { std::string line; std::getline(std::cin, line); if (line == ".") { cont = false; } else { data << line << "\r\n"; } } // Connect to server tr->connect(); // Send the message vmime::string msgData = data.str(); vmime::utility::inputStreamStringAdapter vis(msgData); tr->send(from, to, vis, msgData.length()); // Note: you could also write this: // vmime::message msg; // ... // tr->send(&msg); // Display connection log std::cout << std::endl; std::cout << "Connection Trace:" << std::endl; std::cout << "=================" << std::endl; std::cout << traceStream->str(); tr->disconnect(); } catch (vmime::exception& e) { std::cerr << std::endl; std::cerr << e << std::endl; throw; } catch (std::exception& e) { std::cerr << std::endl; std::cerr << "std::exception: " << e.what() << std::endl; throw; } } /** Connect to a message store interactively. */ static void connectStore() { try { // Request user to enter an URL std::cout << "Enter an URL to connect to store service." << std::endl; std::cout << "Available protocols: " << findAvailableProtocols(vmime::net::service::TYPE_STORE) << std::endl; std::cout << "(eg. pop3://user:pass@myserver.com, imap://myserver.com:123)" << std::endl; std::cout << "> "; std::cout.flush(); vmime::string urlString; std::getline(std::cin, urlString); vmime::utility::url url(urlString); // If no authenticator is given in argument to getStore(), a default one // is used. Its behaviour is to get the user credentials from the // session properties "auth.username" and "auth.password". vmime::shared_ptr st; if (url.getUsername().empty() || url.getPassword().empty()) { st = g_session->getStore(url, vmime::make_shared ()); } else { st = g_session->getStore(url); } #if VMIME_HAVE_TLS_SUPPORT // Enable TLS support if available st->setProperty("connection.tls", true); // Set the time out handler st->setTimeoutHandlerFactory(vmime::make_shared ()); // Set the object responsible for verifying certificates, in the // case a secured connection is used (TLS/SSL) st->setCertificateVerifier( vmime::make_shared () ); #endif // VMIME_HAVE_TLS_SUPPORT // Trace communication between client and server vmime::shared_ptr traceStream = vmime::make_shared (); st->setTracerFactory(vmime::make_shared (traceStream)); // Connect to the mail store st->connect(); // Display some information about the connection vmime::shared_ptr ci = st->getConnectionInfos(); std::cout << std::endl; std::cout << "Connected to '" << ci->getHost() << "' (port " << ci->getPort() << ")" << std::endl; std::cout << "Connection is " << (st->isSecuredConnection() ? "" : "NOT ") << "secured." << std::endl; // Open the default folder in this store vmime::shared_ptr f = st->getDefaultFolder(); // vmime::shared_ptr f = st->getFolder(vmime::utility::path("a")); f->open(vmime::net::folder::MODE_READ_WRITE); vmime::size_t count = f->getMessageCount(); std::cout << std::endl; std::cout << count << " message(s) in your inbox" << std::endl; for (bool cont = true ; cont ; ) { typedef std::map > MessageList; MessageList msgList; try { std::vector choices; choices.push_back("Show message flags"); choices.push_back("Show message structure"); choices.push_back("Show message header"); choices.push_back("Show message envelope"); choices.push_back("Extract whole message"); choices.push_back("Extract attachments"); choices.push_back("Status"); choices.push_back("List folders"); choices.push_back("Change folder"); choices.push_back("Add message (to the current folder)"); choices.push_back("Copy message (into the current folder)"); choices.push_back("Display trace output"); choices.push_back("Return to main menu"); const int choice = printMenu(choices); // Request message number vmime::shared_ptr msg; if (choice == 1 || choice == 2 || choice == 3 || choice == 4 || choice == 5 || choice == 6 || choice == 11) { std::cout << "Enter message number: "; std::cout.flush(); std::string line; std::getline(std::cin, line); std::istringstream iss(line); vmime::size_t num = 0; iss >> num; if (num < 1 || num > f->getMessageCount()) { std::cerr << "Invalid message number." << std::endl; continue; } MessageList::iterator it = msgList.find(num); if (it != msgList.end()) { msg = (*it).second; } else { msg = f->getMessage(num); msgList.insert(MessageList::value_type(num, msg)); } std::cout << std::endl; } switch (choice) { // Show message flags case 1: f->fetchMessage(msg, vmime::net::fetchAttributes::FLAGS); if (msg->getFlags() & vmime::net::message::FLAG_SEEN) { std::cout << "FLAG_SEEN" << std::endl; } if (msg->getFlags() & vmime::net::message::FLAG_RECENT) { std::cout << "FLAG_RECENT" << std::endl; } if (msg->getFlags() & vmime::net::message::FLAG_REPLIED) { std::cout << "FLAG_REPLIED" << std::endl; } if (msg->getFlags() & vmime::net::message::FLAG_DELETED) { std::cout << "FLAG_DELETED" << std::endl; } if (msg->getFlags() & vmime::net::message::FLAG_MARKED) { std::cout << "FLAG_MARKED" << std::endl; } if (msg->getFlags() & vmime::net::message::FLAG_PASSED) { std::cout << "FLAG_PASSED" << std::endl; } break; // Show message structure case 2: f->fetchMessage(msg, vmime::net::fetchAttributes::STRUCTURE); printStructure(msg->getStructure()); break; // Show message header case 3: f->fetchMessage(msg, vmime::net::fetchAttributes::FULL_HEADER); std::cout << msg->getHeader()->generate() << std::endl; break; // Show message envelope case 4: { vmime::net::fetchAttributes attr(vmime::net::fetchAttributes::ENVELOPE); // If you also want to fetch "Received: " fields: //attr.add("Received"); f->fetchMessage(msg, attr); std::cout << msg->getHeader()->generate() << std::endl; break; } // Extract whole message case 5: { vmime::utility::outputStreamAdapter out(std::cout); msg->extract(out); break; } // Extract attachments case 6: { vmime::shared_ptr parsedMsg = msg->getParsedMessage(); std::vector > attchs = vmime::attachmentHelper::findAttachmentsInMessage(parsedMsg); if (attchs.size() > 0) { std::cout << attchs.size() << " attachments found." << std::endl; for (std::vector >::iterator it = attchs.begin() ; it != attchs.end() ; ++it) { vmime::shared_ptr att = *it; // Get attachment size vmime::size_t size = 0; if (att->getData()->isEncoded()) { size = att->getData()->getEncoding().getEncoder()->getDecodedSize(att->getData()->getLength()); } else { size = att->getData()->getLength(); } std::cout << "Found attachment '" << att->getName().getBuffer() << "'" << ", size is " << size << " bytes:" << std::endl; // Get attachment data std::cout << std::endl; std::cout << "========== BEGIN CONTENT ==========" << std::endl; vmime::utility::outputStreamAdapter osa(std::cout); att->getData()->extract(osa); std::cout << std::endl; std::cout << "========== END CONTENT ==========" << std::endl; // Or write it to a file /* vmime::shared_ptr fsf = vmime::platform::getHandler()->getFileSystemFactory(); vmime::shared_ptr file = fsf->create(vmime::utility::path::fromString ("/path/to/attachment-file", "/", vmime::charsets::UTF_8)); // -or- ("C:\\Temp\\attachment-file", "\\", vmime::charsets::UTF_8)); file->createFile(); vmime::shared_ptr output = file->getFileWriter()->getOutputStream(); att->getData()->extract(*output.get()); */ } } else { std::cout << "No attachments found." << std::endl; } break; } // Status case 7: { vmime::size_t count, unseen; f->status(count, unseen); std::cout << "Status: count=" << count << ", unseen=" << unseen << std::endl; break; } // List folders case 8: { vmime::shared_ptr root = st->getRootFolder(); printFolders(root); break; } // Change folder case 9: { std::cout << "Enter folder path (eg. /root/subfolder):" << std::endl; std::cout.flush(); std::string path; std::getline(std::cin, path); vmime::shared_ptr newFolder = st->getRootFolder(); for (std::string::size_type s = 0, p = 0 ; ; s = p + 1) { p = path.find_first_of('/', s); const std::string x = (p == std::string::npos) ? std::string(path.begin() + s, path.end()) : std::string(path.begin() + s, path.begin() + p); if (!x.empty()) { newFolder = newFolder->getFolder(vmime::utility::path::component(x)); } if (p == std::string::npos) { break; } } newFolder->open(vmime::net::folder::MODE_READ_WRITE); count = newFolder->getMessageCount(); std::cout << std::endl; std::cout << count << " message(s) in this folder" << std::endl; f->close(true); // 'true' to expunge deleted messages f = newFolder; break; } // Add message case 10: { vmime::messageBuilder mb; mb.setExpeditor(vmime::mailbox("me@somewhere.com")); vmime::addressList to; to.appendAddress(vmime::make_shared ("you@elsewhere.com")); mb.setRecipients(to); mb.setSubject(vmime::text("Test message from VMime example6")); mb.getTextPart()->setText( vmime::make_shared ( "Body of test message from VMime example6." ) ); vmime::shared_ptr msg = mb.construct(); vmime::net::messageSet set = f->addMessage(msg); if (set.isEmpty()) { std::cout << "Message has successfully been added, " << "but its UID/number is not known." << std::endl; } else { const vmime::net::messageRange& range = set.getRangeAt(0); if (set.isUIDSet()) { const vmime::net::message::uid uid = dynamic_cast (range).getFirst(); std::cout << "Message has successfully been added, " << "its UID is '" << uid << "'." << std::endl; } else { const vmime::size_t number = dynamic_cast (range).getFirst(); std::cout << "Message has successfully been added, " << "its number is '" << number << "'." << std::endl; } } break; } // Copy message case 11: { vmime::net::messageSet set = f->copyMessages(f->getFullPath(), vmime::net::messageSet::byNumber(msg->getNumber())); if (set.isEmpty()) { std::cout << "Message has successfully been copied, " << "but its UID/number is not known." << std::endl; } else { const vmime::net::messageRange& range = set.getRangeAt(0); if (set.isUIDSet()) { const vmime::net::message::uid uid = dynamic_cast (range).getFirst(); std::cout << "Message has successfully been copied, " << "its UID is '" << uid << "'." << std::endl; } else { const vmime::size_t number = dynamic_cast (range).getFirst(); std::cout << "Message has successfully been copied, " << "its number is '" << number << "'." << std::endl; } } break; } // Display trace output case 12: std::cout << std::endl; std::cout << "Connection Trace:" << std::endl; std::cout << "=================" << std::endl; std::cout << traceStream->str(); break; // Main menu case 13: f->close(true); // 'true' to expunge deleted messages cont = false; break; } /* // Append message std::istringstream iss( "From: me@localhost\r\n" "To: you@localhost\r\n" "Subject: Message Text\r\n" "\r\n" "This is a test message...\r\n" "Bye bye!\r\n" ); f->addMessage(iss, iss.str().size()); // Folder renaming { vmime::shared_ptr f = st->getFolder(vmime::net::folder::path("c")); f->rename(vmime::net::folder::path("c2")); vmime::shared_ptr g = st->getFolder(vmime::net::folder::path("c2")); g->rename(vmime::net::folder::path("c")); } // Message copy: copy all messages from 'f' to 'g' { vmime::shared_ptr g = st->getFolder(vmime::net::folder::path("TEMP")); if (!g->exists()) { g->create(vmime::net::folder::TYPE_CONTAINS_MESSAGES); } f->copyMessages(g->getFullPath()); } */ } catch (vmime::exception& e) { std::cerr << std::endl; std::cerr << e << std::endl; } catch (std::exception& e) { std::cerr << std::endl; std::cerr << "std::exception: " << e.what() << std::endl; } } // for(cont) st->disconnect(); } catch (vmime::exception& e) { std::cerr << std::endl; std::cerr << e << std::endl; throw; } catch (std::exception& e) { std::cerr << std::endl; std::cerr << "std::exception: " << e.what() << std::endl; throw; } } /* Show the main menu. * * @return true to quit the program, false to continue */ static bool menu() { std::vector items; items.push_back("Connect to a message store"); items.push_back("Send a message"); items.push_back("Quit"); switch (printMenu(items)) { // Connect to store case 1: connectStore(); return false; // Send a message case 2: sendMessage(); return false; // Quit case 3: return true; // Other choice default: return false; } } int main() { // Set the global C and C++ locale to the user-configured locale. // The locale should use UTF-8 encoding for these tests to run successfully. try { std::locale::global(std::locale("")); } catch (std::exception &) { std::setlocale(LC_ALL, ""); } for (bool quit = false ; !quit ; ) { // Loop on main menu quit = menu(); } return 0; }