diff options
Diffstat (limited to 'src')
204 files changed, 35988 insertions, 0 deletions
diff --git a/src/address.cpp b/src/address.cpp new file mode 100644 index 00000000..481cc209 --- /dev/null +++ b/src/address.cpp @@ -0,0 +1,201 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "address.hpp" + +#include "mailbox.hpp" +#include "mailboxGroup.hpp" + +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +address::address() +{ +} + + +/* + + RFC #2822: + 3.4. ADDRESS SPECIFICATION + + Addresses occur in several message header fields to indicate senders + and recipients of messages. An address may either be an individual + mailbox, or a group of mailboxes. + +address = mailbox / group + +mailbox = name-addr / addr-spec + +name-addr = [display-name] angle-addr + +angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr + +group = display-name ":" [mailbox-list / CFWS] ";" + [CFWS] + +display-name = phrase + +mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list + +address-list = (address *("," address)) / obs-addr-list + +*/ + +address* address::parseNext(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + bool escaped = false; + bool quoted = false; + bool quotedRFC2047 = false; + bool inRouteAddr = false; + bool isGroup = false; + bool stop = false; + + string::size_type pos = position; + + while (pos < end && isspace(buffer[pos])) + ++pos; + + const string::size_type start = pos; + + while (!stop && pos < end) + { + if (escaped) + { + escaped = false; + } + else + { + switch (buffer[pos]) + { + case '\\': + escaped = true; + break; + case '"': + quoted = !quoted; + break; + case '<': + inRouteAddr = true; + break; + case '>': + inRouteAddr = false; + break; + case '=': + + if (pos + 1 < end && buffer[pos + 1] == '?') + { + ++pos; + quotedRFC2047 = true; + } + + break; + + case '?': + + if (quotedRFC2047 && pos + 1 < end && buffer[pos + 1] == '=') + { + ++pos; + quotedRFC2047 = false; + } + + break; + + default: + { + if (!quoted && !quotedRFC2047 && !inRouteAddr) + { + switch (buffer[pos]) + { + case ';': + + if (isGroup) + { + if (pos + 1 < end && buffer[pos + 1] == ',') + ++pos; + } + + stop = true; + break; + + case ':': + + isGroup = true; + break; + + case ',': + + if (!isGroup) stop = true; + break; + } + } + + break; + } + + } + } + + if (!stop) + ++pos; + } + + if (newPosition) + { + if (pos == end) + *newPosition = end; + else + *newPosition = pos + 1; // ',' or ';' + } + + // Parse extracted address (mailbox or group) + if (pos != start) + { + address* parsedAddress = isGroup + ? static_cast<address*>(new mailboxGroup) + : static_cast<address*>(new mailbox); + + try + { + parsedAddress->parse(buffer, start, pos, NULL); + return (parsedAddress); + } + catch (std::exception&) + { + delete (parsedAddress); + throw; + } + } + + return (NULL); +} + + +address& address::operator=(const address& addr) +{ + copyFrom(addr); + return (*this); +} + + +} // vmime diff --git a/src/address.hpp b/src/address.hpp new file mode 100644 index 00000000..a3d8cedb --- /dev/null +++ b/src/address.hpp @@ -0,0 +1,99 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ADDRESS_HPP_INCLUDED +#define VMIME_ADDRESS_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Abstract class representing a mailbox or a group of mailboxes. + * + * This class define a common behaviour for the mailbox + * and mailboxGroup classes. + */ + +class address : public component +{ + friend class addressList; + +protected: + + address(); + +public: + + /** Copy data from another object to this object. + * Both objects must be the same type. + * + * @param addr other object + */ + address& operator=(const address& addr); + + /** Duplicate this object. + * + * @return a copy of this object + */ + virtual address* clone() const = 0; + + /** Copy data from another object to this object. + * Both objects must be the same type. + * + * @param addr other object + */ + virtual void copyFrom(const address& addr) = 0; + + /** Check whether this address is empty (no mailboxes specified + * if this is a mailboxGroup -or- no email specified if this is + * a mailbox). + * + * @return true if this address is empty + */ + virtual const bool empty() const = 0; + + /** Test whether this is object is a mailboxGroup. + * + * @return true if this is a mailboxGroup, false otherwise + */ + virtual const bool isGroup() const = 0; + +protected: + + /** Parse an address from an input buffer. + * + * @param buffer input buffer + * @param position position in the input buffer + * @param end end position in the input buffer + * @param newPosition will receive the new position in the input buffer + * @return a new address object, or null if no more address is available in the input buffer + */ + static address* parseNext(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition); +}; + + +} // vmime + + +#endif // VMIME_ADDRESS_HPP_INCLUDED diff --git a/src/addressList.cpp b/src/addressList.cpp new file mode 100644 index 00000000..c532c16d --- /dev/null +++ b/src/addressList.cpp @@ -0,0 +1,188 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "addressList.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +addressList::addressList() +{ +} + + +addressList::addressList(const class addressList& addressList) + : component() +{ + copyFrom(addressList); +} + + +addressList::~addressList() +{ + clear(); +} + + +void addressList::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + clear(); + + string::size_type pos = position; + + while (pos < end) + { + address* parsedAddress = address::parseNext(buffer, pos, end, &pos); + + if (parsedAddress != NULL) + m_list.push_back(parsedAddress); + } + + if (newPosition) + *newPosition = end; +} + + +void addressList::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + if (!m_list.empty()) + { + string::size_type pos = curLinePos; + const_iterator i = m_list.begin(); + + for ( ; ; ) + { + (*i).generate(os, maxLineLength - 2, pos, &pos); + + if (++i != m_list.end()) + { + os << ", "; + pos += 2; + } + else + { + break; + } + } + + if (newLinePos) + *newLinePos = pos; + } +} + + +/** Return the number of addresses in the list. + * + * @return number of addresses in the list + */ + +const std::vector <address*>::size_type addressList::size() const +{ + return (m_list.size()); +} + + +/** Return the number of addresses in the list. + * + * @return number of addresses in the list + */ + +const std::vector <address*>::size_type addressList::count() const +{ + return (m_list.size()); +} + + +/** Test whether the list is empty. + * + * @return true if the list is empty, false otherwise + */ + +const bool addressList::empty() const +{ + return (m_list.empty()); +} + + +/** Append an address to the list. + * + * @param addr the address to add + */ + +void addressList::append(const address& addr) +{ + m_list.push_back(addr.clone()); +} + + +/** Insert an address at the specified position in the list. + * + * @param it position of the new address + * @param addr the address to insert + */ + +void addressList::insert(const iterator it, const address& addr) +{ + m_list.insert(it.m_iterator, addr.clone()); +} + + +/** Remove the address at the specified position. + * + * @param it position of the address to remove + */ + +void addressList::erase(const iterator it) +{ + delete (*it.m_iterator); + m_list.erase(it.m_iterator); +} + + +/** Remove all the addresses from the list. + */ + +void addressList::clear() +{ + free_container(m_list); +} + + +void addressList::copyFrom(const addressList& source) +{ + clear(); + + for (std::vector <address*>::const_iterator i = source.m_list.begin() ; i != source.m_list.end() ; ++i) + m_list.push_back((*i)->clone()); +} + + +addressList& addressList::operator=(const addressList& source) +{ + copyFrom(source); + return (*this); +} + + +} // vmime diff --git a/src/addressList.hpp b/src/addressList.hpp new file mode 100644 index 00000000..03a53e57 --- /dev/null +++ b/src/addressList.hpp @@ -0,0 +1,148 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ADDRESSLIST_HPP_INCLUDED +#define VMIME_ADDRESSLIST_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "address.hpp" + + +namespace vmime +{ + + +/** A list of addresses. + */ + +class addressList : public component +{ + friend class addressListField; + friend class mailboxListField; + +public: + + addressList(); + addressList(const class addressList& addressList); + + ~addressList(); + +public: + + addressList& operator=(const addressList& source); + + // Address iterator + class const_iterator; + + class iterator + { + friend class addressList; + friend class const_iterator; + + public: + + iterator(std::vector <address*>::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + address& operator*() const { return (**m_iterator); } + address* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + private: + + std::vector <address*>::iterator m_iterator; + }; + + class const_iterator + { + friend class addressList; + + public: + + const_iterator(std::vector <address*>::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const address& operator*() const { return (**m_iterator); } + const address* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + private: + + std::vector <address*>::const_iterator m_iterator; + }; + + iterator begin() { return (m_list.begin()); } + iterator end() { return (m_list.end()); } + + const_iterator begin() const { return (const_iterator(m_list.begin())); } + const_iterator end() const { return (const_iterator(m_list.end())); } + + const std::vector <address*>::size_type size() const; + const std::vector <address*>::size_type count() const; + const bool empty() const; + + const address& operator[](const std::vector <address*>::size_type x) const { return (*m_list[x]); } + address& operator[](const std::vector <address*>::size_type x) { return (*m_list[x]); } + + virtual void append(const address& addr); + virtual void insert(const iterator it, const address& addr); + + void erase(const iterator it); + void clear(); + +protected: + + std::vector <address*> m_list; + + void copyFrom(const addressList& source); + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_ADDRESSLIST_HPP_INCLUDED diff --git a/src/addressListField.cpp b/src/addressListField.cpp new file mode 100644 index 00000000..020d6b10 --- /dev/null +++ b/src/addressListField.cpp @@ -0,0 +1,67 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "addressListField.hpp" + + +namespace vmime +{ + + +addressListField::addressListField() +{ +} + + +void addressListField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_list.parse(buffer, position, end, newPosition); +} + + +void addressListField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + m_list.generate(os, maxLineLength, pos, newLinePos); +} + + +addressListField& addressListField::operator=(const addressList& list) +{ + m_list.copyFrom(list); + return (*this); +} + + +void addressListField::copyFrom(const headerField& field) +{ + const addressListField& source = dynamic_cast<const addressListField&>(field); + m_list = source.m_list; + + headerField::copyFrom(field); +} + + +} // vmime + diff --git a/src/addressListField.hpp b/src/addressListField.hpp new file mode 100644 index 00000000..1cc63ff6 --- /dev/null +++ b/src/addressListField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ADDRESSLISTFIELD_HPP_INCLUDED +#define VMIME_ADDRESSLISTFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "addressList.hpp" + + +namespace vmime +{ + + +class addressListField : public headerField +{ + friend class headerFieldFactory::registerer <addressListField>; + +protected: + + addressListField(); + +public: + + void copyFrom(const headerField& field); + + addressListField& operator=(const addressList& list); + + const addressList& value() const { return (m_list); } + addressList& value() { return (m_list); } + +protected: + + addressList m_list; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_ADDRESSLISTFIELD_HPP_INCLUDED diff --git a/src/attachment.hpp b/src/attachment.hpp new file mode 100644 index 00000000..083f98d8 --- /dev/null +++ b/src/attachment.hpp @@ -0,0 +1,82 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ATTACHMENT_HPP_INCLUDED +#define VMIME_ATTACHMENT_HPP_INCLUDED + + +#include "base.hpp" + +#include "bodyPart.hpp" +#include "mediaType.hpp" +#include "text.hpp" +#include "contentHandler.hpp" +#include "encoding.hpp" + + +namespace vmime +{ + + +class attachment +{ + friend class messageBuilder; + friend class messageParser; + +protected: + + attachment() { } + +public: + + virtual ~attachment() { } + + virtual attachment& operator=(const attachment& attach) = 0; + + /** Return the media type of this attachment. + * @return content type of the attachment + */ + virtual const mediaType& type() const = 0; + + /** Return the description of this attachment. + * @return attachment description + */ + virtual const text& description() const = 0; + + /** Return the data contained in this attachment. + * @return attachment data + */ + virtual const contentHandler& data() const = 0; + + /** Return the encoding used for this attachment. + * @return attachment data encoding + */ + virtual const class encoding& encoding() const = 0; + + /** Generate the attachment in the specified body part. + * @param parent body part in which to generate the attachment + */ + virtual void generateIn(bodyPart& parent) const = 0; +}; + + +} // vmime + + +#endif // VMIME_ATTACHMENT_HPP_INCLUDED diff --git a/src/base.cpp b/src/base.cpp new file mode 100644 index 00000000..84fdc9fc --- /dev/null +++ b/src/base.cpp @@ -0,0 +1,877 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "config.hpp" + +#include "charset.hpp" +#include "base.hpp" + +#include "encoder.hpp" +#include "encoderB64.hpp" +#include "encoderQP.hpp" + +#include "text.hpp" + +#include "parserHelpers.hpp" + +// For initializing +#include "encoderFactory.hpp" +#include "headerFieldFactory.hpp" +#include "parameterFactory.hpp" +#include "textPartFactory.hpp" +#include "options.hpp" + +#if VMIME_HAVE_MESSAGING_FEATURES + #include "messaging/serviceFactory.hpp" +#endif + + +namespace vmime +{ + + +/** "Null" (empty) string. + */ +const string NULL_STRING; + +#if VMIME_WIDE_CHAR_SUPPORT + /** "Null" (empty) wide-char string. + */ + const wstring NULL_WSTRING; +#endif + +/** "Null" (empty) text. + */ +const text NULL_TEXT; + + +/** Return the library name (eg. "libvmime"). + * + * @return library name + */ +const string libname() { return (VMIME_PACKAGE); } + +/** Return the library version (eg. "0.5.2"). + * + * @return library version + */ +const string libversion() { return (VMIME_VERSION " (" __DATE__ " " __TIME__ ")"); } + + +// New line sequence to be used when folding header fields. +const string NEW_LINE_SEQUENCE("\r\n "); +const string::size_type NEW_LINE_SEQUENCE_LENGTH(1); // space + +/** The CR-LF sequence. + */ +const string CRLF("\r\n"); + + +/** The current MIME version supported by VMime. + */ +const string MIME_VERSION("1.0"); + + +// Line length limits +namespace lineLengthLimits +{ + const string::size_type infinite = std::numeric_limits <string::size_type>::max(); +} + + + +/** Test two strings for equality (case insensitive). + * WARNING: use this with ASCII-only strings. + * + * @param s1 first string + * @param s2 second string (must be in lower-case!) + * @param n length of the second string + * @return true if the two strings compare equally, false otherwise + */ + +bool isStringEqualNoCase(const string& s1, const char* s2, const string::size_type n) +{ + // 'n' is the number of characters to compare + // 's2' must be in lowercase letters only + if (s1.length() < n) + return (false); + + bool equal = true; + + for (string::size_type i = 0 ; equal && i < n ; ++i) + equal = (std::tolower(s1[i], std::locale()) == s2[i]); + + return (equal); +} + + +/** Test two strings for equality (case insensitive). + * WARNING: use this with ASCII-only strings. + * + * @param s1 first string + * @param s2 second string + * @return true if the two strings compare equally, false otherwise + */ + +bool isStringEqualNoCase(const string& s1, const string& s2) +{ + if (s1.length() != s2.length()) + return (false); + + bool equal = true; + const string::const_iterator end = s1.end(); + + for (string::const_iterator i = s1.begin(), j = s2.begin(); i != end ; ++i, ++j) + equal = (std::tolower(*i, std::locale()) == std::tolower(*j, std::locale())); + + return (equal); +} + + +/** Test two strings for equality (case insensitive). + * WARNING: use this with ASCII-only strings. + * + * @param begin start position of the first string + * @param end end position of the first string + * @param s second string (must be in lower-case!) + * @param n length of the second string + * @return true if the two strings compare equally, false otherwise + */ + +bool isStringEqualNoCase(const string::const_iterator begin, const string::const_iterator end, + const char* s, const string::size_type n) +{ + if ((string::size_type)(end - begin) < n) + return (false); + + bool equal = true; + char* c = const_cast<char*>(s); + string::size_type r = n; + + for (string::const_iterator i = begin ; equal && r && *c ; ++i, ++c, --r) + equal = (std::tolower(*i, std::locale()) == *c); + + return (r == 0 && equal); +} + + +/** Transform all the characters in a string to lower-case. + * WARNING: use this with ASCII-only strings. + * + * @param str the string to transform + * @return a new string in lower-case + */ + +const string toLower(const string& str) +{ + string out(str); + const string::iterator end = out.end(); + + for (string::iterator i = out.begin() ; i != end ; ++i) + *i = std::tolower(*i, std::locale()); + + return (out); +} + + +/** Strip the space characters (SPC, TAB, CR, LF) at the beginning + * and at the end of the specified string. + * + * @param str string in which to strip spaces + * @return a new string with space characters removed + */ + +const string trim(const string& str) +{ + string::const_iterator b = str.begin(); + string::const_iterator e = str.end(); + + if (b != e) + { + for ( ; b != e && isspace(*b) ; ++b); + for ( ; e != b && isspace(*(e - 1)) ; --e); + } + + return (string(b, e)); +} + + +/** Return the number of 7-bit US-ASCII characters in a string. + * + * @param begin start position + * @param end end position + * @return number of ASCII characters + */ + +string::size_type countASCIIchars + (const string::const_iterator begin, const string::const_iterator end) +{ + string::size_type count = 0; + + for (string::const_iterator i = begin ; i != end ; ++i) + { + if (isascii(*i)) + { + if (*i != '=' || *(i + 1) != '?') // To avoid bad behaviour... + ++count; + } + } + + return (count); +} + + +/** Encode and fold text in respect to RFC-2047. + * + * @param os output stream + * @param in input text + * @param maxLineLength maximum line length for output + * @param firstLineOffset the first line length (may be useful if the current output line is not empty) + * @param lastLineLength will receive the length of the last line written + * @param flags encoding flags (see encodeAndFoldFlags) + */ + +void encodeAndFoldText(utility::outputStream& os, const text& in, const string::size_type maxLineLength, + const string::size_type firstLineOffset, string::size_type* lastLineLength, const int flags) +{ + string::size_type curLineLength = firstLineOffset; + + for (text::const_iterator wi = in.begin() ; wi != in.end() ; ++wi) + { + const word& w = *wi; + const string& buffer = w.buffer(); + + // Calculate the number of ASCII chars to check whether encoding is needed + // and _which_ encoding to use. + const string::size_type asciiCount = countASCIIchars(buffer.begin(), buffer.end()); + + bool noEncoding = (flags & encodeAndFoldFlags::forceNoEncoding) || + (!(flags & encodeAndFoldFlags::forceEncoding) && asciiCount == buffer.length()); + + if (noEncoding) + { + // We will fold lines without encoding them. + + string::const_iterator lastWSpos = buffer.end(); // last white-space position + string::const_iterator curLineStart = buffer.begin(); // current line start + + string::const_iterator p = buffer.begin(); + const string::const_iterator end = buffer.end(); + + bool finished = false; + bool newLine = false; + + while (!finished) + { + for ( ; p != end ; ++p, ++curLineLength) + { + // Exceeded maximum line length, but we have found a white-space + // where we can cut the line... + if (curLineLength >= maxLineLength && lastWSpos != end) + break; + + if (*p == ' ' || *p == '\t') + { + // Remember the position of this white-space character + lastWSpos = p; + } + } + + if (p != end) + ++curLineLength; + + //if (p == end || curLineLength >= maxLineLength) + { + if (p == end || lastWSpos == end) + { + // If we are here, it means that we have found no whitespace + // before the first "maxLineLength" characters. In this case, + // we write the full line no matter of the max line length... + + if (!newLine && p != end && lastWSpos == end && + wi != in.begin() && curLineStart == buffer.begin()) + { + // Here, we are continuing on the line of previous encoded + // word, but there is not even enough space to put the + // first word of this line, so we start a new line. + if (flags & encodeAndFoldFlags::noNewLineSequence) + { + os << CRLF; + curLineLength = 0; + } + else + { + os << NEW_LINE_SEQUENCE; + curLineLength = NEW_LINE_SEQUENCE_LENGTH; + } + + p = curLineStart; + lastWSpos = end; + newLine = true; + } + else + { + os << string(curLineStart, p); + + if (p == end) + { + finished = true; + } + else + { + if (flags & encodeAndFoldFlags::noNewLineSequence) + { + os << CRLF; + curLineLength = 0; + } + else + { + os << NEW_LINE_SEQUENCE; + curLineLength = NEW_LINE_SEQUENCE_LENGTH; + } + + curLineStart = p; + lastWSpos = end; + newLine = true; + } + } + } + else + { + // In this case, there will not be enough space on the line for all the + // characters _after_ the last white-space; so we cut the line at this + // last white-space. + +#if 1 + if (curLineLength != 1 && wi != in.begin()) + os << " "; // Separate from previous word +#endif + + os << string(curLineStart, lastWSpos); + + if (flags & encodeAndFoldFlags::noNewLineSequence) + { + os << CRLF; + curLineLength = 0; + } + else + { + os << NEW_LINE_SEQUENCE; + curLineLength = NEW_LINE_SEQUENCE_LENGTH; + } + + curLineStart = lastWSpos + 1; + + p = lastWSpos + 1; + lastWSpos = end; + newLine = true; + } + } + } + } + /* + RFC #2047: + 4. Encodings + + Initially, the legal values for "encoding" are "Q" and "B". These + encodings are described below. The "Q" encoding is recommended for + use when most of the characters to be encoded are in the ASCII + character set; otherwise, the "B" encoding should be used. + Nevertheless, a mail reader which claims to recognize 'encoded-word's + MUST be able to accept either encoding for any character set which it + supports. + */ + else + { + // We will encode _AND_ fold lines + + /* + RFC #2047: + 2. Syntax of encoded-words + + " While there is no limit to the length of a multiple-line header + field, each line of a header field that contains one or more + 'encoded-word's is limited to 76 characters. " + */ + + const string::size_type maxLineLength3 = + (maxLineLength == lineLengthLimits::infinite) + ? maxLineLength + : std::min(maxLineLength, (const string::size_type) 76); + + // Base64 if more than 60% non-ascii, quoted-printable else (default) + const string::size_type asciiPercent = (100 * asciiCount) / buffer.length(); + const string::value_type encoding = (asciiPercent <= 40) ? 'B' : 'Q'; + + string wordStart("=?" + w.charset().name() + "?" + encoding + "?"); + string wordEnd("?="); + + const string::size_type minWordLength = wordStart.length() + wordEnd.length(); + const string::size_type maxLineLength2 = (maxLineLength3 < minWordLength + 1) + ? maxLineLength3 + minWordLength + 1 : maxLineLength3; + + // Checks whether remaining space on this line is usable. If too few + // characters can be encoded, start a new line. + bool startNewLine = true; + + if (curLineLength + 2 < maxLineLength2) + { + const string::size_type remainingSpaceOnLine = maxLineLength2 - curLineLength - 2; + + if (remainingSpaceOnLine < minWordLength + 10) + { + // Space for no more than 10 encoded chars! + // It is not worth while to continue on this line... + startNewLine = true; + } + else + { + // OK, there is enough usable space on the current line. + startNewLine = false; + } + } + + if (startNewLine) + { + os << NEW_LINE_SEQUENCE; + curLineLength = NEW_LINE_SEQUENCE_LENGTH; + } + + // Encode and fold input buffer + string::const_iterator pos = buffer.begin(); + string::size_type remaining = buffer.length(); + + encoder* theEncoder = ((encoding == 'B') + ? ((encoder*) new encoderB64) + : ((encoder*) new encoderQP)); + + string qpEncodedBuffer; + + if (encoding == 'Q') + { + theEncoder->properties()["rfc2047"] = true; + + // In the case of Quoted-Printable encoding, we cannot simply encode input + // buffer line by line. So, we encode the whole buffer and we will fold it + // in the next loop... + utility::inputStreamStringAdapter in(buffer); + utility::outputStreamStringAdapter out(qpEncodedBuffer); + + theEncoder->encode(in, out); + + pos = qpEncodedBuffer.begin(); + remaining = qpEncodedBuffer.length(); + } + +#if 1 + if (curLineLength != 1 && wi != in.begin()) + { + os << " "; // Separate from previous word + ++curLineLength; + } +#endif + + for ( ; remaining ; ) + { + // Start a new encoded word + os << wordStart; + curLineLength += minWordLength; + + // Compute the number of encoded chars that will fit on this line + const string::size_type fit = maxLineLength2 - curLineLength; + + // Base-64 encoding + if (encoding == 'B') + { + // TODO: WARNING! "Any encoded word which encodes a non-integral + // number of characters or octets is incorrectly formed." + + // Here, we have a formula to compute the maximum number of source + // characters to encode knowing the maximum number of encoded chars + // (with Base64, 3 bytes of input provide 4 bytes of output). + string::size_type count = (fit > 1) ? ((fit - 1) * 3) / 4 : 1; + if (count > remaining) count = remaining; + + utility::inputStreamStringAdapter in + (buffer, pos - buffer.begin(), pos - buffer.begin() + count); + + curLineLength += theEncoder->encode(in, os); + + pos += count; + remaining -= count; + } + // Quoted-Printable encoding + else + { + // TODO: WARNING! "Any encoded word which encodes a non-integral + // number of characters or octets is incorrectly formed." + + // All we have to do here is to take a certain number of character + // (that is less than or equal to "fit") from the QP encoded buffer, + // but we also make sure not to fold a "=XY" encoded char. + const string::const_iterator qpEnd = qpEncodedBuffer.end(); + string::const_iterator lastFoldPos = pos; + string::const_iterator p = pos; + string::size_type n = 0; + + while (n < fit && p != qpEnd) + { + if (*p == '=') + { + if (n + 3 >= fit) + { + lastFoldPos = p; + break; + } + + p += 3; + n += 3; + } + else + { + ++p; + ++n; + } + } + + if (lastFoldPos == pos) + lastFoldPos = p; + + os << string(pos, lastFoldPos); + + curLineLength += (lastFoldPos - pos) + 1; + + pos += n; + remaining -= n; + } + + // End of the encoded word + os << wordEnd; + + if (remaining) + { + os << NEW_LINE_SEQUENCE; + curLineLength = NEW_LINE_SEQUENCE_LENGTH; + } + } + + delete (theEncoder); + } + } + + if (lastLineLength) + *lastLineLength = curLineLength; +} + + +void decodeAndUnfoldText(const string::const_iterator& inStart, const string::const_iterator& inEnd, text& out) +{ + // NOTE: See RFC-2047, Pages 11-12 for knowing about handling + // of white-spaces between encoded words. + + out.clear(); + + string::const_iterator p = inStart; + const string::const_iterator end = inEnd; + + const charset defaultCharset(charsets::US_ASCII); + charset prevWordCharset(defaultCharset); + + bool prevIsEncoded = false; + + string::const_iterator prevPos = p; + + for ( ; ; ) + { + if (p == end || *p == '\n') + { + string::const_iterator textEnd = p; + + if (textEnd != inStart && *(textEnd - 1) == '\r') + --textEnd; + + if (textEnd != prevPos) + { + if (out.size() && prevWordCharset == defaultCharset) + { + out.back().buffer() += string(prevPos, textEnd); + } + else + { + prevWordCharset = defaultCharset; + out.append(word(string(prevPos, textEnd), defaultCharset)); + prevIsEncoded = false; + } + } + + if (p == end) + { + // Finished + break; + } + + // Skip the new-line character + prevPos = ++p; + } + else if (*p == '=' && (p + 1) != end && *(p + 1) == '?') + { + string::const_iterator wordPos = p; + p += 2; // skip '=?' + + if (p != end) + { + const string::const_iterator charsetPos = p; + + for ( ; p != end && *p != '?' ; ++p); + + if (p != end) // a charset is specified + { + const string::const_iterator charsetEnd = p; + const string::const_iterator encPos = ++p; // skip '?' + + for ( ; p != end && *p != '?' ; ++p); + + if (p != end) // an encoding is specified + { + //const string::const_iterator encEnd = p; + const string::const_iterator dataPos = ++p; // skip '?' + + for ( ; p != end && !(*p == '?' && *(p + 1) == '=') ; ++p); + + if (p != end) // some data is specified + { + const string::const_iterator dataEnd = p; + p += 2; // skip '?=' + + encoder* theEncoder = NULL; + + // Base-64 encoding + if (*encPos == 'B' || *encPos == 'b') + { + theEncoder = new encoderB64; + } + // Quoted-Printable encoding + else if (*encPos == 'Q' || *encPos == 'q') + { + theEncoder = new encoderQP; + theEncoder->properties()["rfc2047"] = true; + } + + if (theEncoder) + { + // Decode text + string decodedBuffer; + + utility::inputStreamStringAdapter ein(string(dataPos, dataEnd)); + utility::outputStreamStringAdapter eout(decodedBuffer); + + theEncoder->decode(ein, eout); + delete (theEncoder); + + // Append all the unencoded text before this word + if (prevPos != wordPos) + { + string::const_iterator p = prevPos; + + if (prevIsEncoded) + { + // Check whether there are only white-spaces between + // the two encoded words + for ( ; (p != wordPos) && isspace(*p) ; ++p); + } + + if (p != wordPos) // if not empty + { + if (out.size() && prevWordCharset == defaultCharset) + { + out.back().buffer() += string(prevPos, wordPos); + } + else + { + out.append(word(string(prevPos, wordPos), defaultCharset)); + prevWordCharset = defaultCharset; + } + } + } + + // Append this fresh decoded word to output text + charset thisCharset(string(charsetPos, charsetEnd)); + + if (out.size() && prevWordCharset == thisCharset) + { + out.back().buffer() += decodedBuffer; + } + else + { + prevWordCharset = thisCharset; + out.append(word(decodedBuffer, thisCharset)); + } + + // This word has been decoded: we can advance in the input buffer + prevPos = p; + prevIsEncoded = true; + } + else + { + // Unknown encoding: can't decode this word, we will + // treat this word as ordinary text (RFC-2047, Page 9). + } + } + } + } + } + } + else + { + ++p; + } + + for ( ; p != end && *p != '=' && *p != '\n' ; ++p); + } +} + + +void decodeAndUnfoldText(const string& in, text& out) +{ + decodeAndUnfoldText(in.begin(), in.end(), out); +} + + +/** This function can be used to make several encoded words from a text. + * All the characters in the text must be in the same specified charset. + * + * <p>Eg: giving:</p> + * <pre> <iso-8859-1> "Linux dans un t'el'ephone mobile" + * ("=?iso-8859-1?Q?Linux_dans_un_t=E9l=E9phone_mobile?=") + * </pre><p>it will return:</p> + * <pre> <:us-ascii> "Linux dans un " + * <iso-8859-1> "t'el'ephone " + * <us-ascii> "mobile" + * ("Linux dans un =?iso-8859-1?Q?t=E9l=E9phone_?= mobile") + * </pre> + * + * @param in input string + * @param ch input charset + * @param out output text + */ + +void makeWordsFromText(const string& in, const charset& ch, text& out) +{ + const string::const_iterator end = in.end(); + string::const_iterator p = in.begin(); + string::const_iterator start = in.begin(); + + bool is8bit = false; // is the current word 8-bit? + bool prevIs8bit = false; // is previous word 8-bit? + unsigned int count = 0; // total number of words + + out.clear(); + + for ( ; ; ) + { + if (p == end || isspace(*p)) + { + if (p != end) + ++p; + + if (is8bit) + { + if (prevIs8bit) + { + // No need to create a new encoded word, just append + // the current word to the previous one. + out.back().buffer() += string(start, p); + } + else + { + out.append(word(string(start, p), ch)); + prevIs8bit = true; + ++count; + } + } + else + { + if (count && !prevIs8bit) + { + out.back().buffer() += string(start, p); + } + else + { + out.append(word(string(start, p), charset(charsets::US_ASCII))); + prevIs8bit = false; + ++count; + } + } + + if (p == end) + break; + + is8bit = false; + start = p; + } + else if (!isascii(*p)) + { + is8bit = true; + ++p; + } + else + { + ++p; + } + } +} + + +// +// V-Mime Initializer +// ==================== +// +// Force instanciation of singletons. This is to prevent problems that might +// happen in multithreaded applications... +// +// WARNING: we put the initializer at the end of this compilation unit. This +// ensures this object is initialized _after_ all other global variables in +// the same compilation unit (in particular "lineLengthLimits::infinite", +// which is used by the generate() function (called from "textPartFactory" +// constructor, for example). +// + +class initializer +{ +public: + + initializer() + { + options::getInstance(); + + encoderFactory::getInstance(); + headerFieldFactory::getInstance(); + parameterFactory::getInstance(); + textPartFactory::getInstance(); + + #if VMIME_HAVE_MESSAGING_FEATURES + messaging::serviceFactory::getInstance(); + #endif + } +}; + +initializer theInitializer; + + +} // vmime diff --git a/src/base.hpp b/src/base.hpp new file mode 100644 index 00000000..a00d9c7d --- /dev/null +++ b/src/base.hpp @@ -0,0 +1,216 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_BASE_HPP_INCLUDED +#define VMIME_BASE_HPP_INCLUDED + + +#include <string> +#include <vector> +#include <map> +#include <sstream> +#include <locale> + +#include "config.hpp" +#include "types.hpp" +#include "constants.hpp" +#include "utility/stream.hpp" + + +namespace vmime +{ + class text; + class charset; + + + // "Null" strings + extern const string NULL_STRING; +#if VMIME_WIDE_CHAR_SUPPORT + extern const wstring NULL_WSTRING; +#endif + + extern const text NULL_TEXT; + + + // + // Library name and version + // + + const string libname(); + const string libversion(); + + + // + // Helpful functions used for array -> iterator conversion + // + + template <typename T, size_t N> + inline T const* begin(T const (&array)[N]) + { + return (array); + } + + template <typename T, size_t N> + inline T const* end(T const (&array)[N]) + { + return (array + N); + } + + template <typename T, size_t N> + inline size_t count(T const (&array)[N]) + { + return (N); + } + + + // + // Some helpful functions + // + + bool isStringEqualNoCase(const string& s1, const char* s2, const string::size_type n); + bool isStringEqualNoCase(const string& s1, const string& s2); + bool isStringEqualNoCase(const string::const_iterator begin, const string::const_iterator end, const char* s, const string::size_type n); + + + const string toLower(const string& str); + const string trim(const string& str); + + template <class TYPE> + const string toString(const TYPE& value) + { + std::ostringstream oss; + oss << value; + + return (oss.str()); + } + + template <class TYPE> + const TYPE fromString(const string& value) + { + TYPE ret; + + std::istringstream iss(value); + iss >> ret; + + return (ret); + } + + + // Free the pointer elements in a STL container and empty the container + + template <class CONTAINER> + void free_container(CONTAINER& c) + { + for (typename CONTAINER::iterator it = c.begin() ; it != c.end() ; ++it) + delete (*it); + + c.clear(); + } + + + // Field contents encoding (RFC-2047 and folding) + string::size_type countASCIIchars(const string::const_iterator begin, const string::const_iterator end); + + void encodeAndFoldText(utility::outputStream& os, const text& in, const string::size_type maxLineLength, const string::size_type firstLineOffset, string::size_type* lastLineLength, const int flags); + void decodeAndUnfoldText(const string& in, text& out); + void decodeAndUnfoldText(const string::const_iterator& inStart, const string::const_iterator& inEnd, text& out); + + void makeWordsFromText(const string& in, const charset& ch, text& out); + + + // + // Some constants + // + + // Flags used by "encodeAndFoldText" function + namespace encodeAndFoldFlags + { + enum + { + // If both "forceNoEncoding" and "forceEncoding" are specified, + // "forceNoEncoding" is used by default. + forceNoEncoding = (1 << 0), + forceEncoding = (1 << 1), + + noNewLineSequence = (1 << 2), + + none = 0 + }; + } + + /* + + RFC#2822 + 2.1.1. Line Length Limits + + There are two limits that this standard places on the number of + characters in a line. Each line of characters MUST be no more than + 998 characters, and SHOULD be no more than 78 characters, excluding + the CRLF. + + The 998 character limit is due to limitations in many implementations + which send, receive, or store Internet Message Format messages that + simply cannot handle more than 998 characters on a line. Receiving + implementations would do well to handle an arbitrarily large number + of characters in a line for robustness sake. However, there are so + many implementations which (in compliance with the transport + requirements of [RFC2821]) do not accept messages containing more + than 1000 character including the CR and LF per line, it is important + for implementations not to create such messages. + + The more conservative 78 character recommendation is to accommodate + the many implementations of user interfaces that display these + messages which may truncate, or disastrously wrap, the display of + more than 78 characters per line, in spite of the fact that such + implementations are non-conformant to the intent of this specification + (and that of [RFC2821] if they actually cause information to be lost). + Again, even though this limitation is put on messages, it is encumbant + upon implementations which display messages to handle an arbitrarily + large number of characters in a line (certainly at least up to the 998 + character limit) for the sake of robustness. + */ + + namespace lineLengthLimits + { + extern const string::size_type infinite; + + enum + { + max = 998, + convenient = 78 + }; + } + + + // New line sequence to be used when folding header fields. + extern const string NEW_LINE_SEQUENCE; + extern const string::size_type NEW_LINE_SEQUENCE_LENGTH; + + + // CR-LF sequence + extern const string CRLF; + + + // Mime version + extern const string MIME_VERSION; + +} // vmime + + +#endif // VMIME_BASE_HPP_INCLUDED diff --git a/src/body.cpp b/src/body.cpp new file mode 100644 index 00000000..1b5c853b --- /dev/null +++ b/src/body.cpp @@ -0,0 +1,543 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "bodyPart.hpp" +#include "body.hpp" + +#include "options.hpp" + +#include "contentTypeField.hpp" +#include "contentEncodingField.hpp" + +#include "utility/random.hpp" + +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +body::body(bodyPart& part) + : parts(*this), m_part(part), m_header(part.header()) +{ +} + + +void body::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + parts.clear(); + + // Check whether the body is a MIME-multipart + bool isMultipart = false; + string boundary; + + try + { + const contentTypeField& ctf = dynamic_cast <contentTypeField&> + (m_header.fields.find(headerField::ContentType)); + + if (ctf.value().type() == mediaTypes::MULTIPART) + { + isMultipart = true; + + try + { + boundary = ctf.boundary(); + } + catch (exceptions::no_such_parameter&) + { + // No "boundary" parameter specified: we can try to + // guess it by scanning the body contents... + string::size_type pos = buffer.find("\n--", position); + + if ((pos != string::npos) && (pos < end)) + { + pos += 3; + + const string::size_type start = pos; + + char_t c = buffer[pos]; + string::size_type length = 0; + + // We have to stop after a reasonnably long boundary length (100) + // not to take the whole body contents for a boundary... + while (pos < end && length < 100 && !(c == '\r' || c == '\n')) + { + ++length; + c = buffer[pos++]; + } + + if (pos < end && length < 100) + { + // RFC #1521, Page 31: + // "...the boundary parameter, which consists of 1 to 70 + // characters from a set of characters known to be very + // robust through email gateways, and NOT ending with + // white space..." + while (pos != start && isspace(buffer[pos - 1])) + --pos; + + boundary = string(buffer.begin() + start, + buffer.begin() + pos); + } + } + } + } + } + catch (exceptions::no_such_field&) + { + // No "Content-Type" field... + } + + // This is a multi-part body + if (isMultipart && !boundary.empty()) + { + const string boundarySep("--" + boundary); + + string::size_type partStart = position; + string::size_type pos = buffer.find(boundarySep, position); + + bool lastPart = false; + + if (pos != string::npos && pos < end) + { + m_prologText = string(buffer.begin() + position, buffer.begin() + pos); + } + + for (int index = 0 ; !lastPart && (pos != string::npos) && (pos < end) ; ++index) + { + string::size_type partEnd = pos; + + // Get rid of the [CR]LF just before the boundary string + if (pos - 1 >= position && buffer[pos - 1] == '\n') --partEnd; + if (pos - 2 >= position && buffer[pos - 2] == '\r') --partEnd; + + // Check whether it is the last part (boundary terminated by "--") + pos += boundarySep.length(); + + if (pos + 1 < end && buffer[pos] == '-' && buffer[pos + 1] == '-') + { + lastPart = true; + pos += 2; + } + + // RFC #1521, Page 31: + // "...(If a boundary appears to end with white space, the + // white space must be presumed to have been added by a + // gateway, and must be deleted.)..." + while (pos < end && (buffer[pos] == ' ' || buffer[pos] == '\t')) + ++pos; + + // End of boundary line + if (pos + 1 < end && buffer[pos] == '\r' && buffer[pos + 1] =='\n') + { + pos += 2; + } + else if (pos < end && buffer[pos] == '\n') + { + ++pos; + } + + if (index > 0) + { + bodyPart* part = new bodyPart; + + try + { + part->parse(buffer, partStart, partEnd, NULL); + } + catch (std::exception&) + { + delete (part); + throw; + } + + parts.m_parts.push_back(part); + } + + partStart = pos; + pos = buffer.find(boundarySep, partStart); + } + + if (partStart < end) + m_epilogText = string(buffer.begin() + partStart, buffer.begin() + end); + } + // Treat the contents as 'simple' data + else + { + // Extract the (encoded) contents + m_contents.set(buffer, position, end, encoding()); + } + + if (newPosition) + *newPosition = end; +} + + +void body::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type /* curLinePos */, string::size_type* newLinePos) const +{ + // MIME-Multipart + if (parts.size() != 0) + { + string boundary; + + try + { + contentTypeField& ctf = dynamic_cast<contentTypeField&> + (m_header.fields.find(headerField::ContentType)); + + boundary = ctf.boundary(); + } + catch (exceptions::no_such_field&) + { + // Warning: no content-type and no boundary string specified! + boundary = generateRandomBoundaryString(); + } + catch (exceptions::no_such_parameter&) + { + // Warning: no boundary string specified! + boundary = generateRandomBoundaryString(); + } + + const string& prologText = + m_prologText.empty() + ? (isRootPart() + ? options::getInstance()->multipart.prologText() + : NULL_STRING + ) + : m_prologText; + + const string& epilogText = + m_epilogText.empty() + ? (isRootPart() + ? options::getInstance()->multipart.epilogText() + : NULL_STRING + ) + : m_epilogText; + + if (!prologText.empty()) + { + encodeAndFoldText(os, text(word(prologText, charset())), maxLineLength, 0, + NULL, encodeAndFoldFlags::forceNoEncoding | encodeAndFoldFlags::noNewLineSequence); + + os << CRLF; + } + + os << "--" << boundary; + + for (std::vector <bodyPart*>::const_iterator + p = parts.m_parts.begin() ; p != parts.m_parts.end() ; ++p) + { + os << CRLF; + + (*p)->generate(os, maxLineLength, 0); + + os << CRLF << "--" << boundary; + } + + os << "--" << CRLF; + + if (!epilogText.empty()) + { + encodeAndFoldText(os, text(word(epilogText, charset())), maxLineLength, 0, + NULL, encodeAndFoldFlags::forceNoEncoding | encodeAndFoldFlags::noNewLineSequence); + + os << CRLF; + } + + if (newLinePos) + *newLinePos = 0; + } + // Simple body + else + { + // Generate the contents + m_contents.generate(os, encoding(), maxLineLength); + } +} + + +/* + RFC #1521, Page 32: + 7.2.1. Multipart: The common syntax + + "...Encapsulation boundaries must not appear within the + encapsulations, and must be no longer than 70 characters..." + + + boundary := 0*69<bchars> bcharsnospace + + bchars := bcharsnospace / " " + + bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / "+" /"_" + / "," / "-" / "." / "/" / ":" / "=" / "?" +*/ + +const string body::generateRandomBoundaryString() +{ + // 64 characters that can be _safely_ used in a boundary string + static const char bchars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-+"; + + /* + RFC #1521, Page 19: + + Since the hyphen character ("-") is represented as itself in the + Quoted-Printable encoding, care must be taken, when encapsulating a + quoted-printable encoded body in a multipart entity, to ensure that + the encapsulation boundary does not appear anywhere in the encoded + body. (A good strategy is to choose a boundary that includes a + character sequence such as "=_" which can never appear in a quoted- + printable body. See the definition of multipart messages later in + this document.) + */ + + string::value_type boundary[2 + 48 + 1] = { 0 }; + + boundary[0] = '='; + boundary[1] = '_'; + + // Generate a string of random characters + unsigned int r = utility::random::time(); + unsigned int m = sizeof(unsigned int); + + for (size_t i = 2 ; i < (sizeof(boundary) / sizeof(boundary[0]) - 1) ; ++i) + { + boundary[i] = bchars[r & 63]; + r >>= 6; + + if (--m == 0) + { + r = utility::random::next(); + m = sizeof(unsigned int); + } + } + + return (string(boundary)); +} + + +const bool body::isValidBoundary(const string& boundary) +{ + static const string validChars("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'()+_,-./:=?"); + + const string::const_iterator end = boundary.end(); + bool valid = false; + + if (boundary.length() > 0 && boundary.length() < 70) + { + const string::value_type last = *(end - 1); + + if (!(last == ' ' || last == '\t' || last == '\n')) + { + valid = true; + + for (string::const_iterator i = boundary.begin() ; valid && i != end ; ++i) + valid = (validChars.find_first_of(*i) != string::npos); + } + } + + return (valid); +} + + +// +// Quick-access functions +// + +const mediaType body::contentType() const +{ + try + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&>(m_header.fields.find(headerField::ContentType)); + return (ctf.value()); + } + catch (exceptions::no_such_field&) + { + // Defaults to "text/plain" (RFC-1521) + return (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN)); + } +} + + +const class charset body::charset() const +{ + try + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&>(m_header.fields.find(headerField::ContentType)); + const class charset& cs = ctf.charset(); + + return (cs); + } + catch (exceptions::no_such_parameter&) + { + // Defaults to "us-ascii" (RFC-1521) + return (vmime::charset(charsets::US_ASCII)); + } + catch (exceptions::no_such_field&) + { + // Defaults to "us-ascii" (RFC-1521) + return (vmime::charset(charsets::US_ASCII)); + } +} + + +const class encoding body::encoding() const +{ + try + { + const contentEncodingField& cef = m_header.fields.ContentTransferEncoding(); + return (cef.value()); + } + catch (exceptions::no_such_field&) + { + // Defaults to "7bit" (RFC-1521) + return (vmime::encoding(encodingTypes::SEVEN_BIT)); + } +} + + +const bool body::isRootPart() const +{ + return (m_part.parent() == NULL); +} + + +body& body::operator=(const body& b) +{ + m_prologText = b.m_prologText; + m_epilogText = b.m_epilogText; + + m_contents = b.m_contents; + + parts = b.parts; + + return (*this); +} + + +///////////////////// +// Parts container // +///////////////////// + + +body::partsContainer::partsContainer(class body& body) + : m_body(body) +{ +} + + +// Part insertion +void body::partsContainer::append(bodyPart* part) +{ + part->m_parent = &(m_body.m_part); + + m_parts.push_back(part); + + // Check whether we have a boundary string + try + { + contentTypeField& ctf = dynamic_cast<contentTypeField&> + (m_body.m_header.fields.find(headerField::ContentType)); + + try + { + const string boundary = ctf.boundary(); + + if (boundary.empty() || !isValidBoundary(boundary)) + throw exceptions::no_such_parameter("boundary"); // to generate a new one + } + catch (exceptions::no_such_parameter&) + { + // No "boundary" parameter: generate a random one. + ctf.boundary() = generateRandomBoundaryString(); + } + + if (ctf.value().type() != mediaTypes::MULTIPART) + { + // Warning: multi-part body but the Content-Type is + // not specified as "multipart/..." + } + } + catch (exceptions::no_such_field&) + { + // No "Content-Type" field: create a new one and generate + // a random boundary string. + contentTypeField& ctf = dynamic_cast<contentTypeField&> + (m_body.m_header.fields.get(headerField::ContentType)); + + ctf.value() = mediaType(mediaTypes::MULTIPART, mediaTypes::MULTIPART_MIXED); + ctf.boundary() = generateRandomBoundaryString(); + } +} + + +void body::partsContainer::insert(const iterator it, bodyPart* part) +{ + part->m_parent = &(m_body.m_part); + + m_parts.insert(it.m_iterator, part); +} + + +// Part removing +void body::partsContainer::remove(const iterator it) +{ + delete (*it.m_iterator); + m_parts.erase(it.m_iterator); +} + + +void body::partsContainer::clear() +{ + free_container(m_parts); +} + + +body::partsContainer::~partsContainer() +{ + clear(); +} + + +body::partsContainer& body::partsContainer::operator=(const partsContainer& c) +{ + std::vector <bodyPart*> parts; + + for (std::vector <bodyPart*>::const_iterator it = c.m_parts.begin() ; it != c.m_parts.end() ; ++it) + { + bodyPart* p = (*it)->clone(); + p->m_parent = &(m_body.m_part); + + parts.push_back(p); + } + + for (std::vector <bodyPart*>::iterator it = m_parts.begin() ; it != m_parts.end() ; ++it) + delete (*it); + + m_parts.resize(parts.size()); + std::copy(parts.begin(), parts.end(), m_parts.begin()); + + return (*this); +} + + +} // vmime diff --git a/src/body.hpp b/src/body.hpp new file mode 100644 index 00000000..9e43744c --- /dev/null +++ b/src/body.hpp @@ -0,0 +1,234 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_BODY_HPP_INCLUDED +#define VMIME_BODY_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "header.hpp" + +#include "mediaType.hpp" +#include "charset.hpp" +#include "encoding.hpp" + +#include "contentHandler.hpp" + + +namespace vmime +{ + + +class bodyPart; + + +/** Body section of a MIME part. + */ + +class body : public component +{ + friend class bodyPart; + +protected: + + body(bodyPart& part); + +public: + + // A sub-class for part manipulation + class partsContainer + { + friend class body; + + protected: + + partsContainer(class body& body); + ~partsContainer(); + + public: + + // Part iterator + class const_iterator; + + class iterator + { + friend class body::partsContainer::const_iterator; + friend class body::partsContainer; + + public: + + typedef std::vector <bodyPart*>::iterator::difference_type difference_type; + + iterator(std::vector <bodyPart*>::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + bodyPart& operator*() const { return (**m_iterator); } + bodyPart* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator operator++(int) { iterator i(*this); ++m_iterator; return (i); } + + iterator& operator--() { --m_iterator; return (*this); } + iterator operator--(int) { iterator i(*this); --m_iterator; return (i); } + + iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + iterator operator-(difference_type x) const { return iterator(m_iterator - x); } + + bodyPart& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector <bodyPart*>::iterator m_iterator; + }; + + class const_iterator + { + public: + + typedef std::vector <bodyPart*>::const_iterator::difference_type difference_type; + + const_iterator(std::vector <bodyPart*>::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const bodyPart& operator*() const { return (**m_iterator); } + const bodyPart* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator operator++(int) { const_iterator i(*this); ++m_iterator; return (i); } + + const_iterator& operator--() { --m_iterator; return (*this); } + const_iterator operator--(int) { const_iterator i(*this); --m_iterator; return (i); } + + const_iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + const_iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + const_iterator operator-(difference_type x) const { return const_iterator(m_iterator - x); } + + const bodyPart& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector <bodyPart*>::const_iterator m_iterator; + }; + + public: + + iterator begin() { return (m_parts.begin()); } + iterator end() { return (m_parts.end()); } + + const_iterator begin() const { return (const_iterator(m_parts.begin())); } + const_iterator end() const { return (const_iterator(m_parts.end())); } + + const bodyPart& operator[](const std::vector <bodyPart*>::size_type x) const { return (*m_parts[x]); } + bodyPart& operator[](const std::vector <bodyPart*>::size_type x) { return (*m_parts[x]); } + + // Part insertion + void append(bodyPart* part); + void insert(const iterator it, bodyPart* part); + + // Part removing + void remove(const iterator it); + void clear(); + + // Part count + const size_t count() const { return (m_parts.size()); } + const size_t size() const { return (m_parts.size()); } + + bodyPart& front() { return (*m_parts.front()); } + const bodyPart& front() const { return (*m_parts.front()); } + bodyPart& back() { return (*m_parts.back()); } + const bodyPart& back() const { return (*m_parts.back()); } + + partsContainer& operator=(const partsContainer& c); + + protected: + + body& m_body; + + std::vector <bodyPart*> m_parts; + + } parts; + + typedef partsContainer::iterator iterator; + typedef partsContainer::const_iterator const_iterator; + + + const string& prologText() const { return (m_prologText); } + string& prologText() { return (m_prologText); } + + const string& epilogText() const { return (m_epilogText); } + string& epilogText() { return (m_epilogText); } + + const contentHandler& contents() const { return (m_contents); } + contentHandler& contents() { return (m_contents); } + + // Quick-access functions + const mediaType contentType() const; + const class charset charset() const; + const class encoding encoding() const; + + // Boundary string functions + static const string generateRandomBoundaryString(); + static const bool isValidBoundary(const string& boundary); + + body& operator=(const body& b); + +protected: + + string m_prologText; + string m_epilogText; + + contentHandler m_contents; + + bodyPart& m_part; + header& m_header; + + const bool isRootPart() const; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_BODY_HPP_INCLUDED diff --git a/src/bodyPart.cpp b/src/bodyPart.cpp new file mode 100644 index 00000000..522a34cd --- /dev/null +++ b/src/bodyPart.cpp @@ -0,0 +1,75 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "bodyPart.hpp" + + +namespace vmime +{ + + +bodyPart::bodyPart() + : m_body(*this), m_parent(NULL) +{ +} + + +void bodyPart::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + // Parse the headers + string::size_type pos = position; + m_header.parse(buffer, pos, end, &pos); + + // Parse the body contents + m_body.parse(buffer, pos, end, NULL); + + if (newPosition) + *newPosition = end; +} + + +void bodyPart::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type /* curLinePos */, string::size_type* newLinePos) const +{ + m_header.generate(os, maxLineLength); + + os << CRLF; + + m_body.generate(os, maxLineLength); + + if (newLinePos) + *newLinePos = 0; +} + + +bodyPart* bodyPart::clone() const +{ + bodyPart* p = new bodyPart; + + p->m_parent = NULL; + p->m_header = m_header; + p->m_body = m_body; + + return (p); +} + + +} // vmime + diff --git a/src/bodyPart.hpp b/src/bodyPart.hpp new file mode 100644 index 00000000..21e59b79 --- /dev/null +++ b/src/bodyPart.hpp @@ -0,0 +1,80 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_BODYPART_HPP_INCLUDED +#define VMIME_BODYPART_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "header.hpp" +#include "body.hpp" + + +namespace vmime +{ + + +/** A MIME part. + */ + +class bodyPart : public component +{ +public: + + bodyPart(); + + const class header& header() const { return (m_header); } + class header& header() { return (m_header); } + + const class body& body() const { return (m_body); } + class body& body() { return (m_body); } + + bodyPart* parent() const { return (m_parent); } + + bodyPart* clone() const; + +protected: + + class header m_header; + class body m_body; + + bodyPart* m_parent; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + + + // This is here because of a bug in g++ < 3.4 + friend class body; + friend class body::partsContainer; +}; + + +} // vmime + + +#endif // VMIME_BODYPART_HPP_INCLUDED diff --git a/src/charset.cpp b/src/charset.cpp new file mode 100644 index 00000000..5d5b872c --- /dev/null +++ b/src/charset.cpp @@ -0,0 +1,305 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "charset.hpp" +#include "exception.hpp" +#include "platformDependant.hpp" + + +extern "C" +{ + #include <iconv.h> + + // HACK: prototypes may differ depending on the compiler and/or system (the + // second parameter may or may not be 'const'). This redeclaration is a hack + // to have a common prototype "iconv_cast". + typedef size_t (*iconv_const_hack)(iconv_t cd, const char* * inbuf, + size_t *inbytesleft, char* * outbuf, size_t *outbytesleft); + + #define iconv_const ((iconv_const_hack) iconv) +} + + +namespace vmime +{ + + +charset::charset() + : m_name(charsets::US_ASCII) +{ +} + + +charset::charset(const string& name) + : m_name(name) +{ +} + + +void charset::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_name = string(buffer.begin() + position, buffer.begin() + end); + + if (newPosition) + *newPosition = end; +} + + +void charset::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + os << m_name; + + if (newLinePos) + *newLinePos = curLinePos + m_name.length(); +} + + +/** Convert the contents of an input stream in a specified charset + * to another charset and write the result to an output stream. + * + * @param in input stream to read data from + * @param out output stream to write the converted data + * @param source input charset + * @param dest output charset + */ + +void charset::convert(utility::inputStream& in, utility::outputStream& out, + const charset& source, const charset& dest) +{ + // Get an iconv descriptor + const iconv_t cd = iconv_open(dest.name().c_str(), source.name().c_str()); + + if (cd != (iconv_t) -1) + { + char inBuffer[5]; + char outBuffer[32768]; + size_t inPos = 0; + + bool prevIsInvalid = false; + + while (true) + { + // Fullfill the buffer + size_t inLength = (size_t) in.read(inBuffer + inPos, sizeof(inBuffer) - inPos) + inPos; + size_t outLength = sizeof(outBuffer); + + const char* inPtr = inBuffer; + char* outPtr = outBuffer; + + // Convert input bytes + if (iconv_const(cd, &inPtr, &inLength, &outPtr, &outLength) == (size_t) -1) + { + // Illegal input sequence or input sequence has no equivalent + // sequence in the destination charset. + if (prevIsInvalid) + { + // Write successfully converted bytes + out.write(outBuffer, sizeof(outBuffer) - outLength); + + // Output a special character to indicate we don't known how to + // convert the sequence at this position + out.write("?", 1); + + // Skip a byte and leave unconverted bytes in the input buffer + std::copy((char*) inPtr + 1, inBuffer + sizeof(inBuffer), inBuffer); + inPos = inLength - 1; + } + else + { + // Write successfully converted bytes + out.write(outBuffer, sizeof(outBuffer) - outLength); + + // Leave unconverted bytes in the input buffer + std::copy((char*) inPtr, inBuffer + sizeof(inBuffer), inBuffer); + inPos = inLength; + + prevIsInvalid = true; + } + } + else + { + // Write successfully converted bytes + out.write(outBuffer, sizeof(outBuffer) - outLength); + + inPos = 0; + prevIsInvalid = false; + } + + // Check for end of data + if (in.eof() && inPos == 0) + break; + } + + // Close iconv handle + iconv_close(cd); + } + else + { + throw exceptions::charset_conv_error(); + } +} + + +/** Convert a string buffer in a specified charset to a string + * buffer in another charset. + * + * @param in input buffer + * @param out output buffer + * @param from input charset + * @param to output charset + */ + +template <class STRINGF, class STRINGT> +void charset::iconvert(const STRINGF& in, STRINGT& out, const charset& from, const charset& to) +{ + // Get an iconv descriptor + const iconv_t cd = iconv_open(to.name().c_str(), from.name().c_str()); + + typedef typename STRINGF::value_type ivt; + typedef typename STRINGT::value_type ovt; + + if (cd != (iconv_t) -1) + { + out.clear(); + + char buffer[65536]; + + const char* inBuffer = (const char*) in.data(); + size_t inBytesLeft = in.length(); + + for ( ; inBytesLeft > 0 ; ) + { + size_t outBytesLeft = sizeof(buffer); + char* outBuffer = buffer; + + if (iconv_const(cd, &inBuffer, &inBytesLeft, + &outBuffer, &outBytesLeft) == (size_t) -1) + { + out += STRINGT((ovt*) buffer, sizeof(buffer) - outBytesLeft); + + // Ignore this "blocking" character and continue + out += '?'; + ++inBuffer; + --inBytesLeft; + } + else + { + out += STRINGT((ovt*) buffer, sizeof(buffer) - outBytesLeft); + } + } + + // Close iconv handle + iconv_close(cd); + } + else + { + throw exceptions::charset_conv_error(); + } +} + + +#if VMIME_WIDE_CHAR_SUPPORT + +/** Convert a string buffer in the specified charset to a wide-char + * string buffer. + * + * @param in input buffer + * @param out output buffer + * @param ch input charset + */ + +void charset::decode(const string& in, wstring& out, const charset& ch) +{ + iconvert(in, out, ch, charset("WCHAR_T")); +} + + +/** Convert a wide-char string buffer to a string buffer in the + * specified charset. + * + * @param in input buffer + * @param out output buffer + * @param ch output charset + */ + +void charset::encode(const wstring& in, string& out, const charset& ch) +{ + iconvert(in, out, charset("WCHAR_T"), ch); +} + +#endif + + +/** Convert a string buffer from one charset to another charset. + * + * @param in input buffer + * @param out output buffer + * @param source input charset + * @param dest output charset + */ + +void charset::convert(const string& in, string& out, const charset& source, const charset& dest) +{ + iconvert(in, out, source, dest); +} + + +/** Returns the default charset used on the system. + * + * This function simply calls <code>platformDependantHandler::getLocaleCharset()</code> + * and is provided for convenience. + * + * @return system default charset + */ + +const charset charset::getLocaleCharset() +{ + return (platformDependant::getHandler()->getLocaleCharset()); +} + + +charset& charset::operator=(const charset& source) +{ + m_name = source.m_name; + return (*this); +} + + +charset& charset::operator=(const string& name) +{ + parse(name); + return (*this); +} + + +const bool charset::operator==(const charset& value) const +{ + return (isStringEqualNoCase(m_name, value.m_name)); +} + + +const bool charset::operator!=(const charset& value) const +{ + return !(*this == value); +} + + +} // vmime diff --git a/src/charset.hpp b/src/charset.hpp new file mode 100644 index 00000000..4d7f8c5b --- /dev/null +++ b/src/charset.hpp @@ -0,0 +1,86 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CHARSET_HPP_INCLUDED +#define VMIME_CHARSET_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Charset description (basic type). + */ + +class charset : public component +{ +public: + + charset(); + charset(const string& name); + +public: + + const string name() const { return (m_name); } + + charset& operator=(const charset& source); + charset& operator=(const string& name); + + const bool operator==(const charset& value) const; + const bool operator!=(const charset& value) const; + + static const charset getLocaleCharset(); + +#if VMIME_WIDE_CHAR_SUPPORT + static void decode(const string& in, wstring& out, const charset& ch); + static void encode(const wstring& in, string& out, const charset& ch); +#endif + + // In-memory conversion + static void convert(const string& in, string& out, const charset& source, const charset& dest); + + // Stream conversion + static void convert(utility::inputStream& in, utility::outputStream& out, const charset& source, const charset& dest); + +protected: + + string m_name; + + template <class STRINGF, class STRINGT> + static void iconvert(const STRINGF& in, STRINGT& out, const charset& from, const charset& to); + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_CHARSET_HPP_INCLUDED diff --git a/src/charsetParameter.cpp b/src/charsetParameter.cpp new file mode 100644 index 00000000..ccbe7b43 --- /dev/null +++ b/src/charsetParameter.cpp @@ -0,0 +1,50 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "charsetParameter.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +void charsetParameter::parseValue(const string& buffer, const string::size_type position, + const string::size_type end) +{ + m_value.parse(buffer, position, end); +} + + +const string charsetParameter::generateValue() const +{ + return (m_value.name()); +} + + +void charsetParameter::copyFrom(const parameter& param) +{ + const charsetParameter& source = dynamic_cast<const charsetParameter&>(param); + m_value = source.m_value; + + defaultParameter::copyFrom(param); +} + + +} // vmime diff --git a/src/charsetParameter.hpp b/src/charsetParameter.hpp new file mode 100644 index 00000000..4e5e89d9 --- /dev/null +++ b/src/charsetParameter.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CHARSETPARAMETER_HPP_INCLUDED +#define VMIME_CHARSETPARAMETER_HPP_INCLUDED + + +#include "defaultParameter.hpp" +#include "charset.hpp" + + +namespace vmime +{ + + +class charsetParameter : public defaultParameter +{ +protected: + + charset m_value; + +public: + + void copyFrom(const parameter& param); + + const charset& value() const { return (m_value); } + charset& value() { return (m_value); } + +protected: + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_CHARSETPARAMETER_HPP_INCLUDED diff --git a/src/component.cpp b/src/component.cpp new file mode 100644 index 00000000..b8c65cd1 --- /dev/null +++ b/src/component.cpp @@ -0,0 +1,47 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "component.hpp" + +#include <sstream> + + +namespace vmime +{ + + +void component::parse(const string& buffer) +{ + parse(buffer, 0, buffer.length(), NULL); +} + + +const string component::generate(const string::size_type maxLineLength, + const string::size_type curLinePos) const +{ + std::ostringstream oss; + utility::outputStreamAdapter adapter(oss); + + generate(adapter, maxLineLength, curLinePos, NULL); + + return (oss.str()); +} + + +} diff --git a/src/component.hpp b/src/component.hpp new file mode 100644 index 00000000..f7c874d7 --- /dev/null +++ b/src/component.hpp @@ -0,0 +1,84 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_COMPONENT_HPP_INCLUDED +#define VMIME_COMPONENT_HPP_INCLUDED + + +#include "base.hpp" + + +namespace vmime +{ + + +/** This abstract class is the base for all the classes in the library. + * It defines the methods for parsing and generating all the components. + */ + +class component +{ +protected: + + virtual ~component() {} + +protected: + + /** Parse RFC-822/MIME data for this component. + * + * @param buffer input buffer + */ + void parse(const string& buffer); + + /** Parse RFC-822/MIME data for this component. + * + * @param buffer input buffer + * @param position current position in the input buffer + * @param end end position in the input buffer + * @param newPosition will receive the new position in the input buffer + */ + virtual void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL) = 0; + +public: + + /** Generate RFC-2822/MIME data for this component. + * + * \deprecated Use the new generate() method, which takes an outputStream parameter. + * + * @param maxLineLength maximum line length for output + * @param curLinePos length of the current line in the output buffer + * @return generated data + */ + virtual const string generate(const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0) const; + + /** Generate RFC-2822/MIME data for this component. + * + * @param os output stream + * @param maxLineLength maximum line length for output + * @param curLinePos length of the current line in the output buffer + * @param newLinePos will receive the new line position (length of the last line written) + */ + virtual void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const = 0; +}; + + +} // vmime + + +#endif // VMIME_COMPONENT_HPP_INCLUDED diff --git a/src/constants.cpp b/src/constants.cpp new file mode 100644 index 00000000..c46c7a25 --- /dev/null +++ b/src/constants.cpp @@ -0,0 +1,151 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "constants.hpp" + + +namespace vmime +{ + + +// Media Types +namespace mediaTypes +{ + // Types + const string::value_type* const TEXT = "text"; + const string::value_type* const MULTIPART = "multipart"; + const string::value_type* const MESSAGE = "message"; + const string::value_type* const APPLICATION = "application"; + const string::value_type* const IMAGE = "image"; + const string::value_type* const AUDIO = "audio"; + const string::value_type* const VIDEO = "video"; + + // Sub-types + const string::value_type* const TEXT_PLAIN = "plain"; + const string::value_type* const TEXT_HTML = "html"; + const string::value_type* const TEXT_RICHTEXT = "richtext"; + const string::value_type* const TEXT_ENRICHED = "enriched"; + + const string::value_type* const MULTIPART_MIXED = "mixed"; + const string::value_type* const MULTIPART_RELATED = "related"; + const string::value_type* const MULTIPART_ALTERNATIVE = "alternative"; + const string::value_type* const MULTIPART_PARALLEL = "parallel"; + const string::value_type* const MULTIPART_DIGEST = "digest"; + + const string::value_type* const MESSAGE_RFC822 = "rfc822"; + const string::value_type* const MESSAGE_PARTIAL = "partial"; + const string::value_type* const MESSAGE_EXTERNAL_BODY = "external-body"; + + const string::value_type* const APPLICATION_OCTET_STREAM = "octet-stream"; + + const string::value_type* const IMAGE_JPEG = "jpeg"; + const string::value_type* const IMAGE_GIF = "gif"; + + const string::value_type* const AUDIO_BASIC = "basic"; + + const string::value_type* const VIDEO_MPEG = "mpeg"; +} + + +// Encoding types +namespace encodingTypes +{ + const string::value_type* const SEVEN_BIT = "7bit"; + const string::value_type* const EIGHT_BIT = "8bit"; + const string::value_type* const BASE64 = "base64"; + const string::value_type* const QUOTED_PRINTABLE = "quoted-printable"; + const string::value_type* const BINARY = "binary"; + const string::value_type* const UUENCODE = "uuencode"; +} + + +// Disposition types = "RFC-2183) +namespace dispositionTypes +{ + const string::value_type* const INLINE = "inline"; + const string::value_type* const ATTACHMENT = "attachment"; +} + + +// Charsets +namespace charsets +{ + const string::value_type* const ISO8859_1 = "iso-8859-1"; + const string::value_type* const ISO8859_2 = "iso-8859-2"; + const string::value_type* const ISO8859_3 = "iso-8859-3"; + const string::value_type* const ISO8859_4 = "iso-8859-4"; + const string::value_type* const ISO8859_5 = "iso-8859-5"; + const string::value_type* const ISO8859_6 = "iso-8859-6"; + const string::value_type* const ISO8859_7 = "iso-8859-7"; + const string::value_type* const ISO8859_8 = "iso-8859-8"; + const string::value_type* const ISO8859_9 = "iso-8859-9"; + const string::value_type* const ISO8859_10 = "iso-8859-10"; + const string::value_type* const ISO8859_13 = "iso-8859-13"; + const string::value_type* const ISO8859_14 = "iso-8859-14"; + const string::value_type* const ISO8859_15 = "iso-8859-15"; + const string::value_type* const ISO8859_16 = "iso-8859-16"; + + const string::value_type* const CP_437 = "cp-437"; + const string::value_type* const CP_737 = "cp-737"; + const string::value_type* const CP_775 = "cp-775"; + const string::value_type* const CP_850 = "cp-850"; + const string::value_type* const CP_852 = "cp-852"; + const string::value_type* const CP_853 = "cp-853"; + const string::value_type* const CP_855 = "cp-855"; + const string::value_type* const CP_857 = "cp-857"; + const string::value_type* const CP_858 = "cp-858"; + const string::value_type* const CP_860 = "cp-860"; + const string::value_type* const CP_861 = "cp-861"; + const string::value_type* const CP_862 = "cp-862"; + const string::value_type* const CP_863 = "cp-863"; + const string::value_type* const CP_864 = "cp-864"; + const string::value_type* const CP_865 = "cp-865"; + const string::value_type* const CP_866 = "cp-866"; + const string::value_type* const CP_869 = "cp-869"; + const string::value_type* const CP_874 = "cp-874"; + const string::value_type* const CP_1125 = "cp-1125"; + const string::value_type* const CP_1250 = "cp-1250"; + const string::value_type* const CP_1251 = "cp-1251"; + const string::value_type* const CP_1252 = "cp-1252"; + const string::value_type* const CP_1253 = "cp-1253"; + const string::value_type* const CP_1254 = "cp-1254"; + const string::value_type* const CP_1255 = "cp-1255"; + const string::value_type* const CP_1256 = "cp-1256"; + const string::value_type* const CP_1257 = "cp-1257"; + + const string::value_type* const US_ASCII = "us-ascii"; + + const string::value_type* const UTF_7 = "utf-7"; + const string::value_type* const UTF_8 = "utf-8"; + const string::value_type* const UTF_16 = "utf-16"; + const string::value_type* const UTF_32 = "utf-32"; + + const string::value_type* const WINDOWS_1250 = "windows-1250"; + const string::value_type* const WINDOWS_1251 = "windows-1251"; + const string::value_type* const WINDOWS_1252 = "windows-1252"; + const string::value_type* const WINDOWS_1253 = "windows-1253"; + const string::value_type* const WINDOWS_1254 = "windows-1254"; + const string::value_type* const WINDOWS_1255 = "windows-1255"; + const string::value_type* const WINDOWS_1256 = "windows-1256"; + const string::value_type* const WINDOWS_1257 = "windows-1257"; + const string::value_type* const WINDOWS_1258 = "windows-1258"; +} + + +} // vmime diff --git a/src/constants.hpp b/src/constants.hpp new file mode 100644 index 00000000..1970e626 --- /dev/null +++ b/src/constants.hpp @@ -0,0 +1,156 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CONSTANTS_HPP_INCLUDED +#define VMIME_CONSTANTS_HPP_INCLUDED + + +#include <string> + +#include "types.hpp" + + +namespace vmime +{ + // Media types (predefined types) + namespace mediaTypes + { + // Types + extern const string::value_type* const TEXT; + extern const string::value_type* const MULTIPART; + extern const string::value_type* const MESSAGE; + extern const string::value_type* const APPLICATION; + extern const string::value_type* const IMAGE; + extern const string::value_type* const AUDIO; + extern const string::value_type* const VIDEO; + + // Sub-types + extern const string::value_type* const TEXT_PLAIN; + extern const string::value_type* const TEXT_HTML; + extern const string::value_type* const TEXT_RICHTEXT; + extern const string::value_type* const TEXT_ENRICHED; + + extern const string::value_type* const MULTIPART_MIXED; + extern const string::value_type* const MULTIPART_RELATED; + extern const string::value_type* const MULTIPART_ALTERNATIVE; + extern const string::value_type* const MULTIPART_PARALLEL; + extern const string::value_type* const MULTIPART_DIGEST; + + extern const string::value_type* const MESSAGE_RFC822; + extern const string::value_type* const MESSAGE_PARTIAL; + extern const string::value_type* const MESSAGE_EXTERNAL_BODY; + + extern const string::value_type* const APPLICATION_OCTET_STREAM; + + extern const string::value_type* const IMAGE_JPEG; + extern const string::value_type* const IMAGE_GIF; + + extern const string::value_type* const AUDIO_BASIC; + + extern const string::value_type* const VIDEO_MPEG; + } + + + // Encoding types + namespace encodingTypes + { + extern const string::value_type* const SEVEN_BIT; + extern const string::value_type* const EIGHT_BIT; + extern const string::value_type* const BASE64; + extern const string::value_type* const QUOTED_PRINTABLE; + extern const string::value_type* const BINARY; + extern const string::value_type* const UUENCODE; + } + + + // Disposition types (RFC-2183) + namespace dispositionTypes + { + extern const string::value_type* const INLINE; + extern const string::value_type* const ATTACHMENT; + } + + + // Charsets + namespace charsets + { + extern const string::value_type* const ISO8859_1; + extern const string::value_type* const ISO8859_2; + extern const string::value_type* const ISO8859_3; + extern const string::value_type* const ISO8859_4; + extern const string::value_type* const ISO8859_5; + extern const string::value_type* const ISO8859_6; + extern const string::value_type* const ISO8859_7; + extern const string::value_type* const ISO8859_8; + extern const string::value_type* const ISO8859_9; + extern const string::value_type* const ISO8859_10; + extern const string::value_type* const ISO8859_13; + extern const string::value_type* const ISO8859_14; + extern const string::value_type* const ISO8859_15; + extern const string::value_type* const ISO8859_16; + + extern const string::value_type* const CP_437; + extern const string::value_type* const CP_737; + extern const string::value_type* const CP_775; + extern const string::value_type* const CP_850; + extern const string::value_type* const CP_852; + extern const string::value_type* const CP_853; + extern const string::value_type* const CP_855; + extern const string::value_type* const CP_857; + extern const string::value_type* const CP_858; + extern const string::value_type* const CP_860; + extern const string::value_type* const CP_861; + extern const string::value_type* const CP_862; + extern const string::value_type* const CP_863; + extern const string::value_type* const CP_864; + extern const string::value_type* const CP_865; + extern const string::value_type* const CP_866; + extern const string::value_type* const CP_869; + extern const string::value_type* const CP_874; + extern const string::value_type* const CP_1125; + extern const string::value_type* const CP_1250; + extern const string::value_type* const CP_1251; + extern const string::value_type* const CP_1252; + extern const string::value_type* const CP_1253; + extern const string::value_type* const CP_1254; + extern const string::value_type* const CP_1255; + extern const string::value_type* const CP_1256; + extern const string::value_type* const CP_1257; + + extern const string::value_type* const US_ASCII; + + extern const string::value_type* const UTF_7; + extern const string::value_type* const UTF_8; + extern const string::value_type* const UTF_16; + extern const string::value_type* const UTF_32; + + extern const string::value_type* const WINDOWS_1250; + extern const string::value_type* const WINDOWS_1251; + extern const string::value_type* const WINDOWS_1252; + extern const string::value_type* const WINDOWS_1253; + extern const string::value_type* const WINDOWS_1254; + extern const string::value_type* const WINDOWS_1255; + extern const string::value_type* const WINDOWS_1256; + extern const string::value_type* const WINDOWS_1257; + extern const string::value_type* const WINDOWS_1258; + } +} + + +#endif // VMIME_CONSTANTS_HPP_INCLUDED diff --git a/src/contentDispositionField.cpp b/src/contentDispositionField.cpp new file mode 100644 index 00000000..c076633f --- /dev/null +++ b/src/contentDispositionField.cpp @@ -0,0 +1,62 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "contentDispositionField.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +contentDispositionField::contentDispositionField() +{ +} + + +void contentDispositionField::parseValue(const string& buffer, const string::size_type position, + const string::size_type end) +{ + m_value.parse(buffer, position, end); +} + + +const string contentDispositionField::generateValue() const +{ + return (m_value.generate()); +} + + +contentDispositionField& contentDispositionField::operator=(const disposition& type) +{ + m_value = type; + return (*this); +} + + +void contentDispositionField::copyFrom(const headerField& field) +{ + const contentDispositionField& source = dynamic_cast<const contentDispositionField&>(field); + m_value = source.m_value; + + parameterizedHeaderField::copyFrom(field); +} + + +} // vmime diff --git a/src/contentDispositionField.hpp b/src/contentDispositionField.hpp new file mode 100644 index 00000000..d0cf0b88 --- /dev/null +++ b/src/contentDispositionField.hpp @@ -0,0 +1,79 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CONTENTDISPOSITIONFIELD_HPP_INCLUDED +#define VMIME_CONTENTDISPOSITIONFIELD_HPP_INCLUDED + + +#include "parameterizedHeaderField.hpp" +#include "disposition.hpp" + +#include "dateParameter.hpp" +#include "textParameter.hpp" + + +namespace vmime +{ + + +class contentDispositionField : public parameterizedHeaderField +{ + friend class headerFieldFactory::registerer <contentDispositionField>; + +protected: + + contentDispositionField(); + +public: + + void copyFrom(const headerField& field); + + contentDispositionField& operator=(const disposition& type); + + const disposition& value() const { return (m_value); } + disposition& value() { return (m_value); } + + const datetime& creationDate() const { return (dynamic_cast<const dateParameter&>(parameters.find("creation-date")).value()); } + datetime& creationDate() { return (dynamic_cast<dateParameter&>(parameters.get("creation-date")).value()); } + + const datetime& modificationDate() const { return (dynamic_cast<const dateParameter&>(parameters.find("modification-date")).value()); } + datetime& modificationDate() { return (dynamic_cast<dateParameter&>(parameters.get("modification-date")).value()); } + + const datetime& readDate() const { return (dynamic_cast<const dateParameter&>(parameters.find("read-date")).value()); } + datetime& readDate() { return (dynamic_cast<dateParameter&>(parameters.get("read-date")).value()); } + + const string& filename() const { return (dynamic_cast<const textParameter&>(parameters.find("filename")).value()); } + string& filename() { return (dynamic_cast<textParameter&>(parameters.get("filename")).value()); } + + const string& size() const { return (dynamic_cast<const textParameter&>(parameters.find("size")).value()); } + string& size() { return (dynamic_cast<textParameter&>(parameters.get("size")).value()); } + +protected: + + disposition m_value; + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_CONTENTDISPOSITIONFIELD_HPP_INCLUDED diff --git a/src/contentEncodingField.cpp b/src/contentEncodingField.cpp new file mode 100644 index 00000000..f16fd551 --- /dev/null +++ b/src/contentEncodingField.cpp @@ -0,0 +1,62 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "contentEncodingField.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +contentEncodingField::contentEncodingField() +{ +} + + +void contentEncodingField::parseValue(const string& buffer, const string::size_type position, + const string::size_type end) +{ + m_value.parse(buffer, position, end); +} + + +const string contentEncodingField::generateValue() const +{ + return (m_value.generate()); +} + + +contentEncodingField& contentEncodingField::operator=(const encoding& type) +{ + m_value = type; + return (*this); +} + + +void contentEncodingField::copyFrom(const headerField& field) +{ + const contentEncodingField& source = dynamic_cast<const contentEncodingField&>(field); + m_value = source.m_value; + + parameterizedHeaderField::copyFrom(field); +} + + +} // vmime diff --git a/src/contentEncodingField.hpp b/src/contentEncodingField.hpp new file mode 100644 index 00000000..18d01ca7 --- /dev/null +++ b/src/contentEncodingField.hpp @@ -0,0 +1,61 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CONTENTENCODINGFIELD_HPP_INCLUDED +#define VMIME_CONTENTENCODINGFIELD_HPP_INCLUDED + + +#include "defaultParameterizedHeaderField.hpp" +#include "encoding.hpp" + + +namespace vmime +{ + + +class contentEncodingField : public parameterizedHeaderField +{ + friend class headerFieldFactory::registerer <contentEncodingField>; + +protected: + + contentEncodingField(); + +public: + + void copyFrom(const headerField& field); + + contentEncodingField& operator=(const encoding& type); + + const encoding& value() const { return (m_value); } + encoding& value() { return (m_value); } + +protected: + + encoding m_value; + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_CONTENTENCODINGFIELD_HPP_INCLUDED diff --git a/src/contentHandler.cpp b/src/contentHandler.cpp new file mode 100644 index 00000000..e33d0e5c --- /dev/null +++ b/src/contentHandler.cpp @@ -0,0 +1,369 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "contentHandler.hpp" + + +namespace vmime +{ + + +// No encoding = "binary" encoding +const encoding contentHandler::NO_ENCODING(encodingTypes::BINARY); + + +contentHandler::contentHandler() + : m_type(TYPE_NONE), m_encoding(NO_ENCODING), m_ownedStream(NULL), m_stream(NULL) +{ +} + + +contentHandler::contentHandler(const string& buffer, const vmime::encoding& enc) + : m_type(TYPE_STRING), m_encoding(enc), m_string(buffer), + m_ownedStream(NULL), m_stream(NULL) +{ +} + + +contentHandler::~contentHandler() +{ +} + + +contentHandler::contentHandler(const contentHandler& cts) + : m_type(cts.m_type), m_encoding(cts.m_encoding), m_string(cts.m_string), + m_ownedStream(const_cast <utility::smart_ptr <utility::inputStream>&>(cts.m_ownedStream)), + m_stream(cts.m_stream), m_length(cts.m_length) +{ +} + + +contentHandler& contentHandler::operator=(const contentHandler& cts) +{ + m_type = cts.m_type; + m_encoding = cts.m_encoding; + + m_string = cts.m_string; + + m_ownedStream = const_cast <utility::smart_ptr <utility::inputStream>&>(cts.m_ownedStream); + m_stream = cts.m_stream; + m_length = cts.m_length; + + return (*this); +} + + +void contentHandler::set(const utility::stringProxy& str, const vmime::encoding& enc) +{ + m_type = TYPE_STRING; + m_encoding = enc; + + m_string = str; + + m_ownedStream = NULL; + m_stream = NULL; +} + + +void contentHandler::set(const string& buffer, const vmime::encoding& enc) +{ + m_type = TYPE_STRING; + m_encoding = enc; + + m_string.set(buffer); + + m_ownedStream = NULL; + m_stream = NULL; +} + + +void contentHandler::set(const string& buffer, const string::size_type start, + const string::size_type end, const vmime::encoding& enc) +{ + m_type = TYPE_STRING; + m_encoding = enc; + + m_string.set(buffer, start, end); + + m_ownedStream = NULL; + m_stream = NULL; +} + + +void contentHandler::set(utility::inputStream* const is, const string::size_type length, + const bool own, const vmime::encoding& enc) +{ + m_type = TYPE_STREAM; + m_encoding = enc; + + m_length = length; + + if (own) + { + m_ownedStream = is; + m_stream = NULL; + } + else + { + m_ownedStream = NULL; + m_stream = is; + } + + m_string.detach(); +} + + +contentHandler& contentHandler::operator=(const string& buffer) +{ + set(buffer, NO_ENCODING); + return (*this); +} + + +void contentHandler::generate(utility::outputStream& os, const vmime::encoding& enc, + const string::size_type maxLineLength) const +{ + if (m_type == TYPE_NONE) + return; + + // Managed data is already encoded + if (isEncoded()) + { + // The data is already encoded but the encoding specified for + // the generation is different from the current one. We need + // to re-encode data: decode from input buffer to temporary + // buffer, and then re-encode to output stream... + if (m_encoding != enc) + { + utility::auto_ptr <encoder> theDecoder(m_encoding.getEncoder()); + utility::auto_ptr <encoder> theEncoder(enc.getEncoder()); + + theEncoder->properties()["maxlinelength"] = maxLineLength; + + switch (m_type) + { + default: + { + // No data + break; + } + case TYPE_STRING: + { + utility::inputStreamStringProxyAdapter in(m_string); + + std::ostringstream oss; + utility::outputStreamAdapter tempOut(oss); + + theDecoder->decode(in, tempOut); + + string str = oss.str(); + utility::inputStreamStringAdapter tempIn(str); + + theEncoder->encode(tempIn, os); + + break; + } + case TYPE_STREAM: + { + utility::inputStream& in = const_cast <utility::inputStream&> + (*(m_stream ? m_stream : m_ownedStream.ptr())); + + in.reset(); // may not work... + + std::ostringstream oss; + utility::outputStreamAdapter tempOut(oss); + + theDecoder->decode(in, tempOut); + + string str = oss.str(); + utility::inputStreamStringAdapter tempIn(str); + + theEncoder->encode(tempIn, os); + + break; + } + + } + } + // No encoding to perform + else + { + switch (m_type) + { + default: + { + // No data + break; + } + case TYPE_STRING: + { + m_string.extract(os); + break; + } + case TYPE_STREAM: + { + utility::inputStream& in = const_cast <utility::inputStream&> + (*(m_stream ? m_stream : m_ownedStream.ptr())); + + in.reset(); // may not work... + + utility::bufferedStreamCopy(in, os); + break; + } + + } + } + } + // Need to encode data before + else + { + utility::auto_ptr <encoder> theEncoder(enc.getEncoder()); + theEncoder->properties()["maxlinelength"] = maxLineLength; + + // Encode the contents + switch (m_type) + { + default: + { + // No data + break; + } + case TYPE_STRING: + { + utility::inputStreamStringProxyAdapter in(m_string); + + theEncoder->encode(in, os); + break; + } + case TYPE_STREAM: + { + utility::inputStream& in = const_cast <utility::inputStream&> + (*(m_stream ? m_stream : m_ownedStream.ptr())); + + in.reset(); // may not work... + + theEncoder->encode(in, os); + break; + } + + } + } +} + + +void contentHandler::extract(utility::outputStream& os) const +{ + if (m_type == TYPE_NONE) + return; + + // No decoding to perform + if (!isEncoded()) + { + switch (m_type) + { + default: + { + // No data + break; + } + case TYPE_STRING: + { + m_string.extract(os); + break; + } + case TYPE_STREAM: + { + utility::inputStream& in = const_cast <utility::inputStream&> + (*(m_stream ? m_stream : m_ownedStream.ptr())); + + in.reset(); // may not work... + + utility::bufferedStreamCopy(in, os); + break; + } + + } + } + // Need to decode data + else + { + utility::auto_ptr <encoder> theDecoder(m_encoding.getEncoder()); + + switch (m_type) + { + default: + { + // No data + break; + } + case TYPE_STRING: + { + utility::inputStreamStringProxyAdapter in(m_string); + + theDecoder->decode(in, os); + break; + } + case TYPE_STREAM: + { + utility::inputStream& in = const_cast <utility::inputStream&> + (*(m_stream ? m_stream : m_ownedStream.ptr())); + + in.reset(); // may not work... + + theDecoder->decode(in, os); + break; + } + + } + } +} + + +const string::size_type contentHandler::length() const +{ + switch (m_type) + { + case TYPE_NONE: return (0); + case TYPE_STRING: return (m_string.length()); + case TYPE_STREAM: return (m_length); + } + + return (0); +} + + +const bool contentHandler::empty() const +{ + return (m_type == TYPE_NONE); +} + + +const bool contentHandler::isEncoded() const +{ + return (m_encoding != NO_ENCODING); +} + + +const vmime::encoding& contentHandler::encoding() const +{ + return (m_encoding); +} + + +} // vmime diff --git a/src/contentHandler.hpp b/src/contentHandler.hpp new file mode 100644 index 00000000..a0ffd3d9 --- /dev/null +++ b/src/contentHandler.hpp @@ -0,0 +1,125 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CONTENTHANDLER_HPP_INCLUDED +#define VMIME_CONTENTHANDLER_HPP_INCLUDED + + +#include <limits> + +#include "base.hpp" +#include "utility/stringProxy.hpp" +#include "utility/smartPtr.hpp" +#include "encoding.hpp" + + +namespace vmime +{ + + +class contentHandler +{ +private: + + static const vmime::encoding NO_ENCODING; + +public: + + contentHandler(); + contentHandler(const string& buffer, const vmime::encoding& enc = NO_ENCODING); // for compatibility + ~contentHandler(); + + // Copy + contentHandler(const contentHandler& cts); + contentHandler& operator=(const contentHandler& cts); + + // Set the data contained in the body. + // + // The two first functions take advantage of the COW (copy-on-write) system that + // might be implemented into std::string. This is done using "stringProxy" object. + // + // Set "enc" parameter to anything other than NO_ENCODING if the data managed by + // this content handler is already encoded with the specified encoding (so, no + // encoding/decoding will be performed on generate()/extract()). Note that the + // data may be re-encoded (that is, decoded and encoded) if the encoding passed + // to generate() is different from this one... + // + // The 'length' parameter is optional (user-defined). You can pass 0 if you want, + // VMime does not make use of it. + void set(const utility::stringProxy& str, const vmime::encoding& enc = NO_ENCODING); + void set(const string& buffer, const vmime::encoding& enc = NO_ENCODING); + void set(const string& buffer, const string::size_type start, const string::size_type end, const vmime::encoding& enc = NO_ENCODING); + void set(utility::inputStream* const is, const utility::stream::size_type length, const bool own, const vmime::encoding& enc = NO_ENCODING); + + // For compatibility + contentHandler& operator=(const string& buffer); + + // WRITE: Output the contents into the specified stream. Data will be + // encoded before being written into the stream. This is used internally + // by the body object to generate the message, you may not need to use + // this (see function extract() if you want to get the contents). + void generate(utility::outputStream& os, const vmime::encoding& enc, const string::size_type maxLineLength = lineLengthLimits::infinite) const; + + // READ: Extract the contents into the specified stream. If needed, data + // will be decoded before being written into the stream. + void extract(utility::outputStream& os) const; + + // Returns the actual length of the data. WARNING: this can return 0 if no + // length was specified when setting data of this object. + const string::size_type length() const; + + // Returns 'true' if the data managed by this object is encoded. + const bool isEncoded() const; + + // Returns the encoding used for the data (or "binary" if not encoded). + const vmime::encoding& encoding() const; + + // Returns 'true' if there is no data set. + const bool empty() const; + +private: + + // Source of data managed by this content handler + enum Types + { + TYPE_NONE, + TYPE_STRING, + TYPE_STREAM + }; + + Types m_type; + + // Equals to NO_ENCODING if data is not encoded, otherwise this + // specifies the encoding that have been used to encode the data. + vmime::encoding m_encoding; + + // Used if m_type == TYPE_STRING + utility::stringProxy m_string; + + // Used if m_type == TYPE_STREAM + utility::smart_ptr <utility::inputStream> m_ownedStream; // 'contentHandler' objects are copiable... + utility::inputStream* m_stream; + string::size_type m_length; +}; + + +} // vmime + + +#endif // VMIME_CONTENTHANDLER_HPP_INCLUDED diff --git a/src/contentTypeField.cpp b/src/contentTypeField.cpp new file mode 100644 index 00000000..3149606e --- /dev/null +++ b/src/contentTypeField.cpp @@ -0,0 +1,62 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "contentTypeField.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +contentTypeField::contentTypeField() +{ +} + + +void contentTypeField::parseValue(const string& buffer, + const string::size_type position, const string::size_type end) +{ + m_value.parse(buffer, position, end); +} + + +const string contentTypeField::generateValue() const +{ + return (m_value.generate()); +} + + +contentTypeField& contentTypeField::operator=(const mediaType& type) +{ + m_value = type; + return (*this); +} + + +void contentTypeField::copyFrom(const headerField& field) +{ + const contentTypeField& source = dynamic_cast<const contentTypeField&>(field); + m_value = source.m_value; + + parameterizedHeaderField::copyFrom(field); +} + + +} // vmime diff --git a/src/contentTypeField.hpp b/src/contentTypeField.hpp new file mode 100644 index 00000000..5244e454 --- /dev/null +++ b/src/contentTypeField.hpp @@ -0,0 +1,72 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CONTENTTYPEFIELD_HPP_INCLUDED +#define VMIME_CONTENTTYPEFIELD_HPP_INCLUDED + + +#include "parameterizedHeaderField.hpp" + +#include "mediaType.hpp" +#include "charset.hpp" + +#include "textParameter.hpp" +#include "charsetParameter.hpp" + + +namespace vmime +{ + + +class contentTypeField : public parameterizedHeaderField +{ + friend class headerFieldFactory::registerer <contentTypeField>; + +protected: + + contentTypeField(); + +public: + + void copyFrom(const headerField& field); + + contentTypeField& operator=(const mediaType& type); + + const mediaType& value() const { return (m_value); } + mediaType& value() { return (m_value); } + + const string& boundary() const { return (dynamic_cast<const textParameter&>(parameters.find("boundary")).value()); } + string& boundary() { return (dynamic_cast<textParameter&>(parameters.get("boundary")).value()); } + + const class charset& charset() const { return (dynamic_cast<const charsetParameter&>(parameters.find("charset")).value()); } + class charset& charset() { return (dynamic_cast<charsetParameter&>(parameters.get("charset")).value()); } + +protected: + + mediaType m_value; + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_CONTENTTYPEFIELD_HPP_INCLUDED diff --git a/src/dateField.cpp b/src/dateField.cpp new file mode 100644 index 00000000..ed673505 --- /dev/null +++ b/src/dateField.cpp @@ -0,0 +1,66 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "dateField.hpp" + + +namespace vmime +{ + + +dateField::dateField() +{ +} + + +void dateField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_datetime.parse(buffer, position, end, newPosition); +} + + +void dateField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + m_datetime.generate(os, maxLineLength, pos, newLinePos); +} + + +dateField& dateField::operator=(const class datetime& datetime) +{ + m_datetime = datetime; + return (*this); +} + + +void dateField::copyFrom(const headerField& field) +{ + const dateField& source = dynamic_cast<const dateField&>(field); + m_datetime = source.m_datetime; + + headerField::copyFrom(field); +} + + +} // vmime diff --git a/src/dateField.hpp b/src/dateField.hpp new file mode 100644 index 00000000..76f47128 --- /dev/null +++ b/src/dateField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DATEFIELD_HPP_INCLUDED +#define VMIME_DATEFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "dateTime.hpp" + + +namespace vmime +{ + + +class dateField : public headerField +{ + friend class headerFieldFactory::registerer <dateField>; + +protected: + + dateField(); + +public: + + void copyFrom(const headerField& field); + + dateField& operator=(const class datetime& datetime); + + const datetime& value() const { return (m_datetime); } + datetime& value() { return (m_datetime); } + +protected: + + datetime m_datetime; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_DATEFIELD_HPP_INCLUDED diff --git a/src/dateParameter.cpp b/src/dateParameter.cpp new file mode 100644 index 00000000..f0f4d8cd --- /dev/null +++ b/src/dateParameter.cpp @@ -0,0 +1,50 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "dateParameter.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +void dateParameter::parseValue(const string& buffer, const string::size_type position, + const string::size_type end) +{ + m_value.parse(buffer, position, end); +} + + +const string dateParameter::generateValue() const +{ + return (m_value.generate()); +} + + +void dateParameter::copyFrom(const parameter& param) +{ + const dateParameter& source = dynamic_cast<const dateParameter&>(param); + m_value = source.m_value; + + defaultParameter::copyFrom(param); +} + + +} // vmime diff --git a/src/dateParameter.hpp b/src/dateParameter.hpp new file mode 100644 index 00000000..aa4ffd63 --- /dev/null +++ b/src/dateParameter.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DATEPARAMETER_HPP_INCLUDED +#define VMIME_DATEPARAMETER_HPP_INCLUDED + + +#include "defaultParameter.hpp" +#include "dateTime.hpp" + + +namespace vmime +{ + + +class dateParameter : public defaultParameter +{ +protected: + + datetime m_value; + +public: + + void copyFrom(const parameter& param); + + const datetime& value() const { return (m_value); } + datetime& value() { return (m_value); } + +protected: + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_DATEPARAMETER_HPP_INCLUDED diff --git a/src/dateTime.cpp b/src/dateTime.cpp new file mode 100644 index 00000000..ceaec6f1 --- /dev/null +++ b/src/dateTime.cpp @@ -0,0 +1,702 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include <iomanip> + +#include "dateTime.hpp" +#include "platformDependant.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + +/* + + RFC #822: + 5. DATE AND TIME SPECIFICATION + +date-time = [ day "," ] date time ; dd mm yy + ; hh:mm:ss zzz +day = "Mon" / "Tue" / "Wed" / "Thu" / + "Fri" / "Sat" / "Sun" + +date = 1*2DIGIT month 2DIGIT ; day month year + ; e.g. 20 Jun 82 +month = "Jan" / "Feb" / "Mar" / "Apr" / + "May" / "Jun" / "Jul" / "Aug" / + "Sep" / "Oct" / "Nov" / "Dec" + +time = hour zone ; ANSI and Military + +hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] ; 00:00:00 - 23:59:59 + +zone = "UT" / "GMT" ; Universal Time + ; North American : UT + / "EST" / "EDT" ; Eastern: - 5/ - 4 + / "CST" / "CDT" ; Central: - 6/ - 5 + / "MST" / "MDT" ; Mountain: - 7/ - 6 + / "PST" / "PDT" ; Pacific: - 8/ - 7 + / 1ALPHA ; Military: Z = UT; + ; A:-1; (J not used) + ; M:-12; N:+1; Y:+12 + / ( ("+" / "-") 4DIGIT ) ; Local differential + ; hours+min. (HHMM) +*/ + + +void datetime::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* p = buffer.data() + position; + + // Parse the date and time value + while (p < pend && isspace(*p)) ++p; + + if (p < pend) + { + if (isalpha(*p)) + { + // Ignore week day + while (p < pend && isalpha(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + if (p < pend && *p == ',') ++p; + while (p < pend && isspace(*p)) ++p; + } + + while (p < pend && !isdigit(*p)) ++p; + + if (p < pend && isdigit(*p)) + { + // Month day + comp_t day = 0; + + do + { + day = day * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_day = (day >= 1 && day <= 31) ? day : 1; + + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + else + { + m_day = 1; + + // Skip everything to the next field + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p < pend && isalpha(*p)) + { + // Month + char_t month[4] = { 0 }; + int monthLength = 0; + + do + { + month[monthLength++] = *p; + ++p; + } + while (monthLength < 3 && p < pend && isalpha(*p)); + + while (p < pend && isalpha(*p)) ++p; + + switch (month[0]) + { + case 'a': + case 'A': + { + if (month[1] == 'u' || month[1] == 'U') + m_month = AUGUST; + else + m_month = APRIL; // by default + + break; + } + case 'd': + case 'D': + { + m_month = DECEMBER; + break; + } + case 'f': + case 'F': + { + m_month = FEBRUARY; + break; + } + case 'j': + case 'J': + { + if (month[1] == 'u' || month[1] == 'U') + { + if (month[2] == 'l' || month[2] == 'L') + m_month = JULY; + else // if (month[2] == 'n' || month[2] == 'N') + m_month = JUNE; + } + else + { + m_month = JANUARY; // by default + } + + break; + } + case 'm': + case 'M': + { + if ((month[1] == 'a' || month[1] == 'A') && + (month[2] == 'y' || month[2] == 'Y')) + { + m_month = MAY; + } + else + { + m_month = MARCH; // by default + } + + break; + } + case 'n': + case 'N': + { + m_month = NOVEMBER; + break; + } + case 'o': + case 'O': + { + m_month = OCTOBER; + break; + } + case 's': + case 'S': + { + m_month = SEPTEMBER; + break; + } + default: + { + m_month = JANUARY; // by default + break; + } + + } + + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + else + { + m_month = JANUARY; + + // Skip everything to the next field + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p < pend && isdigit(*p)) + { + // Year + comp_t year = 0; + + do + { + year = year * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + if (year < 70) m_year = year + 2000; + else if (year < 1000) m_year = year + 1900; + else m_year = year; + + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + else + { + m_year = 1970; + + // Skip everything to the next field + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p < pend && isdigit(*p)) + { + // Hour + comp_t hour = 0; + + do + { + hour = hour * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_hour = (hour >= 0 && hour <= 23) ? hour : 0; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && *p == ':') + { + ++p; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && isdigit(*p)) + { + // Minute + comp_t minute = 0; + + do + { + minute = minute * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_minute = (minute >= 0 && minute <= 59) ? minute : 0; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && *p == ':') + { + ++p; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && isdigit(*p)) + { + // Second + comp_t second = 0; + + do + { + second = second * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_second = (second >= 0 && second <= 59) ? second : 0; + + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + else + { + m_second = 0; + } + } + else + { + m_second = 0; + } + } + else + { + m_minute = 0; + } + } + else + { + m_minute = 0; + } + } + else + { + m_hour = 0; + + // Skip everything to the next field + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p + 1 < pend && (*p == '+' || *p == '-') && isdigit(*(p + 1))) + { + const char_t sign = *p; + ++p; + + // Zone offset (in hour/minutes) + comp_t offset = 0; + + do + { + offset = offset * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + const comp_t hourOff = offset / 100; + const comp_t minOff = offset % 100; + + if (sign == '+') + m_zone = hourOff * 60 + minOff; + else + m_zone = -(hourOff * 60 + minOff); + } + else if (p < pend && isalpha(*p)) + { + bool done = false; + + // Zone offset (Time zone name) + char_t zone[4] = { 0 }; + int zoneLength = 0; + + do + { + zone[zoneLength++] = *p; + ++p; + } + while (zoneLength < 3 && p < pend && isdigit(*p)); + + switch (zone[0]) + { + case 'c': + case 'C': + { + if (zoneLength >= 2) + { + if (zone[1] == 's' || zone[1] == 'S') + m_zone = CST; + else + m_zone = CDT; + + done = true; + } + + break; + } + case 'e': + case 'E': + { + if (zoneLength >= 2) + { + if (zone[1] == 's' || zone[1] == 'S') + m_zone = EST; + else + m_zone = EDT; + + done = true; + } + + break; + } + case 'm': + case 'M': + { + if (zoneLength >= 2) + { + if (zone[1] == 's' || zone[1] == 'S') + m_zone = MST; + else + m_zone = MDT; + + done = true; + } + + break; + } + case 'p': + case 'P': + { + if (zoneLength >= 2) + { + if (zone[1] == 's' || zone[1] == 'S') + m_zone = PST; + else + m_zone = PDT; + + done = true; + } + + break; + } + case 'u': + case 'U': + { + if (zoneLength >= 2) + { + m_zone = GMT; // = UTC + done = true; + } + + break; + } + + } + + if (!done) + { + const char_t z = zone[0]; + + // Military time zone + if (z != 'j' && z != 'J') + { + typedef std::map <char_t, int> Map; + static const Map::value_type offsetMapInit[] = + { + Map::value_type('a', -60), + Map::value_type('b', -120), + Map::value_type('c', -180), + Map::value_type('d', -240), + Map::value_type('e', -300), + Map::value_type('f', -360), + Map::value_type('g', -420), + Map::value_type('h', -480), + Map::value_type('i', -540), + Map::value_type('k', -600), + Map::value_type('l', -660), + Map::value_type('m', -720), + + Map::value_type('n', 60), + Map::value_type('o', 120), + Map::value_type('p', 180), + Map::value_type('q', 240), + Map::value_type('r', 300), + Map::value_type('s', 360), + Map::value_type('t', 420), + Map::value_type('u', 480), + Map::value_type('v', 540), + Map::value_type('w', 600), + Map::value_type('x', 660), + Map::value_type('y', 720), + + Map::value_type('z', 0), + }; + static const Map offsetMap + (::vmime::begin(offsetMapInit), + ::vmime::end(offsetMapInit)); + + Map::const_iterator pos = + offsetMap.find(tolower(z)); + + if (pos != offsetMap.end()) + m_zone = (*pos).second; + else + m_zone = GMT; + } + else + { + m_zone = GMT; + } + } + } + else + { + m_zone = 0; + } + } + else + { + m_year = 1970; + m_month = JANUARY; + m_day = 1; + + m_hour = 0; + m_minute = 0; + m_second = 0; + + m_zone = 0; + } + + if (newPosition) + *newPosition = end; +} + + +void datetime::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + static const string::value_type* dayNames[] = + { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static const string::value_type* monthNames[] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + const comp_t z = ((m_zone < 0) ? -m_zone : m_zone); + const comp_t zh = z / 60; + const comp_t zm = z % 60; + + std::ostringstream oss; + oss << dayNames[dayOfWeek(m_year, m_month, m_day)] << ", " + << m_day << " " << monthNames[month() - 1] << " " << year() + << " " << std::setfill('0') << std::setw(2) << m_hour << ":" + << std::setfill('0') << std::setw(2) << m_minute << ":" + << std::setfill('0') << std::setw(2) << m_second + << " " << ((m_zone < 0) ? '-' : '+') << std::setfill('0') << std::setw(2) << zh + << std::setfill('0') << std::setw(2) << zm; + + const string& str = oss.str(); + os << str; + + if (newLinePos) + *newLinePos = curLinePos + str.length(); +} + + +datetime::datetime() + : m_year(1970), m_month(1), m_day(1), + m_hour(0), m_minute(0), m_second(0), m_zone(0) +{ +} + + +datetime::datetime(const comp_t year, const comp_t month, const comp_t day) + : m_year(year), m_month(month), m_day(day), + m_hour(0), m_minute(0), m_second(0), m_zone(0) +{ +} + + +datetime::datetime(const comp_t year, const comp_t month, const comp_t day, + const comp_t hour, const comp_t minute, const comp_t second, + const comp_t zone) + : m_year(year), m_month(month), m_day(day), + m_hour(hour), m_minute(minute), m_second(second), m_zone(zone) +{ +} + + +datetime::datetime(const datetime& d) + : component(), m_year(d.m_year), m_month(d.m_month), m_day(d.m_day), + m_hour(d.m_hour), m_minute(d.m_minute), m_second(d.m_second), m_zone(d.m_zone) +{ +} + + +datetime::datetime(const string& date) +{ + parse(date); +} + + +datetime::~datetime() +{ +} + + +void datetime::copyFrom(const datetime& d) +{ + m_year = d.m_year; + m_month = d.m_month; + m_day = d.m_day; + m_hour = d.m_hour; + m_minute = d.m_minute; + m_second = d.m_second; + m_zone = d.m_zone; +} + + +datetime& datetime::operator=(const datetime& d) +{ + copyFrom(d); + return (*this); +} + + +datetime& datetime::operator=(const string& s) +{ + parse(s); + return (*this); +} + + +void datetime::getTime(comp_t& hour, comp_t& minute, comp_t& second, comp_t& zone) const +{ + hour = m_hour; + minute = m_minute; + second = m_second; + zone = m_zone; +} + + +void datetime::getTime(comp_t& hour, comp_t& minute, comp_t& second) const +{ + hour = m_hour; + minute = m_minute; + second = m_second; +} + + +void datetime::getDate(comp_t& year, comp_t& month, comp_t& day) const +{ + year = m_year; + month = m_month; + day = m_day; +} + + +void datetime::setTime(const comp_t hour, const comp_t minute, + const comp_t second, const comp_t zone) +{ + m_hour = hour; + m_minute = minute; + m_second = second; + m_zone = zone; +} + + +void datetime::setDate(const comp_t year, const comp_t month, const comp_t day) +{ + m_year = year; + m_month = month; + m_day = day; +} + + +const datetime::comp_t datetime::dayOfWeek(const comp_t year, const comp_t month, const comp_t day) +{ + comp_t y = year; + comp_t m = month; + + // From RFC-3339 - Appendix B. Day of the Week + + // Adjust months so February is the last one + m -= 2; + + if (m < 1) + { + m += 12; + --y; + } + + // Split by century + const comp_t cent = y / 100; + y %= 100; + + return (((26 * m - 2) / 10 + day + y + (y >> 2) + (cent >> 2) + 5 * cent) % 7); +} + + +const datetime datetime::now() +{ + return (platformDependant::getHandler()->getCurrentLocalTime()); +} + + +} // vmime diff --git a/src/dateTime.hpp b/src/dateTime.hpp new file mode 100644 index 00000000..b50e6483 --- /dev/null +++ b/src/dateTime.hpp @@ -0,0 +1,242 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DATETIME_HPP_INCLUDED +#define VMIME_DATETIME_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Date and time (basic type). + */ + +class datetime : public component +{ +public: + + // Data type for a date/time component + typedef int comp_t; + + // Constructors + datetime(); + datetime(const comp_t year, const comp_t month, const comp_t day); + datetime(const comp_t year, const comp_t month, const comp_t day, const comp_t hour, const comp_t minute, const comp_t second, const comp_t zone = GMT); + datetime(const datetime& d); + datetime(const string& date); + + // Destructor + ~datetime(); + + // Some time zones (in minutes) + enum TimeZones + { + GMT_12 = -720, // GMT-12h + GMT_11 = -660, // GMT-11h + GMT_10 = -600, // GMT-10h + GMT_9 = -540, // GMT-9h + GMT_8 = -480, // GMT-8h + GMT_7 = -420, // GMT-7h + GMT_6 = -360, // GMT-6h + GMT_5 = -300, // GMT-5h + GMT_4 = -240, // GMT-4h + GMT_3 = -180, // GMT-3h + GMT_2 = -120, // GMT-2h + GMT_1 = -60, // GMT-1h + GMT = 0, // GMT + GMT1 = 60, // GMT+1h + GMT2 = 120, // GMT+2h + GMT3 = 180, // GMT+3h + GMT4 = 240, // GMT+4h + GMT5 = 300, // GMT+5h + GMT6 = 360, // GMT+6h + GMT7 = 420, // GMT+7h + GMT8 = 480, // GMT+8h + GMT9 = 540, // GMT+9h + GMT10 = 600, // GMT+10h + GMT11 = 660, // GMT+11h + GMT12 = 720, // GMT+12h + + UT = GMT, // Universal Time + + EST = GMT_5, // Eastern + EDT = GMT_4, + CST = GMT_6, // Central + CDT = GMT_5, + MST = GMT_7, // Mountain + MDT = GMT_6, + PST = GMT_8, // Pacific + PDT = GMT_7, + + // Military time zones + A = GMT_1, + B = GMT_2, + C = GMT_3, + D = GMT_4, + E = GMT_5, + F = GMT_6, + G = GMT_7, + H = GMT_8, + I = GMT_9, // J not used + K = GMT_10, + L = GMT_11, + M = GMT_12, + + N = GMT1, + O = GMT2, + P = GMT3, + Q = GMT4, + R = GMT5, + S = GMT6, + T = GMT7, + U = GMT8, + V = GMT9, + W = GMT10, + X = GMT11, + Y = GMT12, + + Z = GMT + }; + + // Months list + enum Months + { + // Long + JANUARY = 1, + FEBRUARY = 2, + MARCH = 3, + APRIL = 4, + MAY = 5, + JUNE = 6, + JULY = 7, + AUGUST = 8, + SEPTEMBER = 9, + OCTOBER = 10, + NOVEMBER = 11, + DECEMBER = 12, + + // Short + JAN = 1, + FEB = 2, + MAR = 3, + APR = 4, + JUN = 6, + JUL = 7, + AUG = 8, + SEP = 9, + OCT = 10, + NOV = 11, + DEC = 12 + }; + + // Days of week list + enum DaysOfWeek + { + // Long + SUNDAY = 0, + MONDAY = 1, + TUESDAY = 2, + WEDNESDAY = 3, + THURSDAY = 4, + FRIDAY = 5, + SATURDAY = 6, + + // Short + SUN = 0, + MON = 1, + TUE = 2, + WED = 3, + THU = 4, + FRI = 5, + SAT = 6 + }; + +protected: + + // Date components + comp_t m_year; + comp_t m_month; + comp_t m_day; + + // Time components + comp_t m_hour; + comp_t m_minute; + comp_t m_second; + comp_t m_zone; + +public: + + // Get + const comp_t year() const { return (m_year); } + const comp_t month() const { return (m_month); } + const comp_t day() const { return (m_day); } + const comp_t hour() const { return (m_hour); } + const comp_t minute() const { return (m_minute); } + const comp_t second() const { return (m_second); } + const comp_t zone() const { return (m_zone); } + + void getTime(comp_t& hour, comp_t& minute, comp_t& second, comp_t& zone) const; + void getTime(comp_t& hour, comp_t& minute, comp_t& second) const; + void getDate(comp_t& year, comp_t& month, comp_t& day) const; + + // Set + comp_t& year() { return (m_year); } + comp_t& month() { return (m_month); } + comp_t& day() { return (m_day); } + comp_t& hour() { return (m_hour); } + comp_t& minute() { return (m_minute); } + comp_t& second() { return (m_second); } + comp_t& zone() { return (m_zone); } + + void setTime(const comp_t hour = 0, const comp_t minute = 0, const comp_t second = 0, const comp_t zone = GMT); + void setDate(const comp_t year, const comp_t month, const comp_t day); + + // Assignment + datetime& operator=(const datetime& d); + datetime& operator=(const string& s); + + void copyFrom(const datetime& d); + + // Current date and time + static const datetime now(); + +protected: + + static const comp_t dayOfWeek(const comp_t year, const comp_t month, const comp_t day); + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_DATETIME_HPP_INCLUDED diff --git a/src/defaultAttachment.cpp b/src/defaultAttachment.cpp new file mode 100644 index 00000000..20f649cb --- /dev/null +++ b/src/defaultAttachment.cpp @@ -0,0 +1,91 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "defaultAttachment.hpp" +#include "encoding.hpp" + + +namespace vmime +{ + + +defaultAttachment::defaultAttachment() +{ +} + + +defaultAttachment::defaultAttachment(const contentHandler& data, + const class encoding& enc, const mediaType& type, const text& desc) + : m_type(type), m_desc(desc), m_data(data), m_encoding(enc) +{ +} + + +defaultAttachment::defaultAttachment(const contentHandler& data, + const mediaType& type, const text& desc) + : m_type(type), m_desc(desc), m_data(data), m_encoding(encoding::decide(data)) +{ +} + + +defaultAttachment::defaultAttachment(const defaultAttachment& attach) + : attachment(), m_type(attach.m_type), m_desc(attach.m_desc), + m_data(attach.m_data), m_encoding(attach.m_encoding) +{ +} + + +attachment& defaultAttachment::operator=(const attachment& attach) +{ + const defaultAttachment& att = + dynamic_cast <const defaultAttachment&>(attach); + + m_type = att.m_type; + m_desc = att.m_desc; + m_data = att.m_data; + m_encoding = att.m_encoding; + + return (*this); +} + + +void defaultAttachment::generateIn(bodyPart& parent) const +{ + // Create and append a new part for this attachment + bodyPart* part = new bodyPart; + parent.body().parts.append(part); + + generatePart(*part); +} + + +void defaultAttachment::generatePart(bodyPart& part) const +{ + // Set header fields + part.header().fields.ContentType() = m_type; + if (!m_desc.empty()) part.header().fields.ContentDescription() = m_desc; + part.header().fields.ContentTransferEncoding() = m_encoding; + part.header().fields.ContentDisposition() = disposition(dispositionTypes::ATTACHMENT); + + // Set contents + part.body().contents() = m_data; +} + + +} // vmime diff --git a/src/defaultAttachment.hpp b/src/defaultAttachment.hpp new file mode 100644 index 00000000..2cf11638 --- /dev/null +++ b/src/defaultAttachment.hpp @@ -0,0 +1,69 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DEFAULTATTACHMENT_HPP_INCLUDED +#define VMIME_DEFAULTATTACHMENT_HPP_INCLUDED + + +#include "attachment.hpp" +#include "encoding.hpp" + + +namespace vmime +{ + + +class defaultAttachment : public attachment +{ +protected: + + // For use in derived classes. + defaultAttachment(); + +public: + + defaultAttachment(const contentHandler& data, const class encoding& enc, const mediaType& type, const text& desc = NULL_TEXT); + defaultAttachment(const contentHandler& data, const mediaType& type, const text& desc = NULL_TEXT); + defaultAttachment(const defaultAttachment& attach); + + attachment& operator=(const attachment& attach); + + const mediaType& type() const { return (m_type); } + const text& description() const { return (m_desc); } + const contentHandler& data() const { return (m_data); } + const class encoding& encoding() const { return (m_encoding); } + +protected: + + mediaType m_type; // Media type (eg. "application/octet-stream") + text m_desc; // Description (eg. "The image you requested") + contentHandler m_data; // Attachment data (eg. the file contents) + class encoding m_encoding; // Encoding + + // No need to override "generateIn", use "generatePart" instead (see below). + void generateIn(bodyPart& parent) const; + + virtual void generatePart(bodyPart& part) const; +}; + + +} // vmime + + +#endif // VMIME_DEFAULTATTACHMENT_HPP_INCLUDED diff --git a/src/defaultField.cpp b/src/defaultField.cpp new file mode 100644 index 00000000..77392725 --- /dev/null +++ b/src/defaultField.cpp @@ -0,0 +1,71 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "defaultField.hpp" +#include "text.hpp" + + +namespace vmime +{ + + +defaultField::defaultField() +{ +} + + +void defaultField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_text = string(buffer.begin() + position, buffer.begin() + end); + + if (newPosition) + *newPosition = end; +} + + +void defaultField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + encodeAndFoldText(os, vmime::text(word(m_text, charset())), maxLineLength, + pos, newLinePos, encodeAndFoldFlags::forceNoEncoding); +} + + +defaultField& defaultField::operator=(const string& text) +{ + m_text = text; + return (*this); +} + + +void defaultField::copyFrom(const headerField& field) +{ + const defaultField& source = dynamic_cast<const defaultField&>(field); + m_text = source.m_text; + + headerField::copyFrom(field); +} + + +} // vmime diff --git a/src/defaultField.hpp b/src/defaultField.hpp new file mode 100644 index 00000000..c855ca8c --- /dev/null +++ b/src/defaultField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DEFAULTFIELD_HPP_INCLUDED +#define VMIME_DEFAULTFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" + + +namespace vmime +{ + + +class defaultField : public headerField +{ + friend class headerFieldFactory; + friend class headerFieldFactory::registerer <defaultField>; + +protected: + + defaultField(); + +public: + + void copyFrom(const headerField& field); + + defaultField& operator=(const string& text); + + const string& value() const { return (m_text); } + string& value() { return (m_text); } + +protected: + + string m_text; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_DEFAULTFIELD_HPP_INCLUDED diff --git a/src/defaultParameter.cpp b/src/defaultParameter.cpp new file mode 100644 index 00000000..2d9e72db --- /dev/null +++ b/src/defaultParameter.cpp @@ -0,0 +1,117 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "defaultParameter.hpp" + + +namespace vmime +{ + + +void defaultParameter::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + parseValue(buffer, position, end); + + if (newPosition) + *newPosition = end; +} + + +void defaultParameter::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + const string value = quotedValue(); + + pos += m_name.length() + value.length() + 2; + + if (pos > maxLineLength) + { + os << NEW_LINE_SEQUENCE; + pos = NEW_LINE_SEQUENCE_LENGTH; + } + + os << m_name << "=" << value; + + if (newLinePos) + *newLinePos = pos; +} + + +const string defaultParameter::quotedValue() const +{ + const string value(generateValue()); + + std::ostringstream ss; + string::const_iterator start = value.begin(); + bool quoted = false; + + for (string::const_iterator i = value.begin() ; i != value.end() ; ++i) + { + switch (*i) + { + // Characters that need to be quoted _and_ escaped + case '"': + case '\\': + + ss << string(start, i) << "\\" << *i; + + start = i + 1; + quoted = true; + + break; + + // Other characters that need quoting + case ' ': + case '\t': + case '(': + case ')': + case '<': + case '>': + case '@': + case ',': + case ';': + case ':': + case '/': + case '[': + case ']': + case '?': + case '=': + + quoted = true; + break; + } + } + + if (start != value.end()) + ss << string(start, value.end()); + + return (quoted ? ("\"" + ss.str() + "\"") : (ss.str())); +} + + +void defaultParameter::copyFrom(const parameter& param) +{ + parameter::copyFrom(param); +} + + +} // vmime diff --git a/src/defaultParameter.hpp b/src/defaultParameter.hpp new file mode 100644 index 00000000..feb8c6f9 --- /dev/null +++ b/src/defaultParameter.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DEFAULTPARAMETER_HPP_INCLUDED +#define VMIME_DEFAULTPARAMETER_HPP_INCLUDED + + +#include "parameter.hpp" + + +namespace vmime +{ + + +class defaultParameter : public parameter +{ +protected: + + void copyFrom(const parameter& param); + + virtual void parseValue(const string& buffer, const string::size_type position, const string::size_type end) = 0; + virtual const string generateValue() const = 0; + +private: + + const string quotedValue() const; + + // No need to override these in class that derive from "defaultParameter". + // "defaultParameter" provides a default handling for value parsing/building. + // Instead, you must define two functions: "parseValue" and "generateValue" (see above). + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_DEFAULTPARAMETER_HPP_INCLUDED diff --git a/src/defaultParameterizedHeaderField.cpp b/src/defaultParameterizedHeaderField.cpp new file mode 100644 index 00000000..9ca8d154 --- /dev/null +++ b/src/defaultParameterizedHeaderField.cpp @@ -0,0 +1,62 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "defaultParameterizedHeaderField.hpp" + + +namespace vmime +{ + + +defaultParameterizedHeaderField::defaultParameterizedHeaderField() +{ +} + + +void defaultParameterizedHeaderField::parseValue(const string& buffer, + const string::size_type position, const string::size_type end) +{ + m_value = string(buffer.begin() + position, buffer.begin() + end); +} + + +const string defaultParameterizedHeaderField::generateValue() const +{ + return (m_value); +} + + +void defaultParameterizedHeaderField::copyFrom(const headerField& field) +{ + const defaultParameterizedHeaderField& source = dynamic_cast + <const defaultParameterizedHeaderField&>(field); + m_value = source.m_value; + + parameterizedHeaderField::copyFrom(field); +} + + +defaultParameterizedHeaderField& defaultParameterizedHeaderField::operator=(const string& value) +{ + m_value = value; + return (*this); +} + + +} // vmime diff --git a/src/defaultParameterizedHeaderField.hpp b/src/defaultParameterizedHeaderField.hpp new file mode 100644 index 00000000..d85fde59 --- /dev/null +++ b/src/defaultParameterizedHeaderField.hpp @@ -0,0 +1,63 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DEFAULTPARAMETERIZEDHEADERFIELD_HPP_INCLUDED +#define VMIME_DEFAULTPARAMETERIZEDHEADERFIELD_HPP_INCLUDED + + +#include "parameterizedHeaderField.hpp" +#include "base.hpp" + + +namespace vmime +{ + + +class defaultParameterizedHeaderField : public parameterizedHeaderField +{ + friend class headerFieldFactory::registerer <defaultParameterizedHeaderField>; + +protected: + + defaultParameterizedHeaderField(); + +public: + + void copyFrom(const headerField& field); + + defaultParameterizedHeaderField& operator=(const string& value); + + const string& value() const { return (m_value); } + string& value() { return (m_value); } + +protected: + + string m_value; + +protected: + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_DEFAULTPARAMETERIZEDHEADERFIELD_HPP_INCLUDED diff --git a/src/disposition.cpp b/src/disposition.cpp new file mode 100644 index 00000000..7f8962f0 --- /dev/null +++ b/src/disposition.cpp @@ -0,0 +1,91 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "disposition.hpp" + + +namespace vmime +{ + + +disposition::disposition() + : m_name(dispositionTypes::INLINE) +{ +} + + +disposition::disposition(const string& name) + : m_name(toLower(name)) +{ +} + + +disposition::disposition(const disposition& type) + : component(), m_name(type.m_name) +{ +} + + +void disposition::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_name = toLower(string(buffer.begin() + position, buffer.begin() + end)); + + if (newPosition) + *newPosition = end; +} + + +void disposition::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + os << m_name; + + if (newLinePos) + *newLinePos = curLinePos + m_name.length(); +} + + +disposition& disposition::operator=(const disposition& source) +{ + m_name = source.m_name; + return (*this); +} + + +disposition& disposition::operator=(const string& name) +{ + m_name = toLower(name); + return (*this); +} + + +const bool disposition::operator==(const disposition& value) const +{ + return (toLower(m_name) == value.m_name); +} + + +const bool disposition::operator!=(const disposition& value) const +{ + return !(*this == value); +} + + +} // vmime diff --git a/src/disposition.hpp b/src/disposition.hpp new file mode 100644 index 00000000..c994a723 --- /dev/null +++ b/src/disposition.hpp @@ -0,0 +1,74 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DISPOSITION_HPP_INCLUDED +#define VMIME_DISPOSITION_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Content disposition (basic type). + */ + +class disposition : public component +{ +public: + + disposition(); + disposition(const string& name); + disposition(const disposition& disp); + +public: + + const string& name() const { return (m_name); } + string& name() { return (m_name); } + +public: + + disposition& operator=(const disposition& source); + disposition& operator=(const string& name); + + const bool operator==(const disposition& value) const; + const bool operator!=(const disposition& value) const; + +protected: + + string m_name; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_DISPOSITION_HPP_INCLUDED diff --git a/src/encoder.cpp b/src/encoder.cpp new file mode 100644 index 00000000..6634f7bb --- /dev/null +++ b/src/encoder.cpp @@ -0,0 +1,69 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoder.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +encoder::encoder() +{ +} + + +encoder::~encoder() +{ +} + + +const propertySet& encoder::properties() const +{ + return (m_props); +} + + +propertySet& encoder::properties() +{ + return (m_props); +} + + +const propertySet& encoder::results() const +{ + return (m_results); +} + + +propertySet& encoder::results() +{ + return (m_results); +} + + +const std::vector <string> encoder::availableProperties() const +{ + std::vector <string> list; + return (list); +} + + +} // vmime diff --git a/src/encoder.hpp b/src/encoder.hpp new file mode 100644 index 00000000..1aa97d49 --- /dev/null +++ b/src/encoder.hpp @@ -0,0 +1,95 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODER_HPP_INCLUDED +#define VMIME_ENCODER_HPP_INCLUDED + + +#include "base.hpp" +#include "propertySet.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +class encoder +{ +public: + + encoder(); + virtual ~encoder(); + + /** Encode data. + * + * @param in input data (decoded) + * @param out output stream for encoded data + * @return number of bytes written into output stream + */ + virtual const utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out) = 0; + + /** Decode data. + * + * @param in input data (encoded) + * @param out output stream for decoded data + * @return number of bytes written into output stream + */ + virtual const utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out) = 0; + + /** Return the properties of the encoder. + * + * @return properties of the encoder + */ + const propertySet& properties() const; + + /** Return the properties of the encoder. + * + * @return properties of the encoder + */ + propertySet& properties(); + + /** Return a list of property names that can be set for + * this encoder. + * + * @return list of property names + */ + virtual const std::vector <string> availableProperties() const; + + /** Return the results returned by this encoder. + * + * @return results returned by the encoder + */ + const propertySet& results() const; + +protected: + + propertySet& results(); + +private: + + propertySet m_props; + propertySet m_results; +}; + + +} // vmime + + +#endif // VMIME_ENCODER_HPP_INCLUDED diff --git a/src/encoder7bit.cpp b/src/encoder7bit.cpp new file mode 100644 index 00000000..b5ae3820 --- /dev/null +++ b/src/encoder7bit.cpp @@ -0,0 +1,32 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoder7bit.hpp" + + +namespace vmime +{ + + +encoder7bit::encoder7bit() +{ +} + + +} // vmime diff --git a/src/encoder7bit.hpp b/src/encoder7bit.hpp new file mode 100644 index 00000000..a4b64cdf --- /dev/null +++ b/src/encoder7bit.hpp @@ -0,0 +1,45 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODER7BIT_HPP_INCLUDED +#define VMIME_ENCODER7BIT_HPP_INCLUDED + + +#include "encoderDefault.hpp" + + +namespace vmime +{ + + +/** 7-bit encoder. + */ + +class encoder7bit : public encoderDefault +{ +public: + + encoder7bit(); +}; + + +} // vmime + + +#endif // VMIME_ENCODER7BIT_HPP_INCLUDED diff --git a/src/encoder8bit.cpp b/src/encoder8bit.cpp new file mode 100644 index 00000000..b9b91457 --- /dev/null +++ b/src/encoder8bit.cpp @@ -0,0 +1,32 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoder8bit.hpp" + + +namespace vmime +{ + + +encoder8bit::encoder8bit() +{ +} + + +} // vmime diff --git a/src/encoder8bit.hpp b/src/encoder8bit.hpp new file mode 100644 index 00000000..7cbbd417 --- /dev/null +++ b/src/encoder8bit.hpp @@ -0,0 +1,45 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODER8BIT_HPP_INCLUDED +#define VMIME_ENCODER8BIT_HPP_INCLUDED + + +#include "encoderDefault.hpp" + + +namespace vmime +{ + + +/** 8-bit encoder. + */ + +class encoder8bit : public encoderDefault +{ +public: + + encoder8bit(); +}; + + +} // vmime + + +#endif // VMIME_ENCODER8BIT_HPP_INCLUDED diff --git a/src/encoderB64.cpp b/src/encoderB64.cpp new file mode 100644 index 00000000..25b15ad9 --- /dev/null +++ b/src/encoderB64.cpp @@ -0,0 +1,265 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderB64.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +encoderB64::encoderB64() +{ +} + + +const std::vector <string> encoderB64::availableProperties() const +{ + std::vector <string> list(encoder::availableProperties()); + + list.push_back("maxlinelength"); + + return (list); +} + + +// 7-bits alphabet used to encode binary data +const unsigned char encoderB64::sm_alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +const unsigned char encoderB64::sm_decodeMap[256] = +{ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0x00 - 0x0f + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0x10 - 0x1f + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3e,0xff,0xff,0xff,0x3f, // 0x20 - 0x2f + 0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0xff,0xff,0xff,0x3d,0xff,0xff, // 0x30 - 0x3f + 0xff,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e, // 0x40 - 0x4f + 0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0xff,0xff,0xff,0xff,0xff, // 0x50 - 0x5f + 0xff,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28, // 0x60 - 0x6f + 0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0xff,0xff,0xff,0xff, // 0x70 - 0x7f + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0x80 - 0x8f + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0x90 - 0x9f + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xa0 - 0xaf + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xb0 - 0xbf + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xc0 - 0xcf + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xd0 - 0xdf + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xe0 - 0xef + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xf0 - 0xff +}; + + + +const utility::stream::size_type encoderB64::encode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + const int propMaxLineLength = properties().get <int>("maxlinelength", -1); + + const bool cutLines = (propMaxLineLength != -1); + const int maxLineLength = std::min(propMaxLineLength, 76); + + // Process data + utility::stream::value_type buffer[65536]; + utility::stream::size_type bufferLength = 0; + utility::stream::size_type bufferPos = 0; + + unsigned char bytes[3]; + unsigned char output[4]; + + utility::stream::size_type total = 0; + + int curCol = 0; + + while (bufferPos < bufferLength || !in.eof()) + { + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + + if (bufferLength == 0) + break; + } + + // Get 3 bytes of data + int count = 0; + + while (count < 3 && bufferPos < bufferLength) + bytes[count++] = buffer[bufferPos++]; + + if (count != 3) + { + // There may be more data in the next chunk... + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + } + + while (count < 3 && bufferPos < bufferLength) + bytes[count++] = buffer[bufferPos++]; + } + + // Encode data + switch (count) + { + case 1: + + output[0] = sm_alphabet[(bytes[0] & 0xFC) >> 2]; + output[1] = sm_alphabet[(bytes[0] & 0x03) << 4]; + output[2] = sm_alphabet[64]; // padding + output[3] = sm_alphabet[64]; // padding + + break; + + case 2: + + output[0] = sm_alphabet[(bytes[0] & 0xFC) >> 2]; + output[1] = sm_alphabet[((bytes[0] & 0x03) << 4) | ((bytes[1] & 0xF0) >> 4)]; + output[2] = sm_alphabet[(bytes[1] & 0x0F) << 2]; + output[3] = sm_alphabet[64]; // padding + + break; + + default: + case 3: + + output[0] = sm_alphabet[(bytes[0] & 0xFC) >> 2]; + output[1] = sm_alphabet[((bytes[0] & 0x03) << 4) | ((bytes[1] & 0xF0) >> 4)]; + output[2] = sm_alphabet[((bytes[1] & 0x0F) << 2) | ((bytes[2] & 0xC0) >> 6)]; + output[3] = sm_alphabet[(bytes[2] & 0x3F)]; + + break; + } + + // Write encoded data to output stream + out.write((char*) output, 4); + + total += 4; + curCol += 4; + + if (cutLines && curCol >= maxLineLength - 1) + { + out.write("\r\n", 2); + curCol = 0; + } + } + + return (total); +} + + +const utility::stream::size_type encoderB64::decode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + // Process the data + char buffer[16384]; + int bufferLength = 0; + int bufferPos = 0; + + utility::stream::size_type total = 0; + + unsigned char bytes[4]; + unsigned char output[3]; + + while (bufferPos < bufferLength || !in.eof()) + { + // Need to get more data? + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + + // No more data + if (bufferLength == 0) + break; + } + + // 4 bytes of input provide 3 bytes of output, so + // get the next 4 bytes from the input stream. + int count = 0; + + while (count < 4 && bufferPos < bufferLength) + { + const unsigned char c = buffer[bufferPos++]; + + if (!isspace(c)) + bytes[count++] = c; + } + + if (count != 4) + { + while (count < 4 && !in.eof()) + { + // Data continues on the next chunk + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + + while (count < 4 && bufferPos < bufferLength) + { + const unsigned char c = buffer[bufferPos++]; + + if (!isspace(c)) + bytes[count++] = c; + } + } + } + + // Decode the bytes + unsigned char c1 = bytes[0]; + unsigned char c2 = bytes[1]; + + if (c1 == '=' || c2 == '=') // end + break; + + output[0] = (unsigned char)((sm_decodeMap[c1] << 2) | ((sm_decodeMap[c2] & 0x30) >> 4)); + + c1 = bytes[2]; + + if (c1 == '=') // end + { + out.write((char*) output, 1); + total += 1; + break; + } + + output[1] = (unsigned char)(((sm_decodeMap[c2] & 0xf) << 4) | ((sm_decodeMap[c1] & 0x3c) >> 2)); + + c2 = bytes[3]; + + if (c2 == '=') // end + { + out.write((char*) output, 2); + total += 2; + break; + } + + output[2] = (unsigned char)(((sm_decodeMap[c1] & 0x03) << 6) | sm_decodeMap[c2]); + + out.write((char*) output, 3); + total += 3; + } + + return (total); +} + + +} // vmime diff --git a/src/encoderB64.hpp b/src/encoderB64.hpp new file mode 100644 index 00000000..ff978985 --- /dev/null +++ b/src/encoderB64.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERB64_HPP_INCLUDED +#define VMIME_ENCODERB64_HPP_INCLUDED + + +#include "encoder.hpp" + + +namespace vmime +{ + + +/** Base64 encoder. + */ + +class encoderB64 : public encoder +{ +public: + + encoderB64(); + + const utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out); + const utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out); + + const std::vector <string> availableProperties() const; + +protected: + + static const unsigned char sm_alphabet[]; + static const unsigned char sm_decodeMap[256]; +}; + + +} // vmime + + +#endif // VMIME_ENCODERB64_HPP_INCLUDED diff --git a/src/encoderBinary.cpp b/src/encoderBinary.cpp new file mode 100644 index 00000000..606da99c --- /dev/null +++ b/src/encoderBinary.cpp @@ -0,0 +1,32 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderBinary.hpp" + + +namespace vmime +{ + + +encoderBinary::encoderBinary() +{ +} + + +} // vmime diff --git a/src/encoderBinary.hpp b/src/encoderBinary.hpp new file mode 100644 index 00000000..dadca217 --- /dev/null +++ b/src/encoderBinary.hpp @@ -0,0 +1,45 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERBINARY_HPP_INCLUDED +#define VMIME_ENCODERBINARY_HPP_INCLUDED + + +#include "encoderDefault.hpp" + + +namespace vmime +{ + + +/** Binary encoder. + */ + +class encoderBinary : public encoderDefault +{ +public: + + encoderBinary(); +}; + + +} // vmime + + +#endif // VMIME_ENCODERBINARY_HPP_INCLUDED diff --git a/src/encoderDefault.cpp b/src/encoderDefault.cpp new file mode 100644 index 00000000..cdff3735 --- /dev/null +++ b/src/encoderDefault.cpp @@ -0,0 +1,50 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderDefault.hpp" + + +namespace vmime +{ + + +encoderDefault::encoderDefault() +{ +} + + +const utility::stream::size_type encoderDefault::encode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + // No encoding performed + return (utility::bufferedStreamCopy(in, out)); +} + + +const utility::stream::size_type encoderDefault::decode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + // No decoding performed + return (utility::bufferedStreamCopy(in, out)); +} + + +} // vmime diff --git a/src/encoderDefault.hpp b/src/encoderDefault.hpp new file mode 100644 index 00000000..d54d1f0f --- /dev/null +++ b/src/encoderDefault.hpp @@ -0,0 +1,48 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERDEFAULT_HPP_INCLUDED +#define VMIME_ENCODERDEFAULT_HPP_INCLUDED + + +#include "encoder.hpp" + + +namespace vmime +{ + + +/** Default encoder (simple copy, no encoding/decoding is performed). + */ + +class encoderDefault : public encoder +{ +public: + + encoderDefault(); + + const utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out); + const utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out); +}; + + +} // vmime + + +#endif // VMIME_ENCODERDEFAUL_HPP_INCLUDED diff --git a/src/encoderFactory.cpp b/src/encoderFactory.cpp new file mode 100644 index 00000000..6da09dbb --- /dev/null +++ b/src/encoderFactory.cpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderFactory.hpp" +#include "exception.hpp" + +#include "encoderB64.hpp" +#include "encoderQP.hpp" +#include "encoderUUE.hpp" +#include "encoderBinary.hpp" +#include "encoder7bit.hpp" +#include "encoder8bit.hpp" + + +namespace vmime +{ + + +encoderFactory::encoderFactory() +{ + // Register some default encoders + registerName <encoderB64>("base64"); + registerName <encoderQP>("quoted-printable"); + registerName <encoderUUE>("uuencode"); + registerName <encoder7bit>("7bit"); + registerName <encoder8bit>("8bit"); + registerName <encoderBinary>("binary"); +} + + +encoderFactory::~encoderFactory() +{ + for (NameMap::iterator it = m_nameMap.begin() ; it != m_nameMap.end() ; ++it) + delete ((*it).second); +} + + +encoder* encoderFactory::create(const string& name) +{ + NameMap::const_iterator pos = m_nameMap.find(toLower(name)); + + if (pos != m_nameMap.end()) + { + return ((*pos).second)->create(); + } + else + { + throw exceptions::no_encoder_available(); + return (NULL); + } +} + + +} // vmime diff --git a/src/encoderFactory.hpp b/src/encoderFactory.hpp new file mode 100644 index 00000000..cbd52048 --- /dev/null +++ b/src/encoderFactory.hpp @@ -0,0 +1,183 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERFACTORY_HPP_INCLUDED +#define VMIME_ENCODERFACTORY_HPP_INCLUDED + + +#include "encoder.hpp" +#include "utility/singleton.hpp" + + +namespace vmime +{ + + +/** A factory to create 'encoder' objects for the specified encoding. + */ + +class encoderFactory : public utility::singleton <encoderFactory> +{ + friend class utility::singleton <encoderFactory>; + +private: + + encoderFactory(); + ~encoderFactory(); + +public: + + class registeredEncoder + { + friend class encoderFactory; + + protected: + + virtual ~registeredEncoder() { } + + public: + + virtual encoder* create() = 0; + + virtual const string& name() const = 0; + }; + +private: + + template <class E> + class registeredEncoderImpl : public registeredEncoder + { + friend class encoderFactory; + + protected: + + registeredEncoderImpl(const string& name) : m_name(name) { } + + public: + + encoder* create() + { + return new E; + } + + const string& name() const + { + return (m_name); + } + + private: + + const string m_name; + }; + + + typedef std::map <string, registeredEncoder*> NameMap; + NameMap m_nameMap; + +public: + + template <class E> + void registerName(const string& name) + { + const string _name = toLower(name); + m_nameMap.insert(NameMap::value_type(_name, + new registeredEncoderImpl <E>(_name))); + } + + encoder* create(const string& name); + + const registeredEncoder& operator[](const string& name) const; + + + class iterator; + + class const_iterator + { + friend class encoderFactory; + + public: + + const_iterator() { } + const_iterator(const const_iterator& it) : m_it(it.m_it) { } + const_iterator(const iterator& it) : m_it(it.m_it) { } + + const_iterator& operator=(const const_iterator& it) { m_it = it.m_it; return (*this); } + + const registeredEncoder& operator*() const { return (*(*m_it).second); } + const registeredEncoder* operator->() const { return ((*m_it).second); } + + const_iterator& operator++() { ++m_it; return (*this); } + const_iterator operator++(int) { return (m_it++); } + + const_iterator& operator--() { --m_it; return (*this); } + const_iterator operator--(int) { return (m_it--); } + + const bool operator==(const const_iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const const_iterator& it) const { return (m_it != it.m_it); } + + private: + + const_iterator(const NameMap::const_iterator it) : m_it(it) { } + + NameMap::const_iterator m_it; + }; + + class iterator + { + friend class encoderFactory; + friend class encoderFactory::const_iterator; + + public: + + iterator() { } + iterator(const iterator& it) : m_it(it.m_it) { } + + iterator& operator=(const iterator& it) { m_it = it.m_it; return (*this); } + + registeredEncoder& operator*() const { return (*(*m_it).second); } + registeredEncoder* operator->() const { return ((*m_it).second); } + + iterator& operator++() { ++m_it; return (*this); } + iterator operator++(int) { return (m_it++); } + + iterator& operator--() { --m_it; return (*this); } + iterator operator--(int) { return (m_it--); } + + const bool operator==(const iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const iterator& it) const { return (m_it != it.m_it); } + + private: + + iterator(const NameMap::iterator it) : m_it(it) { } + + NameMap::iterator m_it; + }; + + iterator begin() { return iterator(m_nameMap.begin()); } + iterator end() { return iterator(m_nameMap.end()); } + + const_iterator begin() const { return const_iterator(m_nameMap.begin()); } + const_iterator end() const { return const_iterator(m_nameMap.end()); } +}; + + +} // vmime + + +#endif // VMIME_ENCODERFACTORY_HPP_INCLUDED diff --git a/src/encoderQP.cpp b/src/encoderQP.cpp new file mode 100644 index 00000000..46125c93 --- /dev/null +++ b/src/encoderQP.cpp @@ -0,0 +1,422 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderQP.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +encoderQP::encoderQP() +{ +} + + +const std::vector <string> encoderQP::availableProperties() const +{ + std::vector <string> list(encoder::availableProperties()); + + list.push_back("maxlinelength"); + + list.push_back("text"); // if set, '\r' and '\n' are not hex-encoded. + // WARNING! You should not use this for binary data! + + list.push_back("rfc2047"); // for header fields encoding (RFC #2047) + + return (list); +} + + + +// Encoding table +const unsigned char encoderQP::sm_hexDigits[] = "0123456789ABCDEF"; + +// Decoding table +const unsigned char encoderQP::sm_hexDecodeTable[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +#define QP_ENCODE_HEX(x) \ + outBuffer[outBufferPos] = '='; \ + outBuffer[outBufferPos + 1] = sm_hexDigits[x >> 4]; \ + outBuffer[outBufferPos + 2] = sm_hexDigits[x & 0xF]; \ + outBufferPos += 3; \ + curCol += 3; + + +const utility::stream::size_type encoderQP::encode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + const string::size_type propMaxLineLength = + properties().get <string::size_type>("maxlinelength", (string::size_type) -1); + + const bool rfc2047 = properties().get <bool>("rfc2047", false); + const bool text = properties().get <bool>("text", false); // binary mode by default + + const bool cutLines = (propMaxLineLength != (string::size_type) -1); + const string::size_type maxLineLength = std::min(propMaxLineLength, (string::size_type) 74); + + // Process the data + char buffer[16384]; + int bufferLength = 0; + int bufferPos = 0; + + string::size_type curCol = 0; + + unsigned char outBuffer[16384]; + int outBufferPos = 0; + + utility::stream::size_type total = 0; + + while (bufferPos < bufferLength || !in.eof()) + { + // Flush current output buffer + if (outBufferPos + 6 >= (int) sizeof(outBuffer)) + { + out.write((char*) outBuffer, outBufferPos); + + total += outBufferPos; + outBufferPos = 0; + } + + // Need to get more data? + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + + // No more data + if (bufferLength == 0) + break; + } + + // Get the next char and encode it + const unsigned char c = (unsigned char) buffer[bufferPos++]; + + switch (c) + { + case '.': + { + if (!rfc2047 && curCol == 0) + { + // If a '.' appears at the beginning of a line, we encode it to + // to avoid problems with SMTP servers... ("\r\n.\r\n" means the + // end of data transmission). + QP_ENCODE_HEX('.') + continue; + } + + outBuffer[outBufferPos++] = '.'; + ++curCol; + break; + } + case ' ': + { + // RFC-2047, Page 5, 4.2. The "Q" encoding: + // << The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE) may be + // represented as "_" (underscore, ASCII 95.). >> + if (rfc2047) + { + outBuffer[outBufferPos++] = ' '; + ++curCol; + } + else + { + // Need to get more data? + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + } + + // Spaces cannot appear at the end of a line. So, encode the space. + if (bufferPos >= bufferLength || + (buffer[bufferPos] == '\r' || buffer[bufferPos] == '\n')) + { + QP_ENCODE_HEX(' '); + } + else + { + outBuffer[outBufferPos++] = ' '; + ++curCol; + } + } + + break; + } + case '\t': + { + QP_ENCODE_HEX(c) + break; + } + case '\r': + case '\n': + { + // Text mode (where using CRLF or LF or ... does not + // care for a new line...) + if (text) + { + outBuffer[outBufferPos++] = c; + ++curCol; + } + // Binary mode (where CR and LF bytes are important!) + else + { + QP_ENCODE_HEX(c) + } + + break; + } + case '=': + { + QP_ENCODE_HEX('=') + break; + } + case ',': + case ';': + case ':': + case '_': + { + if (rfc2047) + { + QP_ENCODE_HEX(c) + } + else + { + outBuffer[outBufferPos++] = c; + ++curCol; + } + + break; + } + /* + Rule #2: (Literal representation) Octets with decimal values of 33 + through 60 inclusive, and 62 through 126, inclusive, MAY be + represented as the ASCII characters which correspond to those + octets (EXCLAMATION POINT through LESS THAN, and GREATER THAN + through TILDE, respectively). + */ + default: + { + //if ((c >= 33 && c <= 60) || (c >= 62 && c <= 126)) + if (c >= 33 && c <= 126 && c != 61) + { + outBuffer[outBufferPos++] = c; + ++curCol; + } + // Other characters: '=' + hexadecimal encoding + else + { + QP_ENCODE_HEX(c) + } + + break; + } + + } + + // Soft line break : "=\r\n" + if (cutLines && curCol >= maxLineLength - 1) + { + outBuffer[outBufferPos] = '='; + outBuffer[outBufferPos + 1] = '\r'; + outBuffer[outBufferPos + 2] = '\n'; + + outBufferPos += 3; + curCol = 0; + } + } + + // Flush remaining output buffer + if (outBufferPos != 0) + { + out.write((char*) outBuffer, outBufferPos); + total += outBufferPos; + } + + return (total); +} + + +const utility::stream::size_type encoderQP::decode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + // Process the data + const bool rfc2047 = properties().get <bool>("rfc2047", false); + + char buffer[16384]; + int bufferLength = 0; + int bufferPos = 0; + + unsigned char outBuffer[16384]; + int outBufferPos = 0; + + utility::stream::size_type total = 0; + + while (bufferPos < bufferLength || !in.eof()) + { + // Flush current output buffer + if (outBufferPos >= (int) sizeof(outBuffer)) + { + out.write((char*) outBuffer, outBufferPos); + + total += outBufferPos; + outBufferPos = 0; + } + + // Need to get more data? + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + + // No more data + if (bufferLength == 0) + break; + } + + // Decode the next sequence (hex-encoded byte or printable character) + unsigned char c = (unsigned char) buffer[bufferPos++]; + + switch (c) + { + case '=': + { + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + } + + if (bufferPos < bufferLength) + { + c = (unsigned char) buffer[bufferPos++]; + + switch (c) + { + // Ignore soft line break ("=\r\n" or "=\n") + case '\r': + + // Read one byte more + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + } + + if (bufferPos < bufferLength) + ++bufferPos; + + break; + + case '\n': + + break; + + // Hex-encoded char + default: + { + // We need another byte... + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + } + + if (bufferPos < bufferLength) + { + const unsigned char next = (unsigned char) buffer[bufferPos++]; + + const unsigned char value = + sm_hexDecodeTable[c] * 16 + + sm_hexDecodeTable[next]; + + outBuffer[outBufferPos++] = value; + } + else + { + // Premature end-of-data + } + + break; + } + + } + } + else + { + // Premature end-of-data + } + + break; + } + case '_': + { + if (rfc2047) + { + // RFC-2047, Page 5, 4.2. The "Q" encoding: + // << Note that the "_" always represents hexadecimal 20, even if the SPACE + // character occupies a different code position in the character set in use. >> + outBuffer[outBufferPos++] = 0x20; + break; + } + + // no break here... + } + default: + { + outBuffer[outBufferPos++] = c; + } + + } + } + + // Flush remaining output buffer + if (outBufferPos != 0) + { + out.write((char*) outBuffer, outBufferPos); + total += outBufferPos; + } + + return (total); +} + + +} // vmime diff --git a/src/encoderQP.hpp b/src/encoderQP.hpp new file mode 100644 index 00000000..519f43f1 --- /dev/null +++ b/src/encoderQP.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERQP_HPP_INCLUDED +#define VMIME_ENCODERQP_HPP_INCLUDED + + +#include "encoder.hpp" + + +namespace vmime +{ + + +/** Quoted-printable encoder. + */ + +class encoderQP : public encoder +{ +public: + + encoderQP(); + + const utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out); + const utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out); + + const std::vector <string> availableProperties() const; + +protected: + + static const unsigned char sm_hexDigits[17]; + static const unsigned char sm_hexDecodeTable[256]; +}; + + +} // vmime + + +#endif // VMIME_ENCODERQP_HPP_INCLUDED diff --git a/src/encoderUUE.cpp b/src/encoderUUE.cpp new file mode 100644 index 00000000..78dffa0c --- /dev/null +++ b/src/encoderUUE.cpp @@ -0,0 +1,289 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderUUE.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +encoderUUE::encoderUUE() +{ + properties()["mode"] = 644; + properties()["filename"] = "no_name"; + properties()["maxlinelength"] = 46; +} + + +const std::vector <string> encoderUUE::availableProperties() const +{ + std::vector <string> list(encoder::availableProperties()); + + list.push_back("maxlinelength"); + + list.push_back("mode"); + list.push_back("filename"); + + return (list); +} + + +// This is the character encoding function to make a character printable +static inline const unsigned char UUENCODE(const unsigned char c) +{ + return ((c & 077) + ' '); +} + +// Single character decoding +static inline const unsigned char UUDECODE(const unsigned char c) +{ + return ((c - ' ') & 077); +} + + +const utility::stream::size_type encoderUUE::encode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + const string propFilename = properties().get <string>("filename", ""); + const string propMode = properties().get <string>("mode", "644"); + + const string::size_type maxLineLength = + std::min(properties().get <string::size_type>("maxlinelength", 46), + static_cast <string::size_type>(46)); + + utility::stream::size_type total = 0; + + // Output the prelude text ("begin [mode] [filename]") + out << "begin"; + + if (!propFilename.empty()) + { + out << " " << propMode << " " << propFilename; + total += 2 + propMode.length() + propFilename.length(); + } + + out << "\r\n"; + total += 7; + + // Process the data + utility::stream::value_type inBuffer[64]; + utility::stream::value_type outBuffer[64]; + + while (!in.eof()) + { + // Process up to 45 characters per line + std::fill(inBuffer, inBuffer + sizeof(inBuffer), 0); + + const utility::stream::size_type inLength = in.read(inBuffer, maxLineLength - 1); + + outBuffer[0] = UUENCODE(inLength); // Line length + + utility::stream::size_type j = 1; + + for (utility::stream::size_type i = 0 ; i < inLength ; i += 3, j += 4) + { + const unsigned char c1 = (unsigned char) inBuffer[i]; + const unsigned char c2 = (unsigned char) inBuffer[i + 1]; + const unsigned char c3 = (unsigned char) inBuffer[i + 2]; + + outBuffer[j] = UUENCODE(c1 >> 2); + outBuffer[j + 1] = UUENCODE((c1 << 4) & 060 | (c2 >> 4) & 017); + outBuffer[j + 2] = UUENCODE((c2 << 2) & 074 | (c3 >> 6) & 03); + outBuffer[j + 3] = UUENCODE(c3 & 077); + } + + outBuffer[j] = '\r'; + outBuffer[j + 1] = '\n'; + + out.write(outBuffer, j + 2); + + total += j + 2; + } + + out << "end\r\n"; + total += 5; + + return (total); +} + + +const utility::stream::size_type encoderUUE::decode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + // Process the data + utility::stream::value_type inBuffer[64]; + utility::stream::value_type outBuffer[64]; + + utility::stream::size_type total = 0; + + bool stop = false; + + std::fill(inBuffer, inBuffer + sizeof(inBuffer), 0); + + while (!stop && !in.eof()) + { + // Get the line length + utility::stream::value_type lengthChar; + + if (in.read(&lengthChar, 1) == 0) + break; + + const utility::stream::size_type outLength = UUDECODE(lengthChar); + const utility::stream::size_type inLength = std::min((outLength * 4) / 3, (utility::stream::size_type) 64); + utility::stream::value_type inPos = 0; + + switch (lengthChar) + { + case ' ': + case '\t': + case '\r': + case '\n': + { + // Ignore + continue; + } + case 'b': + { + // Read 5 characters more to check for begin ("begin ...\r\n" or "begin ...\n") + inPos = in.read(inBuffer, 5); + + if (inPos == 5 && + inBuffer[0] == 'e' && + inBuffer[1] == 'g' && + inBuffer[2] == 'i' && + inBuffer[3] == 'n' && + isspace(inBuffer[4])) + { + utility::stream::value_type c = 0; + + utility::stream::size_type count = 0; + utility::stream::value_type buffer[512]; + + while (count < sizeof(buffer) - 1 && in.read(&c, 1) == 1) + { + if (c == '\n') + break; + + buffer[count++] = c; + } + + if (c != '\n') + { + // OOPS! Weird line. Don't try to decode more... + return (total); + } + + // Parse filename and mode + if (count > 0) + { + buffer[count] = '\0'; + + utility::stream::value_type* p = buffer; + + while (*p && isspace(*p)) ++p; + + utility::stream::value_type* modeStart = buffer; + + while (*p && !isspace(*p)) ++p; + + results()["mode"] = string(modeStart, p); + + while (*p && isspace(*p)) ++p; + + utility::stream::value_type* filenameStart = buffer; + + while (*p && !(*p == '\r' || *p == '\n')) ++p; + + results()["filename"] = string(filenameStart, p); + } + // No filename or mode specified + else + { + results()["filename"] = "untitled"; + results()["mode"] = 644; + } + + continue; + } + + break; + } + case 'e': + { + // Read 3 characters more to check for end ("end\r\n" or "end\n") + inPos = in.read(inBuffer, 3); + + if (inPos == 3 && + inBuffer[0] == 'n' && + inBuffer[1] == 'd' && + (inBuffer[2] == '\r' || inBuffer[2] == '\n')) + { + stop = true; + continue; + } + + break; + } + + } + + // Read encoded data + if (in.read(inBuffer + inPos, inLength - inPos) != inLength - inPos) + { + // Premature end of data + break; + } + + // Decode data + for (utility::stream::size_type i = 0, j = 0 ; i < inLength ; i += 4, j += 3) + { + const unsigned char c1 = (unsigned char) inBuffer[i]; + const unsigned char c2 = (unsigned char) inBuffer[i + 1]; + const unsigned char c3 = (unsigned char) inBuffer[i + 2]; + const unsigned char c4 = (unsigned char) inBuffer[i + 3]; + + const utility::stream::size_type n = + std::min(inLength - i, static_cast <utility::stream::size_type>(3)); + + switch (n) + { + default: + case 3: outBuffer[j + 2] = UUDECODE(c3) << 6 | UUDECODE(c4); + case 2: outBuffer[j + 1] = UUDECODE(c2) << 4 | UUDECODE(c3) >> 2; + case 1: outBuffer[j] = UUDECODE(c1) << 2 | UUDECODE(c2) >> 4; + case 0: break; + } + + total += n; + } + + out.write(outBuffer, outLength); + + std::fill(inBuffer, inBuffer + sizeof(inBuffer), 0); + } + + return (total); +} + + +} // vmime diff --git a/src/encoderUUE.hpp b/src/encoderUUE.hpp new file mode 100644 index 00000000..c7784474 --- /dev/null +++ b/src/encoderUUE.hpp @@ -0,0 +1,50 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERUUE_HPP_INCLUDED +#define VMIME_ENCODERUUE_HPP_INCLUDED + + +#include "encoder.hpp" + + +namespace vmime +{ + + +/** UUEncode encoder. + */ + +class encoderUUE : public encoder +{ +public: + + encoderUUE(); + + const utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out); + const utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out); + + const std::vector <string> availableProperties() const; +}; + + +} // vmime + + +#endif // VMIME_ENCODERUUE_HPP_INCLUDED diff --git a/src/encoding.cpp b/src/encoding.cpp new file mode 100644 index 00000000..c797cf18 --- /dev/null +++ b/src/encoding.cpp @@ -0,0 +1,161 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoding.hpp" +#include "encoderFactory.hpp" +#include "contentHandler.hpp" + +#include <algorithm> + + +namespace vmime +{ + + +encoding::encoding() + : m_name(encodingTypes::SEVEN_BIT) +{ +} + + +encoding::encoding(const string& name) + : m_name(toLower(name)) +{ +} + + +encoding::encoding(const encoding& enc) + : component(), m_name(enc.m_name) +{ +} + + +void encoding::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_name = toLower(string(buffer.begin() + position, buffer.begin() + end)); + + if (newPosition) + *newPosition = end; +} + + +void encoding::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + os << m_name; + + if (newLinePos) + *newLinePos = curLinePos + m_name.length(); +} + + +encoder* encoding::getEncoder() const +{ + return (encoderFactory::getInstance()->create(generate())); +} + + +encoding& encoding::operator=(const encoding& source) +{ + m_name = source.m_name; + return (*this); +} + + +encoding& encoding::operator=(const string& name) +{ + m_name = toLower(name); + return (*this); +} + + +const bool encoding::operator==(const encoding& value) const +{ + return (toLower(m_name) == value.m_name); +} + + +const bool encoding::operator!=(const encoding& value) const +{ + return !(*this == value); +} + + +const encoding encoding::decide + (const string::const_iterator begin, const string::const_iterator end) +{ + const string::difference_type length = end - begin; + const string::difference_type count = std::count_if + (begin, end, std::bind2nd(std::less<unsigned char>(), 127)); + + // All is in 7-bit US-ASCII --> 7-bit (or Quoted-Printable...) + if (length == count) + { + // Now, we check if there is any line with more than + // "lineLengthLimits::convenient" characters (7-bit requires that) + string::const_iterator p = begin; + + const string::size_type maxLen = lineLengthLimits::convenient; + string::size_type len = 0; + + for ( ; p != end && len <= maxLen ; ) + { + if (*p == '\n') + { + len = 0; + ++p; + + // May or may not need to be encoded, we don't take + // any risk (avoid problems with SMTP) + if (p != end && *p == '.') + len = maxLen + 1; + } + else + { + ++len; + ++p; + } + } + + if (len > maxLen) + return (encoding(encodingTypes::QUOTED_PRINTABLE)); + else + return (encoding(encodingTypes::SEVEN_BIT)); + } + // Less than 20% non US-ASCII --> Quoted-Printable + else if ((length - count) <= length / 5) + { + return (encoding(encodingTypes::QUOTED_PRINTABLE)); + } + // Otherwise --> Base64 + else + { + return (encoding(encodingTypes::BASE64)); + } +} + + +const encoding encoding::decide(const contentHandler& /* data */) +{ + return (encoding(encodingTypes::BASE64)); +} + + +} // vmime diff --git a/src/encoding.hpp b/src/encoding.hpp new file mode 100644 index 00000000..ca344fff --- /dev/null +++ b/src/encoding.hpp @@ -0,0 +1,87 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODING_HPP_INCLUDED +#define VMIME_ENCODING_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" +#include "encoder.hpp" + + +namespace vmime +{ + + +class contentHandler; + + +/** Content encoding (basic type). + */ + +class encoding : public component +{ +public: + + encoding(); + encoding(const string& name); + encoding(const encoding& enc); + +public: + + const string& name() const { return (m_name); } + string& name() { return (m_name); } + +public: + + encoding& operator=(const encoding& source); + encoding& operator=(const string& name); + + const bool operator==(const encoding& value) const; + const bool operator!=(const encoding& value) const; + + // Decide which encoding to use based on the data + static const encoding decide(const string::const_iterator begin, const string::const_iterator end); + static const encoding decide(const contentHandler& data); + +public: + + // Obtain an encoder/decoder for the current encoding type + encoder* getEncoder() const; + +protected: + + string m_name; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_ENCODING_HPP_INCLUDED diff --git a/src/exception.hpp b/src/exception.hpp new file mode 100644 index 00000000..b7880d31 --- /dev/null +++ b/src/exception.hpp @@ -0,0 +1,558 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_EXCEPTION_HPP_INCLUDED +#define VMIME_EXCEPTION_HPP_INCLUDED + + +#include "config.hpp" +#include "base.hpp" +#include "utility/path.hpp" + + +namespace vmime +{ + + +class exception +{ +protected: + + string m_what; + +public: + + exception(const string& what) : m_what(what) { } + virtual ~exception() { } + + const string what() const throw() { return (m_what); }; +}; + + +namespace exceptions +{ + + +class bad_field_type : public vmime::exception +{ +public: + + bad_field_type() : exception("Bad field type.") {} + ~bad_field_type() throw() {} +}; + + +class charset_conv_error : public vmime::exception +{ +public: + + charset_conv_error() : exception("Charset conversion error.") {} + ~charset_conv_error() throw() {} +}; + + +class no_encoder_available : public vmime::exception +{ +public: + + no_encoder_available() : exception("No encoder available.") {} + ~no_encoder_available() throw() {} +}; + + +class no_such_parameter : public vmime::exception +{ +public: + + no_such_parameter(const string& name) : exception + (string("Parameter not found: '") + name + string("'.")) {} + ~no_such_parameter() throw() {} +}; + + +class no_such_field : public vmime::exception +{ +public: + + no_such_field() : exception("Field not found.") {} + ~no_such_field() throw() {} +}; + + +class open_file_error : public vmime::exception +{ +public: + + open_file_error() : exception("Error opening file.") {} + ~open_file_error() throw() {} +}; + + +class no_factory_available : public vmime::exception +{ +public: + + no_factory_available() : exception("No factory available.") {} + ~no_factory_available() throw() {} +}; + + +class no_platform_dependant_handler : public vmime::exception +{ +public: + + no_platform_dependant_handler() : exception("No platform-dependant handler installed.") {} + ~no_platform_dependant_handler() throw() {} +}; + + +class no_expeditor : public vmime::exception +{ +public: + + no_expeditor() : exception("No expeditor specified.") {} + ~no_expeditor() throw() {} +}; + + +class no_recipient : public vmime::exception +{ +public: + + no_recipient() : exception("No recipient specified.") {} + ~no_recipient() throw() {} +}; + + +class no_object_found : public vmime::exception +{ +public: + + no_object_found() : exception("No object found.") {} + ~no_object_found() throw() {} +}; + + +// There is no property with that name in the set. + +class no_such_property : public vmime::exception +{ +public: + + no_such_property(const string& name) : exception + (std::string("No such property: '") + name + string("'.")) { } + ~no_such_property() throw() {} +}; + + +// Bad type specified when reading property. + +class invalid_property_type : public vmime::exception +{ +public: + + invalid_property_type() : exception("Invalid property type.") {} + ~invalid_property_type() throw() {} +}; + + +// Bad argument was passed to the function. + +class invalid_argument : public vmime::exception +{ +public: + + invalid_argument() : exception("Invalid argument.") {} + ~invalid_argument() throw() {} +}; + + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +/** Base class for exceptions thrown by the messaging module. + */ + +class messaging_exception : public vmime::exception +{ +public: + + messaging_exception(const string& what) : exception(what) {} + ~messaging_exception() throw() {} +}; + + +/** Error while connecting to the server: this may be a DNS resolution error + * or a connection error (for example, time-out while connecting). + */ + +class connection_error : public messaging_exception +{ +public: + + connection_error() : messaging_exception("Connection error.") {} + ~connection_error() throw() {} +}; + + +/** Server did not initiated the connection correctly. + */ + +class connection_greeting_error : public messaging_exception +{ +public: + + connection_greeting_error(const string& response) + : messaging_exception("Greeting error."), m_response(response) {} + ~connection_greeting_error() throw() {} + + const string& response() const { return (m_response); } + +private: + + string m_response; +}; + + +/** Error while giving credentials to the server (wrong username + * or password, or wrong authentication method). + */ + +class authentication_error : public messaging_exception +{ +public: + + authentication_error(const string& response) + : messaging_exception("Authentication error."), m_response(response) {} + ~authentication_error() throw() {} + + const string& response() const { return (m_response); } + +private: + + string m_response; +}; + + +/** Option not supported. + */ + +class unsupported_option : public messaging_exception +{ +public: + + unsupported_option() : messaging_exception("Unsupported option.") {} + ~unsupported_option() throw() {} +}; + + +/** No service available for this protocol. + */ + +class no_service_available : public messaging_exception +{ +public: + + no_service_available() : messaging_exception("No service available for this protocol.") {} + ~no_service_available() throw() {} +}; + + +/** The current state of the object does not permit to execute the + * operation (for example, you try to close a folder which is not open). + */ + +class illegal_state : public messaging_exception +{ +public: + + illegal_state(const string& state) + : messaging_exception("Illegal state to accomplish the operation: '" + state + "'.") {} + ~illegal_state() throw() {} +}; + + +/** Folder not found (does not exist). + */ + +class folder_not_found : public messaging_exception +{ +public: + + folder_not_found() : messaging_exception("Folder not found.") {} + ~folder_not_found() throw() {} +}; + + +/** Message not found (does not exist). + */ + +class message_not_found : public messaging_exception +{ +public: + + message_not_found() : messaging_exception("Message not found.") {} + ~message_not_found() throw() {} +}; + + +/** Operation not supported by the underlying protocol. + */ + +class operation_not_supported : public messaging_exception +{ +public: + + operation_not_supported() : messaging_exception("Operation not supported.") {} + ~operation_not_supported() throw() {} +}; + + +/** The operation timed out (time-out delay is elapsed). + */ + +class operation_timed_out : public messaging_exception +{ +public: + + operation_timed_out() : messaging_exception("Operation timed out.") {} + ~operation_timed_out() throw() {} +}; + + +/** The operation has been cancelled. + */ + +class operation_cancelled : public messaging_exception +{ +public: + + operation_cancelled() : messaging_exception("Operation cancelled by the user.") {} + ~operation_cancelled() throw() {} +}; + + +/** Must call fetchMessage() or fetchHeader() before accessing + * the requested object. + */ + +class unfetched_object : public messaging_exception +{ +public: + + unfetched_object() : messaging_exception("Object not fetched.") {} + ~unfetched_object() throw() {} +}; + + +/** The service is not currently connected. + */ + +class not_connected : public messaging_exception +{ +public: + + not_connected() : messaging_exception("Not connected to a service.") {} + ~not_connected() throw() {} +}; + + +/** The service is already connected (must disconnect before). + */ + +class already_connected : public messaging_exception +{ +public: + + already_connected() : messaging_exception("Already connected to a service. Disconnect and retry.") {} + ~already_connected() throw() {} +}; + + +/** Command error: operation failed (this is specific to the underlying protocol). + */ + +class command_error : public messaging_exception +{ +public: + + command_error(const string& command, const string& response, const string& desc = "") + : messaging_exception(desc.empty() + ? "Error while executing command '" + command + "'." + : "Error while executing command '" + command + "': " + desc + "." + ), + m_command(command), m_response(response) {} + ~command_error() throw() {} + + /** Return the name of the command which have thrown the exception. + * This is protocol-dependant. + * + * @return command name (protocol-dependant) + */ + const string& command() const { return (m_command); } + + /** Return the invalid response line. + * The meaning is protocol-dependant. + * + * @return response line (protocol-dependant) + */ + const string& response() const { return (m_response); } + +private: + + string m_command; + string m_response; +}; + + +/** The server returned an invalid response. + */ + +class invalid_response : public messaging_exception +{ +public: + + invalid_response(const string& command, const string& response) + : messaging_exception(command.empty() + ? "Received invalid response." + : "Received invalid response for command '" + command + "'." + ), + m_command(command), m_response(response) {} + ~invalid_response() throw() {} + + /** Return the name of the command which have thrown the exception. + * This is protocol-dependant. + * + * @return command name (protocol-dependant) + */ + const string& command() const { return (m_command); } + + /** Return the invalid response line. + * The meaning is protocol-dependant. + * + * @return response line (protocol-dependant) + */ + const string& response() const { return (m_response); } + +private: + + string m_command; + string m_response; +}; + + +/** Partial fetch is not supported by the underlying protocol. + */ + +class partial_fetch_not_supported : public messaging_exception +{ +public: + + partial_fetch_not_supported() : messaging_exception("Partial fetch not supported.") {} + ~partial_fetch_not_supported() throw() {} +}; + + +/** The URL is malformed. + */ + +class malformed_url : public messaging_exception +{ +public: + + malformed_url(const string& error) : messaging_exception("Malformed URL: " + error + ".") {} + ~malformed_url() throw() {} +}; + + +/** Folder name is invalid. + */ + +class invalid_folder_name : public messaging_exception +{ +public: + + invalid_folder_name(const string& error) : messaging_exception("Invalid folder name: " + error + ".") {} + ~invalid_folder_name() throw() {} +}; + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + + +#if VMIME_HAVE_FILESYSTEM_FEATURES + + +/** Base class for exceptions thrown by the filesystem features. + */ + +class filesystem_exception : public vmime::exception +{ +public: + + filesystem_exception(const string& what, const utility::path& path) : exception(what), m_path(path) {} + ~filesystem_exception() throw() {} + + /** Return the full path of the file have thrown the exception. + * + * @return full path of the file/directory + */ + const utility::path& path() const { return (m_path); } + +private: + + const utility::path m_path; +}; + + +/** File is not a directory. + */ + +class not_a_directory : public filesystem_exception +{ +public: + + not_a_directory(const utility::path& path) : filesystem_exception("Operation failed: this is not a directory.", path) {} + ~not_a_directory() throw() {} +}; + + +/** File not found. + */ + +class file_not_found : public filesystem_exception +{ +public: + + file_not_found(const utility::path& path) : filesystem_exception("File not found.", path) {} + ~file_not_found() throw() {} +}; + + +#endif // VMIME_HAVE_FILESYSTEM_FEATURES + + +} // exceptions + + +} // vmime + + +#endif // VMIME_EXCEPTION_HPP_INCLUDED diff --git a/src/fileAttachment.cpp b/src/fileAttachment.cpp new file mode 100644 index 00000000..ca7ff789 --- /dev/null +++ b/src/fileAttachment.cpp @@ -0,0 +1,123 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include <fstream> +#include <sstream> + +#include "fileAttachment.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +fileAttachment::fileAttachment(const string& filename, const mediaType& type, const text& desc) +{ + m_type = type; + m_desc = desc; + + setData(filename); + + m_encoding = encoding::decide(m_data); +} + + +fileAttachment::fileAttachment(const string& filename, const mediaType& type, + const class encoding& enc, const text& desc) +{ + m_type = type; + m_desc = desc; + + setData(filename); + + m_encoding = enc; +} + + +void fileAttachment::setData(const string& filename) +{ + std::ifstream* file = new std::ifstream(); + file->open(filename.c_str(), std::ios::in | std::ios::binary); + + if (!*file) + { + delete (file); + throw exceptions::open_file_error(); + } + + m_data.set(new utility::inputStreamPointerAdapter(file, true), 0, true); +} + + +void fileAttachment::generatePart(bodyPart& part) const +{ + defaultAttachment::generatePart(part); + + contentDispositionField& cdf = part.header().fields.ContentDisposition(); + + if (m_fileInfo.hasSize()) cdf.size() = toString(m_fileInfo.getSize()); + if (m_fileInfo.hasFilename()) cdf.filename() = m_fileInfo.getFilename(); + if (m_fileInfo.hasCreationDate()) cdf.creationDate() = m_fileInfo.getCreationDate(); + if (m_fileInfo.hasModificationDate()) cdf.modificationDate() = m_fileInfo.getModificationDate(); + if (m_fileInfo.hasReadDate()) cdf.readDate() = m_fileInfo.getReadDate(); +} + + +// +// fileAttachment::fileInfo +// + +fileAttachment::fileInfo::fileInfo() + : m_filename(NULL), m_size(NULL), m_creationDate(NULL), m_modifDate(NULL), m_readDate(NULL) +{ +} + + +fileAttachment::fileInfo::~fileInfo() +{ + delete (m_filename); + delete (m_size); + delete (m_creationDate); + delete (m_modifDate); + delete (m_readDate); +} + +const bool fileAttachment::fileInfo::hasFilename() const { return (m_filename != NULL); } +const string& fileAttachment::fileInfo::getFilename() const { return (*m_filename); } +void fileAttachment::fileInfo::setFilename(const string& name) { if (m_filename) { *m_filename = name; } else { m_filename = new string(name); } } + +const bool fileAttachment::fileInfo::hasCreationDate() const { return (m_creationDate != NULL); } +const datetime& fileAttachment::fileInfo::getCreationDate() const { return (*m_creationDate); } +void fileAttachment::fileInfo::setCreationDate(const datetime& date) { if (m_creationDate) { *m_creationDate = date; } else { m_creationDate = new datetime(date); } } + +const bool fileAttachment::fileInfo::hasModificationDate() const { return (m_modifDate != NULL); } +const datetime& fileAttachment::fileInfo::getModificationDate() const { return (*m_modifDate); } +void fileAttachment::fileInfo::setModificationDate(const datetime& date) { if (m_modifDate) { *m_modifDate = date; } else { m_modifDate = new datetime(date); } } + +const bool fileAttachment::fileInfo::hasReadDate() const { return (m_readDate != NULL); } +const datetime& fileAttachment::fileInfo::getReadDate() const { return (*m_readDate); } +void fileAttachment::fileInfo::setReadDate(const datetime& date) { if (m_readDate) { *m_readDate = date; } else { m_readDate = new datetime(date); } } + +const bool fileAttachment::fileInfo::hasSize() const { return (m_size != NULL); } +const unsigned int fileAttachment::fileInfo::getSize() const { return (*m_size); } +void fileAttachment::fileInfo::setSize(const unsigned int& size) { if (m_size) { *m_size = size; } else { m_size = new unsigned int(size); } } + + +} // vmime diff --git a/src/fileAttachment.hpp b/src/fileAttachment.hpp new file mode 100644 index 00000000..66cb258d --- /dev/null +++ b/src/fileAttachment.hpp @@ -0,0 +1,90 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_FILEATTACHMENT_HPP_INCLUDED +#define VMIME_FILEATTACHMENT_HPP_INCLUDED + + +#include "defaultAttachment.hpp" + + +namespace vmime +{ + + +class fileAttachment : public defaultAttachment +{ +public: + + fileAttachment(const string& filename, const mediaType& type, const text& desc = NULL_TEXT); + fileAttachment(const string& filename, const mediaType& type, const class encoding& enc, const text& desc = NULL_TEXT); + + class fileInfo + { + public: + + fileInfo(); + ~fileInfo(); + + const bool hasFilename() const; + const string& getFilename() const; + void setFilename(const string& name); + + const bool hasCreationDate() const; + const datetime& getCreationDate() const; + void setCreationDate(const datetime& date); + + const bool hasModificationDate() const; + const datetime& getModificationDate() const; + void setModificationDate(const datetime& date); + + const bool hasReadDate() const; + const datetime& getReadDate() const; + void setReadDate(const datetime& date); + + const bool hasSize() const; + const unsigned int getSize() const; + void setSize(const unsigned int& size); + + protected: + + string* m_filename; + unsigned int* m_size; + datetime* m_creationDate; + datetime* m_modifDate; + datetime* m_readDate; + }; + + const class fileInfo& fileInfo() const { return (m_fileInfo); } + class fileInfo& fileInfo() { return (m_fileInfo); } + +protected: + + void setData(const string& filename); + + class fileInfo m_fileInfo; + + void generatePart(bodyPart& part) const; +}; + + +} // vmime + + +#endif // VMIME_FILEATTACHMENT_HPP_INCLUDED diff --git a/src/header.cpp b/src/header.cpp new file mode 100644 index 00000000..f7745360 --- /dev/null +++ b/src/header.cpp @@ -0,0 +1,452 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "header.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +header::header() +{ +} + + +header::~header() +{ +} + + +/* + + RFC #822: + 3.2. HEADER FIELD DEFINITIONS + +field = field-name ":" [ field-body ] CRLF + +field-name = 1*<any CHAR, excluding CTLs, SPACE, and ":"> + +field-body = field-body-contents + [CRLF LWSP-char field-body] + +field-body-contents = + <the ASCII characters making up the field-body, as + defined in the following sections, and consisting + of combinations of atom, quoted-string, and + specials tokens, or else consisting of texts> +*/ + +void header::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + string::size_type pos = position; + + fields.clear(); + + while (pos < end) + { + char_t c = buffer[pos]; + + // Check for end of headers (empty line): although RFC-822 recommends + // to use CRLF for header/body separator (see 4.1 SYNTAX), here, we + // also check for LF just in case... + if (c == '\n') + { + ++pos; + break; + } + else if (c == '\r' && pos + 1 < end && buffer[pos + 1] == '\n') + { + pos += 2; + break; + } + + // This line may be a field description + if (!isspace(c)) + { + const string::size_type nameStart = pos; // remember the start position of the line + + while (pos < end && (buffer[pos] != ':' && !isspace(buffer[pos]))) + ++pos; + + const string::size_type nameEnd = pos; + + while (pos < end && isspace(buffer[pos])) + ++pos; + + if (buffer[pos] != ':') + { + // Humm...does not seem to be a valid header line. + // Skip this error and advance to the next line + pos = nameStart; + + while (pos < end && buffer[pos] != '\n') + ++pos; + + if (buffer[pos] == '\n') + ++pos; + } + else + { + // Extract the field name + const string name(buffer.begin() + nameStart, + buffer.begin() + nameEnd); + + // Skip ':' character + ++pos; + + // Skip spaces between ':' and the field contents + while (pos < end && (buffer[pos] == ' ' || buffer[pos] == '\t')) + ++pos; + + // Extract the field value + string contents; + + while (pos < end) + { + c = buffer[pos]; + + // Check for end of contents + if (c == '\r' && pos + 1 < end && buffer[pos + 1] == '\n') + { + pos += 2; + break; + } + else if (c == '\n') + { + ++pos; + break; + } + + const string::size_type ctsStart = pos; + string::size_type ctsEnd = pos; + + while (pos < end) + { + c = buffer[pos]; + + // Check for end of line + if (c == '\r' && pos + 1 < end && buffer[pos + 1] == '\n') + { + ctsEnd = pos; + pos += 2; + break; + } + else if (c == '\n') + { + ctsEnd = pos; + ++pos; + break; + } + + ++pos; + } + + if (ctsEnd != ctsStart) + { + // Append this line to contents + contents.append(buffer.begin() + ctsStart, + buffer.begin() + ctsEnd); + } + + // Handle the case of folded lines + if (buffer[pos] == ' ' || buffer[pos] == '\t') + { + // This is a folding white-space: we keep it as is and + // we continue with contents parsing... + } + else + { + // End of this field + break; + } + } + + // Add a new field to list + fields.m_fields.push_back(headerFieldFactory::getInstance()-> + create(headerField::nameToType(name), name, contents)); + } + } + else + { + // Skip this error and advance to the next line + while (pos < end && buffer[pos] != '\n') + ++pos; + + if (buffer[pos] == '\n') + ++pos; + } + } + + // If we have found the header/body separator, skip it + if (pos < end) + { + if (buffer[pos] == '\n') + { + // This is a LF (illegal but...) + ++pos; + } + else if (buffer[pos] == '\r' && pos + 1 < end) + { + // This is a CRLF + pos += 2; + } + } + + if (newPosition) + *newPosition = pos; +} + + +void header::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type /* curLinePos */, string::size_type* newLinePos) const +{ + // Generate the fields + for (std::vector <headerField*>::const_iterator + it = fields.m_fields.begin() ; it != fields.m_fields.end() ; ++it) + { + (*it)->generate(os, maxLineLength); + os << CRLF; + } + + if (newLinePos) + *newLinePos = 0; +} + + +header& header::operator=(const header& h) +{ + fields = h.fields; + + return (*this); +} + + + +////////////////////// +// Fields container // +////////////////////// + + +header::fieldsContainer::fieldsContainer() +{ +} + + +header::fieldsContainer::~fieldsContainer() +{ + for (std::vector <headerField*>::iterator i = m_fields.begin() ; i != m_fields.end() ; ++i) + delete (*i); +} + + +// Checks whether (at least) one field with this type/name exists +const bool header::fieldsContainer::has(const headerField::Types fieldType) const +{ + std::vector <headerField*>::const_iterator pos = m_fields.begin(); + const std::vector <headerField*>::const_iterator end = m_fields.end(); + + for ( ; pos != end && (*pos)->type() != fieldType ; ++pos); + + return (pos != end); +} + + +const bool header::fieldsContainer::has(const string& fieldName) const +{ + headerField::Types type = headerField::nameToType(fieldName); + if (type != headerField::Custom) return (has(type)); + + const string name = toLower(fieldName); + + std::vector <headerField*>::const_iterator pos = m_fields.begin(); + const std::vector <headerField*>::const_iterator end = m_fields.end(); + + for ( ; pos != end && toLower((*pos)->name()) != name ; ++pos); + + return (pos != end); +} + + +// Find the first field that matches the specified type/name. +// If no field is found, an exception is thrown. +headerField& header::fieldsContainer::find(const headerField::Types fieldType) const +{ + // Find the first field that matches the specified type + std::vector <headerField*>::const_iterator pos = m_fields.begin(); + const std::vector <headerField*>::const_iterator end = m_fields.end(); + + for ( ; pos != end && (*pos)->type() != fieldType ; ++pos); + + // No field with this type can be found + if (pos == end) + { + throw exceptions::no_such_field(); + } + // Else, return a reference to the existing field + else + { + return (**pos); + } +} + + +headerField& header::fieldsContainer::find(const string& fieldName) const +{ + headerField::Types type = headerField::nameToType(fieldName); + if (type != headerField::Custom) return (find(type)); + + const string name = toLower(fieldName); + + // Find the first field that matches the specified name + std::vector <headerField*>::const_iterator pos = m_fields.begin(); + const std::vector <headerField*>::const_iterator end = m_fields.end(); + + for ( ; pos != end && toLower((*pos)->name()) != name ; ++pos); + + // No field with this name can be found + if (pos == end) + { + throw exceptions::no_such_field(); + } + // Else, return a reference to the existing field + else + { + return (**pos); + } +} + + +// Find the first field that matches the specified type/name +headerField& header::fieldsContainer::get(const headerField::Types fieldType) +{ + // Find the first field that matches the specified type + std::vector <headerField*>::const_iterator pos = m_fields.begin(); + const std::vector <headerField*>::const_iterator end = m_fields.end(); + + for ( ; pos != end && (*pos)->type() != fieldType ; ++pos); + + // If no field with this type can be found, create a new one + if (pos == end) + { + headerField* field = headerFieldFactory::getInstance()->create(fieldType); + insertSorted(field); + + // Return a reference to the new field + return (*field); + } + // Else, return a reference to the existing field + else + { + return (**pos); + } +} + + +headerField& header::fieldsContainer::get(const string& fieldName) +{ + headerField::Types type = headerField::nameToType(fieldName); + if (type != headerField::Custom) return (get(type)); + + const string name = toLower(fieldName); + + // Find the first field that matches the specified name + std::vector <headerField*>::const_iterator pos = m_fields.begin(); + const std::vector <headerField*>::const_iterator end = m_fields.end(); + + for ( ; pos != end && toLower((*pos)->name()) != name ; ++pos); + + // If no field with this name can be found, create a new one + if (pos == end) + { + headerField* field = headerFieldFactory::getInstance()->create(fieldName); + insertSorted(field); + + // Return a reference to the new field + return (*field); + } + // Else, return a reference to the existing field + else + { + return (**pos); + } +} + + +void header::fieldsContainer::insertSorted(headerField* field) +{ + const headerField::Types type = field->type(); + std::vector <headerField*>::iterator i; + + for (i = m_fields.begin() ; (i != m_fields.end()) && ((*i)->type() < type) ; ++i); + + m_fields.insert(i, field); +} + + +// Field insertion +void header::fieldsContainer::append(const headerField& field) +{ + m_fields.push_back(field.clone()); +} + + +void header::fieldsContainer::insert(const iterator it, const headerField& field) +{ + m_fields.insert(it.m_iterator, field.clone()); +} + + +// Field removing +void header::fieldsContainer::remove(const iterator it) +{ + delete (*it.m_iterator); + m_fields.erase(it.m_iterator); +} + + +void header::fieldsContainer::clear() +{ + for (std::vector <headerField*>::iterator it = m_fields.begin() ; it != m_fields.end() ; ++it) + delete (*it); + + m_fields.clear(); +} + + +header::fieldsContainer& header::fieldsContainer::operator=(const fieldsContainer& c) +{ + std::vector <headerField*> fields; + + for (std::vector <headerField*>::const_iterator it = c.m_fields.begin() ; it != c.m_fields.end() ; ++it) + fields.push_back((*it)->clone()); + + for (std::vector <headerField*>::iterator it = m_fields.begin() ; it != m_fields.end() ; ++it) + delete (*it); + + m_fields.resize(fields.size()); + std::copy(fields.begin(), fields.end(), m_fields.begin()); + + return (*this); +} + + +} // vmime diff --git a/src/header.hpp b/src/header.hpp new file mode 100644 index 00000000..8ce070b9 --- /dev/null +++ b/src/header.hpp @@ -0,0 +1,266 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_HEADER_HPP_INCLUDED +#define VMIME_HEADER_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" +#include "exception.hpp" + +#include "headerField.hpp" +#include "headerFieldFactory.hpp" + +#include "addressListField.hpp" +#include "mailboxListField.hpp" +#include "mailboxField.hpp" +#include "textField.hpp" +#include "dateField.hpp" +#include "contentTypeField.hpp" +#include "contentEncodingField.hpp" +#include "defaultField.hpp" +#include "contentDispositionField.hpp" +#include "messageIdField.hpp" + + +namespace vmime +{ + + +class bodyPart; + + +/** Header section of a MIME part. + */ + +class header : public component +{ + friend class bodyPart; + friend class body; + friend class message; + +protected: + + header(); + ~header(); + +public: + + // A sub-class for field manipulation + class fieldsContainer + { + friend class header; + + protected: + + fieldsContainer(); + ~fieldsContainer(); + + public: + + // Field access + mailboxField& From() { return (dynamic_cast<mailboxField&>(get(headerField::From))); } + mailboxField& Sender() { return (dynamic_cast<mailboxField&>(get(headerField::Sender))); } + mailboxField& ReplyTo() { return (dynamic_cast<mailboxField&>(get(headerField::ReplyTo))); } + mailboxField& DeliveredTo() { return (dynamic_cast<mailboxField&>(get(headerField::DeliveredTo))); } + addressListField& To() { return (dynamic_cast<addressListField&>(get(headerField::To))); } + addressListField& Cc() { return (dynamic_cast<addressListField&>(get(headerField::Cc))); } + addressListField& Bcc() { return (dynamic_cast<addressListField&>(get(headerField::Bcc))); } + dateField& Date() { return (dynamic_cast<dateField&>(get(headerField::Date))); } + textField& Subject() { return (dynamic_cast<textField&>(get(headerField::Subject))); } + textField& Organization() { return (dynamic_cast<textField&>(get(headerField::Organization))); } + textField& UserAgent() { return (dynamic_cast<textField&>(get(headerField::UserAgent))); } + contentTypeField& ContentType() { return (dynamic_cast<contentTypeField&>(get(headerField::ContentType))); } + textField& ContentDescription() { return (dynamic_cast<textField&>(get(headerField::ContentDescription))); } + contentEncodingField& ContentTransferEncoding() { return (dynamic_cast<contentEncodingField&>(get(headerField::ContentTransferEncoding))); } + defaultField& MimeVersion() { return (dynamic_cast<defaultField&>(get(headerField::MimeVersion))); } + contentDispositionField& ContentDisposition() { return (dynamic_cast<contentDispositionField&>(get(headerField::ContentDisposition))); } + messageIdField& ContentId() { return (dynamic_cast<messageIdField&>(get(headerField::ContentId))); } + messageIdField& MessageId() { return (dynamic_cast<messageIdField&>(get(headerField::MessageId))); } + defaultField& ContentLocation() { return (dynamic_cast<defaultField&>(get(headerField::ContentLocation))); } + + const mailboxField& From() const { return (dynamic_cast<mailboxField&>(find(headerField::From))); } + const mailboxField& Sender() const { return (dynamic_cast<mailboxField&>(find(headerField::Sender))); } + const mailboxField& ReplyTo() const { return (dynamic_cast<mailboxField&>(find(headerField::ReplyTo))); } + const mailboxField& DeliveredTo() const { return (dynamic_cast<mailboxField&>(find(headerField::DeliveredTo))); } + const addressListField& To() const { return (dynamic_cast<addressListField&>(find(headerField::To))); } + const addressListField& Cc() const { return (dynamic_cast<addressListField&>(find(headerField::Cc))); } + const addressListField& Bcc() const { return (dynamic_cast<addressListField&>(find(headerField::Bcc))); } + const dateField& Date() const { return (dynamic_cast<dateField&>(find(headerField::Date))); } + const textField& Subject() const { return (dynamic_cast<textField&>(find(headerField::Subject))); } + const textField& Organization() const { return (dynamic_cast<textField&>(find(headerField::Organization))); } + const textField& UserAgent() const { return (dynamic_cast<textField&>(find(headerField::UserAgent))); } + const contentTypeField& ContentType() const { return (dynamic_cast<contentTypeField&>(find(headerField::ContentType))); } + const textField& ContentDescription() const { return (dynamic_cast<textField&>(find(headerField::ContentDescription))); } + const contentEncodingField& ContentTransferEncoding() const { return (dynamic_cast<contentEncodingField&>(find(headerField::ContentTransferEncoding))); } + const defaultField& MimeVersion() const { return (dynamic_cast<defaultField&>(find(headerField::MimeVersion))); } + const contentDispositionField& ContentDisposition() const { return (dynamic_cast<contentDispositionField&>(find(headerField::ContentDisposition))); } + const messageIdField& ContentId() const { return (dynamic_cast<messageIdField&>(find(headerField::ContentId))); } + const messageIdField& MessageId() const { return (dynamic_cast<messageIdField&>(find(headerField::MessageId))); } + const defaultField& ContentLocation() const { return (dynamic_cast<defaultField&>(find(headerField::ContentLocation))); } + + // Checks whether (at least) one field with this type/name exists + const bool has(const headerField::Types fieldType) const; + const bool has(const string& fieldName) const; + + // Find the first field that matches the specified type/name. + // If no field is found, an exception is thrown. + headerField& find(const headerField::Types fieldType) const; + headerField& find(const string& fieldName) const; + + // Find the first field that matches the specified type/name. + // If no field is found, one will be created. + headerField& get(const headerField::Types fieldType); + headerField& get(const string& fieldName); + + // Field iterator + class const_iterator; + + class iterator + { + friend class header::fieldsContainer::const_iterator; + friend class header::fieldsContainer; + + public: + + typedef std::vector <headerField*>::iterator::difference_type difference_type; + + iterator(std::vector <headerField*>::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + headerField& operator*() const { return (**m_iterator); } + headerField* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator operator++(int) { iterator i(*this); ++m_iterator; return (i); } + + iterator& operator--() { --m_iterator; return (*this); } + iterator operator--(int) { iterator i(*this); --m_iterator; return (i); } + + iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + iterator operator-(difference_type x) const { return iterator(m_iterator - x); } + + headerField& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector <headerField*>::iterator m_iterator; + }; + + class const_iterator + { + public: + + typedef std::vector <headerField*>::const_iterator::difference_type difference_type; + + const_iterator(std::vector <headerField*>::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const headerField& operator*() const { return (**m_iterator); } + const headerField* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator operator++(int) { const_iterator i(*this); ++m_iterator; return (i); } + + const_iterator& operator--() { --m_iterator; return (*this); } + const_iterator operator--(int) { const_iterator i(*this); --m_iterator; return (i); } + + const_iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + const_iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + const_iterator operator-(difference_type x) const { return const_iterator(m_iterator - x); } + + const headerField& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector <headerField*>::const_iterator m_iterator; + }; + + public: + + iterator begin() { return (m_fields.begin()); } + iterator end() { return (m_fields.end()); } + + const_iterator begin() const { return (const_iterator(m_fields.begin())); } + const_iterator end() const { return (const_iterator(m_fields.end())); } + + // Field insertion + void append(const headerField& field); + void insert(const iterator it, const headerField& field); + + // Field removing + void remove(const iterator it); + void clear(); + + // Field count + const std::vector <headerField*>::size_type count() const { return (m_fields.size()); } + const std::vector <headerField*>::size_type size() const { return (m_fields.size()); } + const bool empty() const { return (m_fields.empty()); } + + headerField& front() { return (*m_fields.front()); } + const headerField& front() const { return (*m_fields.front()); } + headerField& back() { return (*m_fields.back()); } + const headerField& back() const { return (*m_fields.back()); } + + fieldsContainer& operator=(const fieldsContainer& c); + + protected: + + void insertSorted(headerField* field); + + std::vector <headerField*> m_fields; + + } fields; + + typedef fieldsContainer::iterator iterator; + typedef fieldsContainer::const_iterator const_iterator; + + header& operator=(const header& h); + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_HEADER_HPP_INCLUDED diff --git a/src/headerField.cpp b/src/headerField.cpp new file mode 100644 index 00000000..20f90251 --- /dev/null +++ b/src/headerField.cpp @@ -0,0 +1,266 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "headerField.hpp" +#include "headerFieldFactory.hpp" + + +namespace vmime +{ + + +headerField::headerField() + : m_type(Custom), m_name("Undefined") +{ +} + + +headerField::headerField(const string& fieldName) + : m_type(Custom), m_name(fieldName) +{ +} + + +headerField::~headerField() +{ +} + + +headerField* headerField::clone() const +{ + headerField* field = NULL; + + if (m_type == Custom) + field = headerFieldFactory::getInstance()->create(m_name); + else + field = headerFieldFactory::getInstance()->create(m_type); + + field->copyFrom(*this); + + return (field); +} + + +const bool headerField::operator<(const headerField& field) const +{ + return (m_type < field.m_type); +} + + +headerField& headerField::operator=(const headerField& field) +{ + copyFrom(field); + return (*this); +} + + +void headerField::copyFrom(const headerField& field) +{ + m_type = field.m_type; + m_name = field.m_name; +} + + +void headerField::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + if (m_type == Custom) + { + os << m_name + ": "; + + if (newLinePos) + *newLinePos = curLinePos + m_name.length() + 2; + } + else + { + const string name = typeToName(m_type); + + os << name + ": "; + + if (newLinePos) + *newLinePos = curLinePos + name.length() + 2; + } +} + + +/** Return the field type corresponding to the specified name. + * + * @param name field name + * @return field type (see headerField::Types) or headerField::custom + * if this is a custom field + */ + +const headerField::Types headerField::nameToType(const string& name) +{ + switch (name[0]) + { + case 'B': + case 'b': + { + if (isStringEqualNoCase(name, "bcc", 3)) return (Bcc); + break; + } + case 'C': + case 'c': + { + if (isStringEqualNoCase(name, "cc", 2)) return (Cc); + else if (isStringEqualNoCase(name, "content-type", 12)) return (ContentType); + else if (isStringEqualNoCase(name, "content-transfer-encoding", 25)) return (ContentTransferEncoding); + else if (isStringEqualNoCase(name, "content-description", 19)) return (ContentDescription); + else if (isStringEqualNoCase(name, "content-disposition", 19)) return (ContentDisposition); + else if (isStringEqualNoCase(name, "content-id", 10)) return (ContentId); + else if (isStringEqualNoCase(name, "content-location", 16)) return (ContentLocation); + break; + } + case 'd': + case 'D': + { + if (isStringEqualNoCase(name, "date", 4)) return (Date); + else if (isStringEqualNoCase(name, "delivered-to", 12)) return (DeliveredTo); + break; + } + case 'f': + case 'F': + { + if (isStringEqualNoCase(name, "from", 4)) return (From); + break; + } + case 'm': + case 'M': + { + if (isStringEqualNoCase(name, "mime-version", 12)) return (MimeVersion); + else if (isStringEqualNoCase(name, "message-id", 10)) return (MessageId); + break; + } + case 'o': + case 'O': + { + if (isStringEqualNoCase(name, "organization", 12)) return (Organization); + break; + } + case 'r': + case 'R': + { + if (isStringEqualNoCase(name, "received", 8)) return (Received); + else if (isStringEqualNoCase(name, "reply-to", 8)) return (ReplyTo); + else if (isStringEqualNoCase(name, "return-path", 11)) return (ReturnPath); + break; + } + case 's': + case 'S': + { + if (isStringEqualNoCase(name, "sender", 6)) return (Sender); + else if (isStringEqualNoCase(name, "subject", 7)) return (Subject); + break; + } + case 't': + case 'T': + { + if (isStringEqualNoCase(name, "to", 2)) return (To); + break; + } + case 'u': + case 'U': + { + if (isStringEqualNoCase(name, "user-agent", 10)) return (UserAgent); + break; + } + + } + + return (Custom); +} + + +/** Return the name for the specified field type. + * Eg: returns "From" for headerField::From. + * + * @param type field type + * @return name for the specified field type + */ + +const string headerField::typeToName(const Types type) +{ + switch (type) + { + case From: return "From"; + case Sender: return "Sender"; + case To: return "To"; + case Cc: return "Cc"; + case Bcc: return "Bcc"; + case Date: return "Date"; + case Received: return "Received"; + case Subject: return "Subject"; + case ReplyTo: return "Reply-To"; + case Organization: return "Organization"; + case DeliveredTo: return "Delivered-To"; + case UserAgent: return "User-Agent"; + case ReturnPath: return "Return-Path"; + case ContentType: return "Content-Type"; + case ContentTransferEncoding: return "Content-Transfer-Encoding"; + case ContentDescription: return "Content-Description"; + case MimeVersion: return "Mime-Version"; + case ContentDisposition: return "Content-Disposition"; + case ContentId: return "Content-Id"; + case MessageId: return "Message-Id"; + case ContentLocation: return "Content-Location"; + + case Custom: + case Last: + return "?"; + }; + + return "?"; +} + + +/** Return the type of this field. + * + * @return field type (see headerField::Types) + */ + +const headerField::Types headerField::type() const +{ + return (m_type); +} + + +/** Return the name of this field. + * + * @return field name + */ + +const string headerField::name() const +{ + return ((m_type == Custom) ? m_name : typeToName(m_type)); +} + + +/** Check whether this field is a custom field. + * + * @return true if the field is a custom field, false otherwise + */ + +const bool headerField::isCustom() const +{ + return (m_type == Custom); +} + + +} // vmime diff --git a/src/headerField.hpp b/src/headerField.hpp new file mode 100644 index 00000000..70a74eff --- /dev/null +++ b/src/headerField.hpp @@ -0,0 +1,113 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_HEADERFIELD_HPP_INCLUDED +#define VMIME_HEADERFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Base class for header fields. + */ + +class headerField : public component +{ + friend class headerFieldFactory; + +protected: + + headerField(); + headerField(const string& fieldName); + +public: + + ~headerField(); + +public: + + // Header field types (in the order in which they will appear + // in the message header) + enum Types + { + Received, // Relay + From, // Expeditor + Sender, // Sender + ReplyTo, // Reply-To + To, // Recipient(s) + Cc, // Carbon copy recipient(s) + Bcc, // Blind carbon-copy recipient(s) + Date, // Date sent + Subject, // Subject + Organization, // Organization + UserAgent, // User agent + DeliveredTo, // Delivered-To + ReturnPath, // Return-Path + MimeVersion, // Mime-Version + MessageId, // Message-Id + ContentType, // Content-Type + ContentTransferEncoding, // Content-Transfer-Encoding + ContentDescription, // Content-Description + ContentDisposition, // Content-Disposition + ContentId, // Content-Id + ContentLocation, // Content-Location + + Custom, // Unknown or custom field (eg. X-Priority, X-Mailer, etc.) + + Last + }; + +protected: + + Types m_type; + string m_name; // In case of custom field + +public: + + const bool operator<(const headerField& field) const; + + const Types type() const; + const string name() const; + + const bool isCustom() const; + + virtual void copyFrom(const headerField& field); + headerField& operator=(const headerField& field); + headerField* clone() const; + + static const Types nameToType(const string& name); + static const string typeToName(const Types type); + + + // Component assembling + using component::generate; + + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_HEADERFIELD_HPP_INCLUDED diff --git a/src/headerFieldFactory.cpp b/src/headerFieldFactory.cpp new file mode 100644 index 00000000..0187c26d --- /dev/null +++ b/src/headerFieldFactory.cpp @@ -0,0 +1,139 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "headerFieldFactory.hpp" +#include "exception.hpp" + +#include "defaultField.hpp" + +#include "mailboxField.hpp" +#include "addressListField.hpp" +#include "addressListField.hpp" +#include "addressListField.hpp" +#include "mailboxField.hpp" +#include "dateField.hpp" +#include "relayField.hpp" +#include "textField.hpp" +#include "mailboxField.hpp" +#include "contentTypeField.hpp" +#include "contentEncodingField.hpp" +#include "contentDispositionField.hpp" +#include "messageIdField.hpp" + + +namespace vmime +{ + + +headerFieldFactory::headerFieldFactory() +{ + // Register some default field types + registerType <mailboxField>(headerField::From); + registerType <addressListField>(headerField::To); + registerType <addressListField>(headerField::Cc); + registerType <addressListField>(headerField::Bcc); + registerType <mailboxField>(headerField::Sender); + registerType <dateField>(headerField::Date); + registerType <relayField>(headerField::Received); + registerType <textField>(headerField::Subject); + registerType <mailboxField>(headerField::ReplyTo); + registerType <mailboxField>(headerField::DeliveredTo); + registerType <textField>(headerField::Organization); + registerType <textField>(headerField::UserAgent); + registerType <mailboxField>(headerField::ReturnPath); + registerType <contentTypeField>(headerField::ContentType); + registerType <contentEncodingField>(headerField::ContentTransferEncoding); + registerType <textField>(headerField::ContentDescription); + registerType <defaultField>(headerField::MimeVersion); + registerType <contentDispositionField>(headerField::ContentDisposition); + registerType <messageIdField>(headerField::ContentId); + registerType <messageIdField>(headerField::MessageId); + registerType <defaultField>(headerField::ContentLocation); +} + + +headerFieldFactory::~headerFieldFactory() +{ +} + + +headerField* headerFieldFactory::create + (const string& name, const string& body) +{ + const headerField::Types type = headerField::nameToType(name); + + if (type != headerField::Custom) + { + return (create(type, name, body)); + } + else + { + NameMap::const_iterator pos = m_nameMap.find(toLower(name)); + headerField* field = NULL; + + if (pos != m_nameMap.end()) + { + field = ((*pos).second)(); + } + else + { + field = new defaultField; + } + + field->m_type = headerField::Custom; + field->m_name = name; + + if (body != NULL_STRING) + field->parse(body); + + return (field); + } +} + + +headerField* headerFieldFactory::create(const headerField::Types type, + const string& name, const string& body) +{ + if (type == headerField::Custom) + { + return (create(name, body)); + } + else + { + TypeMap::const_iterator pos = m_typeMap.find(type); + + if (pos != m_typeMap.end()) + { + headerField* field = ((*pos).second)(); + + field->m_type = type; + if (name != NULL_STRING) field->m_name = name; + if (body != NULL_STRING) field->parse(body); + + return (field); + } + else + { + throw exceptions::bad_field_type(); + } + } +} + + +} // vmime diff --git a/src/headerFieldFactory.hpp b/src/headerFieldFactory.hpp new file mode 100644 index 00000000..13024fd6 --- /dev/null +++ b/src/headerFieldFactory.hpp @@ -0,0 +1,85 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_HEADERFIELDFACTORY_HPP_INCLUDED +#define VMIME_HEADERFIELDFACTORY_HPP_INCLUDED + + +#include "headerField.hpp" +#include "utility/singleton.hpp" + + +namespace vmime +{ + + +class headerFieldFactory : public utility::singleton <headerFieldFactory> +{ + friend class utility::singleton <headerFieldFactory>; + +protected: + + headerFieldFactory(); + ~headerFieldFactory(); + + typedef headerField* (*AllocFunc)(void); + typedef std::map <string, AllocFunc> NameMap; + typedef std::map <headerField::Types, AllocFunc> TypeMap; + + NameMap m_nameMap; + TypeMap m_typeMap; + +public: + + template <class TYPE> + class registerer + { + public: + + static headerField* creator() + { + // Allocate a new object + return new TYPE(); + } + }; + + + template <class T> + void registerName(const string& name) + { + m_nameMap.insert(NameMap::value_type(toLower(name), ®isterer<T>::creator)); + } + + headerField* create(const string& name, const string& body = NULL_STRING); + headerField* create(const headerField::Types type, const string& name = NULL_STRING, const string& body = NULL_STRING); + +protected: + + template <class T> + void registerType(const headerField::Types type) + { + m_typeMap.insert(TypeMap::value_type(type, ®isterer<T>::creator)); + } +}; + + +} // vmime + + +#endif // VMIME_HEADERFIELDFACTORY_HPP_INCLUDED diff --git a/src/htmlTextPart.cpp b/src/htmlTextPart.cpp new file mode 100644 index 00000000..f864272d --- /dev/null +++ b/src/htmlTextPart.cpp @@ -0,0 +1,371 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "htmlTextPart.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +htmlTextPart::~htmlTextPart() +{ +} + + +const mediaType htmlTextPart::type() const +{ + return (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML)); +} + + +const int htmlTextPart::getPartCount() const +{ + return (m_plainText.empty() ? 1 : 2); +} + + +void htmlTextPart::generateIn(bodyPart& /* message */, bodyPart& parent) const +{ + // Plain text + if (!m_plainText.empty()) + { + // -- Create a new part + bodyPart* part = new bodyPart(); + parent.body().parts.append(part); + + // -- Set header fields + part->header().fields.ContentType() = mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN); + part->header().fields.ContentType().charset() = m_charset; + part->header().fields.ContentTransferEncoding() = encoding(encodingTypes::QUOTED_PRINTABLE); + + // -- Set contents + part->body().contents() = m_plainText; + } + + // HTML text + // -- Create a new part + bodyPart* htmlPart = new bodyPart(); + + // -- Set header fields + htmlPart->header().fields.ContentType() = mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML); + htmlPart->header().fields.ContentType().charset() = m_charset; + htmlPart->header().fields.ContentTransferEncoding() = encoding(encodingTypes::QUOTED_PRINTABLE); + + // -- Set contents + htmlPart->body().contents() = m_text; + + // Handle the case we have embedded objects + if (!embeddedObjects.empty()) + { + // Create a "multipart/related" body part + bodyPart* relPart = new bodyPart(); + parent.body().parts.append(relPart); + + relPart->header().fields.ContentType() = mediaType + (mediaTypes::MULTIPART, mediaTypes::MULTIPART_RELATED); + + // Add the HTML part into this part + relPart->body().parts.append(htmlPart); + + // Also add images into this part + for (embeddedObjectsContainer::const_iterator i = embeddedObjects.begin() ; + i != embeddedObjects.end() ; ++i) + { + bodyPart* objPart = new bodyPart(); + relPart->body().parts.append(objPart); + + string id = (*i).id(); + + if (id.substr(0, 4) == "CID:") + id = id.substr(4); + + objPart->header().fields.ContentType() = (*i).type(); + objPart->header().fields.ContentId() = messageId("<" + id + ">"); + objPart->header().fields.ContentDisposition() = disposition(dispositionTypes::INLINE); + objPart->header().fields.ContentTransferEncoding() = (*i).encoding(); + //encoding(encodingTypes::BASE64); + + objPart->body().contents() = (*i).data(); + } + } + else + { + // Add the HTML part into the parent part + parent.body().parts.append(htmlPart); + } +} + + +void htmlTextPart::findEmbeddedParts(const bodyPart& part, + std::vector <const bodyPart*>& cidParts, std::vector <const bodyPart*>& locParts) +{ + for (body::const_iterator p = part.body().parts.begin() ; p != part.body().parts.end() ; ++p) + { + try + { + dynamic_cast<messageIdField&>((*p).header().fields.find(headerField::ContentId)); + cidParts.push_back(&(*p)); + } + catch (exceptions::no_such_field) + { + // No "Content-id" field. + // Maybe there is a "Content-Location" field... + try + { + dynamic_cast<messageIdField&>((*p).header().fields.find(headerField::ContentId)); + locParts.push_back(&(*p)); + } + catch (exceptions::no_such_field) + { + // No "Content-Location" field. + // Cannot be an embedded object since it cannot be referenced in HTML text. + } + } + + findEmbeddedParts((*p), cidParts, locParts); + } +} + + +void htmlTextPart::addEmbeddedObject(const bodyPart& part, const string& id) +{ + mediaType type; + + try + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&> + (part.header().fields.find(headerField::ContentType)); + + type = ctf.value(); + } + catch (exceptions::no_such_field) + { + // No "Content-type" field: assume "application/octet-stream". + } + + embeddedObjects.m_list.push_back(new embeddedObject + (part.body().contents(), part.body().encoding(), id, type)); +} + + +void htmlTextPart::parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart) +{ + // Search for possible embedded objects in the _whole_ message. + std::vector <const bodyPart*> cidParts; + std::vector <const bodyPart*> locParts; + + findEmbeddedParts(message, cidParts, locParts); + + // Extract HTML text + std::ostringstream oss; + utility::outputStreamAdapter adapter(oss); + + textPart.body().contents().extract(adapter); + + const string data = oss.str(); + + m_text = textPart.body().contents(); + + try + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&> + (textPart.header().fields.find(headerField::ContentType)); + + m_charset = ctf.charset(); + } + catch (exceptions::no_such_field) + { + // No "Content-type" field. + } + catch (exceptions::no_such_parameter) + { + // No "charset" parameter. + } + + // Extract embedded objects. The algorithm is quite simple: for each previously + // found inline part, we check if its CID/Location is contained in the HTML text. + for (std::vector <const bodyPart*>::const_iterator p = cidParts.begin() ; p != cidParts.end() ; ++p) + { + const messageIdField& midField = dynamic_cast<messageIdField&> + ((**p).header().fields.find(headerField::ContentId)); + + const string searchFor("CID:" + midField.value().id()); + + if (data.find(searchFor) != string::npos) + { + // This part is referenced in the HTML text. + // Add it to the embedded object list. + addEmbeddedObject(**p, "CID:" + midField.value().id()); + } + } + + for (std::vector <const bodyPart*>::const_iterator p = locParts.begin() ; p != locParts.end() ; ++p) + { + const defaultField& locField = dynamic_cast<defaultField&> + ((**p).header().fields.find(headerField::ContentLocation)); + + if (data.find(locField.value()) != string::npos) + { + // This part is referenced in the HTML text. + // Add it to the embedded object list. + addEmbeddedObject(**p, locField.value()); + } + } + + // Extract plain text, if any. + findPlainTextPart(message, parent, textPart); +} + + +bool htmlTextPart::findPlainTextPart(const bodyPart& part, const bodyPart& parent, const bodyPart& textPart) +{ + // We search for the nearest "multipart/alternative" part. + try + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&> + (part.header().fields.find(headerField::ContentType)); + + if (ctf.value().type() == mediaTypes::MULTIPART && + ctf.value().subType() == mediaTypes::MULTIPART_ALTERNATIVE) + { + bodyPart const* foundPart = NULL; + + for (body::const_iterator p = part.body().parts.begin() ; !foundPart && p != part.body().parts.end() ; ++p) + { + if (&(*p) == &parent || // if "text/html" is in "multipart/related" + &(*p) == &textPart) // if not... + { + foundPart = &(*p); + } + } + + if (foundPart) + { + bool found = false; + + // Now, search for the alternative plain text part + for (body::const_iterator p = part.body().parts.begin() ; + !found && p != part.body().parts.end() ; ++p) + { + try + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&> + ((*p).header().fields.find(headerField::ContentType)); + + if (ctf.value().type() == mediaTypes::TEXT && + ctf.value().subType() == mediaTypes::TEXT_PLAIN) + { + m_plainText = (*p).body().contents(); + found = true; + } + } + catch (exceptions::no_such_field) + { + // No "Content-type" field. + } + } + + // If we don't have found the plain text part here, it means that + // it does not exists (the MUA which built this message probably + // did not include it...). + return (found); + } + } + } + catch (exceptions::no_such_field) + { + // No "Content-type" field. + } + + bool found = false; + + for (body::const_iterator p = part.body().parts.begin() ; !found && p != part.body().parts.end() ; ++p) + { + found = findPlainTextPart(*p, parent, textPart); + } + + return (found); +} + + + +//////////////////////////////// +// Embedded objects container // +//////////////////////////////// + + +htmlTextPart::embeddedObjectsContainer::~embeddedObjectsContainer() +{ + free_container(m_list); +} + + +const htmlTextPart::embeddedObject& htmlTextPart::embeddedObjectsContainer::find(const string& id) const +{ + for (std::vector <embeddedObject*>::const_iterator o = m_list.begin() ; o != m_list.end() ; ++o) + { + if ((**o).id() == id) + return (**o); + } + + throw exceptions::no_object_found(); +} + + +const bool htmlTextPart::embeddedObjectsContainer::has(const string& id) const +{ + for (std::vector <embeddedObject*>::const_iterator o = m_list.begin() ; o != m_list.end() ; ++o) + { + if ((**o).id() == id) + return (true); + } + + return (false); +} + + +const string htmlTextPart::embeddedObjectsContainer::add + (const contentHandler& data, const vmime::encoding& enc, const mediaType& type) +{ + const messageId mid(messageId::generateId()); + const string id = "CID:" + mid.id(); + + m_list.push_back(new embeddedObject(data, enc, id, type)); + + return (id); +} + + +const string htmlTextPart::embeddedObjectsContainer::add + (const contentHandler& data, const mediaType& type) +{ + return (add(data, encoding::decide(data), type)); +} + + +const string htmlTextPart::embeddedObjectsContainer::add + (const string& data, const mediaType& type) +{ + return (add(contentHandler(data), encoding::decide(data), type)); +} + + +} // vmime diff --git a/src/htmlTextPart.hpp b/src/htmlTextPart.hpp new file mode 100644 index 00000000..eeb7b618 --- /dev/null +++ b/src/htmlTextPart.hpp @@ -0,0 +1,180 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_HTMLTEXTPART_HPP_INCLUDED +#define VMIME_HTMLTEXTPART_HPP_INCLUDED + + +#include "textPart.hpp" +#include "messageId.hpp" +#include "encoding.hpp" + +#include "contentHandler.hpp" + + +namespace vmime +{ + + +class htmlTextPart : public textPart +{ +protected: + + ~htmlTextPart(); + +public: + + const mediaType type() const; + + const vmime::charset& charset() const { return (m_charset); } + vmime::charset& charset() { return (m_charset); } + + const contentHandler& plainText() const { return (m_plainText); } + contentHandler& plainText() { return (m_plainText); } + + const contentHandler& text() const { return (m_text); } + contentHandler& text() { return (m_text); } + + // Embedded object (eg. image for <IMG> tag) + class embeddedObject + { + public: + + embeddedObject(const contentHandler& data, const vmime::encoding& enc, + const string& id, const mediaType& type) + : m_data(data), m_encoding(enc), m_id(id), m_type(type) + { + } + + public: + + const contentHandler& data() const { return (m_data); } + const vmime::encoding& encoding() const { return (m_encoding); } + const string& id() const { return (m_id); } + const mediaType& type() const { return (m_type); } + + private: + + contentHandler m_data; + vmime::encoding m_encoding; + string m_id; + mediaType m_type; + }; + + // Embedded objects container + class embeddedObjectsContainer + { + friend class htmlTextPart; + + protected: + + ~embeddedObjectsContainer(); + + public: + + // Test the existence/get an embedded object given its identifier. + const bool has(const string& id) const; + const embeddedObject& find(const string& id) const; + + // Embed an object and returns a string which identifies it. + const string add(const string& data, const mediaType& type); + const string add(const contentHandler& data, const mediaType& type); + const string add(const contentHandler& data, const encoding& enc, const mediaType& type); + + // Embedded objects enumerator + class const_iterator + { + public: + + typedef std::vector <embeddedObject*>::const_iterator::difference_type difference_type; + + const_iterator(std::vector <embeddedObject*>::const_iterator it) : m_iterator(it) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const embeddedObject& operator*() const { return (**m_iterator); } + const embeddedObject* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator operator++(int) { const_iterator i(*this); ++m_iterator; return (i); } + + const_iterator& operator--() { --m_iterator; return (*this); } + const_iterator operator--(int) { const_iterator i(*this); --m_iterator; return (i); } + + const_iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + const_iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + const_iterator operator-(difference_type x) const { return const_iterator(m_iterator - x); } + + const embeddedObject& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector <embeddedObject*>::const_iterator m_iterator; + }; + + public: + + const_iterator begin() const { return (const_iterator(m_list.begin())); } + const_iterator end() const { return (const_iterator(m_list.end())); } + + // Object count + const std::vector <embeddedObject*>::size_type count() const { return (m_list.size()); } + const std::vector <embeddedObject*>::size_type size() const { return (m_list.size()); } + const bool empty() const { return (m_list.empty()); } + + embeddedObject& front() { return (*m_list.front()); } + const embeddedObject& front() const { return (*m_list.front()); } + embeddedObject& back() { return (*m_list.back()); } + const embeddedObject& back() const { return (*m_list.back()); } + + protected: + + std::vector <embeddedObject*> m_list; + + } embeddedObjects; + + typedef embeddedObjectsContainer::const_iterator const_iterator; + +protected: + + contentHandler m_plainText; + contentHandler m_text; + vmime::charset m_charset; + + void findEmbeddedParts(const bodyPart& part, std::vector <const bodyPart*>& cidParts, std::vector <const bodyPart*>& locParts); + void addEmbeddedObject(const bodyPart& part, const string& id); + + bool findPlainTextPart(const bodyPart& part, const bodyPart& parent, const bodyPart& textPart); + + const int getPartCount() const; + + void generateIn(bodyPart& message, bodyPart& parent) const; + virtual void parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart); +}; + + +} // vmime + + +#endif // VMIME_HTMLTEXTPART_HPP_INCLUDED diff --git a/src/mailbox.cpp b/src/mailbox.cpp new file mode 100644 index 00000000..6560296c --- /dev/null +++ b/src/mailbox.cpp @@ -0,0 +1,450 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mailbox.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +mailbox::mailbox() +{ +} + + +mailbox::mailbox(const class mailbox& mailbox) + : address(), m_name(mailbox.m_name), m_email(mailbox.m_email) +{ +} + + +mailbox::mailbox(const string& email) + : m_email(email) +{ +} + + +mailbox::mailbox(const text& name, const string& email) + : m_name(name), m_email(email) +{ +} + + +/* + + RFC #2822: + 3.4. ADDRESS SPECIFICATION + +mailbox = name-addr / addr-spec + +name-addr = [display-name] angle-addr + +angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr + +*/ + +void mailbox::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pstart; + + // Ignore blank spaces at the beginning + while (p < pend && isspace(*p)) ++p; + + // Current state for parsing machine + enum States + { + State_None, + State_Name, + State_Address + }; + + States state = State_Name; // let's start with name, we will see later (*) + + // Temporary buffers for extracted name and address + string name; + string address; + + while (p < pend) + { + if (state == State_Name) + { + if (*p == '<') + { + state = State_Address; + continue; + } + + if (*p == '"') // Quoted string + { + ++p; + + bool escaped = false; + + while (p < pend) + { + if (escaped) + { + name += *p; + escaped = false; + } + else if (*p == '\\') + { + escaped = true; + } + else + { + if (*p == '"') + { + ++p; + break; + } + else + { + name += *p; + } + } + + ++p; + } + } + else + { + bool escaped = false; + int comment = 0; + + while (p < pend) + { + if (escaped) + { + if (!comment) name += *p; + escaped = false; + } + else if (comment) + { + if (*p == '\\') + escaped = true; + else if (*p == '(') + ++comment; + else if (*p == ')') + --comment; + } + else if (*p == '\\') + { + escaped = true; + } + else if (*p == '(') + { + ++comment; + } + else if (*p == '<') + { + // Erase any space between display name and <address> + string::iterator q = name.end(); + for ( ; q != name.begin() && isspace(*(q - 1)) ; --q); + name.erase(q, name.end()); + + break; + } + else if (/* isspace(*p) || */ *p == '@') + { + break; + } + else + { + name += *p; + } + + ++p; + } + } + + if (p < pend && *p == '@') + { + // (*) Actually, we were parsing the local-part of an address + // and not a display name... + address = name; + name.clear(); + + bool escaped = false; + int comment = 0; + + while (p < pend) + { + if (escaped) + { + if (!comment) address += *p; + escaped = false; + } + else if (comment) + { + if (*p == '\\') + escaped = true; + else if (*p == '(') + ++comment; + else if (*p == ')') + --comment; + } + else if (*p == '\\') + { + escaped = true; + } + else if (*p == '(') + { + ++comment; + } + else if (isspace(*p)) + { + break; + } + else + { + address += *p; + } + + ++p; + } + + break; + } + else + { + while (p < pend && isspace(*p)) ++p; + state = State_None; + } + } + else if (state == State_Address) + { + // Skip '<' character + if (*p == '<') + ++p; + + bool escaped = false; + int comment = 0; + + while (p < pend) + { + if (escaped) + { + if (!comment) address += *p; + escaped = false; + } + else if (comment) + { + if (*p == '\\') + escaped = true; + else if (*p == '(') + ++comment; + else if (*p == ')') + --comment; + } + else if (*p == '(') + { + ++comment; + } + else if (*p == '\\') + { + escaped = true; + } + else if (*p == '<') + { + // If we found a '<' here, it means that the address + // starts _only_ here...and the stuff we have parsed + // before belongs actually to the display name! + name += address; + address.clear(); + } + else if (*p == '>') + { + break; + } + else if (!isspace(*p)) + { + address += *p; + } + + ++p; + } + + break; + } + else + { + while (p < pend && isspace(*p)) ++p; + + if (p < pend) + { + //if (*p == '<') + state = State_Address; + } + } + } + + decodeAndUnfoldText(name, m_name); + m_email = address; + + if (newPosition) + *newPosition = position + (p - pstart); +} + + +void mailbox::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + if (m_name.empty()) + { + bool newLine = false; + + // No display name is specified, only email address. + if (curLinePos /* + 2 */ + m_email.length() > maxLineLength) + { + os << NEW_LINE_SEQUENCE; + newLine = true; + } + + //os << "<" << m_email << ">"; + os << m_email; + + if (newLinePos) + { + *newLinePos = curLinePos + m_email.length() /* + 2 */; + if (newLine) *newLinePos += 1; + } + } + else + { + // We have to encode the name: + // - if it contains characters in a charset different from "US-ASCII", + // - and/or if it contains one or more of these special chars: + // SPACE TAB " ; , < > ( ) @ / ? . = : + + // Check whether there are words that are not "US-ASCII" + // and/or contain the special chars. + bool forceEncode = false; + + for (text::const_iterator w = m_name.begin() ; !forceEncode && w != m_name.end() ; ++w) + { + if ((*w).charset() == charset(charsets::US_ASCII)) + { + const string& buffer = (*w).buffer(); + + for (string::const_iterator c = buffer.begin() ; + !forceEncode && c != buffer.end() ; ++c) + { + switch (*c) + { + case ' ': + case '\t': + case ';': + case ',': + case '<': case '>': + case '(': case ')': + case '@': + case '/': + case '?': + case '.': + case '=': + case ':': + case '"': + + forceEncode = true; + break; + } + } + } + else + { + forceEncode = true; + } + } + + string::size_type pos = curLinePos; + bool newLine = true; + + encodeAndFoldText(os, m_name, maxLineLength, pos, &pos, + forceEncode ? encodeAndFoldFlags::forceEncoding : encodeAndFoldFlags::none); + + if (pos + m_email.length() + 3 > maxLineLength) + { + os << NEW_LINE_SEQUENCE; + newLine = true; + } + + os << " <" << m_email << ">"; + + if (newLinePos) + { + *newLinePos = pos + m_email.length() + 3; + if (newLine) *newLinePos += NEW_LINE_SEQUENCE.length(); + } + } +} + + +const bool mailbox::operator==(const class mailbox& mailbox) const +{ + return (m_name == mailbox.m_name && m_email == mailbox.m_email); +} + + +const bool mailbox::operator!=(const class mailbox& mailbox) const +{ + return !(*this == mailbox); +} + + +void mailbox::copyFrom(const address& addr) +{ + const mailbox& source = dynamic_cast<const mailbox&>(addr); + + m_name = source.m_name; + m_email = source.m_email; +} + + +address* mailbox::clone() const +{ + return new mailbox(*this); +} + + +const bool mailbox::empty() const +{ + return (m_email.empty()); +} + + +void mailbox::clear() +{ + m_name.clear(); + m_email.clear(); +} + + +const bool mailbox::isGroup() const +{ + return (false); +} + + +} // vmime diff --git a/src/mailbox.hpp b/src/mailbox.hpp new file mode 100644 index 00000000..3fa79045 --- /dev/null +++ b/src/mailbox.hpp @@ -0,0 +1,105 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MAILBOX_HPP_INCLUDED +#define VMIME_MAILBOX_HPP_INCLUDED + + +#include "address.hpp" +#include "text.hpp" + + +namespace vmime +{ + + +/** A mailbox: full name + email (basic type). + */ + +class mailbox : public address +{ + friend class mailboxGroup; + friend class mailboxField; + +public: + + mailbox(); + mailbox(const class mailbox& mailbox); + mailbox(const string& email); + mailbox(const text& name, const string& email); + + /** Return the full name of the mailbox (empty if not specified). + * + * @return full name of the mailbox + */ + const text& name() const { return (m_name); } + + /** Return the full name of the mailbox (empty if not specified). + * + * @return full name of the mailbox + */ + text& name() { return (m_name); } + + /** Return the email of the mailbox. + * + * @return email of the mailbox + */ + const string& email() const { return (m_email); } + + /** Return the email of the mailbox. + * + * @return email of the mailbox + */ + string& email() { return (m_email); } + + // Comparison + const bool operator==(const class mailbox& mailbox) const; + const bool operator!=(const class mailbox& mailbox) const; + + // Assignment + void copyFrom(const address& addr); + address* clone() const; + + const bool empty() const; + + void clear(); + + + const bool isGroup() const; + +protected: + + text m_name; + string m_email; + +public: + + using address::parse; + using address::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MAILBOX_HPP_INCLUDED diff --git a/src/mailboxField.cpp b/src/mailboxField.cpp new file mode 100644 index 00000000..e4e0164c --- /dev/null +++ b/src/mailboxField.cpp @@ -0,0 +1,95 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mailboxField.hpp" +#include "mailboxGroup.hpp" + + +namespace vmime +{ + + +mailboxField::mailboxField() +{ +} + + +void mailboxField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_mailbox.clear(); + + // Here, we cannot simply call "m_mailbox.parse()" because it + // may have more than one address specified (even if this field + // should contain only one). We are never too much careful... + address* parsedAddress = address::parseNext(buffer, position, end, newPosition); + + if (parsedAddress) + { + if (parsedAddress->isGroup()) + { + // If it is a group of mailboxes, take the first + // mailbox of the group + mailboxGroup* group = static_cast <mailboxGroup*>(parsedAddress); + + if (!group->empty()) + m_mailbox = *(group->begin()); + } + else + { + // Parse only if it is a mailbox + m_mailbox = *static_cast <mailbox*>(parsedAddress); + } + } + + delete (parsedAddress); + + if (newPosition) + *newPosition = end; +} + + +void mailboxField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + m_mailbox.generate(os, maxLineLength, pos, newLinePos); +} + + +mailboxField& mailboxField::operator=(const class mailbox& mailbox) +{ + m_mailbox = mailbox; + return (*this); +} + + +void mailboxField::copyFrom(const headerField& field) +{ + const mailboxField& source = dynamic_cast<const mailboxField&>(field); + m_mailbox = source.m_mailbox; + + headerField::copyFrom(field); +} + + +} // vmime diff --git a/src/mailboxField.hpp b/src/mailboxField.hpp new file mode 100644 index 00000000..13cd8e49 --- /dev/null +++ b/src/mailboxField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MAILBOXFIELD_HPP_INCLUDED +#define VMIME_MAILBOXFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "mailbox.hpp" + + +namespace vmime +{ + + +class mailboxField : public headerField +{ + friend class headerFieldFactory::registerer <mailboxField>; + +protected: + + mailboxField(); + +public: + + void copyFrom(const headerField& field); + + mailboxField& operator=(const class mailbox& mailbox); + + const mailbox& value() const { return (m_mailbox); } + mailbox& value() { return (m_mailbox); } + +protected: + + mailbox m_mailbox; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MAILBOXFIELD_HPP_INCLUDED diff --git a/src/mailboxGroup.cpp b/src/mailboxGroup.cpp new file mode 100644 index 00000000..ff708313 --- /dev/null +++ b/src/mailboxGroup.cpp @@ -0,0 +1,237 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mailboxGroup.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +mailboxGroup::mailboxGroup() +{ +} + + +mailboxGroup::mailboxGroup(const class mailboxGroup& mailboxGroup) + : address() +{ + copyFrom(mailboxGroup); +} + + +mailboxGroup::mailboxGroup(const text& name) + : m_name(name) +{ +} + + +mailboxGroup::~mailboxGroup() +{ + clear(); +} + + +void mailboxGroup::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pstart; + + while (p < pend && isspace(*p)) + ++p; + + string name; + + while (p < pend && *p != ':') + { + name += *p; + ++p; + } + + if (p < pend && *p == ':') + ++p; + + + string::size_type pos = position + (p - pstart); + + while (pos < end) + { + address* parsedAddress = address::parseNext(buffer, pos, end, &pos); + + if (parsedAddress) + { + if (parsedAddress->isGroup()) + { + mailboxGroup* group = static_cast <mailboxGroup*>(parsedAddress); + + // Sub-groups are not allowed in mailbox groups: so, we add all + // the contents of the sub-group into this group... + for (mailboxGroup::const_iterator + it = group->begin() ; it != group->end() ; ++it) + { + m_list.push_back(static_cast <mailbox*>((*it).clone())); + } + + delete (parsedAddress); + } + else + { + m_list.push_back(static_cast <mailbox*>(parsedAddress)); + } + } + } + + decodeAndUnfoldText(name, m_name); + + if (newPosition) + *newPosition = end; +} + + +void mailboxGroup::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + // We have to encode the name: + // - if it contains characters in a charset different from "US-ASCII", + // - and/or if it contains one or more of these special chars: + // SPACE TAB " ; , < > ( ) @ / ? . = : + + // Check whether there are words that are not "US-ASCII" + // and/or contain the special chars. + bool forceEncode = false; + + for (text::const_iterator w = m_name.begin() ; !forceEncode && w != m_name.end() ; ++w) + { + if ((*w).charset() == charset(charsets::US_ASCII)) + { + const string& buffer = (*w).buffer(); + + for (string::const_iterator c = buffer.begin() ; + !forceEncode && c != buffer.end() ; ++c) + { + switch (*c) + { + case ' ': + case '\t': + case ';': + case ',': + case '<': case '>': + case '(': case ')': + case '@': + case '/': + case '?': + case '.': + case '=': + case ':': + + forceEncode = true; + break; + } + } + } + } + + string::size_type pos = curLinePos; + + encodeAndFoldText(os, m_name, maxLineLength - 2, pos, &pos, + forceEncode ? encodeAndFoldFlags::forceEncoding : encodeAndFoldFlags::none); + + os << ":"; + ++pos; + + for (const_iterator it = m_list.begin() ; it != m_list.end() ; ++it) + { + if (it != m_list.begin()) + { + os << ", "; + pos += 2; + } + else + { + os << " "; + ++pos; + } + + (*it).generate(os, maxLineLength - 2, pos, &pos); + } + + os << ";"; + pos++; + + if (newLinePos) + *newLinePos = pos; +} + + +address* mailboxGroup::clone() const +{ + return new mailboxGroup(*this); +} + + +// Mailbox insertion +void mailboxGroup::append(const mailbox& field) +{ + m_list.push_back(static_cast<mailbox*>(field.clone())); +} + + +void mailboxGroup::insert(const iterator it, const mailbox& field) +{ + m_list.insert(it.m_iterator, static_cast<mailbox*>(field.clone())); +} + + +// Mailbox removing +void mailboxGroup::erase(const iterator it) +{ + delete (*it.m_iterator); + m_list.erase(it.m_iterator); +} + + +void mailboxGroup::clear() +{ + free_container(m_list); +} + + +void mailboxGroup::copyFrom(const address& addr) +{ + const mailboxGroup& source = dynamic_cast<const mailboxGroup&>(addr); + + m_name = source.m_name; + + clear(); + + for (std::vector <mailbox*>::const_iterator i = source.m_list.begin() ; i != source.m_list.end() ; ++i) + m_list.push_back(static_cast<mailbox*>((*i)->clone())); +} + + +const bool mailboxGroup::isGroup() const +{ + return (true); +} + + +} // vmime diff --git a/src/mailboxGroup.hpp b/src/mailboxGroup.hpp new file mode 100644 index 00000000..0187f37c --- /dev/null +++ b/src/mailboxGroup.hpp @@ -0,0 +1,155 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MAILBOXGROUP_HPP_INCLUDED +#define VMIME_MAILBOXGROUP_HPP_INCLUDED + + +#include "address.hpp" +#include "mailbox.hpp" +#include "text.hpp" + + +namespace vmime +{ + + +/** A group of mailboxes (basic type). + */ + +class mailboxGroup : public address +{ +public: + + mailboxGroup(); + mailboxGroup(const class mailboxGroup& mailboxGroup); + mailboxGroup(const text& name); + + ~mailboxGroup(); + + // Properties set/get + const text& name() const { return (m_name); } + text& name() { return (m_name); } + + // Assignment + void copyFrom(const address& addr); + address* clone() const; + +public: + + // Mailbox iterator + class const_iterator; + + class iterator + { + friend class mailboxGroup; + friend class const_iterator; + + public: + + iterator(std::vector <mailbox*>::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + mailbox& operator*() const { return (**m_iterator); } + mailbox* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + private: + + std::vector <mailbox*>::iterator m_iterator; + }; + + class const_iterator + { + friend class mailboxGroup; + + public: + + const_iterator(std::vector <mailbox*>::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const mailbox& operator*() const { return (**m_iterator); } + const mailbox* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + private: + + std::vector <mailbox*>::const_iterator m_iterator; + }; + + iterator begin() { return (m_list.begin()); } + iterator end() { return (m_list.end()); } + + const_iterator begin() const { return (const_iterator(m_list.begin())); } + const_iterator end() const { return (const_iterator(m_list.end())); } + + const std::vector <mailbox*>::size_type size() const { return (m_list.size()); } + const std::vector <mailbox*>::size_type count() const { return (m_list.size()); } + const bool empty() const { return (m_list.empty()); } + + const mailbox& operator[](const std::vector <mailbox*>::size_type x) const { return (*m_list[x]); } + mailbox& operator[](const std::vector <mailbox*>::size_type x) { return (*m_list[x]); } + + // Mailbox insertion + virtual void append(const mailbox& field); + virtual void insert(const iterator it, const mailbox& field); + + // Mailbox removing + void erase(const iterator it); + void clear(); + + + const bool isGroup() const; + +protected: + + text m_name; + std::vector <mailbox*> m_list; + +public: + + using address::parse; + using address::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MAILBOXGROUP_HPP_INCLUDED diff --git a/src/mailboxList.cpp b/src/mailboxList.cpp new file mode 100644 index 00000000..ded7ede1 --- /dev/null +++ b/src/mailboxList.cpp @@ -0,0 +1,46 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mailboxList.hpp" + + +namespace vmime +{ + + +// Address insertion +void mailboxList::append(const address& addr) +{ + // Ensure this is a "mailbox" object + const mailbox& mb = dynamic_cast<const mailbox&>(addr); + + m_list.push_back(mb.clone()); +} + + +void mailboxList::insert(const iterator it, const address& addr) +{ + // Ensure this is a "mailbox" object + const mailbox& mb = dynamic_cast<const mailbox&>(addr); + + m_list.insert(it.m_iterator, mb.clone()); +} + + +} // vmime diff --git a/src/mailboxList.hpp b/src/mailboxList.hpp new file mode 100644 index 00000000..28c6152b --- /dev/null +++ b/src/mailboxList.hpp @@ -0,0 +1,127 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MAILBOXLIST_HPP_INCLUDED +#define VMIME_MAILBOXLIST_HPP_INCLUDED + + +#include "addressList.hpp" +#include "mailbox.hpp" + + +namespace vmime +{ + + +/** A list of mailboxes (basic type). + */ + +class mailboxList : public addressList +{ + friend class mailboxGroup; + +public: + + // + // The following functions have the same name and work *exactly* like + // the ones in "addressList", except we don't accept anything other + // than objects of type "mailbox" (instead of a generic "address"). + // + // This prevents user from inserting mailbox groups where it is not + // allowed by the RFC. + // + + // Address iterator + class const_iterator; + + class iterator + { + friend class mailboxList; + friend class const_iterator; + + protected: + + iterator(std::vector <address*>::iterator it) : m_iterator(it) { } + + public: + + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + mailbox& operator*() const { return static_cast<mailbox&>(**m_iterator); } + mailbox* operator->() const { return static_cast<mailbox*>(*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + private: + + std::vector <address*>::iterator m_iterator; + }; + + class const_iterator + { + friend class mailboxList; + + protected: + + const_iterator(std::vector <address*>::const_iterator it) : m_iterator(it) { } + + public: + + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const mailbox& operator*() const { return static_cast<const mailbox&>(**m_iterator); } + const mailbox* operator->() const { return static_cast<const mailbox*>(*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + private: + + std::vector <address*>::const_iterator m_iterator; + }; + + iterator begin() { return (m_list.begin()); } + iterator end() { return (m_list.end()); } + + const_iterator begin() const { return (const_iterator(m_list.begin())); } + const_iterator end() const { return (const_iterator(m_list.end())); } + + // Address insertion + void append(const address& addr); + void insert(const iterator it, const address& addr); +}; + + +} // vmime + + +#endif // VMIME_MAILBOXLIST_HPP_INCLUDED diff --git a/src/mailboxListField.cpp b/src/mailboxListField.cpp new file mode 100644 index 00000000..69c328f5 --- /dev/null +++ b/src/mailboxListField.cpp @@ -0,0 +1,60 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mailboxListField.hpp" + + +namespace vmime +{ + + +mailboxListField::mailboxListField() +{ +} + + +void mailboxListField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_list.parse(buffer, position, end, newPosition); +} + + +void mailboxListField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + m_list.generate(os, maxLineLength, pos, newLinePos); +} + + +void mailboxListField::copyFrom(const headerField& field) +{ + const mailboxListField& source = dynamic_cast<const mailboxListField&>(field); + m_list = source.m_list; + + headerField::copyFrom(field); +} + + +} // vmime + diff --git a/src/mailboxListField.hpp b/src/mailboxListField.hpp new file mode 100644 index 00000000..6e01c278 --- /dev/null +++ b/src/mailboxListField.hpp @@ -0,0 +1,68 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MAILBOXLISTFIELD_HPP_INCLUDED +#define VMIME_MAILBOXLISTFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "mailboxList.hpp" + + +namespace vmime +{ + + +class mailboxListField : public headerField +{ + friend class headerFieldFactory::registerer <mailboxListField>; + +protected: + + mailboxListField(); + +public: + + void copyFrom(const headerField& field); + + const mailboxList& value() const { return (m_list); } + mailboxList& value() { return (m_list); } + +protected: + + mailboxList m_list; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MAILBOXLISTFIELD_HPP_INCLUDED diff --git a/src/mediaType.cpp b/src/mediaType.cpp new file mode 100644 index 00000000..6681a0dc --- /dev/null +++ b/src/mediaType.cpp @@ -0,0 +1,127 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mediaType.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +mediaType::mediaType() + : m_type(mediaTypes::APPLICATION), m_subType(mediaTypes::APPLICATION_OCTET_STREAM) +{ +} + + +mediaType::mediaType(const string& type) +{ + parse(type); +} + + +mediaType::mediaType(const string& type, const string& subType) +{ + set(type, subType); +} + + +void mediaType::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pstart; + + // Extract the type + const string::size_type typeStart = position; + + while (p < pend && *p != '/') ++p; + + m_type = toLower(string(buffer.begin() + typeStart, + buffer.begin() + position + (p - pstart))); + + if (p < pend) + { + // Skip '/' character + ++p; + + // Extract the sub-type + m_subType = toLower(string(buffer.begin() + position + (p - pstart), + buffer.begin() + end)); + } + + if (newPosition) + *newPosition = end; +} + + +void mediaType::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + const string value = toLower(m_type) + "/" + toLower(m_subType); + + if (curLinePos + value.length() > maxLineLength) + { + os << NEW_LINE_SEQUENCE; + os << value; + + if (newLinePos) + *newLinePos = NEW_LINE_SEQUENCE_LENGTH + value.length(); + } + else + { + os << value; + + if (newLinePos) + *newLinePos = curLinePos + value.length(); + } +} + + +const bool mediaType::operator==(const mediaType& type) const +{ + return (m_type == type.m_type && m_subType == type.m_subType); +} + + +const bool mediaType::operator!=(const mediaType& type) const +{ + return !(*this == type); +} + + +mediaType& mediaType::operator=(const mediaType& type) +{ + m_type = type.m_type; + m_subType = type.m_subType; + + return (*this); +} + + +mediaType& mediaType::operator=(const string& type) +{ + parse(type); + return (*this); +} + + +} // vmime diff --git a/src/mediaType.hpp b/src/mediaType.hpp new file mode 100644 index 00000000..f4148e5c --- /dev/null +++ b/src/mediaType.hpp @@ -0,0 +1,78 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MEDIATYPE_HPP_INCLUDED +#define VMIME_MEDIATYPE_HPP_INCLUDED + + +#include "component.hpp" + + +namespace vmime +{ + + +/** Content media type (basic type). + */ + +class mediaType : public component +{ +public: + + mediaType(); + mediaType(const string& type); + mediaType(const string& type, const string& subType); + +public: + + const bool operator==(const mediaType& type) const; + const bool operator!=(const mediaType& type) const; + + mediaType& operator=(const mediaType& type); + mediaType& operator=(const string& type); + + const string& type() const { return (m_type); }; + string& type() { return (m_type); } + + const string& subType() const { return (m_subType); }; + string& subType() { return (m_subType); } + + void set(const string& type) { parse(type); } + void set(const string& type, const string& subType) { m_type = type; m_subType = subType; } + +protected: + + string m_type; + string m_subType; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MEDIATYPE_HPP_INCLUDED diff --git a/src/message.cpp b/src/message.cpp new file mode 100644 index 00000000..bd6f6085 --- /dev/null +++ b/src/message.cpp @@ -0,0 +1,65 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "message.hpp" +#include "options.hpp" + +#include <sstream> + + +namespace vmime +{ + + +message::message() +{ +} + + +void message::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + // We override this function to change the default value for the + // "maxLineLength" parameter. So, the user will just have to call + // message::generate() without any argument to use the maximum line + // length specified in vmime::options... + bodyPart::generate(os, maxLineLength, curLinePos, newLinePos); +} + + +const string message::generate(const string::size_type maxLineLength, + const string::size_type curLinePos) const +{ + std::ostringstream oss; + utility::outputStreamAdapter adapter(oss); + + generate(adapter, maxLineLength, curLinePos, NULL); + + return (oss.str()); +} + + +void message::parse(const string& buffer) +{ + bodyPart::parse(buffer); +} + + +} // vmime + diff --git a/src/message.hpp b/src/message.hpp new file mode 100644 index 00000000..9b3c6cc9 --- /dev/null +++ b/src/message.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGE_HPP_INCLUDED +#define VMIME_MESSAGE_HPP_INCLUDED + + +#include "bodyPart.hpp" +#include "options.hpp" + + +namespace vmime +{ + + +/** A MIME message. + */ + +class message : public bodyPart +{ +public: + + message(); + + + // Component parsing & assembling + void generate(utility::outputStream& os, const string::size_type maxLineLength = options::getInstance()->message.maxLineLength(), const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + + const string generate(const string::size_type maxLineLength = options::getInstance()->message.maxLineLength(), const string::size_type curLinePos = 0) const; + + void parse(const string& buffer); +}; + + + +} // vmime + + +#endif // VMIME_MESSAGE_HPP_INCLUDED diff --git a/src/messageBuilder.cpp b/src/messageBuilder.cpp new file mode 100644 index 00000000..52ee6390 --- /dev/null +++ b/src/messageBuilder.cpp @@ -0,0 +1,188 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "messageBuilder.hpp" + +#include "textPartFactory.hpp" + + +namespace vmime +{ + + +messageBuilder::messageBuilder() + : m_textPart(NULL) +{ + // By default there is one text part of type "text/plain" + constructTextPart(mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN)); +} + + +messageBuilder::~messageBuilder() +{ + delete (m_textPart); + + free_container(m_attach); +} + + +message* messageBuilder::construct() const +{ + // Create a new message + message* msg = new message; + + // Generate the header fields + msg->header().fields.Subject() = m_subject; + + if (m_from.empty()) + throw exceptions::no_expeditor(); + + if (m_to.empty() || (*m_to.begin()).empty()) + throw exceptions::no_recipient(); + + msg->header().fields.From() = m_from; + msg->header().fields.To() = m_to; + + if (!m_cc.empty()) + msg->header().fields.Cc() = m_cc; + + if (!m_bcc.empty()) + msg->header().fields.Bcc() = m_bcc; + + // Add a "Date" field + msg->header().fields.Date() = datetime::now(); + + // Add a "Mime-Version" header field + msg->header().fields.MimeVersion().value() = MIME_VERSION; + + // If there is one or more attachments (or other parts that are + // not "text/...") and if there is more than one parts for the + // text part, we generate these text parts into a sub-part: + // + // [message] + // | + // +-- multipart/mixed + // | + // +-- multipart/alternative + // | | + // | +-- text part #1 (eg. plain text "text/plain") + // | +-- text part #2 (eg. HTML "text/html") + // | +-- ... + // | + // +-- application/octet-stream (attachment #1) + // | + // +-- ... (other attachments/parts) + // + if (!m_attach.empty() && m_textPart->getPartCount() > 1) + { + // Set parent part (message) to "multipart/mixed" + msg->header().fields.ContentType() = mediaType + (mediaTypes::MULTIPART, mediaTypes::MULTIPART_MIXED); + + // Create a sub-part "multipart/alternative" for text parts + bodyPart* subPart = new bodyPart; + msg->body().parts.append(subPart); + + subPart->header().fields.ContentType() = mediaType + (mediaTypes::MULTIPART, mediaTypes::MULTIPART_ALTERNATIVE); + + // Generate the text parts into this sub-part (normally, this + // sub-part will have the "multipart/alternative" content-type...) + m_textPart->generateIn(*msg, *subPart); + } + else + { + // Generate the text part(s) directly into the message + m_textPart->generateIn(*msg, *msg); + + // If any attachment, set message content-type to "multipart/mixed" + if (!m_attach.empty()) + { + msg->header().fields.ContentType() = mediaType + (mediaTypes::MULTIPART, mediaTypes::MULTIPART_MIXED); + } + // Else, set it to "multipart/alternative" if there are more than one text part. + else if (m_textPart->getPartCount() > 1) + { + msg->header().fields.ContentType() = mediaType + (mediaTypes::MULTIPART, mediaTypes::MULTIPART_ALTERNATIVE); + } + } + + // Generate the attachments + if (!m_attach.empty()) + { + for (std::vector <attachment*>::const_iterator a = m_attach.begin() ; a != m_attach.end() ; ++a) + { + (*a)->generateIn(*msg); + } + } + + // If there is only one part in the message, move it into the message + // (hence, the message will not be multipart...) + if (msg->body().parts.size() == 1) + { + const bodyPart& part = msg->body().parts.front(); + + // First, copy (and replace) the header fields + const header::fieldsContainer& hdr = part.header().fields; + + for (header::const_iterator f = hdr.begin() ; f != hdr.end() ; ++f) + msg->header().fields.get((*f).name()) = *f; + + // Second, copy the body contents and sub-parts (this also remove + // the body part we are copying...) + msg->body() = part.body(); + } + + return (msg); +} + + +void messageBuilder::attach(attachment* attach) +{ + m_attach.push_back(attach); +} + + +void messageBuilder::constructTextPart(const mediaType& type) +{ + class textPart* part = NULL; + + try + { + part = textPartFactory::getInstance()->create(type); + } + catch (exceptions::no_factory_available& e) + { + throw; + } + + delete (m_textPart); + m_textPart = part; +} + + +class textPart& messageBuilder::textPart() +{ + return (*m_textPart); +} + + +} // vmime diff --git a/src/messageBuilder.hpp b/src/messageBuilder.hpp new file mode 100644 index 00000000..ce7d34b1 --- /dev/null +++ b/src/messageBuilder.hpp @@ -0,0 +1,99 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGEBUILDER_HPP_INCLUDED +#define VMIME_MESSAGEBUILDER_HPP_INCLUDED + + +#include "base.hpp" + +#include "mailbox.hpp" +#include "addressList.hpp" +#include "text.hpp" +#include "message.hpp" +#include "mediaType.hpp" +#include "attachment.hpp" +#include "textPart.hpp" +#include "bodyPart.hpp" + + +namespace vmime +{ + + +/** A helper for building MIME messages. + */ + +class messageBuilder +{ +public: + + messageBuilder(); + ~messageBuilder(); + +public: + + // Expeditor and recipients + const mailbox& expeditor() const { return (m_from); } + mailbox& expeditor() { return (m_from); } + + const addressList& recipients() const { return (m_to); } + addressList& recipients() { return (m_to); } + + const addressList& copyRecipients() const { return (m_cc); } + addressList& copyRecipients() { return (m_cc); } + + const addressList& blindCopyRecipients() const { return (m_bcc); } + addressList& blindCopyRecipients() { return (m_bcc); } + + // Subject + const text& subject() const { return (m_subject); } + text& subject() { return (m_subject); } + + // Attachements + void attach(attachment* attach); + const std::vector <attachment*>& attachments() const { return (m_attach); } + + // Text parts + void constructTextPart(const mediaType& type); + class textPart& textPart(); + + // Construction + message* construct() const; + +protected: + + mailbox m_from; + + addressList m_to; + addressList m_cc; + addressList m_bcc; + + text m_subject; + + class textPart* m_textPart; + + std::vector <attachment*> m_attach; +}; + + +} // vmime + + +#endif // VMIME_MESSAGEBUILDER_HPP_INCLUDED diff --git a/src/messageId.cpp b/src/messageId.cpp new file mode 100644 index 00000000..1ea99d1f --- /dev/null +++ b/src/messageId.cpp @@ -0,0 +1,184 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "messageId.hpp" +#include "utility/random.hpp" +#include "platformDependant.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +messageId::messageId() +{ +} + + +messageId::messageId(const string& id) +{ + parse(id); +} + + +messageId::messageId(const messageId& mid) + : component(), m_left(mid.m_left), m_right(mid.m_right) +{ +} + + +messageId::messageId(const string& left, const string& right) + : m_left(left), m_right(right) +{ +} + + +/* + RFC-2822: + 3.6.4. Identification fields + + msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] +*/ + +void messageId::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pstart; + + m_left.clear(); + m_right.clear(); + + unsigned int commentLevel = 0; + bool escape = false; + bool stop = false; + + for ( ; !stop && p < pend ; ++p) + { + if (escape) + { + // Ignore this character + } + else + { + switch (*p) + { + case '(': ++commentLevel; break; + case ')': --commentLevel; break; + case '\\': escape = true; break; + case '<': + { + if (commentLevel == 0) + { + stop = true; + break; + } + } + + } + } + } + + if (p < pend) + { + // Extract left part + const string::size_type leftStart = position + (p - pstart); + + while (p < pend && *p != '@') ++p; + + m_left = string(buffer.begin() + leftStart, + buffer.begin() + position + (p - pstart)); + + if (p < pend) + { + // Skip '@' + ++p; + + // Extract right part + const string::size_type rightStart = position + (p - pstart); + + while (p < pend && *p != '>') ++p; + + m_right = string(buffer.begin() + rightStart, + buffer.begin() + position + (p - pstart)); + } + } + + if (newPosition) + *newPosition = end; +} + + +const string messageId::id() const +{ + return (m_left + '@' + m_right); +} + + +void messageId::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + os << '<' << m_left << '@' << m_right << '>'; + + if (newLinePos) + *newLinePos = curLinePos + m_left.length() + m_right.length() + 3; +} + + +messageId& messageId::operator=(const messageId& source) +{ + m_left = source.m_left; + m_right = source.m_right; + return (*this); +} + + +messageId& messageId::operator=(const string& id) +{ + parse(id); + return (*this); +} + + +messageId messageId::generateId() +{ + std::ostringstream left; + + left << "vmime"; + left << '.'; + left << std::hex << utility::random::time(); + left << '.'; + left << std::hex << utility::random::process(); + left << '.'; + left << std::hex << utility::random::next(); + left << std::hex << utility::random::next(); + + return (messageId(left.str(), platformDependant::getHandler()->getHostName())); +} + + +const bool messageId::operator==(const messageId& mid) const +{ + return (m_left == mid.m_left && m_right == mid.m_right); +} + + +} // vmime diff --git a/src/messageId.hpp b/src/messageId.hpp new file mode 100644 index 00000000..e280dfaf --- /dev/null +++ b/src/messageId.hpp @@ -0,0 +1,82 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGEID_HPP_INCLUDED +#define VMIME_MESSAGEID_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Message identifier (basic type). + */ + +class messageId : public component +{ +public: + + messageId(); + messageId(const string& id); + messageId(const messageId& mid); + messageId(const string& left, const string& right); + +public: + + const string& left() const { return (m_left); } + string& left() { return (m_left); } + + const string& right() const { return (m_right); } + string& right() { return (m_right); } + +public: + + messageId& operator=(const messageId& source); + messageId& operator=(const string& id); + + const bool operator==(const messageId& mid) const; + + static messageId generateId(); + + const string id() const; + +protected: + + string m_left; + string m_right; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MESSAGEID_HPP_INCLUDED diff --git a/src/messageIdField.cpp b/src/messageIdField.cpp new file mode 100644 index 00000000..ecdb901c --- /dev/null +++ b/src/messageIdField.cpp @@ -0,0 +1,66 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "messageIdField.hpp" + + +namespace vmime +{ + + +messageIdField::messageIdField() +{ +} + + +void messageIdField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_id.parse(buffer, position, end, newPosition); +} + + +void messageIdField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + m_id.generate(os, maxLineLength, pos, newLinePos); +} + + +messageIdField& messageIdField::operator=(const messageId& mid) +{ + m_id = mid; + return (*this); +} + + +void messageIdField::copyFrom(const headerField& field) +{ + const messageIdField& source = dynamic_cast<const messageIdField&>(field); + m_id = source.m_id; + + headerField::copyFrom(field); +} + + +} // vmime diff --git a/src/messageIdField.hpp b/src/messageIdField.hpp new file mode 100644 index 00000000..4809e5ed --- /dev/null +++ b/src/messageIdField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGEIDFIELD_HPP_INCLUDED +#define VMIME_MESSAGEIDFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "messageId.hpp" + + +namespace vmime +{ + + +class messageIdField : public headerField +{ + friend class headerFieldFactory::registerer <messageIdField>; + +protected: + + messageIdField(); + +public: + + void copyFrom(const headerField& field); + + messageIdField& operator=(const messageId& mid); + + const messageId& value() const { return (m_id); } + messageId& value() { return (m_id); } + +protected: + + messageId m_id; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MESSAGEIDFIELD_HPP_INCLUDED diff --git a/src/messageParser.cpp b/src/messageParser.cpp new file mode 100644 index 00000000..d68c95e9 --- /dev/null +++ b/src/messageParser.cpp @@ -0,0 +1,321 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "messageParser.hpp" + +#include "defaultAttachment.hpp" +#include "textPartFactory.hpp" + +#include "relayField.hpp" + + +namespace vmime +{ + + +messageParser::messageParser(const string& buffer) +{ + vmime::message msg; + msg.parse(buffer); + + parse(msg); +} + + +messageParser::messageParser(const message& msg) +{ + parse(msg); +} + + +messageParser::~messageParser() +{ + free_container(m_attach); + free_container(m_textParts); + + for (std::map <attachment*, contentDispositionField*>::iterator + it = m_attachInfo.begin() ; it != m_attachInfo.end() ; ++it) + { + delete ((*it).second); + } +} + + +void messageParser::parse(const message& msg) +{ + // Header fields (if field is present, copy its value, else do nothing) +#define TRY_FIELD(x) try { x; } catch (exceptions::no_such_field) { } + TRY_FIELD(m_from = dynamic_cast<mailboxField&>(msg.header().fields.find(headerField::From)).value()); + + TRY_FIELD(m_to = dynamic_cast<addressListField&>(msg.header().fields.find(headerField::To)).value()); + TRY_FIELD(m_cc = dynamic_cast<addressListField&>(msg.header().fields.find(headerField::Cc)).value()); + TRY_FIELD(m_bcc = dynamic_cast<addressListField&>(msg.header().fields.find(headerField::Bcc)).value()); + + TRY_FIELD(m_subject = dynamic_cast<textField&>(msg.header().fields.find(headerField::Subject)).value()); +#undef TRY_FIELD + + // Date + try + { + vmime::relayField& recv = static_cast<vmime::relayField&>(msg.header().fields.find(headerField::Received)); + m_date = recv.date(); + } + catch (vmime::exceptions::no_such_field&) + { + try + { + vmime::dateField& date = static_cast<vmime::dateField&>(msg.header().fields.find(headerField::Date)); + m_date = date.value(); + } + catch (vmime::exceptions::no_such_field&) + { + m_date = datetime::now(); + } + } + + // Attachments + findAttachments(msg); + + // Text parts + findTextParts(msg, msg); +} + + +void messageParser::findAttachments(const bodyPart& part) +{ + // We simply search for parts that are not "Content-disposition: inline". + for (body::const_iterator p = part.body().parts.begin() ; p != part.body().parts.end() ; ++p) + { + const header& hdr = (*p).header(); + const body& bdy = (*p).body(); + + // Is this part an attachment? + bool isAttachment = false; + const contentDispositionField* contentDispField = NULL; + + try + { + const contentDispositionField& cdf = dynamic_cast<contentDispositionField&> + (hdr.fields.find(headerField::ContentDisposition)); + + if (cdf.value().name() != dispositionTypes::INLINE) + { + contentDispField = &cdf; + isAttachment = true; + } + } + catch (exceptions::no_such_field) + { + // No "Content-disposition" field: assume "attachment" if + // type is not "text/..." or "multipart/...". + mediaType type; + + try + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&> + (hdr.fields.find(headerField::ContentType)); + + type = ctf.value(); + } + catch (exceptions::no_such_field) + { + // No "Content-type" field: assume "application/octet-stream". + type = mediaType(mediaTypes::APPLICATION, + mediaTypes::APPLICATION_OCTET_STREAM); + } + + if (type.type() != mediaTypes::TEXT && type.type() != mediaTypes::MULTIPART) + isAttachment = true; + } + + if (isAttachment) + { + // Determine the media type of this attachment + mediaType type; + + try + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&> + (hdr.fields.find(headerField::ContentType)); + + type = ctf.value(); + } + catch (exceptions::no_such_field) + { + // No "Content-type" field: assume "application/octet-stream". + type = mediaType(mediaTypes::APPLICATION, + mediaTypes::APPLICATION_OCTET_STREAM); + } + + // Get the description (if available) + text description; + + try + { + const textField& cd = dynamic_cast<textField&> + (hdr.fields.find(headerField::ContentDescription)); + + description = cd.value(); + } + catch (exceptions::no_such_field) + { + // No description available. + } + + // Construct the attachment object + attachment* attach = new defaultAttachment + (bdy.contents(), bdy.encoding(), type, description); + + if (contentDispField != NULL) + { + m_attachInfo.insert(std::map <attachment*, contentDispositionField*>:: + value_type(attach, static_cast <contentDispositionField*> + (contentDispField->clone()))); + } + + // Add the attachment to the list + m_attach.push_back(attach); + } + + // Try to find attachments in sub-parts + if (bdy.parts.size()) + findAttachments(*p); + } +} + + +void messageParser::findTextParts(const bodyPart& msg, const bodyPart& part) +{ + // Handle the case in which the message is not multipart: if the body part is + // "text/*", take this part. + if (part.body().parts.count() == 0) + { + mediaType type(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN); + bool accept = false; + + try + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&> + (msg.header().fields.find(headerField::ContentType)); + + if (ctf.value().type() == mediaTypes::TEXT) + { + type = ctf.value(); + accept = true; + } + } + catch (exceptions::no_such_field) + { + // No "Content-type" field: assume "text/plain". + accept = true; + } + + if (accept) + { + textPart* textPart = textPartFactory::getInstance()->create(type); + textPart->parse(msg, msg, msg); + + m_textParts.push_back(textPart); + } + } + // Multipart message + else + { + findSubTextParts(msg, part); + } +} + + +bool messageParser::findSubTextParts(const bodyPart& msg, const bodyPart& part) +{ + // In general, all the text parts are contained in parallel in the same + // parent part (or message). + // So, wherever the text parts are, all we have to do is to find the first + // MIME part which is a text part. + + std::vector <const bodyPart*> textParts; + + for (body::const_iterator p = part.body().parts.begin() ; + p != part.body().parts.end() ; ++p) + { + try + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&> + ((*p).header().fields.find(headerField::ContentType)); + + if (ctf.value().type() == mediaTypes::TEXT) + { + textParts.push_back(&(*p)); + } + } + catch (exceptions::no_such_field) + { + // No "Content-type" field. + } + } + + if (textParts.size()) + { + // Okay. So we have found at least one text part + for (std::vector <const bodyPart*>::const_iterator p = textParts.begin() ; p != textParts.end() ; ++p) + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&> + ((*p)->header().fields.find(headerField::ContentType)); + + try + { + textPart* textPart = textPartFactory::getInstance()->create(ctf.value()); + textPart->parse(msg, part, **p); + + m_textParts.push_back(textPart); + } + catch (exceptions::no_factory_available& e) + { + // Content-type not recognized. + } + } + + //return true; + } + + //else + { + bool found = false; + + for (body::const_iterator p = part.body().parts.begin() ; + !found && p != part.body().parts.end() ; ++p) + { + found = findSubTextParts(msg, *p); + } + + return found; + } +} + + +const contentDispositionField* messageParser::attachmentInfo(attachment* a) const +{ + std::map <attachment*, contentDispositionField*>::const_iterator + it = m_attachInfo.find(a); + + return (it != m_attachInfo.end() ? (*it).second : NULL); +} + + +} // vmime diff --git a/src/messageParser.hpp b/src/messageParser.hpp new file mode 100644 index 00000000..d3dc1478 --- /dev/null +++ b/src/messageParser.hpp @@ -0,0 +1,98 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGEPARSER_HPP_INCLUDED +#define VMIME_MESSAGEPARSER_HPP_INCLUDED + + +#include "base.hpp" + +#include "message.hpp" +#include "attachment.hpp" + +#include "textPart.hpp" + + +namespace vmime +{ + + +/** A helper for parsing MIME messages. + */ + +class messageParser +{ +public: + + messageParser(const string& buffer); + messageParser(const message& msg); + ~messageParser(); + +public: + + // Expeditor and recipients + const mailbox& expeditor() const { return (m_from); } + + const addressList& recipients() const { return (m_to); } + const addressList& copyRecipients() const { return (m_cc); } + const addressList& blindCopyRecipients() const { return (m_bcc); } + + // Subject + const text& subject() const { return (m_subject); } + + // Date + const datetime& date() const { return (m_date); } + + // Attachments + const std::vector <attachment*>& attachments() const { return (m_attach); } + const contentDispositionField* attachmentInfo(attachment* a) const; + + // Text parts + const std::vector <textPart*>& textParts() const { return (m_textParts); } + +protected: + + mailbox m_from; + + addressList m_to; + addressList m_cc; + addressList m_bcc; + + text m_subject; + + datetime m_date; + + std::vector <attachment*> m_attach; + std::map <attachment*, contentDispositionField*> m_attachInfo; + + std::vector <textPart*> m_textParts; + + void parse(const message& msg); + + void findAttachments(const bodyPart& part); + + void findTextParts(const bodyPart& msg, const bodyPart& part); + bool findSubTextParts(const bodyPart& msg, const bodyPart& part); +}; + + +} // vmime + + +#endif // VMIME_MESSAGEPARSER_HPP_INCLUDED diff --git a/src/messaging/IMAPConnection.cpp b/src/messaging/IMAPConnection.cpp new file mode 100644 index 00000000..007cf353 --- /dev/null +++ b/src/messaging/IMAPConnection.cpp @@ -0,0 +1,263 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPTag.hpp" +#include "IMAPConnection.hpp" +#include "IMAPUtils.hpp" +#include "IMAPStore.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" + +#include <sstream> + + +namespace vmime { +namespace messaging { + + +IMAPConnection::IMAPConnection(IMAPStore* store, authenticator* auth) + : m_store(store), m_auth(auth), m_socket(NULL), m_parser(NULL), m_tag(NULL), + m_hierarchySeparator('\0'), m_state(STATE_NONE), m_timeoutHandler(NULL) +{ +} + + +IMAPConnection::~IMAPConnection() +{ + if (isConnected()) + disconnect(); + else if (m_socket) + internalDisconnect(); + + delete (m_tag); + delete (m_parser); +} + + +void IMAPConnection::connect() +{ + if (isConnected()) + throw exceptions::already_connected(); + + m_state = STATE_NONE; + m_hierarchySeparator = '\0'; + + const string address = m_store->session().properties()[m_store->infos().propertyPrefix() + "server.address"]; + const port_t port = m_store->session().properties().get(m_store->infos().propertyPrefix() + "server.port", m_store->infos().defaultPort()); + + // Create the time-out handler + if (session().properties().exists + (m_store->infos().propertyPrefix() + "timeout.factory")) + { + timeoutHandlerFactory* tof = platformDependant::getHandler()-> + getTimeoutHandlerFactory(session().properties() + [m_store->infos().propertyPrefix() + "timeout.factory"]); + + m_timeoutHandler = tof->create(); + } + + // Create and connect the socket + socketFactory* sf = platformDependant::getHandler()->getSocketFactory + (m_store->session().properties().get + (m_store->infos().propertyPrefix() + "server.socket-factory", string("default"))); + + m_socket = sf->create(); + m_socket->connect(address, port); + + delete (m_tag); + m_tag = new IMAPTag(); + + delete (m_parser); + m_parser = new IMAPParser(m_tag, m_socket, m_timeoutHandler); + + + setState(STATE_NON_AUTHENTICATED); + + + // Connection greeting + // + // eg: C: <connection to server> + // --- S: * OK mydomain.org IMAP4rev1 v12.256 server ready + + utility::auto_ptr <IMAPParser::greeting> greet(m_parser->readGreeting()); + + if (greet->resp_cond_bye()) + { + internalDisconnect(); + throw exceptions::connection_greeting_error(m_parser->lastLine()); + } + else if (greet->resp_cond_auth()->condition() != IMAPParser::resp_cond_auth::PREAUTH) + { + const authenticationInfos auth = m_auth->requestAuthInfos(); + + // TODO: other authentication methods + + send(true, "LOGIN " + IMAPUtils::quoteString(auth.username()) + + " " + IMAPUtils::quoteString(auth.password()), true); + + utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse()); + + if (resp->isBad()) + { + internalDisconnect(); + throw exceptions::command_error("LOGIN", m_parser->lastLine()); + } + else if (resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + internalDisconnect(); + throw exceptions::authentication_error(m_parser->lastLine()); + } + } + + // Get the hierarchy separator character + initHierarchySeparator(); + + // Switch to state "Authenticated" + setState(STATE_AUTHENTICATED); +} + + +const bool IMAPConnection::isConnected() const +{ + return (m_socket && m_socket->isConnected() && + (m_state == STATE_AUTHENTICATED || m_state == STATE_SELECTED)); +} + + +void IMAPConnection::disconnect() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + internalDisconnect(); +} + + +void IMAPConnection::internalDisconnect() +{ + send(true, "LOGOUT", true); + + m_socket->disconnect(); + + delete (m_socket); + m_socket = NULL; + + delete (m_timeoutHandler); + m_timeoutHandler = NULL; + + m_state = STATE_LOGOUT; +} + + +void IMAPConnection::initHierarchySeparator() +{ + send(true, "LIST \"\" \"\"", true); + + utility::auto_ptr <IMAPParser::response> resp(m_parser->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + internalDisconnect(); + throw exceptions::command_error("LIST", m_parser->lastLine(), "bad response"); + } + + const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = + resp->continue_req_or_response_data(); + + if (respDataList.size() < 1 || respDataList[0]->response_data() == NULL) + { + internalDisconnect(); + throw exceptions::command_error("LIST", m_parser->lastLine(), "unexpected response"); + } + + const IMAPParser::mailbox_data* mailboxData = + static_cast <const IMAPParser::response_data*>(respDataList[0]->response_data())-> + mailbox_data(); + + if (mailboxData == NULL || mailboxData->type() != IMAPParser::mailbox_data::LIST) + { + internalDisconnect(); + throw exceptions::command_error("LIST", m_parser->lastLine(), "invalid type"); + } + + if (mailboxData->mailbox_list()->quoted_char() == '\0') + { + internalDisconnect(); + throw exceptions::command_error("LIST", m_parser->lastLine(), "no hierarchy separator"); + } + + m_hierarchySeparator = mailboxData->mailbox_list()->quoted_char(); +} + + +void IMAPConnection::send(bool tag, const string& what, bool end) +{ +#if VMIME_DEBUG + std::ostringstream oss; + + if (tag) + { + ++(*m_tag); + + oss << (string) *m_tag; + oss << " "; + } + + oss << what; + + if (end) + oss << "\r\n"; + + m_socket->send(oss.str()); +#else + if (tag) + { + ++(*m_tag); + + m_socket->send(*m_tag); + m_socket->send(" "); + } + + m_socket->send(what); + + if (end) + { + m_socket->send("\r\n"); + } +#endif +} + + +void IMAPConnection::sendRaw(const char* buffer, const int count) +{ + m_socket->sendRaw(buffer, count); +} + + +IMAPParser::response* IMAPConnection::readResponse(IMAPParser::literalHandler* lh) +{ + return (m_parser->readResponse(lh)); +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPConnection.hpp b/src/messaging/IMAPConnection.hpp new file mode 100644 index 00000000..ae92f1e3 --- /dev/null +++ b/src/messaging/IMAPConnection.hpp @@ -0,0 +1,110 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPCONNECTION_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPCONNECTION_HPP_INCLUDED + + +#include "authenticator.hpp" +#include "socket.hpp" +#include "../config.hpp" +#include "timeoutHandler.hpp" + +#include "IMAPParser.hpp" + + +namespace vmime { +namespace messaging { + + +class IMAPTag; +class IMAPStore; + + +class IMAPConnection +{ +public: + + IMAPConnection(IMAPStore* store, authenticator* auth); + ~IMAPConnection(); + + + void connect(); + const bool isConnected() const; + void disconnect(); + + + enum ProtocolStates + { + STATE_NONE, + STATE_NON_AUTHENTICATED, + STATE_AUTHENTICATED, + STATE_SELECTED, + STATE_LOGOUT + }; + + const ProtocolStates state() const { return (m_state); } + void setState(const ProtocolStates state) { m_state = state; } + + + const char hierarchySeparator() const { return (m_hierarchySeparator); } + + + void send(bool tag, const string& what, bool end); + void sendRaw(const char* buffer, const int count); + + IMAPParser::response* readResponse(IMAPParser::literalHandler* lh = NULL); + + + const IMAPTag* tag() const { return (m_tag); } + const IMAPParser* parser() const { return (m_parser); } + + const IMAPStore* store() const { return (m_store); } + IMAPStore* store() { return (m_store); } + +private: + + IMAPStore* m_store; + + authenticator* m_auth; + + socket* m_socket; + + IMAPParser* m_parser; + + IMAPTag* m_tag; + + char m_hierarchySeparator; + + ProtocolStates m_state; + + timeoutHandler* m_timeoutHandler; + + + void internalDisconnect(); + + void initHierarchySeparator(); +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPCONNECTION_HPP_INCLUDED diff --git a/src/messaging/IMAPFolder.cpp b/src/messaging/IMAPFolder.cpp new file mode 100644 index 00000000..717e4817 --- /dev/null +++ b/src/messaging/IMAPFolder.cpp @@ -0,0 +1,1490 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPFolder.hpp" + +#include "IMAPStore.hpp" +#include "IMAPParser.hpp" +#include "IMAPMessage.hpp" +#include "IMAPUtils.hpp" +#include "IMAPConnection.hpp" + +#include "../exception.hpp" +#include "../utility/smartPtr.hpp" + +#include <algorithm> +#include <sstream> + + +namespace vmime { +namespace messaging { + + +IMAPFolder::IMAPFolder(const folder::path& path, IMAPStore* store, const int type, const int flags) + : m_store(store), m_connection(m_store->connection()), m_path(path), + m_name(path.last()), m_mode(-1), m_open(false), m_type(type), m_flags(flags), + m_messageCount(0), m_uidValidity(0) +{ + m_store->registerFolder(this); +} + + +IMAPFolder::~IMAPFolder() +{ + if (m_store) + { + if (m_open) + close(false); + + m_store->unregisterFolder(this); + } + else if (m_open) + { + delete (m_connection); + onClose(); + } +} + + +const int IMAPFolder::mode() const +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + return (m_mode); +} + + +const int IMAPFolder::type() +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + // Root folder + if (m_path.empty()) + { + return (TYPE_CONTAINS_FOLDERS); + } + else + { + if (m_type == TYPE_UNDEFINED) + testExistAndGetType(); + + return (m_type); + } +} + + +const int IMAPFolder::flags() +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + // Root folder + if (m_path.empty()) + { + return (FLAG_CHILDREN | FLAG_NO_OPEN); + } + else + { + if (m_flags == FLAG_UNDEFINED) + testExistAndGetType(); + + return (m_flags); + } +} + + +const folder::path::component IMAPFolder::name() const +{ + return (m_name); +} + + +const folder::path IMAPFolder::fullPath() const +{ + return (m_path); +} + + +void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + // Open a connection for this folder + IMAPConnection* connection = + new IMAPConnection(m_store, m_store->oneTimeAuthenticator()); + + try + { + connection->connect(); + + // Emit the "SELECT" command + // + // Example: C: A142 SELECT INBOX + // S: * 172 EXISTS + // S: * 1 RECENT + // S: * OK [UNSEEN 12] Message 12 is first unseen + // S: * OK [UIDVALIDITY 3857529045] UIDs valid + // S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) + // S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited + // S: A142 OK [READ-WRITE] SELECT completed + + std::ostringstream oss; + + if (mode == MODE_READ_ONLY) + oss << "EXAMINE "; + else + oss << "SELECT "; + + oss << IMAPUtils::quoteString(IMAPUtils::pathToString + (connection->hierarchySeparator(), fullPath())); + + connection->send(true, oss.str(), true); + + // Read the response + utility::auto_ptr <IMAPParser::response> resp(connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("SELECT", + connection->parser()->lastLine(), "bad response"); + } + + const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = + resp->continue_req_or_response_data(); + + for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("SELECT", + connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::response_data* responseData = (*it)->response_data(); + + // OK Untagged responses: UNSEEN, PERMANENTFLAGS, UIDVALIDITY (optional) + if (responseData->resp_cond_state()) + { + const IMAPParser::resp_text_code* code = + responseData->resp_cond_state()->resp_text()->resp_text_code(); + + if (code != NULL) + { + switch (code->type()) + { + case IMAPParser::resp_text_code::UIDVALIDITY: + + m_uidValidity = code->nz_number()->value(); + break; + + default: + + break; + } + } + } + // Untagged responses: FLAGS, EXISTS, RECENT (required) + else if (responseData->mailbox_data()) + { + switch (responseData->mailbox_data()->type()) + { + default: break; + + case IMAPParser::mailbox_data::FLAGS: + { + m_type = IMAPUtils::folderTypeFromFlags + (responseData->mailbox_data()->mailbox_flag_list()); + + m_flags = IMAPUtils::folderFlagsFromFlags + (responseData->mailbox_data()->mailbox_flag_list()); + + break; + } + case IMAPParser::mailbox_data::EXISTS: + { + m_messageCount = responseData->mailbox_data()->number()->value(); + break; + } + case IMAPParser::mailbox_data::RECENT: + { + // TODO + break; + } + + } + } + } + + // Check for access mode (read-only or read-write) + const IMAPParser::resp_text_code* respTextCode = resp->response_done()-> + response_tagged()->resp_cond_state()->resp_text()->resp_text_code(); + + if (respTextCode) + { + const int openMode = + (respTextCode->type() == IMAPParser::resp_text_code::READ_WRITE) + ? MODE_READ_WRITE : MODE_READ_ONLY; + + if (failIfModeIsNotAvailable && + mode == MODE_READ_WRITE && openMode == MODE_READ_ONLY) + { + throw exceptions::operation_not_supported(); + } + } + + + m_connection = connection; + m_open = true; + m_mode = mode; + } + catch (std::exception&) + { + delete (connection); + throw; + } +} + + +void IMAPFolder::close(const bool expunge) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + IMAPConnection* oldConnection = m_connection; + + // Emit the "CLOSE" command to expunge messages marked + // as deleted (this is fastest than "EXPUNGE") + if (expunge) + { + if (m_mode == MODE_READ_ONLY) + throw exceptions::operation_not_supported(); + + oldConnection->send(true, "CLOSE", true); + } + + // Close this folder connection + oldConnection->disconnect(); + + // Now use default store connection + m_connection = m_store->connection(); + + m_open = false; + m_mode = -1; + + m_uidValidity = 0; + + onClose(); + + delete (oldConnection); +} + + +void IMAPFolder::onClose() +{ + for (std::vector <IMAPMessage*>::iterator it = m_messages.begin() ; + it != m_messages.end() ; ++it) + { + (*it)->onFolderClosed(); + } + + m_messages.clear(); +} + + +void IMAPFolder::create(const int type) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (isOpen()) + throw exceptions::illegal_state("Folder is open"); + else if (exists()) + throw exceptions::illegal_state("Folder already exists"); + + // Emit the "CREATE" command + // + // Example: C: A003 CREATE owatagusiam/ + // S: A003 OK CREATE completed + // C: A004 CREATE owatagusiam/blurdybloop + // S: A004 OK CREATE completed + + string mailbox = IMAPUtils::pathToString + (m_connection->hierarchySeparator(), fullPath()); + + if (type & TYPE_CONTAINS_FOLDERS) + mailbox += m_connection->hierarchySeparator(); + + std::ostringstream oss; + oss << "CREATE " << IMAPUtils::quoteString(mailbox); + + m_connection->send(true, oss.str(), true); + + + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("CREATE", + m_connection->parser()->lastLine(), "bad response"); + } + + // Notify folder created + events::folderEvent event(this, events::folderEvent::TYPE_CREATED, m_path, m_path); + notifyFolder(event); +} + + +const bool IMAPFolder::exists() +{ + if (!isOpen() && !m_store) + throw exceptions::illegal_state("Store disconnected"); + + return (testExistAndGetType() != TYPE_UNDEFINED); +} + + +const int IMAPFolder::testExistAndGetType() +{ + m_type = TYPE_UNDEFINED; + + // To test whether a folder exists, we simple list it using + // the "LIST" command, and there should be one unique mailbox + // with this name... + // + // Eg. Test whether '/foo/bar' exists + // + // C: a005 list "" foo/bar + // S: * LIST (\NoSelect) "/" foo/bar + // S: a005 OK LIST completed + // + // ==> OK, exists + // + // Test whether '/foo/bar/zap' exists + // + // C: a005 list "" foo/bar/zap + // S: a005 OK LIST completed + // + // ==> NO, does not exist + + std::ostringstream oss; + oss << "LIST \"\" "; + oss << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), fullPath())); + + m_connection->send(true, oss.str(), true); + + + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("LIST", + m_connection->parser()->lastLine(), "bad response"); + } + + // Check whether the result mailbox list contains this folder + const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = + resp->continue_req_or_response_data(); + + for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("LIST", + m_connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::mailbox_data* mailboxData = + (*it)->response_data()->mailbox_data(); + + // We are only interested in responses of type "LIST" + if (mailboxData != NULL && mailboxData->type() == IMAPParser::mailbox_data::LIST) + { + // Get the folder type/flags at the same time + m_type = IMAPUtils::folderTypeFromFlags + (mailboxData->mailbox_list()->mailbox_flag_list()); + + m_flags = IMAPUtils::folderFlagsFromFlags + (mailboxData->mailbox_list()->mailbox_flag_list()); + } + } + + return (m_type); +} + + +const bool IMAPFolder::isOpen() const +{ + return (m_open); +} + + +message* IMAPFolder::getMessage(const int num) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + if (num < 1 || num > m_messageCount) + throw exceptions::message_not_found(); + + return new IMAPMessage(this, num); +} + + +std::vector <message*> IMAPFolder::getMessages(const int from, const int to) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::vector <message*> v; + + for (int i = from ; i <= to ; ++i) + v.push_back(new IMAPMessage(this, i)); + + return (v); +} + + +std::vector <message*> IMAPFolder::getMessages(const std::vector <int>& nums) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::vector <message*> v; + + for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it) + v.push_back(new IMAPMessage(this, *it)); + + return (v); +} + + +const int IMAPFolder::getMessageCount() +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + return (m_messageCount); +} + + +folder* IMAPFolder::getFolder(const folder::path::component& name) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + return new IMAPFolder(m_path / name, m_store); +} + + +std::vector <folder*> IMAPFolder::getFolders(const bool recursive) +{ + if (!isOpen() && !m_store) + throw exceptions::illegal_state("Store disconnected"); + + // Eg. List folders in '/foo/bar' + // + // C: a005 list "foo/bar" * + // S: * LIST (\NoSelect) "/" foo/bar + // S: * LIST (\NoInferiors) "/" foo/bar/zap + // S: a005 OK LIST completed + + std::ostringstream oss; + oss << "LIST "; + oss << IMAPUtils::quoteString + (IMAPUtils::pathToString(m_connection->hierarchySeparator(), fullPath())); + + if (recursive) + oss << " *"; + else + oss << " %"; + + m_connection->send(true, oss.str(), true); + + + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("LIST", m_connection->parser()->lastLine(), "bad response"); + } + + const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = + resp->continue_req_or_response_data(); + + + std::vector <folder*> v; + + try + { + for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("LIST", + m_connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::mailbox_data* mailboxData = + (*it)->response_data()->mailbox_data(); + + if (mailboxData == NULL || mailboxData->type() != IMAPParser::mailbox_data::LIST) + continue; + + // Get folder path + const class IMAPParser::mailbox* mailbox = + mailboxData->mailbox_list()->mailbox(); + + folder::path path = IMAPUtils::stringToPath + (mailboxData->mailbox_list()->quoted_char(), mailbox->name()); + + if (recursive || m_path.isDirectParentOf(path)) + { + // Append folder to list + const class IMAPParser::mailbox_flag_list* mailbox_flag_list = + mailboxData->mailbox_list()->mailbox_flag_list(); + + v.push_back(new IMAPFolder(path, m_store, + IMAPUtils::folderTypeFromFlags(mailbox_flag_list), + IMAPUtils::folderFlagsFromFlags(mailbox_flag_list))); + } + } + } + catch (std::exception&) + { + for (std::vector <folder*>::iterator it = v.begin() ; it != v.end() ; ++it) + delete (*it); + + throw; + } + + return (v); +} + + +void IMAPFolder::fetchMessages(std::vector <message*>& msg, const int options, + progressionListener* progress) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + const int total = msg.size(); + int current = 0; + + if (progress) + progress->start(total); + + for (std::vector <message*>::iterator it = msg.begin() ; + it != msg.end() ; ++it) + { + dynamic_cast <IMAPMessage*>(*it)->fetch(this, options); + + if (progress) + progress->progress(++current, total); + } + + if (progress) + progress->stop(total); +} + + +void IMAPFolder::fetchMessage(message* msg, const int options) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + dynamic_cast <IMAPMessage*>(msg)->fetch(this, options); +} + + +const int IMAPFolder::getFetchCapabilities() const +{ + return (FETCH_ENVELOPE | FETCH_CONTENT_INFO | FETCH_STRUCTURE | + FETCH_FLAGS | FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID); +} + + +folder* IMAPFolder::getParent() +{ + return (m_path.empty() ? NULL : new IMAPFolder(m_path.parent(), m_store)); +} + + +const class store& IMAPFolder::store() const +{ + return (*m_store); +} + + +class store& IMAPFolder::store() +{ + return (*m_store); +} + + +void IMAPFolder::registerMessage(IMAPMessage* msg) +{ + m_messages.push_back(msg); +} + + +void IMAPFolder::unregisterMessage(IMAPMessage* msg) +{ + std::remove(m_messages.begin(), m_messages.end(), msg); +} + + +void IMAPFolder::onStoreDisconnected() +{ + m_store = NULL; +} + + +void IMAPFolder::deleteMessage(const int num) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Build the request text + std::ostringstream command; + command << "STORE " << num << " +FLAGS.SILENT (\\Deleted)"; + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STORE", + m_connection->parser()->lastLine(), "bad response"); + } + + // Update local flags + for (std::vector <IMAPMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if ((*it)->number() == num && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags |= message::FLAG_DELETED; + } + } + + // Notify message flags changed + std::vector <int> nums; + nums.push_back(num); + + events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums); + + notifyMessageChanged(event); +} + + +void IMAPFolder::deleteMessages(const int from, const int to) +{ + if (from < 1 || (to < from && to != -1)) + throw exceptions::invalid_argument(); + + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Build the request text + std::ostringstream command; + command << "STORE " << from << ":"; + + if (to == -1) command << m_messageCount; + else command << to; + + command << " +FLAGS.SILENT (\\Deleted)"; + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STORE", + m_connection->parser()->lastLine(), "bad response"); + } + + // Update local flags + const int to2 = (to == -1) ? m_messageCount : to; + const int count = to - from + 1; + + for (std::vector <IMAPMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if ((*it)->number() >= from && (*it)->number() <= to2 && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags |= message::FLAG_DELETED; + } + } + + // Notify message flags changed + std::vector <int> nums; + nums.resize(count); + + for (int i = from, j = 0 ; i <= to2 ; ++i, ++j) + nums[j] = i; + + events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums); + + notifyMessageChanged(event); +} + + +void IMAPFolder::deleteMessages(const std::vector <int>& nums) +{ + if (nums.empty()) + throw exceptions::invalid_argument(); + + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Sort the list of message numbers + std::vector <int> list; + + list.resize(nums.size()); + std::copy(nums.begin(), nums.end(), list.begin()); + + std::sort(list.begin(), list.end()); + + // Build the request text + std::ostringstream command; + command << "STORE "; + command << IMAPUtils::listToSet(list, m_messageCount, true); + command << " +FLAGS.SILENT (\\Deleted)"; + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STORE", + m_connection->parser()->lastLine(), "bad response"); + } + + // Update local flags + for (std::vector <IMAPMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if (std::binary_search(list.begin(), list.end(), (*it)->number())) + { + if ((*it)->m_flags != message::FLAG_UNDEFINED) + (*it)->m_flags |= message::FLAG_DELETED; + } + } + + // Notify message flags changed + events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, list); + + notifyMessageChanged(event); +} + + +void IMAPFolder::setMessageFlags(const int from, const int to, const int flags, const int mode) +{ + if (from < 1 || (to < from && to != -1)) + throw exceptions::invalid_argument(); + + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + std::ostringstream oss; + + if (to == -1) + oss << from << ":*"; + else + oss << from << ":" << to; + + setMessageFlags(oss.str(), flags, mode); + + // Update local flags + const int to2 = (to == -1) ? m_messageCount : to; + const int count = to - from + 1; + + switch (mode) + { + case message::FLAG_MODE_ADD: + { + for (std::vector <IMAPMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if ((*it)->number() >= from && (*it)->number() <= to2 && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags |= flags; + } + } + + break; + } + case message::FLAG_MODE_REMOVE: + { + for (std::vector <IMAPMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if ((*it)->number() >= from && (*it)->number() <= to2 && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags &= ~flags; + } + } + + break; + } + default: + case message::FLAG_MODE_SET: + { + for (std::vector <IMAPMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if ((*it)->number() >= from && (*it)->number() <= to2 && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags = flags; + } + } + + break; + } + + } + + // Notify message flags changed + std::vector <int> nums; + nums.resize(count); + + for (int i = from, j = 0 ; i <= to2 ; ++i, ++j) + nums[j] = i; + + events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums); + + notifyMessageChanged(event); +} + + +void IMAPFolder::setMessageFlags(const std::vector <int>& nums, const int flags, const int mode) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Sort the list of message numbers + std::vector <int> list; + + list.resize(nums.size()); + std::copy(nums.begin(), nums.end(), list.begin()); + + std::sort(list.begin(), list.end()); + + // Delegates call + setMessageFlags(IMAPUtils::listToSet(list, m_messageCount, true), flags, mode); + + // Update local flags + switch (mode) + { + case message::FLAG_MODE_ADD: + { + for (std::vector <IMAPMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if (std::binary_search(list.begin(), list.end(), (*it)->number()) && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags |= flags; + } + } + + break; + } + case message::FLAG_MODE_REMOVE: + { + for (std::vector <IMAPMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if (std::binary_search(list.begin(), list.end(), (*it)->number()) && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags &= ~flags; + } + } + + break; + } + default: + case message::FLAG_MODE_SET: + { + for (std::vector <IMAPMessage*>::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if (std::binary_search(list.begin(), list.end(), (*it)->number()) && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags = flags; + } + } + + break; + } + + } + + // Notify message flags changed + events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums); + + notifyMessageChanged(event); +} + + +void IMAPFolder::setMessageFlags(const string& set, const int flags, const int mode) +{ + // Build the request text + std::ostringstream command; + command << "STORE " << set; + + switch (mode) + { + case message::FLAG_MODE_ADD: command << " +FLAGS.SILENT "; break; + case message::FLAG_MODE_REMOVE: command << " -FLAGS.SILENT "; break; + default: + case message::FLAG_MODE_SET: command << " FLAGS.SILENT "; break; + } + + const string flagList = IMAPUtils::messageFlagList(flags); + + if (!flagList.empty()) + { + command << flagList; + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STORE", + m_connection->parser()->lastLine(), "bad response"); + } + } +} + + +void IMAPFolder::addMessage(vmime::message* msg, const int flags, + vmime::datetime* date, progressionListener* progress) +{ + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + msg->generate(ossAdapter); + + const std::string& str = oss.str(); + utility::inputStreamStringAdapter strAdapter(str); + + addMessage(strAdapter, str.length(), flags, date, progress); +} + + +void IMAPFolder::addMessage(utility::inputStream& is, const int size, const int flags, + vmime::datetime* date, progressionListener* progress) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Build the request text + std::ostringstream command; + command << "APPEND " << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), fullPath())) << ' '; + + const string flagList = IMAPUtils::messageFlagList(flags); + + if (flags != message::FLAG_UNDEFINED && !flagList.empty()) + { + command << flagList; + command << ' '; + } + + if (date != NULL) + { + command << IMAPUtils::dateTime(*date); + command << ' '; + } + + command << '{' << size << '}'; + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + bool ok = false; + const std::vector <IMAPParser::continue_req_or_response_data*>& respList + = resp->continue_req_or_response_data(); + + for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator + it = respList.begin() ; !ok && (it != respList.end()) ; ++it) + { + if ((*it)->continue_req()) + ok = true; + } + + if (!ok) + { + throw exceptions::command_error("APPEND", + m_connection->parser()->lastLine(), "bad response"); + } + + // Send message data + const int total = size; + int current = 0; + + if (progress) + progress->start(total); + + char buffer[65536]; + + while (!is.eof()) + { + // Read some data from the input stream + const int read = is.read(buffer, sizeof(buffer)); + current += read; + + // Put read data into socket output stream + m_connection->sendRaw(buffer, read); + + // Notify progression + if (progress) + progress->progress(current, total); + } + + m_connection->send(false, "", true); + + if (progress) + progress->stop(total); + + // Get the response + utility::auto_ptr <IMAPParser::response> finalResp(m_connection->readResponse()); + + if (finalResp->isBad() || finalResp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("APPEND", + m_connection->parser()->lastLine(), "bad response"); + } + + // Notify message added + std::vector <int> nums; + nums.push_back(m_messageCount + 1); + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + m_messageCount++; + notifyMessageCount(event); +} + + +void IMAPFolder::expunge() +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Send the request + m_connection->send(true, "EXPUNGE", true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("EXPUNGE", + m_connection->parser()->lastLine(), "bad response"); + } + + // Update the numbering of the messages + const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = + resp->continue_req_or_response_data(); + + std::vector <int> nums; + + for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("EXPUNGE", + m_connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::message_data* messageData = + (*it)->response_data()->message_data(); + + // We are only interested in responses of type "EXPUNGE" + if (messageData == NULL || + messageData->type() != IMAPParser::message_data::EXPUNGE) + { + continue; + } + + const int number = messageData->number(); + + nums.push_back(number); + + for (std::vector <IMAPMessage*>::iterator jt = + m_messages.begin() ; jt != m_messages.end() ; ++jt) + { + if ((*jt)->m_num == number) + (*jt)->m_expunged = true; + else if ((*jt)->m_num > number) + (*jt)->m_num--; + } + } + + // Notify message expunged + events::messageCountEvent event(this, events::messageCountEvent::TYPE_REMOVED, nums); + + m_messageCount -= nums.size(); + notifyMessageCount(event); +} + + +void IMAPFolder::rename(const folder::path& newPath) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (isOpen()) + throw exceptions::illegal_state("Folder open"); + + // Build the request text + std::ostringstream command; + command << "RENAME "; + command << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), fullPath())) << " "; + command << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), newPath)); + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("RENAME", + m_connection->parser()->lastLine(), "bad response"); + } + + // Notify folder renamed + folder::path oldPath(m_path); + + m_path = newPath; + m_name = newPath.last(); + + events::folderEvent event(this, events::folderEvent::TYPE_RENAMED, oldPath, newPath); + notifyFolder(event); +} + + +void IMAPFolder::copyMessage(const folder::path& dest, const int num) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + // Construct set + std::ostringstream set; + set << num; + + // Delegate message copy + copyMessages(set.str(), dest); + + // Notify message count changed + std::vector <int> nums; + nums.push_back(num); + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == dest) + { + (*it)->m_messageCount++; + (*it)->notifyMessageCount(event); + } + } +} + + +void IMAPFolder::copyMessages(const folder::path& dest, const int from, const int to) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (from < 1 || (to < from && to != -1)) + throw exceptions::invalid_argument(); + + // Construct set + std::ostringstream set; + + if (to == -1) + set << from << ":*"; + else + set << from << ":" << to; + + // Delegate message copy + copyMessages(set.str(), dest); + + // Notify message count changed + const int to2 = (to == -1) ? m_messageCount : to; + const int count = to - from + 1; + + std::vector <int> nums; + nums.resize(count); + + for (int i = from, j = 0 ; i <= to2 ; ++i, ++j) + nums[j] = i; + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == dest) + { + (*it)->m_messageCount += count; + (*it)->notifyMessageCount(event); + } + } +} + + +void IMAPFolder::copyMessages(const folder::path& dest, const std::vector <int>& nums) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + // Delegate message copy + copyMessages(IMAPUtils::listToSet(nums, m_messageCount), dest); + + // Notify message count changed + const int count = nums.size(); + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == dest) + { + (*it)->m_messageCount += count; + (*it)->notifyMessageCount(event); + } + } +} + + +void IMAPFolder::copyMessages(const string& set, const folder::path& dest) +{ + // Build the request text + std::ostringstream command; + command << "COPY " << set << " "; + command << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), dest)); + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("COPY", + m_connection->parser()->lastLine(), "bad response"); + } +} + + +void IMAPFolder::status(int& count, int& unseen) +{ + count = 0; + unseen = 0; + + // Build the request text + std::ostringstream command; + command << "STATUS "; + command << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), fullPath())); + command << "(MESSAGES UNSEEN)"; + + // Send the request + m_store->m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp(m_store->m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STATUS", + m_store->m_connection->parser()->lastLine(), "bad response"); + } + + const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = + resp->continue_req_or_response_data(); + + for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("STATUS", + m_store->m_connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::response_data* responseData = (*it)->response_data(); + + if (responseData->mailbox_data() && + responseData->mailbox_data()->type() == IMAPParser::mailbox_data::STATUS) + { + const std::vector <IMAPParser::status_info*>& statusList = + responseData->mailbox_data()->status_info_list(); + + for (std::vector <IMAPParser::status_info*>::const_iterator + jt = statusList.begin() ; jt != statusList.end() ; ++jt) + { + switch ((*jt)->status_att()->type()) + { + case IMAPParser::status_att::MESSAGES: + + count = (*jt)->number()->value(); + break; + + case IMAPParser::status_att::UNSEEN: + + unseen = (*jt)->number()->value(); + break; + + default: + + break; + } + } + } + } + + // Notify message count changed (new messages) + if (m_messageCount != count) + { + const int oldCount = m_messageCount; + + m_messageCount = count; + + if (count > oldCount) + { + std::vector <int> nums; + nums.reserve(count - oldCount); + + for (int i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j) + nums[j] = i; + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list <IMAPFolder*>::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == m_path) + { + (*it)->m_messageCount = count; + (*it)->notifyMessageCount(event); + } + } + } + } +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPFolder.hpp b/src/messaging/IMAPFolder.hpp new file mode 100644 index 00000000..bbedb030 --- /dev/null +++ b/src/messaging/IMAPFolder.hpp @@ -0,0 +1,154 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPFOLDER_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPFOLDER_HPP_INCLUDED + + +#include <vector> +#include <map> + +#include "../types.hpp" +#include "folder.hpp" + + +namespace vmime { +namespace messaging { + + +class IMAPStore; +class IMAPMessage; +class IMAPConnection; + + +/** IMAP folder implementation. + */ + +class IMAPFolder : public folder +{ +protected: + + friend class IMAPStore; + friend class IMAPMessage; + + + IMAPFolder(const folder::path& path, IMAPStore* store, const int type = TYPE_UNDEFINED, const int flags = FLAG_UNDEFINED); + IMAPFolder(const IMAPFolder&) : folder() { } + + ~IMAPFolder(); + +public: + + const int mode() const; + + const int type(); + + const int flags(); + + const folder::path::component name() const; + const folder::path fullPath() const; + + void open(const int mode, bool failIfModeIsNotAvailable = false); + void close(const bool expunge); + void create(const int type); + + const bool exists(); + + const bool isOpen() const; + + message* getMessage(const int num); + std::vector <message*> getMessages(const int from = 1, const int to = -1); + std::vector <message*> getMessages(const std::vector <int>& nums); + const int getMessageCount(); + + folder* getFolder(const folder::path::component& name); + std::vector <folder*> getFolders(const bool recursive = false); + + void rename(const folder::path& newPath); + + void deleteMessage(const int num); + void deleteMessages(const int from = 1, const int to = -1); + void deleteMessages(const std::vector <int>& nums); + + void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET); + void setMessageFlags(const std::vector <int>& nums, const int flags, const int mode = message::FLAG_MODE_SET); + + void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + + void copyMessage(const folder::path& dest, const int num); + void copyMessages(const folder::path& dest, const int from = 1, const int to = -1); + void copyMessages(const folder::path& dest, const std::vector <int>& nums); + + void status(int& count, int& unseen); + + void expunge(); + + folder* getParent(); + + const class store& store() const; + class store& store(); + + + void fetchMessages(std::vector <message*>& msg, const int options, progressionListener* progress = NULL); + void fetchMessage(message* msg, const int options); + + const int getFetchCapabilities() const; + +private: + + void registerMessage(IMAPMessage* msg); + void unregisterMessage(IMAPMessage* msg); + + void onStoreDisconnected(); + + void onClose(); + + const int testExistAndGetType(); + + void setMessageFlags(const string& set, const int flags, const int mode); + + void copyMessages(const string& set, const folder::path& dest); + + + IMAPStore* m_store; + IMAPConnection* m_connection; + + folder::path m_path; + folder::path::component m_name; + + int m_mode; + bool m_open; + + int m_type; + int m_flags; + + int m_messageCount; + + int m_uidValidity; + + std::vector <IMAPMessage*> m_messages; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPFOLDER_HPP_INCLUDED diff --git a/src/messaging/IMAPMessage.cpp b/src/messaging/IMAPMessage.cpp new file mode 100644 index 00000000..1c2dce7c --- /dev/null +++ b/src/messaging/IMAPMessage.cpp @@ -0,0 +1,843 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPParser.hpp" +#include "IMAPMessage.hpp" +#include "IMAPFolder.hpp" +#include "IMAPStore.hpp" +#include "IMAPConnection.hpp" +#include "IMAPUtils.hpp" + +#include <sstream> +#include <iterator> + + +namespace vmime { +namespace messaging { + + +// +// IMAPheader +// + + +class IMAPheader : public header +{ +public: + + IMAPheader() + { + } + + void parse(const string& str) + { + header::parse(str); + } +}; + + + +// +// IMAPpart +// + +class IMAPpart : public part +{ +private: + + IMAPpart(IMAPpart* parent, const int number, const IMAPParser::body_type_mpart* mpart); + IMAPpart(IMAPpart* parent, const int number, const IMAPParser::body_type_1part* part); + +public: + + const class structure& structure() const; + class structure& structure(); + + const IMAPpart* parent() const { return (m_parent); } + + const mediaType& type() const { return (m_mediaType); } + const int size() const { return (m_size); } + const int number() const { return (m_number); } + + const class header& header() const; + + + static IMAPpart* create(IMAPpart* parent, const int number, const IMAPParser::body* body) + { + if (body->body_type_mpart()) + return new IMAPpart(parent, number, body->body_type_mpart()); + else + return new IMAPpart(parent, number, body->body_type_1part()); + } + + + IMAPheader& getOrCreateHeader() + { + if (m_header != NULL) + return (*m_header); + else + return (*(m_header = new IMAPheader())); + } + +private: + + IMAPstructure* m_structure; + IMAPpart* m_parent; + IMAPheader* m_header; + + int m_number; + int m_size; + mediaType m_mediaType; +}; + + + +// +// IMAPstructure +// + + +class IMAPstructure : public structure +{ +private: + + IMAPstructure() + { + } + +public: + + IMAPstructure(const IMAPParser::body* body) + { + m_parts.push_back(IMAPpart::create(NULL, 1, body)); + } + + IMAPstructure(IMAPpart* parent, const std::vector <IMAPParser::body*>& list) + { + int number = 1; + + for (std::vector <IMAPParser::body*>::const_iterator + it = list.begin() ; it != list.end() ; ++it, ++number) + { + m_parts.push_back(IMAPpart::create(parent, number, *it)); + } + } + + + const part& operator[](const int x) const + { + return (*m_parts[x - 1]); + } + + part& operator[](const int x) + { + return (*m_parts[x - 1]); + } + + const int count() const + { + return (m_parts.size()); + } + + + static IMAPstructure* emptyStructure() + { + return (&m_emptyStructure); + } + +private: + + static IMAPstructure m_emptyStructure; + + std::vector <IMAPpart*> m_parts; +}; + + +IMAPstructure IMAPstructure::m_emptyStructure; + + + +IMAPpart::IMAPpart(IMAPpart* parent, const int number, const IMAPParser::body_type_mpart* mpart) + : m_parent(parent), m_header(NULL), m_number(number), m_size(0) +{ + m_mediaType = vmime::mediaType + ("multipart", mpart->media_subtype()->value()); + + m_structure = new IMAPstructure(this, mpart->list()); +} + + +IMAPpart::IMAPpart(IMAPpart* parent, const int number, const IMAPParser::body_type_1part* part) + : m_parent(parent), m_header(NULL), m_number(number), m_size(0) +{ + if (part->body_type_text()) + { + m_mediaType = vmime::mediaType + ("text", part->body_type_text()-> + media_text()->media_subtype()->value()); + + m_size = part->body_type_text()->body_fields()->body_fld_octets()->value(); + } + else if (part->body_type_msg()) + { + m_mediaType = vmime::mediaType + ("message", part->body_type_msg()-> + media_message()->media_subtype()->value()); + } + else + { + m_mediaType = vmime::mediaType + (part->body_type_basic()->media_basic()->media_type()->value(), + part->body_type_basic()->media_basic()->media_subtype()->value()); + + m_size = part->body_type_basic()->body_fields()->body_fld_octets()->value(); + } + + m_structure = NULL; +} + + +const class header& IMAPpart::header() const +{ + if (m_header == NULL) + throw exceptions::unfetched_object(); + else + return (*m_header); +} + + +const class structure& IMAPpart::structure() const +{ + if (m_structure != NULL) + return (*m_structure); + else + return (*IMAPstructure::emptyStructure()); +} + + +class structure& IMAPpart::structure() +{ + if (m_structure != NULL) + return (*m_structure); + else + return (*IMAPstructure::emptyStructure()); +} + + + +// +// IMAPMessage_literalHandler +// + +class IMAPMessage_literalHandler : public IMAPParser::literalHandler +{ +public: + + IMAPMessage_literalHandler(utility::outputStream& os, progressionListener* progress) + : m_os(os), m_progress(progress) + { + } + + target* targetFor(const IMAPParser::component& comp, const int /* data */) + { + if (typeid(comp) == typeid(IMAPParser::msg_att_item)) + { + const int type = static_cast + <const IMAPParser::msg_att_item&>(comp).type(); + + if (type == IMAPParser::msg_att_item::BODY_SECTION || + type == IMAPParser::msg_att_item::RFC822_TEXT) + { + return new targetStream(m_progress, m_os); + } + } + + return (NULL); + } + +private: + + utility::outputStream& m_os; + progressionListener* m_progress; +}; + + + +// +// IMAPMessage +// + + +IMAPMessage::IMAPMessage(IMAPFolder* folder, const int num) + : m_folder(folder), m_num(num), m_size(-1), m_flags(FLAG_UNDEFINED), + m_expunged(false), m_header(NULL), m_structure(NULL) +{ + m_folder->registerMessage(this); +} + + +IMAPMessage::~IMAPMessage() +{ + m_folder->unregisterMessage(this); + delete dynamic_cast <IMAPheader*>(m_header); +} + + +void IMAPMessage::onFolderClosed() +{ + m_folder = NULL; +} + + +const int IMAPMessage::number() const +{ + return (m_num); +} + + +const message::uid IMAPMessage::uniqueId() const +{ + return (m_uid); +} + + +const int IMAPMessage::size() const +{ + if (m_size == -1) + throw exceptions::unfetched_object(); + + return (m_size); +} + + +const bool IMAPMessage::isExpunged() const +{ + return (m_expunged); +} + + +const int IMAPMessage::flags() const +{ + if (m_flags == FLAG_UNDEFINED) + throw exceptions::unfetched_object(); + + return (m_flags); +} + + +const class structure& IMAPMessage::structure() const +{ + if (m_structure == NULL) + throw exceptions::unfetched_object(); + + return (*m_structure); +} + + +class structure& IMAPMessage::structure() +{ + if (m_structure == NULL) + throw exceptions::unfetched_object(); + + return (*m_structure); +} + + +const class header& IMAPMessage::header() const +{ + if (m_header == NULL) + throw exceptions::unfetched_object(); + + return (*m_header); +} + + +void IMAPMessage::extract(utility::outputStream& os, progressionListener* progress, + const int start, const int length) const +{ + if (!m_folder) + throw exceptions::folder_not_found(); + + extract(NULL, os, progress, start, length, false); +} + + +void IMAPMessage::extractPart + (const part& p, utility::outputStream& os, progressionListener* progress, + const int start, const int length) const +{ + if (!m_folder) + throw exceptions::folder_not_found(); + + extract(&p, os, progress, start, length, false); +} + + +void IMAPMessage::fetchPartHeader(part& p) +{ + if (!m_folder) + throw exceptions::folder_not_found(); + + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + extract(&p, ossAdapter, NULL, 0, -1, true); + + static_cast <IMAPpart&>(p).getOrCreateHeader().parse(oss.str()); +} + + +void IMAPMessage::extract(const part* p, utility::outputStream& os, progressionListener* progress, + const int start, const int length, const bool headerOnly) const +{ + IMAPMessage_literalHandler literalHandler(os, progress); + + // Construct section identifier + std::ostringstream section; + + if (p != NULL) + { + const IMAPpart* currentPart = static_cast <const IMAPpart*>(p); + std::vector <int> numbers; + + numbers.push_back(currentPart->number()); + currentPart = currentPart->parent(); + + while (currentPart != NULL) + { + numbers.push_back(currentPart->number()); + currentPart = currentPart->parent(); + } + + numbers.erase(numbers.end() - 1); + + for (std::vector <int>::reverse_iterator it = numbers.rbegin() ; it != numbers.rend() ; ++it) + { + if (it != numbers.rbegin()) section << "."; + section << *it; + } + } + + // Build the request text + std::ostringstream command; + + command << "FETCH " << m_num << " BODY["; + command << section.str(); + if (headerOnly) command << ".MIME"; // "MIME" not "HEADER" for parts + command << "]"; + + if (start != 0 || length != -1) + command << "<" << start << "." << length << ">"; + + // Send the request + m_folder->m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp + (m_folder->m_connection->readResponse(&literalHandler)); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("FETCH", + m_folder->m_connection->parser()->lastLine(), "bad response"); + } + + + if (!headerOnly) + { + // TODO: update the flags (eg. flag "\Seen" may have been set) + } +} + + +void IMAPMessage::fetch(IMAPFolder* folder, const int options) +{ + if (m_folder != folder) + throw exceptions::folder_not_found(); + + // TODO: optimization: send the request for multiple + // messages at the same time (FETCH x:y) + + // Example: + // C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) + // S: * 2 FETCH .... + // S: * 3 FETCH .... + // S: * 4 FETCH .... + // S: A654 OK FETCH completed + + std::vector <string> items; + + if (options & folder::FETCH_SIZE) + items.push_back("RFC822.SIZE"); + + if (options & folder::FETCH_FLAGS) + items.push_back("FLAGS"); + + if (options & folder::FETCH_STRUCTURE) + items.push_back("BODYSTRUCTURE"); + + if (options & folder::FETCH_UID) + items.push_back("UID"); + + if (options & folder::FETCH_FULL_HEADER) + items.push_back("RFC822.HEADER"); + else + { + if (options & folder::FETCH_ENVELOPE) + items.push_back("ENVELOPE"); + + if (options & folder::FETCH_CONTENT_INFO) + items.push_back("BODY[HEADER.FIELDS (CONTENT-TYPE)]"); + } + + // Build the request text + std::ostringstream command; + command << "FETCH " << m_num << " ("; + + for (std::vector <string>::const_iterator it = items.begin() ; + it != items.end() ; ++it) + { + if (it != items.begin()) command << " "; + command << *it; + } + + command << ")"; + + // Send the request + m_folder->m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp(m_folder->m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("FETCH", + m_folder->m_connection->parser()->lastLine(), "bad response"); + } + + const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = + resp->continue_req_or_response_data(); + + for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("FETCH", + m_folder->m_connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::message_data* messageData = + (*it)->response_data()->message_data(); + + // We are only interested in responses of type "FETCH" + if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH) + continue; + + if (static_cast <int>(messageData->number()) != m_num) + continue; + + // Process fetch response for this message + processFetchResponse(options, messageData->msg_att()); + } +} + + +void IMAPMessage::processFetchResponse + (const int options, const IMAPParser::msg_att* msgAtt) +{ + // Get message attributes + const std::vector <IMAPParser::msg_att_item*> atts = + msgAtt->items(); + + int flags = 0; + + for (std::vector <IMAPParser::msg_att_item*>::const_iterator + it = atts.begin() ; it != atts.end() ; ++it) + { + switch ((*it)->type()) + { + case IMAPParser::msg_att_item::FLAGS: + { + flags |= IMAPUtils::messageFlagsFromFlags((*it)->flag_list()); + break; + } + case IMAPParser::msg_att_item::UID: + { + std::ostringstream oss; + oss << m_folder->m_uidValidity << ":" << (*it)->unique_id()->value(); + + m_uid = oss.str(); + break; + } + case IMAPParser::msg_att_item::ENVELOPE: + { + if (!(options & folder::FETCH_FULL_HEADER)) + { + const IMAPParser::envelope* env = (*it)->envelope(); + vmime::header& hdr = getOrCreateHeader(); + + // Date + hdr.fields.Date() = env->env_date()->value(); + + // Subject + text subject; + decodeAndUnfoldText(env->env_subject()->value(), subject); + + hdr.fields.Subject() = subject; + + // From + mailboxList from; + convertAddressList(*(env->env_from()), from); + + if (!from.empty()) + hdr.fields.From() = *(from.begin()); + + // To + mailboxList to; + convertAddressList(*(env->env_to()), to); + + hdr.fields.To() = to; + + // Sender + mailboxList sender; + convertAddressList(*(env->env_sender()), sender); + + if (!sender.empty()) + hdr.fields.Sender() = *(sender.begin()); + + // Reply-to + mailboxList replyTo; + convertAddressList(*(env->env_reply_to()), replyTo); + + if (!replyTo.empty()) + hdr.fields.ReplyTo() = *(replyTo.begin()); + + // Cc + mailboxList cc; + convertAddressList(*(env->env_cc()), cc); + + if (!cc.empty()) + hdr.fields.Cc() = cc; + + // Bcc + mailboxList bcc; + convertAddressList(*(env->env_bcc()), bcc); + + if (!bcc.empty()) + hdr.fields.Bcc() = bcc; + } + + break; + } + case IMAPParser::msg_att_item::BODY_STRUCTURE: + { + delete (m_structure); + m_structure = new IMAPstructure((*it)->body()); + break; + } + case IMAPParser::msg_att_item::RFC822_HEADER: + { + getOrCreateHeader().parse((*it)->nstring()->value()); + break; + } + case IMAPParser::msg_att_item::RFC822_SIZE: + { + m_size = (*it)->number()->value(); + break; + } + case IMAPParser::msg_att_item::BODY_SECTION: + { + if (!(options & folder::FETCH_FULL_HEADER)) + { + if ((*it)->section()->section_text1() && + (*it)->section()->section_text1()->type() + == IMAPParser::section_text::HEADER_FIELDS) + { + IMAPheader tempHeader; + tempHeader.parse((*it)->nstring()->value()); + + vmime::header& hdr = getOrCreateHeader(); + + for (header::const_iterator jt = tempHeader.fields.begin() ; + jt != tempHeader.fields.end() ; ++jt) + { + hdr.fields.append(*jt); + } + } + } + + break; + } + case IMAPParser::msg_att_item::INTERNALDATE: + case IMAPParser::msg_att_item::RFC822: + case IMAPParser::msg_att_item::RFC822_TEXT: + case IMAPParser::msg_att_item::BODY: + { + break; + } + + } + } + + if (options & folder::FETCH_FLAGS) + m_flags = flags; +} + + +IMAPheader& IMAPMessage::getOrCreateHeader() +{ + if (m_header != NULL) + return (*m_header); + else + return (*(m_header = new IMAPheader())); +} + + +void IMAPMessage::convertAddressList + (const IMAPParser::address_list& src, mailboxList& dest) +{ + for (std::vector <IMAPParser::address*>::const_iterator + it = src.addresses().begin() ; it != src.addresses().end() ; ++it) + { + const IMAPParser::address& addr = **it; + + text name; + decodeAndUnfoldText(addr.addr_name()->value(), name); + + string email = addr.addr_mailbox()->value() + + "@" + addr.addr_host()->value(); + + dest.append(mailbox(name, email)); + } +} + + +void IMAPMessage::setFlags(const int flags, const int mode) +{ + if (!m_folder) + throw exceptions::folder_not_found(); + else if (m_folder->m_mode == folder::MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Build the request text + std::ostringstream command; + command << "STORE " << m_num; + + switch (mode) + { + case FLAG_MODE_ADD: command << " +FLAGS"; break; + case FLAG_MODE_REMOVE: command << " -FLAGS"; break; + default: + case FLAG_MODE_SET: command << " FLAGS"; break; + } + + if (m_flags == FLAG_UNDEFINED) // Update local flags only if they + command << ".SILENT "; // have been fetched previously + else + command << " "; + + std::vector <string> flagList; + + if (flags & FLAG_REPLIED) flagList.push_back("\\Answered"); + if (flags & FLAG_MARKED) flagList.push_back("\\Flagged"); + if (flags & FLAG_DELETED) flagList.push_back("\\Deleted"); + if (flags & FLAG_SEEN) flagList.push_back("\\Seen"); + + if (!flagList.empty()) + { + command << "("; + + if (flagList.size() >= 2) + { + std::copy(flagList.begin(), flagList.end() - 1, + std::ostream_iterator <string>(command, " ")); + } + + command << *(flagList.end() - 1) << ")"; + + // Send the request + m_folder->m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr <IMAPParser::response> resp(m_folder->m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STORE", + m_folder->m_connection->parser()->lastLine(), "bad response"); + } + + // Update the local flags for this message + if (m_flags != FLAG_UNDEFINED) + { + const std::vector <IMAPParser::continue_req_or_response_data*>& respDataList = + resp->continue_req_or_response_data(); + + int newFlags = 0; + + for (std::vector <IMAPParser::continue_req_or_response_data*>::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + continue; + + const IMAPParser::message_data* messageData = + (*it)->response_data()->message_data(); + + // We are only interested in responses of type "FETCH" + if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH) + continue; + + // Get message attributes + const std::vector <IMAPParser::msg_att_item*> atts = + messageData->msg_att()->items(); + + for (std::vector <IMAPParser::msg_att_item*>::const_iterator + it = atts.begin() ; it != atts.end() ; ++it) + { + if ((*it)->type() == IMAPParser::msg_att_item::FLAGS) + newFlags |= IMAPUtils::messageFlagsFromFlags((*it)->flag_list()); + } + } + + m_flags = newFlags; + } + + // Notify message flags changed + std::vector <int> nums; + nums.push_back(m_num); + + events::messageChangedEvent event(m_folder, events::messageChangedEvent::TYPE_FLAGS, nums); + + for (std::list <IMAPFolder*>::iterator it = m_folder->m_store->m_folders.begin() ; + it != m_folder->m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == m_folder->m_path) + (*it)->notifyMessageChanged(event); + } + } +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPMessage.hpp b/src/messaging/IMAPMessage.hpp new file mode 100644 index 00000000..74c7a36f --- /dev/null +++ b/src/messaging/IMAPMessage.hpp @@ -0,0 +1,108 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPMESSAGE_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPMESSAGE_HPP_INCLUDED + + +#include "message.hpp" +#include "folder.hpp" +#include "../mailboxList.hpp" + + +namespace vmime { +namespace messaging { + + +class IMAPheader; +class IMAPstructure; + + +/** IMAP message implementation. + */ + +class IMAPMessage : public message +{ +protected: + + friend class IMAPFolder; + + IMAPMessage(IMAPFolder* folder, const int num); + IMAPMessage(const IMAPMessage&) : message() { } + + ~IMAPMessage(); + +public: + + const int number() const; + + const uid uniqueId() const; + + const int size() const; + + const bool isExpunged() const; + + const class structure& structure() const; + class structure& structure(); + + const class header& header() const; + + const int flags() const; + void setFlags(const int flags, const int mode = FLAG_MODE_SET); + + void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + + void fetchPartHeader(part& p); + +private: + + void fetch(IMAPFolder* folder, const int options); + + void processFetchResponse(const int options, const IMAPParser::msg_att* msgAtt); + + void extract(const part* p, utility::outputStream& os, progressionListener* progress, const int start, const int length, const bool headerOnly) const; + + + void convertAddressList(const IMAPParser::address_list& src, mailboxList& dest); + + + IMAPheader& getOrCreateHeader(); + + + void onFolderClosed(); + + IMAPFolder* m_folder; + + int m_num; + int m_size; + int m_flags; + bool m_expunged; + uid m_uid; + + class IMAPheader* m_header; + class IMAPstructure* m_structure; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPMESSAGE_HPP_INCLUDED diff --git a/src/messaging/IMAPParser.hpp b/src/messaging/IMAPParser.hpp new file mode 100644 index 00000000..2e4a56ab --- /dev/null +++ b/src/messaging/IMAPParser.hpp @@ -0,0 +1,5075 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPPARSER_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPPARSER_HPP_INCLUDED + + +#include "../base.hpp" +#include "../dateTime.hpp" +#include "../charset.hpp" +#include "../exception.hpp" +#include "../utility/smartPtr.hpp" + +#include "../encoderB64.hpp" +#include "../encoderQP.hpp" + +#include "../platformDependant.hpp" + +#include "progressionListener.hpp" +#include "timeoutHandler.hpp" +#include "socket.hpp" + +#include "IMAPTag.hpp" + +#include <vector> +#include <stdexcept> + + +//#define DEBUG_RESPONSE 1 + + +#if DEBUG_RESPONSE +# include <iostream> +#endif + + +namespace vmime { +namespace messaging { + + +#if DEBUG_RESPONSE + static string DEBUG_RESPONSE_level; + static std::vector <string> DEBUG_RESPONSE_components; + +# define DEBUG_ENTER_COMPONENT(x) \ + DEBUG_RESPONSE_components.push_back(x); \ + std::cout << DEBUG_RESPONSE_level \ + << "(" << DEBUG_RESPONSE_level.length() << ") " \ + << (x) << std::endl; +# define DEBUG_FOUND(x, y) \ + std::cout << "FOUND: " << x << ": " << y << std::endl; +#else +# define DEBUG_ENTER_COMPONENT(x) +# define DEBUG_FOUND(x, y) +#endif + + +class IMAPParser +{ +public: + + IMAPParser(IMAPTag* tag, socket* sok, timeoutHandler* _timeoutHandler) + : m_tag(tag), m_socket(sok), m_progress(NULL), + m_literalHandler(NULL), m_timeoutHandler(_timeoutHandler) + { + } + + + const IMAPTag* tag() const + { + return (m_tag); + } + + + const string lastLine() const + { + // Remove blanks and new lines at the end of the line. + string line(m_lastLine); + + string::const_iterator it = line.end(); + int count = 0; + + while (it != line.begin()) + { + const unsigned char c = *(it - 1); + + if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) + break; + + ++count; + --it; + } + + line.resize(line.length() - count); + + return (line); + } + + + + // + // literalHandler : literal content handler + // + + class component; + + class literalHandler + { + public: + + virtual ~literalHandler() { } + + + // Abstract target class + class target + { + protected: + + target(class progressionListener* progress) : m_progress(progress) {} + target(const target&) {} + + public: + + virtual ~target() { } + + + class progressionListener* progressionListener() { return (m_progress); } + + virtual void putData(const string& chunk) = 0; + + private: + + class progressionListener* m_progress; + }; + + + // Target: put in a string + class targetString : public target + { + public: + + targetString(class progressionListener* progress, vmime::string& str) + : target(progress), m_string(str) { } + + const vmime::string& string() const { return (m_string); } + vmime::string& string() { return (m_string); } + + + void putData(const vmime::string& chunk) + { + m_string += chunk; + } + + private: + + vmime::string& m_string; + }; + + + // Target: redirect to an output stream + class targetStream : public target + { + public: + + targetStream(class progressionListener* progress, utility::outputStream& stream) + : target(progress), m_stream(stream) { } + + const utility::outputStream& stream() const { return (m_stream); } + utility::outputStream& stream() { return (m_stream); } + + + void putData(const string& chunk) + { + m_stream.write(chunk.data(), chunk.length()); + } + + private: + + utility::outputStream& m_stream; + }; + + + // Called when the parser needs to know what to do with a literal + // . comp: the component in which we are at this moment + // . data: data specific to the component (may not be used) + // + // Returns : + // . == NULL to put the literal into the response + // . != NULL to redirect the literal to the specified target + + virtual target* targetFor(const component& comp, const int data) = 0; + }; + + + // + // Base class for a terminal or a non-terminal + // + + class component + { + public: + + component() { } + virtual ~component() { } + + virtual void go(IMAPParser& parser, string& line, string::size_type* currentPos) = 0; + + + const string makeResponseLine(const string& comp, const string& line, + const string::size_type pos) + { +#if DEBUG_RESPONSE + if (pos > line.length()) + std::cout << "WARNING: component::makeResponseLine(): pos > line.length()" << std::endl; +#endif + + string result(line.substr(0, pos)); + result += "[^]"; // indicates current parser position + result += line.substr(pos, line.length()); + if (!comp.empty()) result += " [" + comp + "]"; + + return (result); + } + }; + + + + // + // Parse one character + // + + template <char C> + class one_char : public component + { + public: + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT(string("one_char <") + C + ">: current='" + ((*currentPos < line.length() ? line[*currentPos] : '?')) + "'"); + + const string::size_type pos = *currentPos; + + if (pos < line.length() && line[pos] == C) + *currentPos = pos + 1; + else + throw exceptions::invalid_response("", makeResponseLine("", line, pos)); + } + }; + + + // + // SPACE ::= <ASCII SP, space, 0x20> + // + + class SPACE : public component + { + public: + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("SPACE"); + + string::size_type pos = *currentPos; + + while (pos < line.length() && (line[pos] == ' ' || line[pos] == '\t')) + ++pos; + + if (pos > *currentPos) + *currentPos = pos; + else + throw exceptions::invalid_response("", makeResponseLine("SPACE", line, pos)); + } + }; + + + // + // CR ::= <ASCII CR, carriage return, 0x0D> + // LF ::= <ASCII LF, line feed, 0x0A> + // CRLF ::= CR LF + // + + class CRLF : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("CRLF"); + + string::size_type pos = *currentPos; + + parser.check <SPACE>(line, &pos, true); + + if (pos + 1 < line.length() && + line[pos] == 0x0d && line[pos + 1] == 0x0a) + { + *currentPos = pos + 2; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("CRLF", line, pos)); + } + } + }; + + + // + // SPACE ::= <ASCII SP, space, 0x20> + // CTL ::= <any ASCII control character and DEL, 0x00 - 0x1f, 0x7f> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // ATOM_CHAR ::= <any CHAR except atom_specials> + // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials + // list_wildcards ::= "%" / "*" + // quoted_specials ::= <"> / "\" + // + // tag ::= 1*<any ATOM_CHAR except "+"> (named "xtag") + // + + class xtag : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("tag"); + + string::size_type pos = *currentPos; + + bool end = false; + + string tagString; + tagString.reserve(10); + + while (!end && pos < line.length()) + { + const unsigned char c = line[pos]; + + switch (c) + { + case '+': + case '(': + case ')': + case '{': + case 0x20: // SPACE + case '%': // list_wildcards + case '*': // list_wildcards + case '"': // quoted_specials + case '\\': // quoted_specials + + end = true; + break; + + default: + + if (c <= 0x1f || c >= 0x7f) + end = true; + else + { + tagString += c; + ++pos; + } + + break; + } + } + + if (tagString == (string) *(parser.tag())) + { + *currentPos = pos; + } + else + { + // Invalid tag + throw exceptions::invalid_response("", makeResponseLine("tag", line, pos)); + } + } + }; + + + // + // digit ::= "0" / digit_nz + // digit_nz ::= "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" + // + // number ::= 1*digit + // ;; Unsigned 32-bit integer + // ;; (0 <= n < 4,294,967,296) + // + + class number : public component + { + public: + + number(const bool nonZero = false) + : m_nonZero(nonZero), m_value(0) + { + } + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("number"); + + string::size_type pos = *currentPos; + + bool valid = true; + unsigned int val = 0; + + while (valid && pos < line.length()) + { + const char c = line[pos]; + + if (c >= '0' && c <= '9') + { + val = (val * 10) + (c - '0'); + ++pos; + } + else + { + valid = false; + } + } + + // Check for non-null length (and for non-zero number) + if (!(m_nonZero && val == 0) && pos != *currentPos) + { + m_value = val; + *currentPos = pos; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("number", line, pos)); + } + } + + private: + + const bool m_nonZero; + unsigned int m_value; + + public: + + const unsigned int value() const { return (m_value); } + }; + + + // nz_number ::= digit_nz *digit + // ;; Non-zero unsigned 32-bit integer + // ;; (0 < n < 4,294,967,296) + // + + class nz_number : public number + { + public: + + nz_number() : number(true) + { + } + }; + + + // + // text ::= 1*TEXT_CHAR + // + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // TEXT_CHAR ::= <any CHAR except CR and LF> + // + + class text : public component + { + public: + + text(bool allow8bits = false, const char except = 0) + : m_allow8bits(allow8bits), m_except(except) + { + } + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("text"); + + string::size_type pos = *currentPos; + string::size_type len = 0; + + if (m_allow8bits) + { + const unsigned char except = m_except; + + for (bool end = false ; !end && pos < line.length() ; ) + { + const unsigned char c = line[pos]; + + if (c == 0x00 || c == 0x0d || c == 0x0a || c == except) + { + end = true; + } + else + { + ++pos; + ++len; + } + } + } + else + { + const unsigned char except = m_except; + + for (bool end = false ; !end && pos < line.length() ; ) + { + const unsigned char c = line[pos]; + + if (c < 0x01 || c > 0x7f || c == 0x0d || c == 0x0a || c == except) + { + end = true; + } + else + { + ++pos; + ++len; + } + } + } + + if (len != 0) + { + m_value.resize(len); + std::copy(line.begin() + *currentPos, line.begin() + pos, m_value.begin()); + + *currentPos = pos; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("text", line, pos)); + } + } + + private: + + string m_value; + const bool m_allow8bits; + const char m_except; + + public: + + const string& value() const { return (m_value); } + }; + + + class text8 : public text + { + public: + + text8() : text(true) + { + } + }; + + + template <char C> + class text_except : public text + { + public: + + text_except() : text(false, C) + { + } + }; + + + template <char C> + class text8_except : public text + { + public: + + text8_except() : text(true, C) + { + } + }; + + + // + // QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= <any CHAR except CR and LF> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // + + class QUOTED_CHAR : public component + { + public: + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("quoted_char"); + + string::size_type pos = *currentPos; + + const unsigned char c = (pos < line.length() ? line[pos] : 0); + + if (c >= 0x01 && c <= 0x7f && // 0x01 - 0x7f + c != '"' && c != '\\' && // quoted_specials + c != '\r' && c != '\n') // CR and LF + { + m_value = c; + *currentPos = pos + 1; + } + else if (c == '\\' && pos + 1 < line.length() && + (line[pos + 1] == '"' || line[pos + 1] == '\\')) + { + m_value = line[pos + 1]; + *currentPos = pos + 2; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("QUOTED_CHAR", line, pos)); + } + } + + private: + + char m_value; + + public: + + const char value() const { return (m_value); } + }; + + + // + // quoted ::= <"> *QUOTED_CHAR <"> + // QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= <any CHAR except CR and LF> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // + + class quoted_text : public component + { + public: + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("quoted_text"); + + string::size_type pos = *currentPos; + string::size_type len = 0; + bool valid = false; + + m_value.reserve(line.length() - pos); + + for (bool end = false, quoted = false ; !end && pos < line.length() ; ) + { + const unsigned char c = line[pos]; + + if (quoted) + { + if (c == '"' || c == '\\') + m_value += c; + else + { + m_value += '\\'; + m_value += c; + } + + quoted = false; + + ++pos; + ++len; + } + else + { + if (c == '\\') + { + quoted = true; + + ++pos; + ++len; + } + else if (c == '"') + { + valid = true; + end = true; + } + else if (c >= 0x01 && c <= 0x7f && // CHAR + c != 0x0a && c != 0x0d) // CR and LF + { + m_value += c; + + ++pos; + ++len; + } + else + { + valid = false; + end = true; + } + } + } + + if (valid) + { + *currentPos = pos; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("quoted_text", line, pos)); + } + } + + private: + + string m_value; + + public: + + const string& value() const { return (m_value); } + }; + + + // + // nil ::= "NIL" + // + + class NIL : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("NIL"); + + string::size_type pos = *currentPos; + + parser.checkWithArg <special_atom>(line, &pos, "nil"); + + *currentPos = pos; + } + }; + + + // + // string ::= quoted / literal ----> named 'xstring' + // + // nil ::= "NIL" + // quoted ::= <"> *QUOTED_CHAR <"> + // QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= <any CHAR except CR and LF> + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // literal ::= "{" number "}" CRLF *CHAR8 + // ;; Number represents the number of CHAR8 octets + // CHAR8 ::= <any 8-bit octet except NUL, 0x01 - 0xff> + // + + class xstring : public component + { + public: + + xstring(const bool canBeNIL = false, component* comp = NULL, const int data = 0) + : m_canBeNIL(canBeNIL), m_component(comp), m_data(data) + { + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("string"); + + string::size_type pos = *currentPos; + + if (m_canBeNIL && + parser.checkWithArg <special_atom>(line, &pos, "nil", true)) + { + // NIL + } + else + { + pos = *currentPos; + + // quoted ::= <"> *QUOTED_CHAR <"> + if (parser.check <one_char <'"'> >(line, &pos, true)) + { + utility::auto_ptr <quoted_text> text(parser.get <quoted_text>(line, &pos)); + parser.check <one_char <'"'> >(line, &pos); + + if (parser.m_literalHandler != NULL) + { + literalHandler::target* target = + parser.m_literalHandler->targetFor(*m_component, m_data); + + if (target != NULL) + { + m_value = "[literal-handler]"; + + const string::size_type length = text->value().length(); + progressionListener* progress = target->progressionListener(); + + if (progress) + { + progress->start(length); + } + + target->putData(text->value()); + + if (progress) + { + progress->progress(length, length); + progress->stop(length); + } + + delete (target); + } + else + { + m_value = text->value(); + } + } + else + { + m_value = text->value(); + } + + DEBUG_FOUND("string[quoted]", "<length=" << m_value.length() << ", value='" << m_value << "'>"); + } + // literal ::= "{" number "}" CRLF *CHAR8 + else + { + parser.check <one_char <'{'> >(line, &pos); + + number* num = parser.get <number>(line, &pos); + + const string::size_type length = num->value(); + delete (num); + + parser.check <one_char <'}'> >(line, &pos); + + parser.check <CRLF>(line, &pos); + + + if (parser.m_literalHandler != NULL) + { + literalHandler::target* target = + parser.m_literalHandler->targetFor(*m_component, m_data); + + if (target != NULL) + { + m_value = "[literal-handler]"; + + parser.m_progress = target->progressionListener(); + parser.readLiteral(*target, length); + parser.m_progress = NULL; + + delete (target); + } + else + { + literalHandler::targetString target(NULL, m_value); + parser.readLiteral(target, length); + } + } + else + { + literalHandler::targetString target(NULL, m_value); + parser.readLiteral(target, length); + } + + line += parser.readLine(); + + DEBUG_FOUND("string[literal]", "<length=" << length << ", value='" << m_value << "'>"); + } + } + + *currentPos = pos; + } + + private: + + bool m_canBeNIL; + string m_value; + + component* m_component; + const int m_data; + + public: + + const string& value() const { return (m_value); } + }; + + + // + // nstring ::= string / nil + // + + class nstring : public xstring + { + public: + + nstring(component* comp = NULL, const int data = 0) + : xstring(true, comp, data) + { + } + }; + + + // + // astring ::= atom / string + // + + class astring : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("astring"); + + string::size_type pos = *currentPos; + + xstring* str = NULL; + + if ((str = parser.get <xstring>(line, &pos, true))) + { + m_value = str->value(); + delete (str); + } + else + { + atom* at = parser.get <atom>(line, &pos); + m_value = at->value(); + delete (at); + } + + *currentPos = pos; + } + + private: + + string m_value; + + public: + + const string& value() const { return (m_value); } + }; + + + // + // atom ::= 1*ATOM_CHAR + // + // ATOM_CHAR ::= <any CHAR except atom_specials> + // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials + // CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> + // CTL ::= <any ASCII control character and DEL, 0x00 - 0x1f, 0x7f> + // list_wildcards ::= "%" / "*" + // quoted_specials ::= <"> / "\" + // SPACE ::= <ASCII SP, space, 0x20> + // + + class atom : public component + { + public: + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("atom"); + + string::size_type pos = *currentPos; + string::size_type len = 0; + + for (bool end = false ; !end && pos < line.length() ; ) + { + const unsigned char c = line[pos]; + + switch (c) + { + case '(': + case ')': + case '{': + case 0x20: // SPACE + case '%': // list_wildcards + case '*': // list_wildcards + case '"': // quoted_specials + case '\\': // quoted_specials + + case '[': + case ']': // for "special_atom" + + end = true; + break; + + default: + + if (c <= 0x1f || c >= 0x7f) + end = true; + else + { + ++pos; + ++len; + } + } + } + + if (len != 0) + { + m_value.resize(len); + std::copy(line.begin() + *currentPos, line.begin() + pos, m_value.begin()); + + *currentPos = pos; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("atom", line, pos)); + } + } + + private: + + string m_value; + + public: + + const string& value() const { return (m_value); } + }; + + + // + // special atom (eg. "CAPABILITY", "FLAGS", "STATUS"...) + // + // " Except as noted otherwise, all alphabetic characters are case- + // insensitive. The use of upper or lower case characters to define + // token strings is for editorial clarity only. Implementations MUST + // accept these strings in a case-insensitive fashion. " + // + + class special_atom : public atom + { + public: + + special_atom(const char* str) + : m_string(str) // 'string' must be in lower-case + { + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT(string("special_atom(") + m_string + ")"); + + string::size_type pos = *currentPos; + + atom::go(parser, line, &pos); + + const char* cmp = value().c_str(); + const char* with = m_string; + + bool ok = true; + + while (ok && *cmp && *with) + { + ok = (std::tolower(*cmp, std::locale()) == *with); + + ++cmp; + ++with; + } + + if (!ok || *cmp || *with) + { + throw exceptions::invalid_response("", makeResponseLine(string("special_atom <") + m_string + ">", line, pos)); + } + else + { + *currentPos = pos; + } + } + + private: + + const char* m_string; + }; + + + // + // text_mime2 ::= "=?" <charset> "?" <encoding> "?" <encoded-text> "?=" + // ;; Syntax defined in [MIME-HDRS] + // + + class text_mime2 : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("text_mime2"); + + string::size_type pos = *currentPos; + + atom* theCharset = NULL, *theEncoding = NULL; + text* theText = NULL; + + try + { + parser.check <one_char <'='> >(line, &pos); + parser.check <one_char <'?'> >(line, &pos); + + theCharset = parser.get <atom>(line, &pos); + + parser.check <one_char <'?'> >(line, &pos); + + theEncoding = parser.get <atom>(line, &pos); + + parser.check <one_char <'?'> >(line, &pos); + + theText = parser.get <text8_except <'?'> >(line, &pos); + + parser.check <one_char <'?'> >(line, &pos); + parser.check <one_char <'='> >(line, &pos); + } + catch (std::exception& e) + { + delete (theCharset); + delete (theEncoding); + delete (theText); + + throw; + } + + m_charset = theCharset->value(); + delete (theCharset); + + // Decode text + encoder* theEncoder = NULL; + + if (theEncoding->value()[0] == 'q' || theEncoding->value()[0] == 'Q') + { + // Quoted-printable + theEncoder = new encoderQP; + theEncoder->properties()["rfc2047"] = true; + } + else if (theEncoding->value()[0] == 'b' || theEncoding->value()[0] == 'B') + { + // Base64 + theEncoder = new encoderB64; + } + + if (theEncoder) + { + utility::inputStreamStringAdapter in(theText->value()); + utility::outputStreamStringAdapter out(m_value); + + theEncoder->decode(in, out); + delete (theEncoder); + } + // No decoder available + else + { + m_value = theText->value(); + } + + delete (theEncoding); + delete (theText); + + *currentPos = pos; + } + + private: + + vmime::charset m_charset; + string m_value; + + public: + + const vmime::charset& charset() const { return (m_charset); } + const string& value() const { return (m_value); } + }; + + + // + // flag ::= "\Answered" / "\Flagged" / "\Deleted" / + // "\Seen" / "\Draft" / flag_keyword / flag_extension + // + // flag_extension ::= "\" atom + // ;; Future expansion. Client implementations + // ;; MUST accept flag_extension flags. Server + // ;; implementations MUST NOT generate + // ;; flag_extension flags except as defined by + // ;; future standard or standards-track + // ;; revisions of this specification. + // + // flag_keyword ::= atom + // + + class flag : public component + { + public: + + flag() + : m_flag_keyword(NULL) + { + } + + ~flag() + { + delete (m_flag_keyword); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("flag_keyword"); + + string::size_type pos = *currentPos; + + if (parser.check <one_char <'\\'> >(line, &pos, true)) + { + if (parser.check <one_char <'*'> >(line, &pos, true)) + { + m_type = STAR; + } + else + { + atom* at = parser.get <atom>(line, &pos); + const string name = toLower(at->value()); + delete (at); + + if (name == "answered") + m_type = ANSWERED; + else if (name == "flagged") + m_type = FLAGGED; + else if (name == "deleted") + m_type = DELETED; + else if (name == "seen") + m_type = SEEN; + else if (name == "draft") + m_type = DRAFT; + else + { + m_type = UNKNOWN; + m_name = name; + } + } + } + else + { + m_flag_keyword = parser.get <atom>(line, &pos); + } + + *currentPos = pos; + } + + + enum Type + { + UNKNOWN, + ANSWERED, + FLAGGED, + DELETED, + SEEN, + DRAFT, + STAR // * = custom flags allowed + }; + + private: + + Type m_type; + string m_name; + + IMAPParser::atom* m_flag_keyword; + + public: + + const Type type() const { return (m_type); } + const string& name() const { return (m_name); } + + const IMAPParser::atom* flag_keyword() const { return (m_flag_keyword); } + }; + + + // + // flag_list ::= "(" #flag ")" + // + + class flag_list : public component + { + public: + + ~flag_list() + { + for (std::vector <flag*>::iterator it = m_flags.begin() ; + it != m_flags.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("flag_list"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'('> >(line, &pos); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + m_flags.push_back(parser.get <flag>(line, &pos)); + parser.check <SPACE>(line, &pos, true); + } + + *currentPos = pos; + } + + private: + + std::vector <flag*> m_flags; + + public: + + const std::vector <flag*>& flags() const { return (m_flags); } + }; + + + // + // mailbox ::= "INBOX" / astring + // ;; INBOX is case-insensitive. All case variants of + // ;; INBOX (e.g. "iNbOx") MUST be interpreted as INBOX + // ;; not as an astring. Refer to section 5.1 for + // ;; further semantic details of mailbox names. + // + + class mailbox : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("mailbox"); + + string::size_type pos = *currentPos; + + if (parser.checkWithArg <special_atom>(line, &pos, "inbox", true)) + { + m_type = INBOX; + m_name = "INBOX"; + } + else + { + m_type = OTHER; + + astring* astr = parser.get <astring>(line, &pos); + m_name = astr->value(); + delete (astr); + } + + *currentPos = pos; + } + + + enum Type + { + INBOX, + OTHER + }; + + private: + + Type m_type; + string m_name; + + public: + + const Type type() const { return (m_type); } + const string& name() const { return (m_name); } + }; + + + // + // mailbox_flag := "\Marked" / "\Noinferiors" / + // "\Noselect" / "\Unmarked" / flag_extension + // + + class mailbox_flag : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("mailbox_flag"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'\\'> >(line, &pos); + + atom* at = parser.get <atom>(line, &pos); + const string name = toLower(at->value()); + delete (at); + + if (name == "marked") + m_type = MARKED; + else if (name == "noinferiors") + m_type = NOINFERIORS; + else if (name == "noselect") + m_type = NOSELECT; + else if (name == "unmarked") + m_type = UNMARKED; + else + { + m_type = UNKNOWN; + m_name = name; + } + + *currentPos = pos; + } + + + enum Type + { + UNKNOWN, + MARKED, + NOINFERIORS, + NOSELECT, + UNMARKED + }; + + private: + + Type m_type; + string m_name; + + public: + + const Type type() const { return (m_type); } + const string& name() const { return (m_name); } + }; + + + // + // mailbox_flag_list ::= "(" #(mailbox_flag) ")" + // + + class mailbox_flag_list : public component + { + public: + + ~mailbox_flag_list() + { + for (std::vector <mailbox_flag*>::iterator it = m_flags.begin() ; + it != m_flags.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("mailbox_flag_list"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'('> >(line, &pos); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + m_flags.push_back(parser.get <mailbox_flag>(line, &pos)); + parser.check <SPACE>(line, &pos, true); + } + + *currentPos = pos; + } + + private: + + std::vector <mailbox_flag*> m_flags; + + public: + + const std::vector <mailbox_flag*>& flags() const { return (m_flags); } + }; + + + // + // mailbox_list ::= mailbox_flag_list SPACE + // (<"> QUOTED_CHAR <"> / nil) SPACE mailbox + // + + class mailbox_list : public component + { + public: + + mailbox_list() + : m_mailbox_flag_list(NULL), + m_mailbox(NULL), m_quoted_char('\0') + { + } + + ~mailbox_list() + { + delete (m_mailbox_flag_list); + delete (m_mailbox); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("mailbox_list"); + + string::size_type pos = *currentPos; + + m_mailbox_flag_list = parser.get <IMAPParser::mailbox_flag_list>(line, &pos); + + parser.check <SPACE>(line, &pos); + + if (!parser.check <NIL>(line, &pos, true)) + { + parser.check <one_char <'"'> >(line, &pos); + + QUOTED_CHAR* qc = parser.get <QUOTED_CHAR>(line, &pos); + m_quoted_char = qc->value(); + delete (qc); + + parser.check <one_char <'"'> >(line, &pos); + } + + parser.check <SPACE>(line, &pos); + + m_mailbox = parser.get <IMAPParser::mailbox>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::mailbox_flag_list* m_mailbox_flag_list; + IMAPParser::mailbox* m_mailbox; + char m_quoted_char; + + public: + + const IMAPParser::mailbox_flag_list* mailbox_flag_list() const { return (m_mailbox_flag_list); } + const IMAPParser::mailbox* mailbox() const { return (m_mailbox); } + const char quoted_char() const { return (m_quoted_char); } + }; + + + // + // resp_text_code ::= "ALERT" / "PARSE" / + // "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / + // "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + // "UIDVALIDITY" SPACE nz_number / + // "UNSEEN" SPACE nz_number / + // atom [SPACE 1*<any TEXT_CHAR except "]">] + + class resp_text_code : public component + { + public: + + resp_text_code() + : m_nz_number(NULL), m_atom(NULL), m_flag_list(NULL), m_text(NULL) + { + } + + ~resp_text_code() + { + delete (m_nz_number); + delete (m_atom); + delete (m_flag_list); + delete (m_text); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_text_code"); + + string::size_type pos = *currentPos; + + // "ALERT" + if (parser.checkWithArg <special_atom>(line, &pos, "alert", true)) + { + m_type = ALERT; + } + // "PARSE" + else if (parser.checkWithArg <special_atom>(line, &pos, "parse", true)) + { + m_type = PARSE; + } + // "PERMANENTFLAGS" SPACE flag_list + else if (parser.checkWithArg <special_atom>(line, &pos, "permanentflags", true)) + { + m_type = PERMANENTFLAGS; + + parser.check <SPACE>(line, &pos); + + m_flag_list = parser.get <IMAPParser::flag_list>(line, &pos); + } + // "READ-ONLY" + else if (parser.checkWithArg <special_atom>(line, &pos, "read-only", true)) + { + m_type = READ_ONLY; + } + // "READ-WRITE" + else if (parser.checkWithArg <special_atom>(line, &pos, "read-write", true)) + { + m_type = READ_WRITE; + } + // "TRYCREATE" + else if (parser.checkWithArg <special_atom>(line, &pos, "trycreate", true)) + { + m_type = TRYCREATE; + } + // "UIDVALIDITY" SPACE nz_number + else if (parser.checkWithArg <special_atom>(line, &pos, "uidvalidity", true)) + { + m_type = UIDVALIDITY; + + parser.check <SPACE>(line, &pos); + m_nz_number = parser.get <IMAPParser::nz_number>(line, &pos); + } + // "UNSEEN" SPACE nz_number + else if (parser.checkWithArg <special_atom>(line, &pos, "unseen", true)) + { + m_type = UNSEEN; + + parser.check <SPACE>(line, &pos); + m_nz_number = parser.get <IMAPParser::nz_number>(line, &pos); + } + // atom [SPACE 1*<any TEXT_CHAR except "]">] + else + { + m_type = OTHER; + + m_atom = parser.get <IMAPParser::atom>(line, &pos); + + if (parser.check <SPACE>(line, &pos, true)) + m_text = parser.get <text_except <']'> >(line, &pos); + } + + *currentPos = pos; + } + + + enum Type + { + ALERT, + PARSE, + PERMANENTFLAGS, + READ_ONLY, + READ_WRITE, + TRYCREATE, + UIDVALIDITY, + UNSEEN, + OTHER + }; + + private: + + Type m_type; + + IMAPParser::nz_number* m_nz_number; + IMAPParser::atom* m_atom; + IMAPParser::flag_list* m_flag_list; + IMAPParser::text* m_text; + + public: + + const Type type() const { return (m_type); } + + const IMAPParser::nz_number* nz_number() const { return (m_nz_number); } + const IMAPParser::atom* atom() const { return (m_atom); } + const IMAPParser::flag_list* flag_list() const { return (m_flag_list); } + const IMAPParser::text* text() const { return (m_text); } + }; + + + // + // resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text) + // ;; text SHOULD NOT begin with "[" or "=" + + class resp_text : public component + { + public: + + resp_text() + : m_resp_text_code(NULL) + { + } + + ~resp_text() + { + delete (m_resp_text_code); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_text"); + + string::size_type pos = *currentPos; + + if (parser.check <one_char <'['> >(line, &pos, true)) + { + m_resp_text_code = parser.get <IMAPParser::resp_text_code>(line, &pos); + + parser.check <one_char <']'> >(line, &pos); + parser.check <SPACE>(line, &pos); + } + + text_mime2* text1 = parser.get <text_mime2>(line, &pos, true); + + if (text1 != NULL) + { + m_text = text1->value(); + delete (text1); + } + else + { + IMAPParser::text* text2 = + parser.get <IMAPParser::text>(line, &pos); + + m_text = text2->value(); + delete (text2); + } + + *currentPos = pos; + } + + private: + + IMAPParser::resp_text_code* m_resp_text_code; + string m_text; + + public: + + const IMAPParser::resp_text_code* resp_text_code() const { return (m_resp_text_code); } + const string& text() const { return (m_text); } + }; + + + // + // continue_req ::= "+" SPACE (resp_text / base64) + // + + class continue_req : public component + { + public: + + continue_req() + : m_resp_text(NULL) + { + } + + ~continue_req() + { + delete (m_resp_text); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("continue_req"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'+'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + m_resp_text = parser.get <IMAPParser::resp_text>(line, &pos); + + parser.check <CRLF>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_text* m_resp_text; + + public: + + const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } + }; + + + // + // auth_type ::= atom + // ;; Defined by [IMAP-AUTH] + // + + class auth_type : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("auth_type"); + + atom* at = parser.get <atom>(line, currentPos); + m_name = toLower(at->value()); + delete (at); + + if (m_name == "kerberos_v4") + m_type = KERBEROS_V4; + else if (m_name == "gssapi") + m_type = GSSAPI; + else if (m_name == "skey") + m_type = SKEY; + else + m_type = UNKNOWN; + } + + + enum Type + { + UNKNOWN, + + // RFC 1731 - IMAP4 Authentication Mechanisms + KERBEROS_V4, + GSSAPI, + SKEY + }; + + private: + + Type m_type; + string m_name; + + public: + + const Type type() const { return (m_type); } + const string name() const { return (m_name); } + }; + + + // + // status_att ::= "MESSAGES" / "RECENT" / "UIDNEXT" / + // "UIDVALIDITY" / "UNSEEN" + // + + class status_att : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("status_att"); + + string::size_type pos = *currentPos; + + if (parser.checkWithArg <special_atom>(line, &pos, "messages", true)) + { + m_type = MESSAGES; + } + else if (parser.checkWithArg <special_atom>(line, &pos, "recent", true)) + { + m_type = RECENT; + } + else if (parser.checkWithArg <special_atom>(line, &pos, "uidnext", true)) + { + m_type = UIDNEXT; + } + else if (parser.checkWithArg <special_atom>(line, &pos, "uidvalidity", true)) + { + m_type = UIDVALIDITY; + } + else + { + parser.checkWithArg <special_atom>(line, &pos, "unseen"); + m_type = UNSEEN; + } + + *currentPos = pos; + } + + + enum Type + { + MESSAGES, + RECENT, + UIDNEXT, + UIDVALIDITY, + UNSEEN + }; + + private: + + Type m_type; + + public: + + const Type type() const { return (m_type); } + }; + + + // + // capability ::= "AUTH=" auth_type / atom + // ;; New capabilities MUST begin with "X" or be + // ;; registered with IANA as standard or standards-track + // + + class capability : public component + { + public: + + capability() + : m_auth_type(NULL), m_atom(NULL) + { + } + + ~capability() + { + delete (m_auth_type); + delete (m_atom); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("capability"); + + string::size_type pos = *currentPos; + + class atom* at = parser.get <IMAPParser::atom>(line, &pos); + + string value = at->value(); + const char* str = value.c_str(); + + if ((str[0] == 'a' || str[0] == 'A') && + (str[1] == 'u' || str[1] == 'U') && + (str[2] == 't' || str[2] == 'T') && + (str[3] == 'h' || str[3] == 'H') && + (str[4] == '=')) + { + string::size_type pos = 5; + m_auth_type = parser.get <IMAPParser::auth_type>(value, &pos); + delete (at); + } + else + { + m_atom = at; + } + + *currentPos = pos; + } + + private: + + IMAPParser::auth_type* m_auth_type; + IMAPParser::atom* m_atom; + + public: + + const IMAPParser::auth_type* auth_type() const { return (m_auth_type); } + const IMAPParser::atom* atom() const { return (m_atom); } + }; + + + // + // capability_data ::= "CAPABILITY" SPACE [1#capability SPACE] "IMAP4rev1" + // [SPACE 1#capability] + // ;; IMAP4rev1 servers which offer RFC 1730 + // ;; compatibility MUST list "IMAP4" as the first + // ;; capability. + // + + class capability_data : public component + { + public: + + ~capability_data() + { + for (std::vector <capability*>::iterator it = m_capabilities.begin() ; + it != m_capabilities.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("capability_data"); + + string::size_type pos = *currentPos; + + parser.checkWithArg <special_atom>(line, &pos, "capability"); + parser.check <SPACE>(line, &pos); + + bool IMAP4rev1 = false; + + for (bool end = false ; !end && !IMAP4rev1 ; ) + { + if (parser.checkWithArg <special_atom>(line, &pos, "imap4rev1", true)) + { + IMAP4rev1 = true; + } + else + { + capability* cap = parser.get <capability>(line, &pos); + end = (cap == NULL); + + if (cap) + { + m_capabilities.push_back(cap); + } + } + + parser.check <SPACE>(line, &pos); + } + + + if (parser.check <SPACE>(line, &pos, true)) + { + for (capability* cap = NULL ; + (cap = parser.get <capability>(line, &pos)) != NULL ; ) + { + m_capabilities.push_back(cap); + + parser.check <SPACE>(line, &pos); + } + } + + *currentPos = pos; + } + + private: + + std::vector <capability*> m_capabilities; + + public: + + const std::vector <capability*>& capabilities() const { return (m_capabilities); } + }; + + + // + // date_day_fixed ::= (SPACE digit) / 2digit + // ;; Fixed-format version of date_day + // + // date_month ::= "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / + // "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" + // + // date_year ::= 4digit + // + // time ::= 2digit ":" 2digit ":" 2digit + // ;; Hours minutes seconds + // + // zone ::= ("+" / "-") 4digit + // ;; Signed four-digit value of hhmm representing + // ;; hours and minutes west of Greenwich (that is, + // ;; (the amount that the given time differs from + // ;; Universal Time). Subtracting the timezone + // ;; from the given time will give the UT form. + // ;; The Universal Time zone is "+0000". + // + // date_time ::= <"> date_day_fixed "-" date_month "-" date_year + // SPACE time SPACE zone <"> + // + + class date_time : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("date_time"); + + string::size_type pos = *currentPos; + + // <"> date_day_fixed "-" date_month "-" date_year + parser.check <one_char <'"'> >(line, &pos); + parser.check <SPACE>(line, &pos, true); + + utility::auto_ptr <number> nd(parser.get <number>(line, &pos)); + + parser.check <one_char <'-'> >(line, &pos); + + utility::auto_ptr <atom> amo(parser.get <atom>(line, &pos)); + + parser.check <one_char <'-'> >(line, &pos); + + utility::auto_ptr <number> ny(parser.get <number>(line, &pos)); + + parser.check <SPACE>(line, &pos, true); + + // 2digit ":" 2digit ":" 2digit + utility::auto_ptr <number> nh(parser.get <number>(line, &pos)); + + parser.check <one_char <':'> >(line, &pos); + + utility::auto_ptr <number> nmi(parser.get <number>(line, &pos)); + + parser.check <one_char <':'> >(line, &pos); + + utility::auto_ptr <number> ns(parser.get <number>(line, &pos)); + + parser.check <SPACE>(line, &pos, true); + + // ("+" / "-") 4digit + int sign = 1; + + if (!(parser.check <one_char <'+'> >(line, &pos, true))) + parser.check <one_char <'-'> >(line, &pos); + + utility::auto_ptr <number> nz(parser.get <number>(line, &pos)); + + parser.check <one_char <'"'> >(line, &pos); + + + m_datetime.hour() = std::min(std::max(nh->value(), 0u), 23u); + m_datetime.minute() = std::min(std::max(nmi->value(), 0u), 59u); + m_datetime.second() = std::min(std::max(ns->value(), 0u), 59u); + + const int zone = static_cast <int>(nz->value()); + const int zh = zone / 100; // hour offset + const int zm = zone % 100; // minute offset + + m_datetime.zone() = ((zh * 60) + zm) * sign; + + m_datetime.day() = std::min(std::max(nd->value(), 1u), 31u); + m_datetime.year() = ny->value(); + + const string month(vmime::toLower(amo->value())); + vmime::datetime::comp_t mon = vmime::datetime::JANUARY; + + if (month.length() >= 3) + { + switch (month[0]) + { + case 'j': + { + switch (month[1]) + { + case 'a': mon = vmime::datetime::JANUARY; break; + case 'u': + { + switch (month[2]) + { + case 'n': mon = vmime::datetime::JUNE; break; + default: mon = vmime::datetime::JULY; break; + } + + break; + } + + } + + break; + } + case 'f': mon = vmime::datetime::FEBRUARY; break; + case 'm': + { + switch (month[2]) + { + case 'r': mon = vmime::datetime::MARCH; break; + default: mon = vmime::datetime::MAY; break; + } + + break; + } + case 'a': + { + switch (month[1]) + { + case 'p': mon = vmime::datetime::APRIL; break; + default: mon = vmime::datetime::AUGUST; break; + } + + break; + } + case 's': mon = vmime::datetime::SEPTEMBER; break; + case 'o': mon = vmime::datetime::OCTOBER; break; + case 'n': mon = vmime::datetime::NOVEMBER; break; + case 'd': mon = vmime::datetime::DECEMBER; break; + } + } + + m_datetime.month() = mon; + + *currentPos = pos; + } + + private: + + vmime::datetime m_datetime; + }; + + + // + // header_fld_name ::= astring + // + + typedef astring header_fld_name; + + + // + // header_list ::= "(" 1#header_fld_name ")" + // + + class header_list : public component + { + public: + + ~header_list() + { + for (std::vector <header_fld_name*>::iterator it = m_fld_names.begin() ; + it != m_fld_names.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("header_list"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'('> >(line, &pos); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + m_fld_names.push_back(parser.get <header_fld_name>(line, &pos)); + parser.check <SPACE>(line, &pos, true); + } + + *currentPos = pos; + } + + private: + + std::vector <header_fld_name*> m_fld_names; + + public: + + const std::vector <header_fld_name*>& fld_names() const { return (m_fld_names); } + }; + + + // + // body_extension ::= nstring / number / "(" 1#body_extension ")" + // ;; Future expansion. Client implementations + // ;; MUST accept body_extension fields. Server + // ;; implementations MUST NOT generate + // ;; body_extension fields except as defined by + // ;; future standard or standards-track + // ;; revisions of this specification. + // + + class body_extension : public component + { + public: + + body_extension() + : m_nstring(NULL), m_number(NULL) + { + } + + ~body_extension() + { + delete (m_nstring); + delete (m_number); + + for (std::vector <body_extension*>::iterator it = m_body_extensions.begin() ; + it != m_body_extensions.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + string::size_type pos = *currentPos; + + if (parser.check <one_char <'('> >(line, &pos, true)) + { + m_body_extensions.push_back + (parser.get <body_extension>(line, &pos)); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + m_body_extensions.push_back(parser.get <body_extension>(line, &pos, true)); + } + else + { + if (!(m_nstring = parser.get <IMAPParser::nstring>(line, &pos, true))) + m_number = parser.get <IMAPParser::number>(line, &pos); + } + + *currentPos = pos; + } + + private: + + IMAPParser::nstring* m_nstring; + IMAPParser::number* m_number; + + std::vector <body_extension*> m_body_extensions; + + public: + + IMAPParser::nstring* nstring() const { return (m_nstring); } + IMAPParser::number* number() const { return (m_number); } + + const std::vector <body_extension*>& body_extensions() const { return (m_body_extensions); } + }; + + + // + // section_text ::= "HEADER" / "HEADER.FIELDS" [".NOT"] + // SPACE header_list / "TEXT" / "MIME" + // + + class section_text : public component + { + public: + + section_text() + : m_header_list(NULL) + { + } + + ~section_text() + { + delete (m_header_list); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("section_text"); + + string::size_type pos = *currentPos; + + // "HEADER.FIELDS" [".NOT"] SPACE header_list + const bool b1 = parser.checkWithArg <special_atom>(line, &pos, "header.fields.not", true); + const bool b2 = (b1 ? false : parser.checkWithArg <special_atom>(line, &pos, "header.fields", true)); + + if (b1 || b2) + { + m_type = b1 ? HEADER_FIELDS_NOT : HEADER_FIELDS; + + parser.check <SPACE>(line, &pos); + m_header_list = parser.get <IMAPParser::header_list>(line, &pos); + } + // "HEADER" + else if (parser.checkWithArg <special_atom>(line, &pos, "header", true)) + { + m_type = HEADER; + } + // "MIME" + else if (parser.checkWithArg <special_atom>(line, &pos, "mime", true)) + { + m_type = MIME; + } + // "TEXT" + else + { + m_type = TEXT; + + parser.checkWithArg <special_atom>(line, &pos, "text"); + } + + *currentPos = pos; + } + + + enum Type + { + HEADER, + HEADER_FIELDS, + HEADER_FIELDS_NOT, + MIME, + TEXT + }; + + private: + + Type m_type; + IMAPParser::header_list* m_header_list; + + public: + + const Type type() const { return (m_type); } + const IMAPParser::header_list* header_list() const { return (m_header_list); } + }; + + + // + // section ::= "[" [section_text / (nz_number *["." nz_number] + // ["." (section_text / "MIME")])] "]" + // + + class section : public component + { + public: + + section() + : m_section_text1(NULL), m_section_text2(NULL) + { + } + + ~section() + { + delete (m_section_text1); + delete (m_section_text2); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("section"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'['> >(line, &pos); + + if (!parser.check <one_char <']'> >(line, &pos, true)) + { + if (!(m_section_text1 = parser.get <section_text>(line, &pos, true))) + { + nz_number* num = parser.get <nz_number>(line, &pos); + m_nz_numbers.push_back(num->value()); + delete (num); + + while (parser.check <one_char <'.'> >(line, &pos, true)) + { + if ((num = parser.get <nz_number>(line, &pos, true))) + { + m_nz_numbers.push_back(num->value()); + delete (num); + } + else + { + m_section_text2 = parser.get <section_text>(line, &pos); + break; + } + } + } + + parser.check <one_char <']'> >(line, &pos); + } + + *currentPos = pos; + } + + private: + + section_text* m_section_text1; + section_text* m_section_text2; + std::vector <unsigned int> m_nz_numbers; + + public: + + const section_text* section_text1() const { return (m_section_text1); } + const section_text* section_text2() const { return (m_section_text2); } + const std::vector <unsigned int>& nz_numbers() const { return (m_nz_numbers); } + }; + + + // + // addr_adl ::= nstring + // ;; Holds route from [RFC-822] route-addr if + // ;; non-NIL + // + // addr_host ::= nstring + // ;; NIL indicates [RFC-822] group syntax. + // ;; Otherwise, holds [RFC-822] domain name + // + // addr_mailbox ::= nstring + // ;; NIL indicates end of [RFC-822] group; if + // ;; non-NIL and addr_host is NIL, holds + // ;; [RFC-822] group name. + // ;; Otherwise, holds [RFC-822] local-part + // + // addr_name ::= nstring + // ;; Holds phrase from [RFC-822] mailbox if + // ;; non-NIL + // + // address ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox + // SPACE addr_host ")" + // + + class address : public component + { + public: + + address() + : m_addr_name(NULL), m_addr_adl(NULL), + m_addr_mailbox(NULL), m_addr_host(NULL) + { + } + + ~address() + { + delete (m_addr_name); + delete (m_addr_adl); + delete (m_addr_mailbox); + delete (m_addr_host); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("address"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'('> >(line, &pos); + m_addr_name = parser.get <nstring>(line, &pos); + parser.check <SPACE>(line, &pos); + m_addr_adl = parser.get <nstring>(line, &pos); + parser.check <SPACE>(line, &pos); + m_addr_mailbox = parser.get <nstring>(line, &pos); + parser.check <SPACE>(line, &pos); + m_addr_host = parser.get <nstring>(line, &pos); + parser.check <one_char <')'> >(line, &pos); + + *currentPos = pos; + } + + private: + + nstring* m_addr_name; + nstring* m_addr_adl; + nstring* m_addr_mailbox; + nstring* m_addr_host; + + public: + + nstring* addr_name() const { return (m_addr_name); } + nstring* addr_adl() const { return (m_addr_adl); } + nstring* addr_mailbox() const { return (m_addr_mailbox); } + nstring* addr_host() const { return (m_addr_host); } + }; + + + // + // address_list ::= "(" 1*address ")" / nil + // + + class address_list : public component + { + public: + + ~address_list() + { + for (std::vector <address*>::iterator it = m_addresses.begin() ; + it != m_addresses.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("address_list"); + + string::size_type pos = *currentPos; + + if (!parser.check <NIL>(line, &pos, true)) + { + parser.check <one_char <'('> >(line, &pos); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + m_addresses.push_back(parser.get <address>(line, &pos)); + parser.check <SPACE>(line, &pos, true); + } + } + + *currentPos = pos; + } + + private: + + std::vector <address*> m_addresses; + + public: + + const std::vector <address*>& addresses() const { return (m_addresses); } + }; + + + // + // env_bcc ::= "(" 1*address ")" / nil + // + + typedef address_list env_bcc; + + + // + // env_cc ::= "(" 1*address ")" / nil + // + + typedef address_list env_cc; + + + // + // env_date ::= nstring + // + + typedef nstring env_date; + + + // + // env_from ::= "(" 1*address ")" / nil + // + + typedef address_list env_from; + + + // + // env_in_reply_to ::= nstring + // + + typedef nstring env_in_reply_to; + + + // + // env_message_id ::= nstring + // + + typedef nstring env_message_id; + + + // + // env_reply_to ::= "(" 1*address ")" / nil + // + + typedef address_list env_reply_to; + + + // + // env_sender ::= "(" 1*address ")" / nil + // + + typedef address_list env_sender; + + + // + // env_subject ::= nstring + // + + typedef nstring env_subject; + + + // + // env_to ::= "(" 1*address ")" / nil + // + + typedef address_list env_to; + + + // + // envelope ::= "(" env_date SPACE env_subject SPACE env_from + // SPACE env_sender SPACE env_reply_to SPACE env_to + // SPACE env_cc SPACE env_bcc SPACE env_in_reply_to + // SPACE env_message_id ")" + // + + class envelope : public component + { + public: + + envelope() + : m_env_date(NULL), m_env_subject(NULL), + m_env_from(NULL), m_env_sender(NULL), m_env_reply_to(NULL), + m_env_to(NULL), m_env_cc(NULL), m_env_bcc(NULL), + m_env_in_reply_to(NULL), m_env_message_id(NULL) + { + } + + ~envelope() + { + delete (m_env_date); + delete (m_env_subject); + delete (m_env_from); + delete (m_env_sender); + delete (m_env_reply_to); + delete (m_env_to); + delete (m_env_cc); + delete (m_env_bcc); + delete (m_env_in_reply_to); + delete (m_env_message_id); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("envelope"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'('> >(line, &pos); + + m_env_date = parser.get <IMAPParser::env_date>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_subject = parser.get <IMAPParser::env_subject>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_from = parser.get <IMAPParser::env_from>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_sender = parser.get <IMAPParser::env_sender>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_reply_to = parser.get <IMAPParser::env_reply_to>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_to = parser.get <IMAPParser::env_to>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_cc = parser.get <IMAPParser::env_cc>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_bcc = parser.get <IMAPParser::env_bcc>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_in_reply_to = parser.get <IMAPParser::env_in_reply_to>(line, &pos); + parser.check <SPACE>(line, &pos); + + m_env_message_id = parser.get <IMAPParser::env_message_id>(line, &pos); + + parser.check <one_char <')'> >(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::env_date* m_env_date; + IMAPParser::env_subject* m_env_subject; + IMAPParser::env_from* m_env_from; + IMAPParser::env_sender* m_env_sender; + IMAPParser::env_reply_to* m_env_reply_to; + IMAPParser::env_to* m_env_to; + IMAPParser::env_cc* m_env_cc; + IMAPParser::env_bcc* m_env_bcc; + IMAPParser::env_in_reply_to* m_env_in_reply_to; + IMAPParser::env_message_id* m_env_message_id; + + public: + + const IMAPParser::env_date* env_date() const { return (m_env_date); } + const IMAPParser::env_subject* env_subject() const { return (m_env_subject); } + const IMAPParser::env_from* env_from() const { return (m_env_from); } + const IMAPParser::env_sender* env_sender() const { return (m_env_sender); } + const IMAPParser::env_reply_to* env_reply_to() const { return (m_env_reply_to); } + const IMAPParser::env_to* env_to() const { return (m_env_to); } + const IMAPParser::env_cc* env_cc() const { return (m_env_cc); } + const IMAPParser::env_bcc* env_bcc() const { return (m_env_bcc); } + const IMAPParser::env_in_reply_to* env_in_reply_to() const { return (m_env_in_reply_to); } + const IMAPParser::env_message_id* env_message_id() const { return (m_env_message_id); } + }; + + + // + // body_fld_desc ::= nstring + // + + typedef nstring body_fld_desc; + + + // + // body_fld_id ::= nstring + // + + typedef nstring body_fld_id; + + + // + // body_fld_md5 ::= nstring + // + + typedef nstring body_fld_md5; + + + // + // body_fld_octets ::= number + // + + typedef number body_fld_octets; + + + // + // body_fld_lines ::= number + // + + typedef number body_fld_lines; + + + // + // body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/ + // "QUOTED-PRINTABLE") <">) / string + // + + typedef xstring body_fld_enc; + + + // + // body_fld_param_item ::= string SPACE string + // + + class body_fld_param_item : public component + { + public: + + body_fld_param_item() + : m_string1(NULL), m_string2(NULL) + { + } + + ~body_fld_param_item() + { + delete (m_string1); + delete (m_string2); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_fld_param_item"); + + string::size_type pos = *currentPos; + + m_string1 = parser.get <xstring>(line, &pos); + parser.check <SPACE>(line, &pos); + m_string2 = parser.get <xstring>(line, &pos); + + DEBUG_FOUND("body_fld_param_item", "<" << m_string1->value() << ", " << m_string2->value() << ">"); + + *currentPos = pos; + } + + private: + + xstring* m_string1; + xstring* m_string2; + + public: + + const xstring* string1() const { return (m_string1); } + const xstring* string2() const { return (m_string2); } + }; + + + // + // body_fld_param ::= "(" 1#(body_fld_param_item) ")" / nil + // + + class body_fld_param : public component + { + public: + + ~body_fld_param() + { + for (std::vector <body_fld_param_item*>::iterator it = m_items.begin() ; + it != m_items.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_fld_param"); + + string::size_type pos = *currentPos; + + if (!parser.check <NIL>(line, &pos, true)) + { + parser.check <one_char <'('> >(line, &pos); + + m_items.push_back(parser.get <body_fld_param_item>(line, &pos)); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + parser.check <SPACE>(line, &pos); + m_items.push_back(parser.get <body_fld_param_item>(line, &pos)); + } + } + + *currentPos = pos; + } + + private: + + std::vector <body_fld_param_item*> m_items; + + public: + + const std::vector <body_fld_param_item*>& items() const { return (m_items); } + }; + + + // + // body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil + // + + class body_fld_dsp : public component + { + public: + + body_fld_dsp() + : m_string(NULL), m_body_fld_param(NULL) + { + } + + ~body_fld_dsp() + { + delete (m_string); + delete (m_body_fld_param); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_fld_dsp"); + + string::size_type pos = *currentPos; + + if (!parser.check <NIL>(line, &pos, true)) + { + parser.check <one_char <'('> >(line, &pos); + m_string = parser.get <xstring>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_param = parser.get <class body_fld_param>(line, &pos); + parser.check <one_char <')'> >(line, &pos); + } + + *currentPos = pos; + } + + private: + + class xstring* m_string; + class body_fld_param* m_body_fld_param; + + public: + + const class xstring* str() const { return (m_string); } + const class body_fld_param* body_fld_param() const { return (m_body_fld_param); } + }; + + + // + // body_fld_lang ::= nstring / "(" 1#string ")" + // + + class body_fld_lang : public component + { + public: + + ~body_fld_lang() + { + for (std::vector <xstring*>::iterator it = m_strings.begin() ; + it != m_strings.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_fld_lang"); + + string::size_type pos = *currentPos; + + if (parser.check <one_char <'('> >(line, &pos, true)) + { + m_strings.push_back(parser.get <class xstring>(line, &pos)); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + m_strings.push_back(parser.get <class xstring>(line, &pos)); + } + else + { + m_strings.push_back(parser.get <class nstring>(line, &pos)); + } + + *currentPos = pos; + } + + private: + + std::vector <xstring*> m_strings; + + public: + + const std::vector <xstring*>& strings() const { return (m_strings); } + }; + + + // + // body_fields ::= body_fld_param SPACE body_fld_id SPACE + // body_fld_desc SPACE body_fld_enc SPACE + // body_fld_octets + // + + class body_fields : public component + { + public: + + body_fields() + : m_body_fld_param(NULL), m_body_fld_id(NULL), + m_body_fld_desc(NULL), m_body_fld_enc(NULL), m_body_fld_octets(NULL) + { + } + + ~body_fields() + { + delete (m_body_fld_param); + delete (m_body_fld_id); + delete (m_body_fld_desc); + delete (m_body_fld_enc); + delete (m_body_fld_octets); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_fields"); + + string::size_type pos = *currentPos; + + m_body_fld_param = parser.get <IMAPParser::body_fld_param>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_id = parser.get <IMAPParser::body_fld_id>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_desc = parser.get <IMAPParser::body_fld_desc>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_enc = parser.get <IMAPParser::body_fld_enc>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_octets = parser.get <IMAPParser::body_fld_octets>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::body_fld_param* m_body_fld_param; + IMAPParser::body_fld_id* m_body_fld_id; + IMAPParser::body_fld_desc* m_body_fld_desc; + IMAPParser::body_fld_enc* m_body_fld_enc; + IMAPParser::body_fld_octets* m_body_fld_octets; + + public: + + const IMAPParser::body_fld_param* body_fld_param() const { return (m_body_fld_param); } + const IMAPParser::body_fld_id* body_fld_id() const { return (m_body_fld_id); } + const IMAPParser::body_fld_desc* body_fld_desc() const { return (m_body_fld_desc); } + const IMAPParser::body_fld_enc* body_fld_enc() const { return (m_body_fld_enc); } + const IMAPParser::body_fld_octets* body_fld_octets() const { return (m_body_fld_octets); } + }; + + + // + // media_subtype ::= string + // ;; Defined in [MIME-IMT] + // + + typedef xstring media_subtype; + + + // + // media_text ::= <"> "TEXT" <"> SPACE media_subtype + // ;; Defined in [MIME-IMT] + // + + class media_text : public component + { + public: + + media_text() + : m_media_subtype(NULL) + { + } + + ~media_text() + { + delete (m_media_subtype); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("media_text"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'"'> >(line, &pos); + parser.checkWithArg <special_atom>(line, &pos, "text"); + parser.check <one_char <'"'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + m_media_subtype = parser.get <IMAPParser::media_subtype>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_subtype* m_media_subtype; + + public: + + const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } + }; + + + // + // media_message ::= <"> "MESSAGE" <"> SPACE <"> "RFC822" <"> + // ;; Defined in [MIME-IMT] + // + + class media_message : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("media_message"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'"'> >(line, &pos); + parser.checkWithArg <special_atom>(line, &pos, "message"); + parser.check <one_char <'"'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + //parser.check <one_char <'"'> >(line, &pos); + //parser.checkWithArg <special_atom>(line, &pos, "rfc822"); + //parser.check <one_char <'"'> >(line, &pos); + + m_media_subtype = parser.get <IMAPParser::media_subtype>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_subtype* m_media_subtype; + + public: + + const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } + }; + + + // + // media_basic ::= (<"> ("APPLICATION" / "AUDIO" / "IMAGE" / + // "MESSAGE" / "VIDEO") <">) / string) + // SPACE media_subtype + // ;; Defined in [MIME-IMT] + + class media_basic : public component + { + public: + + media_basic() + : m_media_type(NULL), m_media_subtype(NULL) + { + } + + ~media_basic() + { + delete (m_media_type); + delete (m_media_subtype); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("media_basic"); + + string::size_type pos = *currentPos; + + m_media_type = parser.get <xstring>(line, &pos); + + parser.check <SPACE>(line, &pos); + + m_media_subtype = parser.get <IMAPParser::media_subtype>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::xstring* m_media_type; + IMAPParser::media_subtype* m_media_subtype; + + public: + + const IMAPParser::xstring* media_type() const { return (m_media_type); } + const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } + }; + + + // + // body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp + // [SPACE body_fld_lang + // [SPACE 1#body_extension]]] + // ;; MUST NOT be returned on non-extensible + // ;; "BODY" fetch + // + + class body_ext_1part : public component + { + public: + + body_ext_1part() + : m_body_fld_md5(NULL), m_body_fld_dsp(NULL), m_body_fld_lang(NULL) + { + } + + ~body_ext_1part() + { + delete (m_body_fld_md5); + delete (m_body_fld_dsp); + delete (m_body_fld_lang); + + for (std::vector <body_extension*>::iterator it = m_body_extensions.begin() ; + it != m_body_extensions.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_ext_1part"); + + string::size_type pos = *currentPos; + + m_body_fld_md5 = parser.get <IMAPParser::body_fld_md5>(line, &pos); + + // [SPACE body_fld_dsp + if (parser.check <SPACE>(line, &pos, true)) + { + m_body_fld_dsp = parser.get <IMAPParser::body_fld_dsp>(line, &pos); + + // [SPACE body_fld_lang + if (parser.check <SPACE>(line, &pos, true)) + { + m_body_fld_lang = parser.get <IMAPParser::body_fld_lang>(line, &pos); + + // [SPACE 1#body_extension] + if (parser.check <SPACE>(line, &pos, true)) + { + m_body_extensions.push_back + (parser.get <body_extension>(line, &pos)); + + body_extension* ext = NULL; + + while ((ext = parser.get <body_extension>(line, &pos, true)) != NULL) + m_body_extensions.push_back(ext); + } + } + } + + *currentPos = pos; + } + + private: + + IMAPParser::body_fld_md5* m_body_fld_md5; + IMAPParser::body_fld_dsp* m_body_fld_dsp; + IMAPParser::body_fld_lang* m_body_fld_lang; + + std::vector <body_extension*> m_body_extensions; + + public: + + const IMAPParser::body_fld_md5* body_fld_md5() const { return (m_body_fld_md5); } + const IMAPParser::body_fld_dsp* body_fld_dsp() const { return (m_body_fld_dsp); } + const IMAPParser::body_fld_lang* body_fld_lang() const { return (m_body_fld_lang); } + + const std::vector <body_extension*> body_extensions() const { return (m_body_extensions); } + }; + + + // + // body_ext_mpart ::= body_fld_param + // [SPACE body_fld_dsp SPACE body_fld_lang + // [SPACE 1#body_extension]] + // ;; MUST NOT be returned on non-extensible + // ;; "BODY" fetch + + class body_ext_mpart : public component + { + public: + + body_ext_mpart() + : m_body_fld_param(NULL), m_body_fld_dsp(NULL), m_body_fld_lang(NULL) + { + } + + ~body_ext_mpart() + { + delete (m_body_fld_param); + delete (m_body_fld_dsp); + delete (m_body_fld_lang); + + for (std::vector <body_extension*>::iterator it = m_body_extensions.begin() ; + it != m_body_extensions.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_ext_mpart"); + + string::size_type pos = *currentPos; + + m_body_fld_param = parser.get <IMAPParser::body_fld_param>(line, &pos); + + // [SPACE body_fld_dsp SPACE body_fld_lang [SPACE 1#body_extension]] + if (parser.check <SPACE>(line, &pos, true)) + { + m_body_fld_dsp = parser.get <IMAPParser::body_fld_dsp>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_lang = parser.get <IMAPParser::body_fld_lang>(line, &pos); + + // [SPACE 1#body_extension] + if (parser.check <SPACE>(line, &pos, true)) + { + m_body_extensions.push_back + (parser.get <body_extension>(line, &pos)); + + body_extension* ext = NULL; + + while ((ext = parser.get <body_extension>(line, &pos, true)) != NULL) + m_body_extensions.push_back(ext); + } + } + + *currentPos = pos; + } + + private: + + IMAPParser::body_fld_param* m_body_fld_param; + IMAPParser::body_fld_dsp* m_body_fld_dsp; + IMAPParser::body_fld_lang* m_body_fld_lang; + + std::vector <body_extension*> m_body_extensions; + + public: + + const IMAPParser::body_fld_param* body_fld_param() const { return (m_body_fld_param); } + const IMAPParser::body_fld_dsp* body_fld_dsp() const { return (m_body_fld_dsp); } + const IMAPParser::body_fld_lang* body_fld_lang() const { return (m_body_fld_lang); } + + const std::vector <body_extension*> body_extensions() const { return (m_body_extensions); } + }; + + + // + // body_type_basic ::= media_basic SPACE body_fields + // ;; MESSAGE subtype MUST NOT be "RFC822" + // + + class body_type_basic : public component + { + public: + + body_type_basic() + : m_media_basic(NULL), m_body_fields(NULL) + { + } + + ~body_type_basic() + { + delete (m_media_basic); + delete (m_body_fields); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_type_basic"); + + string::size_type pos = *currentPos; + + m_media_basic = parser.get <IMAPParser::media_basic>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fields = parser.get <IMAPParser::body_fields>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_basic* m_media_basic; + IMAPParser::body_fields* m_body_fields; + + public: + + const IMAPParser::media_basic* media_basic() const { return (m_media_basic); } + const IMAPParser::body_fields* body_fields() const { return (m_body_fields); } + }; + + + // + // body_type_msg ::= media_message SPACE body_fields SPACE envelope + // SPACE body SPACE body_fld_lines + // + + class xbody; + typedef xbody body; + + class body_type_msg : public component + { + public: + + body_type_msg() + : m_media_message(NULL), m_body_fields(NULL), + m_envelope(NULL), m_body(NULL), m_body_fld_lines(NULL) + { + } + + ~body_type_msg() + { + delete (m_media_message); + delete (m_body_fields); + delete (m_envelope); + delete (m_body); + delete (m_body_fld_lines); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_type_msg"); + + string::size_type pos = *currentPos; + + m_media_message = parser.get <IMAPParser::media_message>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fields = parser.get <IMAPParser::body_fields>(line, &pos); + parser.check <SPACE>(line, &pos); + m_envelope = parser.get <IMAPParser::envelope>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body = parser.get <IMAPParser::xbody>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_lines = parser.get <IMAPParser::body_fld_lines>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_message* m_media_message; + IMAPParser::body_fields* m_body_fields; + IMAPParser::envelope* m_envelope; + IMAPParser::xbody* m_body; + IMAPParser::body_fld_lines* m_body_fld_lines; + + public: + + const IMAPParser::media_message* media_message() const { return (m_media_message); } + const IMAPParser::body_fields* body_fields() const { return (m_body_fields); } + const IMAPParser::envelope* envelope() const { return (m_envelope); } + const IMAPParser::xbody* body() const { return (m_body); } + const IMAPParser::body_fld_lines* body_fld_lines() const { return (m_body_fld_lines); } + }; + + + // + // body_type_text ::= media_text SPACE body_fields SPACE body_fld_lines + // + + class body_type_text : public component + { + public: + + body_type_text() + : m_media_text(NULL), + m_body_fields(NULL), m_body_fld_lines(NULL) + { + } + + ~body_type_text() + { + delete (m_media_text); + delete (m_body_fields); + delete (m_body_fld_lines); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_type_text"); + + string::size_type pos = *currentPos; + + m_media_text = parser.get <IMAPParser::media_text>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fields = parser.get <IMAPParser::body_fields>(line, &pos); + parser.check <SPACE>(line, &pos); + m_body_fld_lines = parser.get <IMAPParser::body_fld_lines>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_text* m_media_text; + IMAPParser::body_fields* m_body_fields; + IMAPParser::body_fld_lines* m_body_fld_lines; + + public: + + const IMAPParser::media_text* media_text() const { return (m_media_text); } + const IMAPParser::body_fields* body_fields() const { return (m_body_fields); } + const IMAPParser::body_fld_lines* body_fld_lines() const { return (m_body_fld_lines); } + }; + + + // + // body_type_1part ::= (body_type_basic / body_type_msg / body_type_text) + // [SPACE body_ext_1part] + // + + class body_type_1part : public component + { + public: + + body_type_1part() + : m_body_type_basic(NULL), m_body_type_msg(NULL), + m_body_type_text(NULL), m_body_ext_1part(NULL) + { + } + + ~body_type_1part() + { + delete (m_body_type_basic); + delete (m_body_type_msg); + delete (m_body_type_text); + + delete (m_body_ext_1part); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_type_1part"); + + string::size_type pos = *currentPos; + + if (!(m_body_type_text = parser.get <IMAPParser::body_type_text>(line, &pos, true))) + if (!(m_body_type_msg = parser.get <IMAPParser::body_type_msg>(line, &pos, true))) + m_body_type_basic = parser.get <IMAPParser::body_type_basic>(line, &pos); + + if (parser.check <SPACE>(line, &pos, true)) + m_body_ext_1part = parser.get <IMAPParser::body_ext_1part>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::body_type_basic* m_body_type_basic; + IMAPParser::body_type_msg* m_body_type_msg; + IMAPParser::body_type_text* m_body_type_text; + + IMAPParser::body_ext_1part* m_body_ext_1part; + + public: + + const IMAPParser::body_type_basic* body_type_basic() const { return (m_body_type_basic); } + const IMAPParser::body_type_msg* body_type_msg() const { return (m_body_type_msg); } + const IMAPParser::body_type_text* body_type_text() const { return (m_body_type_text); } + + const IMAPParser::body_ext_1part* body_ext_1part() const { return (m_body_ext_1part); } + }; + + + // + // body_type_mpart ::= 1*body SPACE media_subtype + // [SPACE body_ext_mpart] + // + + class body_type_mpart : public component + { + public: + + body_type_mpart() + : m_media_subtype(NULL), m_body_ext_mpart(NULL) + { + } + + ~body_type_mpart() + { + delete (m_media_subtype); + delete (m_body_ext_mpart); + + for (std::vector <xbody*>::iterator it = m_list.begin() ; + it != m_list.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_type_mpart"); + + string::size_type pos = *currentPos; + + m_list.push_back(parser.get <xbody>(line, &pos)); + + for (xbody* b ; (b = parser.get <xbody>(line, &pos, true)) ; ) + m_list.push_back(b); + + parser.check <SPACE>(line, &pos); + + m_media_subtype = parser.get <IMAPParser::media_subtype>(line, &pos); + + if (parser.check <SPACE>(line, &pos, true)) + m_body_ext_mpart = parser.get <IMAPParser::body_ext_mpart>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_subtype* m_media_subtype; + IMAPParser::body_ext_mpart* m_body_ext_mpart; + + std::vector <xbody*> m_list; + + public: + + const std::vector <IMAPParser::xbody*>& list() const { return (m_list); } + + const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } + const IMAPParser::body_ext_mpart* body_ext_mpart() const { return (m_body_ext_mpart); } + }; + + + // + // xbody ::= "(" body_type_1part / body_type_mpart ")" + // + + class xbody : public component + { + public: + + xbody() + : m_body_type_1part(NULL), m_body_type_mpart(NULL) + { + } + + ~xbody() + { + delete (m_body_type_1part); + delete (m_body_type_mpart); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'('> >(line, &pos); + + if (!(m_body_type_1part = parser.get <IMAPParser::body_type_1part>(line, &pos, true))) + m_body_type_mpart = parser.get <IMAPParser::body_type_mpart>(line, &pos); + + parser.check <one_char <')'> >(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::body_type_1part* m_body_type_1part; + IMAPParser::body_type_mpart* m_body_type_mpart; + + public: + + const IMAPParser::body_type_1part* body_type_1part() const { return (m_body_type_1part); } + const IMAPParser::body_type_mpart* body_type_mpart() const { return (m_body_type_mpart); } + }; + + + // + // uniqueid ::= nz_number + // ;; Strictly ascending + // + // msg_att_item ::= "ENVELOPE" SPACE envelope / + // "FLAGS" SPACE "(" #(flag / "\Recent") ")" / + // "INTERNALDATE" SPACE date_time / + // "RFC822" [".HEADER" / ".TEXT"] SPACE nstring / + // "RFC822.SIZE" SPACE number / + // "BODY" ["STRUCTURE"] SPACE body / + // "BODY" section ["<" number ">"] SPACE nstring / + // "UID" SPACE uniqueid + // + + class msg_att_item : public component + { + public: + + msg_att_item() + : m_date_time(NULL), m_number(NULL), m_envelope(NULL), + m_uniqueid(NULL), m_nstring(NULL), m_body(NULL), m_flag_list(NULL) + { + } + + ~msg_att_item() + { + delete (m_date_time); + delete (m_number); + delete (m_envelope); + delete (m_uniqueid); + delete (m_nstring); + delete (m_body); + delete (m_flag_list); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("msg_att_item"); + + string::size_type pos = *currentPos; + + // "ENVELOPE" SPACE envelope + if (parser.checkWithArg <special_atom>(line, &pos, "envelope", true)) + { + m_type = ENVELOPE; + + parser.check <SPACE>(line, &pos); + m_envelope = parser.get <IMAPParser::envelope>(line, &pos); + } + // "FLAGS" SPACE "(" #(flag / "\Recent") ")" + else if (parser.checkWithArg <special_atom>(line, &pos, "flags", true)) + { + m_type = FLAGS; + + parser.check <SPACE>(line, &pos); + + m_flag_list = parser.get <IMAPParser::flag_list>(line, &pos); + } + // "INTERNALDATE" SPACE date_time + else if (parser.checkWithArg <special_atom>(line, &pos, "internaldate", true)) + { + m_type = INTERNALDATE; + + parser.check <SPACE>(line, &pos); + m_date_time = parser.get <IMAPParser::date_time>(line, &pos); + } + // "RFC822" ".HEADER" SPACE nstring + else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822.header", true)) + { + m_type = RFC822_HEADER; + + parser.check <SPACE>(line, &pos); + + m_nstring = parser.get <IMAPParser::nstring>(line, &pos); + } + // "RFC822" ".TEXT" SPACE nstring + else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822.text", true)) + { + m_type = RFC822_TEXT; + + parser.check <SPACE>(line, &pos); + + m_nstring = parser.getWithArgs <IMAPParser::nstring> + (line, &pos, this, RFC822_TEXT); + } + // "RFC822.SIZE" SPACE number + else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822.size", true)) + { + m_type = RFC822_SIZE; + + parser.check <SPACE>(line, &pos); + m_number = parser.get <IMAPParser::number>(line, &pos); + } + // "RFC822" SPACE nstring + else if (parser.checkWithArg <special_atom>(line, &pos, "rfc822", true)) + { + m_type = RFC822; + + parser.check <SPACE>(line, &pos); + + m_nstring = parser.get <IMAPParser::nstring>(line, &pos); + } + // "BODY" "STRUCTURE" SPACE body + else if (parser.checkWithArg <special_atom>(line, &pos, "bodystructure", true)) + { + m_type = BODY_STRUCTURE; + + parser.check <SPACE>(line, &pos); + + m_body = parser.get <IMAPParser::body>(line, &pos); + } + // "BODY" section ["<" number ">"] SPACE nstring + // "BODY" SPACE body + else if (parser.checkWithArg <special_atom>(line, &pos, "body", true)) + { + m_section = parser.get <IMAPParser::section>(line, &pos, true); + + // "BODY" section ["<" number ">"] SPACE nstring + if (m_section != NULL) + { + m_type = BODY_SECTION; + + if (parser.check <one_char <'<'> >(line, &pos, true)) + { + m_number = parser.get <IMAPParser::number>(line, &pos); + parser.check <one_char <'>'> >(line, &pos); + } + + parser.check <SPACE>(line, &pos); + + m_nstring = parser.getWithArgs <IMAPParser::nstring> + (line, &pos, this, BODY_SECTION); + } + // "BODY" SPACE body + else + { + m_type = BODY; + + parser.check <SPACE>(line, &pos); + + m_body = parser.get <IMAPParser::body>(line, &pos); + } + } + // "UID" SPACE uniqueid + else + { + m_type = UID; + + parser.checkWithArg <special_atom>(line, &pos, "uid"); + parser.check <SPACE>(line, &pos); + + m_uniqueid = parser.get <nz_number>(line, &pos); + } + + *currentPos = pos; + } + + + enum Type + { + ENVELOPE, + FLAGS, + INTERNALDATE, + RFC822, + RFC822_SIZE, + RFC822_HEADER, + RFC822_TEXT, + BODY, + BODY_SECTION, + BODY_STRUCTURE, + UID + }; + + private: + + Type m_type; + + IMAPParser::date_time* m_date_time; + IMAPParser::number* m_number; + IMAPParser::envelope* m_envelope; + IMAPParser::nz_number* m_uniqueid; + IMAPParser::nstring* m_nstring; + IMAPParser::xbody* m_body; + IMAPParser::flag_list* m_flag_list; + IMAPParser::section* m_section; + + public: + + const Type type() const { return (m_type); } + + const IMAPParser::date_time* date_time() const { return (m_date_time); } + const IMAPParser::number* number() const { return (m_number); } + const IMAPParser::envelope* envelope() const { return (m_envelope); } + const IMAPParser::nz_number* unique_id() const { return (m_uniqueid); } + const IMAPParser::nstring* nstring() const { return (m_nstring); } + const IMAPParser::xbody* body() const { return (m_body); } + const IMAPParser::flag_list* flag_list() const { return (m_flag_list); } + const IMAPParser::section* section() const { return (m_section); } + }; + + + // + // msg_att ::= "(" 1#(msg_att_item) ")" + // + + class msg_att : public component + { + public: + + ~msg_att() + { + for (std::vector <msg_att_item*>::iterator it = m_items.begin() ; + it != m_items.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("msg_att"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'('> >(line, &pos); + + m_items.push_back(parser.get <msg_att_item>(line, &pos)); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + { + parser.check <SPACE>(line, &pos); + m_items.push_back(parser.get <msg_att_item>(line, &pos)); + } + + *currentPos = pos; + } + + private: + + std::vector <msg_att_item*> m_items; + + public: + + const std::vector <msg_att_item*>& items() const { return (m_items); } + }; + + + // + // message_data ::= nz_number SPACE ("EXPUNGE" / + // ("FETCH" SPACE msg_att)) + // + + class message_data : public component + { + public: + + message_data() + : m_number(0), m_msg_att(NULL) + { + } + + ~message_data() + { + delete (m_msg_att); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("message_data"); + + string::size_type pos = *currentPos; + + nz_number* num = parser.get <nz_number>(line, &pos); + m_number = num->value(); + delete (num); + + parser.check <SPACE>(line, &pos); + + if (parser.checkWithArg <special_atom>(line, &pos, "expunge", true)) + { + m_type = EXPUNGE; + } + else + { + parser.checkWithArg <special_atom>(line, &pos, "fetch"); + + parser.check <SPACE>(line, &pos); + + m_type = FETCH; + m_msg_att = parser.get <IMAPParser::msg_att>(line, &pos); + } + + *currentPos = pos; + } + + + enum Type + { + EXPUNGE, + FETCH + }; + + private: + + Type m_type; + unsigned int m_number; + IMAPParser::msg_att* m_msg_att; + + public: + + const Type type() const { return (m_type); } + const unsigned int number() const { return (m_number); } + const IMAPParser::msg_att* msg_att() const { return (m_msg_att); } + }; + + + // + // resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text + // ;; Status condition + // + + class resp_cond_state : public component + { + public: + + resp_cond_state() + : m_resp_text(NULL), m_status(BAD) + { + } + + ~resp_cond_state() + { + delete (m_resp_text); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_cond_state"); + + string::size_type pos = *currentPos; + + if (parser.checkWithArg <special_atom>(line, &pos, "ok", true)) + { + m_status = OK; + } + else if (parser.checkWithArg <special_atom>(line, &pos, "no", true)) + { + m_status = NO; + } + else + { + parser.checkWithArg <special_atom>(line, &pos, "bad"); + m_status = BAD; + } + + parser.check <SPACE>(line, &pos); + + m_resp_text = parser.get <IMAPParser::resp_text>(line, &pos); + + *currentPos = pos; + } + + + enum Status + { + OK, + NO, + BAD + }; + + private: + + IMAPParser::resp_text* m_resp_text; + Status m_status; + + public: + + const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } + const Status status() const { return (m_status); } + }; + + + // + // resp_cond_bye ::= "BYE" SPACE resp_text + // + + class resp_cond_bye : public component + { + public: + + resp_cond_bye() + : m_resp_text(NULL) + { + } + + ~resp_cond_bye() + { + delete (m_resp_text); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_cond_bye"); + + string::size_type pos = *currentPos; + + parser.checkWithArg <special_atom>(line, &pos, "bye"); + + parser.check <SPACE>(line, &pos); + + m_resp_text = parser.get <IMAPParser::resp_text>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_text* m_resp_text; + + public: + + const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } + }; + + + // + // resp_cond_auth ::= ("OK" / "PREAUTH") SPACE resp_text + // ;; Authentication condition + // + + class resp_cond_auth : public component + { + public: + + resp_cond_auth() + : m_resp_text(NULL) + { + } + + ~resp_cond_auth() + { + delete (m_resp_text); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_cond_auth"); + + string::size_type pos = *currentPos; + + if (parser.checkWithArg <special_atom>(line, &pos, "ok", true)) + { + m_cond = OK; + } + else + { + parser.checkWithArg <special_atom>(line, &pos, "preauth"); + + m_cond = PREAUTH; + } + + parser.check <SPACE>(line, &pos); + + m_resp_text = parser.get <IMAPParser::resp_text>(line, &pos); + + *currentPos = pos; + } + + + enum Condition + { + OK, + PREAUTH + }; + + private: + + Condition m_cond; + IMAPParser::resp_text* m_resp_text; + + public: + + const Condition condition() const { return (m_cond); } + const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } + }; + + + // + // status_info ::= status_att SPACE number + // + + class status_info : public component + { + public: + + status_info() + : m_status_att(NULL), m_number(NULL) + { + } + + ~status_info() + { + delete (m_status_att); + delete (m_number); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("status_info"); + + string::size_type pos = *currentPos; + + m_status_att = parser.get <IMAPParser::status_att>(line, &pos); + parser.check <SPACE>(line, &pos); + m_number = parser.get <IMAPParser::number>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::status_att* m_status_att; + IMAPParser::number* m_number; + + public: + + const IMAPParser::status_att* status_att() const { return (m_status_att); } + const IMAPParser::number* number() const { return (m_number); } + }; + + + // + // mailbox_data ::= "FLAGS" SPACE mailbox_flag_list / + // "LIST" SPACE mailbox_list / + // "LSUB" SPACE mailbox_list / + // "MAILBOX" SPACE text / + // "SEARCH" [SPACE 1#nz_number] / + // "STATUS" SPACE mailbox SPACE + // "(" #<status_att number ")" / + // number SPACE "EXISTS" / + // number SPACE "RECENT" + // + + class mailbox_data : public component + { + public: + + mailbox_data() + : m_number(NULL), m_mailbox_flag_list(NULL), m_mailbox_list(NULL), + m_mailbox(NULL), m_text(NULL) + { + } + + ~mailbox_data() + { + delete (m_number); + delete (m_mailbox_flag_list); + delete (m_mailbox_list); + delete (m_mailbox); + delete (m_text); + + for (std::vector <nz_number*>::iterator it = m_search_nz_number_list.begin() ; + it != m_search_nz_number_list.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("mailbox_data"); + + string::size_type pos = *currentPos; + + m_number = parser.get <IMAPParser::number>(line, &pos, true); + + if (m_number) + { + parser.check <SPACE>(line, &pos); + + if (parser.checkWithArg <special_atom>(line, &pos, "exists", true)) + { + m_type = EXISTS; + } + else + { + parser.checkWithArg <special_atom>(line, &pos, "recent"); + + m_type = RECENT; + } + } + else + { + // "FLAGS" SPACE mailbox_flag_list + if (parser.checkWithArg <special_atom>(line, &pos, "flags", true)) + { + parser.check <SPACE>(line, &pos); + + m_mailbox_flag_list = parser.get <IMAPParser::mailbox_flag_list>(line, &pos); + + m_type = FLAGS; + } + // "LIST" SPACE mailbox_list + else if (parser.checkWithArg <special_atom>(line, &pos, "list", true)) + { + parser.check <SPACE>(line, &pos); + + m_mailbox_list = parser.get <IMAPParser::mailbox_list>(line, &pos); + + m_type = LIST; + } + // "LSUB" SPACE mailbox_list + else if (parser.checkWithArg <special_atom>(line, &pos, "lsub", true)) + { + parser.check <SPACE>(line, &pos); + + m_mailbox_list = parser.get <IMAPParser::mailbox_list>(line, &pos); + + m_type = LSUB; + } + // "MAILBOX" SPACE text + else if (parser.checkWithArg <special_atom>(line, &pos, "mailbox", true)) + { + parser.check <SPACE>(line, &pos); + + m_text = parser.get <IMAPParser::text>(line, &pos); + + m_type = MAILBOX; + } + // "SEARCH" [SPACE 1#nz_number] + else if (parser.checkWithArg <special_atom>(line, &pos, "search", true)) + { + if (parser.check <SPACE>(line, &pos, true)) + { + m_search_nz_number_list.push_back + (parser.get <nz_number>(line, &pos)); + + while (parser.check <SPACE>(line, &pos, true)) + { + m_search_nz_number_list.push_back + (parser.get <nz_number>(line, &pos)); + } + } + + m_type = SEARCH; + } + // "STATUS" SPACE mailbox SPACE + // "(" #<status_att number)] ")" + // + // "(" [status_att SPACE number *(SPACE status_att SPACE number)] ")" + else + { + parser.checkWithArg <special_atom>(line, &pos, "status"); + parser.check <SPACE>(line, &pos); + + m_mailbox = parser.get <IMAPParser::mailbox>(line, &pos); + + parser.check <SPACE>(line, &pos); + parser.check <one_char <'('> >(line, &pos); + + m_status_info_list.push_back(parser.get <status_info>(line, &pos)); + + while (!parser.check <one_char <')'> >(line, &pos, true)) + m_status_info_list.push_back(parser.get <status_info>(line, &pos)); + + m_type = STATUS; + } + } + + *currentPos = pos; + } + + + enum Type + { + FLAGS, + LIST, + LSUB, + MAILBOX, + SEARCH, + STATUS, + EXISTS, + RECENT + }; + + private: + + Type m_type; + + IMAPParser::number* m_number; + IMAPParser::mailbox_flag_list* m_mailbox_flag_list; + IMAPParser::mailbox_list* m_mailbox_list; + IMAPParser::mailbox* m_mailbox; + IMAPParser::text* m_text; + std::vector <nz_number*> m_search_nz_number_list; + std::vector <status_info*> m_status_info_list; + + public: + + const Type type() const { return (m_type); } + + const IMAPParser::number* number() const { return (m_number); } + const IMAPParser::mailbox_flag_list* mailbox_flag_list() const { return (m_mailbox_flag_list); } + const IMAPParser::mailbox_list* mailbox_list() const { return (m_mailbox_list); } + const IMAPParser::mailbox* mailbox() const { return (m_mailbox); } + const IMAPParser::text* text() const { return (m_text); } + const std::vector <nz_number*>& search_nz_number_list() const { return (m_search_nz_number_list); } + const std::vector <status_info*>& status_info_list() const { return (m_status_info_list); } + }; + + + // + // response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / + // mailbox_data / message_data / capability_data) CRLF + // + + class response_data : public component + { + public: + + response_data() + : m_resp_cond_state(NULL), m_resp_cond_bye(NULL), + m_mailbox_data(NULL), m_message_data(NULL), m_capability_data(NULL) + { + } + + ~response_data() + { + delete (m_resp_cond_state); + delete (m_resp_cond_bye); + delete (m_mailbox_data); + delete (m_message_data); + delete (m_capability_data); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("response_data"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'*'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + if (!(m_resp_cond_state = parser.get <IMAPParser::resp_cond_state>(line, &pos, true))) + if (!(m_resp_cond_bye = parser.get <IMAPParser::resp_cond_bye>(line, &pos, true))) + if (!(m_mailbox_data = parser.get <IMAPParser::mailbox_data>(line, &pos, true))) + if (!(m_message_data = parser.get <IMAPParser::message_data>(line, &pos, true))) + m_capability_data = parser.get <IMAPParser::capability_data>(line, &pos); + + parser.check <CRLF>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_cond_state* m_resp_cond_state; + IMAPParser::resp_cond_bye* m_resp_cond_bye; + IMAPParser::mailbox_data* m_mailbox_data; + IMAPParser::message_data* m_message_data; + IMAPParser::capability_data* m_capability_data; + + public: + + const IMAPParser::resp_cond_state* resp_cond_state() const { return (m_resp_cond_state); } + const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); } + const IMAPParser::mailbox_data* mailbox_data() const { return (m_mailbox_data); } + const IMAPParser::message_data* message_data() const { return (m_message_data); } + const IMAPParser::capability_data* capability_data() const { return (m_capability_data); } + }; + + + class continue_req_or_response_data : public component + { + public: + + continue_req_or_response_data() + : m_continue_req(NULL), m_response_data(NULL) + { + } + + ~continue_req_or_response_data() + { + delete (m_continue_req); + delete (m_response_data); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("continue_req_or_response_data"); + + string::size_type pos = *currentPos; + + if (!(m_continue_req = parser.get <IMAPParser::continue_req>(line, &pos, true))) + m_response_data = parser.get <IMAPParser::response_data>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::continue_req* m_continue_req; + IMAPParser::response_data* m_response_data; + + public: + + const IMAPParser::continue_req* continue_req() const { return (m_continue_req); } + const IMAPParser::response_data* response_data() const { return (m_response_data); } + }; + + + // + // response_fatal ::= "*" SPACE resp_cond_bye CRLF + // ;; Server closes connection immediately + // + + class response_fatal : public component + { + public: + + response_fatal() + : m_resp_cond_bye(NULL) + { + } + + ~response_fatal() + { + delete (m_resp_cond_bye); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("response_fatal"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'*'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + m_resp_cond_bye = parser.get <IMAPParser::resp_cond_bye>(line, &pos); + + parser.check <CRLF>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_cond_bye* m_resp_cond_bye; + + public: + + const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); } + }; + + + // + // response_tagged ::= tag SPACE resp_cond_state CRLF + // + + class response_tagged : public component + { + public: + + response_tagged() + : m_resp_cond_state(NULL) + { + } + + ~response_tagged() + { + delete (m_resp_cond_state); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("response_tagged"); + + string::size_type pos = *currentPos; + + parser.check <IMAPParser::xtag>(line, &pos); + parser.check <SPACE>(line, &pos); + m_resp_cond_state = parser.get <IMAPParser::resp_cond_state>(line, &pos); + parser.check <CRLF>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_cond_state* m_resp_cond_state; + + public: + + const IMAPParser::resp_cond_state* resp_cond_state() const { return (m_resp_cond_state); } + }; + + + // + // response_done ::= response_tagged / response_fatal + // + + class response_done : public component + { + public: + + response_done() + : m_response_tagged(NULL), m_response_fatal(NULL) + { + } + + ~response_done() + { + delete (m_response_tagged); + delete (m_response_fatal); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("response_done"); + + string::size_type pos = *currentPos; + + if (!(m_response_tagged = parser.get <IMAPParser::response_tagged>(line, &pos, true))) + m_response_fatal = parser.get <IMAPParser::response_fatal>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::response_tagged* m_response_tagged; + IMAPParser::response_fatal* m_response_fatal; + + public: + + const IMAPParser::response_tagged* response_tagged() const { return (m_response_tagged); } + const IMAPParser::response_fatal* response_fatal() const { return (m_response_fatal); } + }; + + + // + // response ::= *(continue_req / response_data) response_done + // + + class response : public component + { + public: + + response() + : m_response_done(NULL) + { + } + + ~response() + { + for (std::vector <IMAPParser::continue_req_or_response_data*>::iterator + it = m_continue_req_or_response_data.begin() ; + it != m_continue_req_or_response_data.end() ; ++it) + { + delete (*it); + } + + delete (m_response_done); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("response"); + + string::size_type pos = *currentPos; + string curLine = line; + bool partial = false; // partial response + + IMAPParser::continue_req_or_response_data* resp = NULL; + + while ((resp = parser.get <IMAPParser::continue_req_or_response_data>(curLine, &pos, true)) != NULL) + { + m_continue_req_or_response_data.push_back(resp); + + // Partial response (continue_req) + if (resp->continue_req()) + { + partial = true; + break; + } + + // We have read a CRLF, read another line + curLine = parser.readLine(); + pos = 0; + } + + if (!partial) + m_response_done = parser.get <IMAPParser::response_done>(curLine, &pos); + + *currentPos = pos; + } + + + const bool isBad() const + { + if (!response_done()) // incomplete (partial) response + return (true); + + if (response_done()->response_fatal()) + return (true); + + if (response_done()->response_tagged()->resp_cond_state()-> + status() == IMAPParser::resp_cond_state::BAD) + { + return (true); + } + + return (false); + } + + private: + + std::vector <IMAPParser::continue_req_or_response_data*> m_continue_req_or_response_data; + IMAPParser::response_done* m_response_done; + + public: + + const std::vector <IMAPParser::continue_req_or_response_data*>& continue_req_or_response_data() const { return (m_continue_req_or_response_data); } + const IMAPParser::response_done* response_done() const { return (m_response_done); } + }; + + + // + // greeting ::= "*" SPACE (resp_cond_auth / resp_cond_bye) CRLF + // + + class greeting : public component + { + public: + + greeting() + : m_resp_cond_auth(NULL), m_resp_cond_bye(NULL) + { + } + + ~greeting() + { + delete (m_resp_cond_auth); + delete (m_resp_cond_bye); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("greeting"); + + string::size_type pos = *currentPos; + + parser.check <one_char <'*'> >(line, &pos); + parser.check <SPACE>(line, &pos); + + if (!(m_resp_cond_auth = parser.get <IMAPParser::resp_cond_auth>(line, &pos, true))) + m_resp_cond_bye = parser.get <IMAPParser::resp_cond_bye>(line, &pos); + + parser.check <CRLF>(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_cond_auth* m_resp_cond_auth; + IMAPParser::resp_cond_bye* m_resp_cond_bye; + + public: + + const IMAPParser::resp_cond_auth* resp_cond_auth() const { return (m_resp_cond_auth); } + const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); } + }; + + + + // + // The main functions used to parse a response + // + + response* readResponse(literalHandler* lh = NULL) + { + string::size_type pos = 0; + string line = readLine(); + + m_literalHandler = lh; + response* resp = get <response>(line, &pos); + m_literalHandler = NULL; + + return (resp); + } + + + greeting* readGreeting() + { + string::size_type pos = 0; + string line = readLine(); + + return get <greeting>(line, &pos); + } + + + // + // Get a token and advance + // + + template <class TYPE> + TYPE* get(string& line, string::size_type* currentPos, + const bool noThrow = false) + { + component* resp = new TYPE; + return internalGet <TYPE>(resp, line, currentPos, noThrow); + } + + + template <class TYPE, class ARG1_TYPE, class ARG2_TYPE> + TYPE* getWithArgs(string& line, string::size_type* currentPos, + ARG1_TYPE arg1, ARG2_TYPE arg2, const bool noThrow = false) + { + component* resp = new TYPE(arg1, arg2); + return internalGet <TYPE>(resp, line, currentPos, noThrow); + } + + +private: + + template <class TYPE> + TYPE* internalGet(component* resp, string& line, string::size_type* currentPos, + const bool noThrow = false) + { +#if DEBUG_RESPONSE + DEBUG_RESPONSE_level += " "; +#endif + + try + { + resp->go(*this, line, currentPos); + +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + + DEBUG_RESPONSE_level.erase(DEBUG_RESPONSE_level.begin() + DEBUG_RESPONSE_level.length() - 1); + DEBUG_RESPONSE_components.pop_back(); +#endif + } + catch (...) + { +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + + DEBUG_RESPONSE_level.erase(DEBUG_RESPONSE_level.begin() + DEBUG_RESPONSE_level.length() - 1); + DEBUG_RESPONSE_components.pop_back(); +#endif + + delete (resp); + if (!noThrow) throw; + return (NULL); + } + + return static_cast <TYPE*>(resp); + } + + +public: + + // + // Check a token and advance + // + + template <class TYPE> + const bool check(string& line, string::size_type* currentPos, + const bool noThrow = false) + { + try + { + TYPE term; + term.go(*this, line, currentPos); + +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + DEBUG_RESPONSE_components.pop_back(); +#endif + } + catch (...) + { +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + DEBUG_RESPONSE_components.pop_back(); +#endif + + if (!noThrow) throw; + return false; + } + + return true; + } + + template <class TYPE, class ARG_TYPE> + const bool checkWithArg(string& line, string::size_type* currentPos, + const ARG_TYPE arg, const bool noThrow = false) + { + try + { + TYPE term(arg); + term.go(*this, line, currentPos); + +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + DEBUG_RESPONSE_components.pop_back(); +#endif + } + catch (...) + { +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + DEBUG_RESPONSE_components.pop_back(); +#endif + + if (!noThrow) throw; + return false; + } + + return true; + } + + +private: + + IMAPTag* m_tag; + socket* m_socket; + + progressionListener* m_progress; + + literalHandler* m_literalHandler; + + timeoutHandler* m_timeoutHandler; + + + string m_buffer; + int m_pos; + + string m_lastLine; + +public: + + // + // Read one line + // + + const string readLine() + { + string::size_type pos; + + while ((pos = m_buffer.find('\n')) == string::npos) + { + read(); + } + + string line; + line.resize(pos + 1); + std::copy(m_buffer.begin(), m_buffer.begin() + pos + 1, line.begin()); + + m_buffer.erase(m_buffer.begin(), m_buffer.begin() + pos + 1); + + m_lastLine = line; + +#if DEBUG_RESPONSE + std::cout << std::endl << "Read line:" << std::endl << line << std::endl; +#endif + + return (line); + } + + + // + // Read available data from socket stream + // + + void read() + { + string receiveBuffer; + + while (receiveBuffer.empty()) + { + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + throw exceptions::operation_timed_out(); + } + + // We have received data: reset the time-out counter + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) // buffer is empty + { + platformDependant::getHandler()->wait(); + continue; + } + + // We have received data ... + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + } + + m_buffer += receiveBuffer; + } + + + void readLiteral(literalHandler::target& buffer, string::size_type count) + { + string::size_type len = 0; + string receiveBuffer; + + if (m_progress) + m_progress->start(count); + + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + if (!m_buffer.empty()) + { + if (m_buffer.length() > count) + { + buffer.putData(string(m_buffer.begin(), m_buffer.begin() + count)); + m_buffer.erase(m_buffer.begin(), m_buffer.begin() + count); + len = count; + } + else + { + len += m_buffer.length(); + buffer.putData(m_buffer); + m_buffer.clear(); + } + } + + while (len < count) + { + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + throw exceptions::operation_timed_out(); + } + + // Receive data from the socket + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) // buffer is empty + { + platformDependant::getHandler()->wait(); + continue; + } + + // We have received data: reset the time-out counter + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + if (len + receiveBuffer.length() > count) + { + const string::size_type remaining = count - len; + + // Get the needed amount of data + buffer.putData(string(receiveBuffer.begin(), receiveBuffer.begin() + remaining)); + + // Put the remaining data into the internal response buffer + receiveBuffer.erase(receiveBuffer.begin(), receiveBuffer.begin() + remaining); + m_buffer += receiveBuffer; + + len = count; + } + else + { + buffer.putData(receiveBuffer); + len += receiveBuffer.length(); + } + + // Notify progression + if (m_progress) + m_progress->progress(len, count); + } + + if (m_progress) + m_progress->stop(count); + } +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPPARSER_HPP_INCLUDED diff --git a/src/messaging/IMAPStore.cpp b/src/messaging/IMAPStore.cpp new file mode 100644 index 00000000..7d2bc823 --- /dev/null +++ b/src/messaging/IMAPStore.cpp @@ -0,0 +1,257 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPStore.hpp" +#include "IMAPFolder.hpp" +#include "IMAPConnection.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" + +#include <map> + + +namespace vmime { +namespace messaging { + + +// +// IMAPauthenticator: private class used internally +// +// Used to request user credentials only in the first authentication +// and reuse this information the next times +// + +class IMAPauthenticator : public authenticator +{ +public: + + IMAPauthenticator(authenticator* auth) + : m_auth(auth), m_infos(NULL) + { + } + + ~IMAPauthenticator() + { + delete (m_infos); + } + + const authenticationInfos requestAuthInfos() const + { + if (m_infos == NULL) + m_infos = new authenticationInfos(m_auth->requestAuthInfos()); + + return (*m_infos); + } + +private: + + authenticator* m_auth; + mutable authenticationInfos* m_infos; +}; + + + +// +// IMAPStore +// + +IMAPStore::IMAPStore(class session& sess, class authenticator* auth) + : store(sess, infosInstance(), auth), + m_connection(NULL), m_oneTimeAuth(NULL) +{ +} + + +IMAPStore::~IMAPStore() +{ + if (isConnected()) + disconnect(); +} + + +authenticator* IMAPStore::oneTimeAuthenticator() +{ + return (m_oneTimeAuth); +} + + +const string IMAPStore::protocolName() const +{ + return "imap"; +} + + +folder* IMAPStore::getRootFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new IMAPFolder(folder::path(), this); +} + + +folder* IMAPStore::getDefaultFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new IMAPFolder(folder::path::component("INBOX"), this); +} + + +folder* IMAPStore::getFolder(const folder::path& path) +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new IMAPFolder(path, this); +} + + +void IMAPStore::connect() +{ + if (isConnected()) + throw exceptions::already_connected(); + + m_oneTimeAuth = new IMAPauthenticator(&authenticator()); + + m_connection = new IMAPConnection(this, m_oneTimeAuth); + + try + { + m_connection->connect(); + } + catch (std::exception&) + { + delete (m_connection); + m_connection = NULL; + throw; + } +} + + +const bool IMAPStore::isConnected() const +{ + return (m_connection && m_connection->isConnected()); +} + + +void IMAPStore::disconnect() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + for (std::list <IMAPFolder*>::iterator it = m_folders.begin() ; + it != m_folders.end() ; ++it) + { + (*it)->onStoreDisconnected(); + } + + m_folders.clear(); + + + m_connection->disconnect(); + + delete (m_oneTimeAuth); + m_oneTimeAuth = NULL; + + delete (m_connection); + m_connection = NULL; +} + + +void IMAPStore::noop() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + m_connection->send(true, "NOOP", true); + + utility::auto_ptr <IMAPParser::response> resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("NOOP", m_connection->parser()->lastLine()); + } +} + + +IMAPConnection* IMAPStore::connection() +{ + return (m_connection); +} + + +void IMAPStore::registerFolder(IMAPFolder* folder) +{ + m_folders.push_back(folder); +} + + +void IMAPStore::unregisterFolder(IMAPFolder* folder) +{ + std::list <IMAPFolder*>::iterator it = std::find(m_folders.begin(), m_folders.end(), folder); + if (it != m_folders.end()) m_folders.erase(it); +} + + + + +// Service infos + +IMAPStore::_infos IMAPStore::sm_infos; + + +const port_t IMAPStore::_infos::defaultPort() const +{ + return (143); +} + + +const string IMAPStore::_infos::propertyPrefix() const +{ + return "store.imap."; +} + + +const std::vector <string> IMAPStore::_infos::availableProperties() const +{ + std::vector <string> list; + + // IMAP-specific options + //list.push_back("auth.mechanism"); + + // Common properties + list.push_back("auth.username"); + list.push_back("auth.password"); + + list.push_back("server.address"); + list.push_back("server.port"); + list.push_back("server.socket-factory"); + + list.push_back("timeout.factory"); + + return (list); +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPStore.hpp b/src/messaging/IMAPStore.hpp new file mode 100644 index 00000000..cab9419f --- /dev/null +++ b/src/messaging/IMAPStore.hpp @@ -0,0 +1,112 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPSTORE_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPSTORE_HPP_INCLUDED + + +#include "store.hpp" +#include "socket.hpp" +#include "folder.hpp" +#include "../config.hpp" + +#include <ostream> + + +namespace vmime { +namespace messaging { + + +class IMAPParser; +class IMAPTag; +class IMAPConnection; + + +/** IMAP store service. + */ + +class IMAPStore : public store +{ + friend class IMAPFolder; + friend class IMAPMessage; + +public: + + IMAPStore(class session& sess, class authenticator* auth); + ~IMAPStore(); + + const string protocolName() const; + + folder* getDefaultFolder(); + folder* getRootFolder(); + folder* getFolder(const folder::path& path); + + static const serviceInfos& infosInstance() { return (sm_infos); } + const serviceInfos& infos() const { return (sm_infos); } + + void connect(); + const bool isConnected() const; + void disconnect(); + + void noop(); + +private: + + // Connection + IMAPConnection* m_connection; + + // Used to request the authentication informations only the + // first time, and reuse these informations the next time. + class authenticator* m_oneTimeAuth; + + + + class authenticator* oneTimeAuthenticator(); + + + IMAPConnection* connection(); + + + void registerFolder(IMAPFolder* folder); + void unregisterFolder(IMAPFolder* folder); + + std::list <IMAPFolder*> m_folders; + + + + // Service infos + class _infos : public serviceInfos + { + public: + + const port_t defaultPort() const; + + const string propertyPrefix() const; + const std::vector <string> availableProperties() const; + }; + + static _infos sm_infos; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPSTORE_HPP_INCLUDED diff --git a/src/messaging/IMAPTag.cpp b/src/messaging/IMAPTag.cpp new file mode 100644 index 00000000..023130bb --- /dev/null +++ b/src/messaging/IMAPTag.cpp @@ -0,0 +1,97 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPTag.hpp" + + +namespace vmime { +namespace messaging { + + +const int IMAPTag::sm_maxNumber = 52 * 10 * 10 * 10; + + +IMAPTag::IMAPTag(const int number) + : m_number(number) +{ + m_tag.resize(4); +} + + +IMAPTag::IMAPTag(const IMAPTag& tag) + : m_number(tag.m_number) +{ + m_tag.resize(4); +} + + +IMAPTag::IMAPTag() + : m_number(0) +{ + m_tag.resize(4); +} + + +IMAPTag& IMAPTag::operator++() +{ + ++m_number; + + if (m_number >= sm_maxNumber) + m_number = 1; + + generate(); + + return (*this); +} + + +const IMAPTag IMAPTag::operator++(int) +{ + IMAPTag old(*this); + operator++(); + return (old); +} + + +const int IMAPTag::number() const +{ + return (m_number); +} + + +IMAPTag::operator string() const +{ + return (m_tag); +} + + +void IMAPTag::generate() +{ + static const char prefixChars[53] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + m_tag[0] = prefixChars[m_number / 1000]; + m_tag[1] = '0' + (m_number % 1000) / 100; + m_tag[2] = '0' + (m_number % 100) / 10; + m_tag[3] = '0' + (m_number % 10); +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPTag.hpp b/src/messaging/IMAPTag.hpp new file mode 100644 index 00000000..c7c48f31 --- /dev/null +++ b/src/messaging/IMAPTag.hpp @@ -0,0 +1,64 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPTAG_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPTAG_HPP_INCLUDED + + +#include "types.hpp" + + +namespace vmime { +namespace messaging { + + +class IMAPTag +{ +private: + + IMAPTag(const int number); + IMAPTag(const IMAPTag& tag); + +public: + + IMAPTag(); + + IMAPTag& operator++(); // ++IMAPTag + const IMAPTag operator++(int); // IMAPTag++ + + const int number() const; + + operator string() const; + +private: + + void generate(); + + static const int sm_maxNumber; + + int m_number; + string m_tag; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPTAG_HPP_INCLUDED diff --git a/src/messaging/IMAPUtils.cpp b/src/messaging/IMAPUtils.cpp new file mode 100644 index 00000000..2413c86d --- /dev/null +++ b/src/messaging/IMAPUtils.cpp @@ -0,0 +1,553 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPUtils.hpp" +#include "message.hpp" + +#include <sstream> +#include <iterator> +#include <algorithm> + + +namespace vmime { +namespace messaging { + + +const string IMAPUtils::quoteString(const string& text) +{ + // + // ATOM_CHAR ::= <any CHAR except atom_specials> + // + // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / + // list_wildcards / quoted_specials + // + // list_wildcards ::= "%" / "*" + // + // quoted_specials ::= <"> / "\" + // + // CHAR ::= <any 7-bit US-ASCII character except NUL, + // 0x01 - 0x7f> + // + // CTL ::= <any ASCII control character and DEL, + // 0x00 - 0x1f, 0x7f> + // + + bool needQuoting = text.empty(); + + for (string::const_iterator it = text.begin() ; + !needQuoting && it != text.end() ; ++it) + { + const unsigned char c = *it; + + switch (c) + { + case '(': + case ')': + case '{': + case 0x20: // SPACE + case '%': + case '*': + case '"': + case '\\': + + needQuoting = true; + break; + + default: + + if (c <= 0x1f || c >= 0x7f) + needQuoting = true; + } + } + + if (needQuoting) + { + string quoted; + quoted.reserve((text.length() * 3) / 2 + 2); + + quoted += '"'; + + for (string::const_iterator it = text.begin() ; + !needQuoting && it != text.end() ; ++it) + { + const unsigned char c = *it; + + if (c == '\\' || c == '"') + quoted += '\\'; + + quoted += c; + } + + quoted += '"'; + + return (quoted); + } + else + { + return (text); + } +} + + +const string IMAPUtils::pathToString + (const char hierarchySeparator, const folder::path& path) +{ + string result; + + for (int i = 0 ; i < path.size() ; ++i) + { + if (i > 0) result += hierarchySeparator; + result += toModifiedUTF7(hierarchySeparator, path[i]); + } + + return (result); +} + + +const folder::path IMAPUtils::stringToPath + (const char hierarchySeparator, const string& str) +{ + folder::path result; + string::const_iterator begin = str.begin(); + + for (string::const_iterator it = str.begin() ; it != str.end() ; ++it) + { + if (*it == hierarchySeparator) + { + result /= fromModifiedUTF7(string(begin, it)); + begin = it + 1; + } + } + + if (begin != str.end()) + { + result /= fromModifiedUTF7(string(begin, str.end())); + } + + return (result); +} + + +const string IMAPUtils::toModifiedUTF7 + (const char hierarchySeparator, const folder::path::component& text) +{ + // We will replace the hierarchy separator with an equivalent + // UTF-7 sequence, so we compute it here... + const char base64alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,="; + + const unsigned int hs = (unsigned int)(unsigned char) hierarchySeparator; + + string hsUTF7; + hsUTF7.resize(3); + + hsUTF7[0] = base64alphabet[0]; + hsUTF7[1] = base64alphabet[(hs & 0xF0) >> 4]; + hsUTF7[2] = base64alphabet[(hs & 0x0F) << 2]; + + // Transcode path component to UTF-7 charset. + // WARNING: This may throw "exceptions::charset_conv_error" + const string cvt = text.getConvertedText(charset(charsets::UTF_7)); + + // Transcode to modified UTF-7 (RFC-2060). + string out; + out.reserve((cvt.length() * 3) / 2); + + bool inB64sequence = false; + + for (string::const_iterator it = cvt.begin() ; it != cvt.end() ; ++it) + { + const unsigned char c = *it; + + // Replace hierarchy separator with an equivalent UTF-7 Base64 sequence + if (!inB64sequence && c == hierarchySeparator) + { + out += "&" + hsUTF7 + "-"; + continue; + } + + switch (c) + { + // Beginning of Base64 sequence: replace '+' with '&' + case '+': + { + if (!inB64sequence) + { + inB64sequence = true; + out += '&'; + } + else + { + out += '+'; + } + + break; + } + // End of Base64 sequence + case '-': + { + inB64sequence = false; + out += '-'; + break; + } + // ',' is used instead of '/' in modified Base64 + case '/': + { + out += inB64sequence ? ',' : '/'; + break; + } + // '&' (0x26) is represented by the two-octet sequence "&-" + case '&': + { + if (!inB64sequence) + out += "&-"; + else + out += '&'; + + break; + } + default: + { + out += c; + break; + } + + } + } + + return (out); +} + + +const folder::path::component IMAPUtils::fromModifiedUTF7(const string& text) +{ + // Transcode from modified UTF-7 (RFC-2060). + string out; + out.reserve(text.length()); + + bool inB64sequence = false; + unsigned char prev = 0; + + for (string::const_iterator it = text.begin() ; it != text.end() ; ++it) + { + const unsigned char c = *it; + + switch (c) + { + // Start of Base64 sequence + case '&': + { + if (!inB64sequence) + { + inB64sequence = true; + out += '+'; + } + else + { + out += '&'; + } + + break; + } + // End of Base64 sequence (or "&-" --> "&") + case '-': + { + if (inB64sequence && prev == '&') + out += '&'; + else + out += '-'; + + inB64sequence = false; + break; + } + // ',' is used instead of '/' in modified Base64 + case ',': + { + out += (inB64sequence ? '/' : ','); + break; + } + default: + { + out += c; + break; + } + + } + + prev = c; + } + + // Store it as UTF-8 by default + string cvt; + charset::convert(out, cvt, + charset(charsets::UTF_7), charset(charsets::UTF_8)); + + return (folder::path::component(cvt, charset(charsets::UTF_8))); +} + + +const int IMAPUtils::folderTypeFromFlags(const IMAPParser::mailbox_flag_list* list) +{ + // Get folder type + int type = folder::TYPE_CONTAINS_MESSAGES | folder::TYPE_CONTAINS_FOLDERS; + const std::vector <IMAPParser::mailbox_flag*>& flags = list->flags(); + + for (std::vector <IMAPParser::mailbox_flag*>::const_iterator it = flags.begin() ; + it != flags.end() ; ++it) + { + if ((*it)->type() == IMAPParser::mailbox_flag::NOSELECT) + type &= ~folder::TYPE_CONTAINS_MESSAGES; + } + + if (type & folder::TYPE_CONTAINS_MESSAGES) + type &= ~folder::TYPE_CONTAINS_FOLDERS; + + return (type); +} + + +const int IMAPUtils::folderFlagsFromFlags(const IMAPParser::mailbox_flag_list* list) +{ + // Get folder flags + int folderFlags = folder::FLAG_CHILDREN; + const std::vector <IMAPParser::mailbox_flag*>& flags = list->flags(); + + for (std::vector <IMAPParser::mailbox_flag*>::const_iterator it = flags.begin() ; + it != flags.end() ; ++it) + { + if ((*it)->type() == IMAPParser::mailbox_flag::NOSELECT) + folderFlags |= folder::FLAG_NO_OPEN; + else if ((*it)->type() == IMAPParser::mailbox_flag::NOINFERIORS) + folderFlags &= ~folder::FLAG_CHILDREN; + } + + return (folderFlags); +} + + +const int IMAPUtils::messageFlagsFromFlags(const IMAPParser::flag_list* list) +{ + const std::vector <IMAPParser::flag*>& flagList = list->flags(); + int flags = 0; + + for (std::vector <IMAPParser::flag*>::const_iterator + it = flagList.begin() ; it != flagList.end() ; ++it) + { + switch ((*it)->type()) + { + case IMAPParser::flag::ANSWERED: + flags |= message::FLAG_REPLIED; + break; + case IMAPParser::flag::FLAGGED: + flags |= message::FLAG_MARKED; + break; + case IMAPParser::flag::DELETED: + flags |= message::FLAG_DELETED; + break; + case IMAPParser::flag::SEEN: + flags |= message::FLAG_SEEN; + break; + + default: + //case IMAPParser::flag::UNKNOWN: + //case IMAPParser::flag::DRAFT: + break; + } + } + + return (flags); +} + + +const string IMAPUtils::messageFlagList(const int flags) +{ + std::vector <string> flagList; + + if (flags & message::FLAG_REPLIED) flagList.push_back("\\Answered"); + if (flags & message::FLAG_MARKED) flagList.push_back("\\Flagged"); + if (flags & message::FLAG_DELETED) flagList.push_back("\\Deleted"); + if (flags & message::FLAG_SEEN) flagList.push_back("\\Seen"); + + if (!flagList.empty()) + { + std::ostringstream res; + res << "("; + + if (flagList.size() >= 2) + { + std::copy(flagList.begin(), flagList.end() - 1, + std::ostream_iterator <string>(res, " ")); + } + + res << *(flagList.end() - 1) << ")"; + + return (res.str()); + } + + return ""; +} + + +// This function builds a "IMAP set" given a list. Try to group consecutive +// message numbers to reduce the list. +// +// Example: +// IN = "1,2,3,4,5,7,8,13,15,16,17" +// OUT = "1:5,7:8,13,15:*" for a mailbox with a total of 17 messages (max = 17) + +const string IMAPUtils::listToSet(const std::vector <int>& list, const int max, + const bool alreadySorted) +{ + // Sort a copy of the list (if not already sorted) + std::vector <int> temp; + + if (!alreadySorted) + { + temp.resize(list.size()); + std::copy(list.begin(), list.end(), temp.begin()); + + std::sort(temp.begin(), temp.end()); + } + + const std::vector <int>& theList = (alreadySorted ? list : temp); + + // Build the set + std::ostringstream res; + int previous = -1, setBegin = -1; + + for (std::vector <int>::const_iterator it = theList.begin() ; + it != theList.end() ; ++it) + { + const int current = *it; + + if (previous == -1) + { + res << current; + + previous = current; + setBegin = current; + } + else + { + if (current == previous + 1) + { + previous = current; + } + else + { + if (setBegin != previous) + { + res << ":" << previous << "," << current; + + previous = current; + setBegin = current; + } + else + { + if (setBegin != current) // skip duplicates + res << "," << current; + + previous = current; + setBegin = current; + } + } + } + } + + if (previous != setBegin) + { + if (previous == max) + res << ":*"; + else + res << ":" << previous; + } + + return (res.str()); +} + + +const string IMAPUtils::dateTime(const vmime::datetime& date) +{ + std::ostringstream res; + + // date_time ::= <"> date_day_fixed "-" date_month "-" date_year + // SPACE time SPACE zone <"> + // + // time ::= 2digit ":" 2digit ":" 2digit + // ;; Hours minutes seconds + // zone ::= ("+" / "-") 4digit + // ;; Signed four-digit value of hhmm representing + // ;; hours and minutes west of Greenwich + res << '"'; + + // Date + if (date.day() < 10) res << ' '; + res << date.day(); + + res << '-'; + + static const char* monthNames[12] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + res << monthNames[std::min(std::max(date.month() - 1, 0), 11)]; + + res << '-'; + + if (date.year() < 10) res << '0'; + if (date.year() < 100) res << '0'; + if (date.year() < 1000) res << '0'; + res << date.year(); + + res << ' '; + + // Time + if (date.hour() < 10) res << '0'; + res << date.hour() << ':'; + + if (date.minute() < 10) res << '0'; + res << date.minute() << ':'; + + if (date.second() < 10) res << '0'; + res << date.second(); + + res << ' '; + + // Zone + const int zs = (date.zone() < 0 ? -1 : 1); + const int zh = (date.zone() * zs) / 60; + const int zm = (date.zone() * zs) % 60; + + res << (zs < 0 ? '-' : '+'); + + if (zh < 10) res << '0'; + res << zh; + + if (zm < 10) res << '0'; + res << zm; + + res << '"'; + + + return (res.str()); +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPUtils.hpp b/src/messaging/IMAPUtils.hpp new file mode 100644 index 00000000..9d523eb7 --- /dev/null +++ b/src/messaging/IMAPUtils.hpp @@ -0,0 +1,65 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPUTILS_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPUTILS_HPP_INCLUDED + + +#include "folder.hpp" +#include "../types.hpp" +#include "IMAPParser.hpp" +#include "../dateTime.hpp" + +#include <vector> + + +namespace vmime { +namespace messaging { + + +class IMAPUtils +{ +public: + + static const string pathToString(const char hierarchySeparator, const folder::path& path); + static const folder::path stringToPath(const char hierarchySeparator, const string& str); + + static const string toModifiedUTF7(const char hierarchySeparator, const folder::path::component& text); + static const folder::path::component fromModifiedUTF7(const string& text); + + static const string quoteString(const string& text); + + static const int folderTypeFromFlags(const IMAPParser::mailbox_flag_list* list); + static const int folderFlagsFromFlags(const IMAPParser::mailbox_flag_list* list); + + static const int messageFlagsFromFlags(const IMAPParser::flag_list* list); + + static const string messageFlagList(const int flags); + + static const string listToSet(const std::vector <int>& list, const int max = -1, const bool alreadySorted = false); + + static const string dateTime(const vmime::datetime& date); +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPUTILS_HPP_INCLUDED diff --git a/src/messaging/POP3Folder.cpp b/src/messaging/POP3Folder.cpp new file mode 100644 index 00000000..663d03b5 --- /dev/null +++ b/src/messaging/POP3Folder.cpp @@ -0,0 +1,661 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "POP3Folder.hpp" + +#include "POP3Store.hpp" +#include "POP3Message.hpp" + +#include "../exception.hpp" + + +namespace vmime { +namespace messaging { + + +POP3Folder::POP3Folder(const folder::path& path, POP3Store* store) + : m_store(store), m_path(path), m_name(path.last()), m_mode(-1), m_open(false) +{ + m_store->registerFolder(this); +} + + +POP3Folder::~POP3Folder() +{ + if (m_store) + { + if (m_open) + close(false); + + m_store->unregisterFolder(this); + } + else if (m_open) + { + onClose(); + } +} + + +const int POP3Folder::mode() const +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + return (m_mode); +} + + +const int POP3Folder::type() +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + if (m_path.empty()) + return (TYPE_CONTAINS_FOLDERS); + else if (m_path.size() == 1 && m_path[0].buffer() == "INBOX") + return (TYPE_CONTAINS_MESSAGES); + else + throw exceptions::folder_not_found(); +} + + +const int POP3Folder::flags() +{ + return (0); +} + + +const folder::path::component POP3Folder::name() const +{ + return (m_name); +} + + +const folder::path POP3Folder::fullPath() const +{ + return (m_path); +} + + +void POP3Folder::open(const int mode, bool failIfModeIsNotAvailable) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + if (m_path.empty()) + { + if (mode != MODE_READ_ONLY && failIfModeIsNotAvailable) + throw exceptions::operation_not_supported(); + + m_open = true; + m_mode = mode; + + m_messageCount = 0; + } + else if (m_path.size() == 1 && m_path[0].buffer() == "INBOX") + { + m_store->sendRequest("STAT"); + + string response; + m_store->readResponse(response, false); + + if (!m_store->isSuccessResponse(response)) + throw exceptions::command_error("STAT", response); + + m_store->stripResponseCode(response, response); + + std::istringstream iss(response); + iss >> m_messageCount; + + if (iss.fail()) + throw exceptions::invalid_response("STAT", response); + + m_open = true; + m_mode = mode; + } + else + { + throw exceptions::folder_not_found(); + } +} + +void POP3Folder::close(const bool expunge) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + if (!expunge) + { + m_store->sendRequest("RSET"); + + string response; + m_store->readResponse(response, false); + } + + m_open = false; + m_mode = -1; + + onClose(); +} + + +void POP3Folder::onClose() +{ + for (MessageMap::iterator it = m_messages.begin() ; it != m_messages.end() ; ++it) + (*it).first->onFolderClosed(); + + m_messages.clear(); +} + + +void POP3Folder::create(const int /* type */) +{ + throw exceptions::operation_not_supported(); +} + + +const bool POP3Folder::exists() +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + return (m_path.empty() || (m_path.size() == 1 && m_path[0].buffer() == "INBOX")); +} + + +const bool POP3Folder::isOpen() const +{ + return (m_open); +} + + +message* POP3Folder::getMessage(const int num) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (num < 1 || num > m_messageCount) + throw exceptions::message_not_found(); + + return new POP3Message(this, num); +} + + +std::vector <message*> POP3Folder::getMessages(const int from, const int to) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (to < from || from < 1 || to < 1 || from > m_messageCount || to > m_messageCount) + throw exceptions::message_not_found(); + + std::vector <message*> v; + + for (int i = from ; i <= to ; ++i) + v.push_back(new POP3Message(this, i)); + + return (v); +} + + +std::vector <message*> POP3Folder::getMessages(const std::vector <int>& nums) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::vector <message*> v; + + try + { + for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it) + { + if (*it < 1|| *it > m_messageCount) + throw exceptions::message_not_found(); + + v.push_back(new POP3Message(this, *it)); + } + } + catch (std::exception& e) + { + for (std::vector <message*>::iterator it = v.begin() ; it != v.end() ; ++it) + delete (*it); + + throw; + } + + return (v); +} + + +const int POP3Folder::getMessageCount() +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + return (m_messageCount); +} + + +folder* POP3Folder::getFolder(const folder::path::component& name) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + return new POP3Folder(m_path / name, m_store); +} + + +std::vector <folder*> POP3Folder::getFolders(const bool /* recursive */) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + if (m_path.empty()) + { + std::vector <folder*> v; + v.push_back(new POP3Folder(folder::path::component("INBOX"), m_store)); + return (v); + } + else + { + std::vector <folder*> v; + return (v); + } +} + + +void POP3Folder::fetchMessages(std::vector <message*>& msg, const int options, + progressionListener* progress) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + const int total = msg.size(); + int current = 0; + + if (progress) + progress->start(total); + + for (std::vector <message*>::iterator it = msg.begin() ; + it != msg.end() ; ++it) + { + dynamic_cast <POP3Message*>(*it)->fetch(this, options); + + if (progress) + progress->progress(++current, total); + } + + if (options & FETCH_UID) + { + // Send the "UIDL" command + std::ostringstream command; + command << "UIDL"; + + m_store->sendRequest(command.str()); + + // Get the response + string response; + m_store->readResponse(response, true, NULL); + + if (m_store->isSuccessResponse(response)) + { + m_store->stripFirstLine(response, response, NULL); + + // C: UIDL + // S: +OK + // S: 1 whqtswO00WBw418f9t5JxYwZ + // S: 2 QhdPYR:00WBw1Ph7x7 + // S: . + + std::istringstream iss(response); + std::map <int, string> ids; + + string line; + + while (std::getline(iss, line)) + { + string::iterator it = line.begin(); + + while (it != line.end() && (*it == ' ' || *it == '\t')) + ++it; + + if (it != line.end()) + { + int number = 0; + + while (it != line.end() && (*it >= '0' && *it <= '9')) + { + number = (number * 10) + (*it - '0'); + ++it; + } + + while (it != line.end() && !(*it == ' ' || *it == '\t')) ++it; + while (it != line.end() && (*it == ' ' || *it == '\t')) ++it; + + if (it != line.end()) + { + ids.insert(std::map <int, string>::value_type + (number, string(it, line.end()))); + } + } + } + + for (std::vector <message*>::iterator it = msg.begin() ; + it != msg.end() ; ++it) + { + POP3Message* m = dynamic_cast <POP3Message*>(*it); + + std::map <int, string>::const_iterator id = + ids.find(m->m_num); + + if (id != ids.end()) + m->m_uid = (*id).second; + } + } + } + + if (progress) + progress->stop(total); +} + + +void POP3Folder::fetchMessage(message* msg, const int options) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + dynamic_cast <POP3Message*>(msg)->fetch(this, options); + + if (options & FETCH_UID) + { + // Send the "UIDL" command + std::ostringstream command; + command << "UIDL"; + + m_store->sendRequest(command.str()); + + // Get the response + string response; + m_store->readResponse(response, false, NULL); + + if (m_store->isSuccessResponse(response)) + { + m_store->stripResponseCode(response, response); + + // C: UIDL 2 + // S: +OK 2 QhdPYR:00WBw1Ph7x7 + string::iterator it = response.begin(); + + while (it != response.end() && (*it == ' ' || *it == '\t')) ++it; + while (it != response.end() && !(*it == ' ' || *it == '\t')) ++it; + while (it != response.end() && (*it == ' ' || *it == '\t')) ++it; + + if (it != response.end()) + { + dynamic_cast <POP3Message*>(msg)->m_uid = + string(it, response.end()); + } + } + } +} + + +const int POP3Folder::getFetchCapabilities() const +{ + return (FETCH_ENVELOPE | FETCH_CONTENT_INFO | + FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID); +} + + +folder* POP3Folder::getParent() +{ + return (m_path.empty() ? NULL : new POP3Folder(m_path.parent(), m_store)); +} + + +const class store& POP3Folder::store() const +{ + return (*m_store); +} + + +class store& POP3Folder::store() +{ + return (*m_store); +} + + +void POP3Folder::registerMessage(POP3Message* msg) +{ + m_messages.insert(MessageMap::value_type(msg, msg->number())); +} + + +void POP3Folder::unregisterMessage(POP3Message* msg) +{ + m_messages.erase(msg); +} + + +void POP3Folder::onStoreDisconnected() +{ + m_store = NULL; +} + + +void POP3Folder::deleteMessage(const int num) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::ostringstream command; + command << "DELE " << num; + + m_store->sendRequest(command.str()); + + string response; + m_store->readResponse(response, false); + + if (!m_store->isSuccessResponse(response)) + throw exceptions::command_error("DELE", response); +} + + +void POP3Folder::deleteMessages(const int from, const int to) +{ + if (from < 1 || (to < from && to != -1)) + throw exceptions::invalid_argument(); + + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + for (int i = from ; i < to ; ++i) + { + std::ostringstream command; + command << "DELE " << i; + + m_store->sendRequest(command.str()); + + string response; + m_store->readResponse(response, false); + + if (!m_store->isSuccessResponse(response)) + throw exceptions::command_error("DELE", response); + } +} + + +void POP3Folder::deleteMessages(const std::vector <int>& nums) +{ + if (nums.empty()) + throw exceptions::invalid_argument(); + + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + for (std::vector <int>::const_iterator + it = nums.begin() ; it != nums.end() ; ++it) + { + std::ostringstream command; + command << "DELE " << (*it); + + m_store->sendRequest(command.str()); + + string response; + m_store->readResponse(response, false); + + if (!m_store->isSuccessResponse(response)) + throw exceptions::command_error("DELE", response); + } +} + + +void POP3Folder::setMessageFlags(const int /* from */, const int /* to */, + const int /* flags */, const int /* mode */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::setMessageFlags(const std::vector <int>& /* nums */, + const int /* flags */, const int /* mode */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::rename(const folder::path& /* newPath */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::addMessage(vmime::message* /* msg */, const int /* flags */, + vmime::datetime* /* date */, progressionListener* /* progress */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::addMessage(utility::inputStream& /* is */, const int /* size */, const int /* flags */, + vmime::datetime* /* date */, progressionListener* /* progress */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::copyMessage(const folder::path& /* dest */, const int /* num */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::copyMessages(const folder::path& /* dest */, const int /* from */, const int /* to */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::copyMessages(const folder::path& /* dest */, const std::vector <int>& /* nums */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::status(int& count, int& unseen) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + m_store->sendRequest("STAT"); + + string response; + m_store->readResponse(response, false); + + if (!m_store->isSuccessResponse(response)) + throw exceptions::command_error("STAT", response); + + m_store->stripResponseCode(response, response); + + std::istringstream iss(response); + iss >> count; + + unseen = count; + + // Update local message count + if (m_messageCount != count) + { + const int oldCount = m_messageCount; + + m_messageCount = count; + + if (count > oldCount) + { + std::vector <int> nums; + nums.reserve(count - oldCount); + + for (int i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j) + nums[j] = i; + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list <POP3Folder*>::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == m_path) + { + (*it)->m_messageCount = count; + (*it)->notifyMessageCount(event); + } + } + } + } +} + + +void POP3Folder::expunge() +{ + // Not supported by POP3 protocol (deleted messages are automatically + // expunged at the end of the session...). +} + + +} // messaging +} // vmime diff --git a/src/messaging/POP3Folder.hpp b/src/messaging/POP3Folder.hpp new file mode 100644 index 00000000..cfb83bef --- /dev/null +++ b/src/messaging/POP3Folder.hpp @@ -0,0 +1,142 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_POP3FOLDER_HPP_INCLUDED +#define VMIME_MESSAGING_POP3FOLDER_HPP_INCLUDED + + +#include <vector> +#include <map> + +#include "../types.hpp" +#include "folder.hpp" +#include "../config.hpp" + + +namespace vmime { +namespace messaging { + + +class POP3Store; +class POP3Message; + + +/** POP3 folder implementation. + */ + +class POP3Folder : public folder +{ +protected: + + friend class POP3Store; + friend class POP3Message; + + POP3Folder(const folder::path& path, POP3Store* store); + POP3Folder(const POP3Folder&) : folder() { } + + ~POP3Folder(); + +public: + + const int mode() const; + + const int type(); + + const int flags(); + + const folder::path::component name() const; + const folder::path fullPath() const; + + void open(const int mode, bool failIfModeIsNotAvailable = false); + void close(const bool expunge); + void create(const int type); + + const bool exists(); + + const bool isOpen() const; + + message* getMessage(const int num); + std::vector <message*> getMessages(const int from = 1, const int to = -1); + std::vector <message*> getMessages(const std::vector <int>& nums); + const int getMessageCount(); + + folder* getFolder(const folder::path::component& name); + std::vector <folder*> getFolders(const bool recursive = false); + + void rename(const folder::path& newPath); + + void deleteMessage(const int num); + void deleteMessages(const int from = 1, const int to = -1); + void deleteMessages(const std::vector <int>& nums); + + void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET); + void setMessageFlags(const std::vector <int>& nums, const int flags, const int mode = message::FLAG_MODE_SET); + + void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + + void copyMessage(const folder::path& dest, const int num); + void copyMessages(const folder::path& dest, const int from = 1, const int to = -1); + void copyMessages(const folder::path& dest, const std::vector <int>& nums); + + void status(int& count, int& unseen); + + void expunge(); + + folder* getParent(); + + const class store& store() const; + class store& store(); + + + void fetchMessages(std::vector <message*>& msg, const int options, progressionListener* progress = NULL); + void fetchMessage(message* msg, const int options); + + const int getFetchCapabilities() const; + +private: + + void registerMessage(POP3Message* msg); + void unregisterMessage(POP3Message* msg); + + void onStoreDisconnected(); + + void onClose(); + + + POP3Store* m_store; + + folder::path m_path; + folder::path::component m_name; + + int m_mode; + bool m_open; + + int m_messageCount; + + typedef std::map <POP3Message*, int> MessageMap; + MessageMap m_messages; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_POP3FOLDER_HPP_INCLUDED diff --git a/src/messaging/POP3Message.cpp b/src/messaging/POP3Message.cpp new file mode 100644 index 00000000..66b3f466 --- /dev/null +++ b/src/messaging/POP3Message.cpp @@ -0,0 +1,220 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "POP3Message.hpp" +#include "POP3Folder.hpp" +#include "POP3Store.hpp" + +#include <sstream> + + +namespace vmime { +namespace messaging { + + + +class POP3header : public header +{ +public: + + POP3header(const string& str) + { + parse(str); + } +}; + + + +POP3Message::POP3Message(POP3Folder* folder, const int num) + : m_folder(folder), m_num(num), m_header(NULL) +{ + m_folder->registerMessage(this); +} + + +POP3Message::~POP3Message() +{ + if (m_folder) + m_folder->unregisterMessage(this); + + delete dynamic_cast <POP3header*>(m_header); +} + + +void POP3Message::onFolderClosed() +{ + m_folder = NULL; +} + + +const int POP3Message::number() const +{ + return (m_num); +} + + +const message::uid POP3Message::uniqueId() const +{ + return (m_uid); +} + + +const int POP3Message::size() const +{ + if (!m_folder) + throw exceptions::illegal_state("Folder closed"); + + POP3Folder::MessageMap::const_iterator it = + m_folder->m_messages.find(const_cast <POP3Message*>(this)); + + return ((it != m_folder->m_messages.end()) ? (*it).second : 0); +} + + +const bool POP3Message::isExpunged() const +{ + return (false); +} + + +const int POP3Message::flags() const +{ + return (FLAG_RECENT); +} + + +const class structure& POP3Message::structure() const +{ + throw exceptions::operation_not_supported(); +} + + +class structure& POP3Message::structure() +{ + throw exceptions::operation_not_supported(); +} + + +const class header& POP3Message::header() const +{ + if (m_header == NULL) + throw exceptions::unfetched_object(); + + return (*m_header); +} + + +void POP3Message::extract(utility::outputStream& os, progressionListener* progress, + const int start, const int length) const +{ + if (!m_folder) + throw exceptions::illegal_state("Folder closed"); + else if (!m_folder->m_store) + throw exceptions::illegal_state("Store disconnected"); + + if (start != 0 && length != -1) + throw exceptions::partial_fetch_not_supported(); + + // Emit the "RETR" command + std::ostringstream oss; + oss << "RETR " << m_num; + + const_cast <POP3Folder*>(m_folder)->m_store->sendRequest(oss.str()); + + try + { + POP3Folder::MessageMap::const_iterator it = + m_folder->m_messages.find(const_cast <POP3Message*>(this)); + + const int totalSize = (it != m_folder->m_messages.end()) + ? (*it).second : 0; + + const_cast <POP3Folder*>(m_folder)->m_store-> + readResponse(os, progress, totalSize); + } + catch (exceptions::command_error& e) + { + throw exceptions::command_error("RETR", e.response()); + } +} + + +void POP3Message::extractPart + (const part& /* p */, utility::outputStream& /* os */, progressionListener* /* progress */, + const int /* start */, const int /* length */) const +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Message::fetchPartHeader(part& /* p */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Message::fetch(POP3Folder* folder, const int options) +{ + if (m_folder != folder) + throw exceptions::folder_not_found(); + + // FETCH_STRUCTURE and FETCH_FLAGS are not supported by POP3. + if (options & (folder::FETCH_STRUCTURE | folder::FETCH_FLAGS)) + throw exceptions::operation_not_supported(); + + // Check for the real need to fetch the full header + if (!((options & folder::FETCH_ENVELOPE) || + (options & folder::FETCH_CONTENT_INFO) || + (options & folder::FETCH_FULL_HEADER))) + { + return; + } + + // No need to differenciate between FETCH_ENVELOPE, + // FETCH_CONTENT_INFO, ... since POP3 only permits to + // retrieve the whole header and not fields in particular. + + // Emit the "TOP" command + std::ostringstream oss; + oss << "TOP " << m_num << " 0"; + + m_folder->m_store->sendRequest(oss.str()); + + try + { + string buffer; + m_folder->m_store->readResponse(buffer, true); + + m_header = new POP3header(buffer); + } + catch (exceptions::command_error& e) + { + throw exceptions::command_error("TOP", e.response()); + } +} + + +void POP3Message::setFlags(const int /* flags */, const int /* mode */) +{ + throw exceptions::operation_not_supported(); +} + + +} // messaging +} // vmime diff --git a/src/messaging/POP3Message.hpp b/src/messaging/POP3Message.hpp new file mode 100644 index 00000000..193624b8 --- /dev/null +++ b/src/messaging/POP3Message.hpp @@ -0,0 +1,88 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_POP3MESSAGE_HPP_INCLUDED +#define VMIME_MESSAGING_POP3MESSAGE_HPP_INCLUDED + + +#include "message.hpp" +#include "folder.hpp" +#include "../config.hpp" + + +namespace vmime { +namespace messaging { + + +/** POP3 message implementation. + */ + +class POP3Message : public message +{ +protected: + + friend class POP3Folder; + + POP3Message(POP3Folder* folder, const int num); + POP3Message(const POP3Message&) : message() { } + + ~POP3Message(); + +public: + + const int number() const; + + const uid uniqueId() const; + + const int size() const; + + const bool isExpunged() const; + + const class structure& structure() const; + class structure& structure(); + + const class header& header() const; + + const int flags() const; + void setFlags(const int flags, const int mode = FLAG_MODE_SET); + + void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + + void fetchPartHeader(part& p); + +private: + + void fetch(POP3Folder* folder, const int options); + + void onFolderClosed(); + + POP3Folder* m_folder; + int m_num; + uid m_uid; + + class header* m_header; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_POP3MESSAGE_HPP_INCLUDED diff --git a/src/messaging/POP3Store.cpp b/src/messaging/POP3Store.cpp new file mode 100644 index 00000000..c105bc4b --- /dev/null +++ b/src/messaging/POP3Store.cpp @@ -0,0 +1,603 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "POP3Store.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" +#include "../messageId.hpp" +#include "../utility/md5.hpp" + +#include "POP3Folder.hpp" + +#include <algorithm> + + +namespace vmime { +namespace messaging { + + +POP3Store::POP3Store(class session& sess, class authenticator* auth) + : store(sess, infosInstance(), auth), m_socket(NULL), + m_authentified(false), m_timeoutHandler(NULL) +{ +} + + +POP3Store::~POP3Store() +{ + if (isConnected()) + disconnect(); + else if (m_socket) + internalDisconnect(); +} + + +const string POP3Store::protocolName() const +{ + return "pop3"; +} + + +folder* POP3Store::getDefaultFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new POP3Folder(folder::path(folder::path::component("INBOX")), this); +} + + +folder* POP3Store::getRootFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new POP3Folder(folder::path(), this); +} + + +folder* POP3Store::getFolder(const folder::path& path) +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new POP3Folder(path, this); +} + + +void POP3Store::connect() +{ + if (isConnected()) + throw exceptions::already_connected(); + + const string address = session().properties()[sm_infos.propertyPrefix() + "server.address"]; + const port_t port = session().properties().get(sm_infos.propertyPrefix() + "server.port", sm_infos.defaultPort()); + + // Create the time-out handler + if (session().properties().exists + (sm_infos.propertyPrefix() + "timeout.factory")) + { + timeoutHandlerFactory* tof = platformDependant::getHandler()-> + getTimeoutHandlerFactory(session().properties() + [sm_infos.propertyPrefix() + "timeout.factory"]); + + m_timeoutHandler = tof->create(); + } + + // Create and connect the socket + socketFactory* sf = platformDependant::getHandler()->getSocketFactory + (session().properties().get(sm_infos.propertyPrefix() + "server.socket-factory", string("default"))); + + m_socket = sf->create(); + m_socket->connect(address, port); + + // Connection + // + // eg: C: <connection to server> + // --- S: +OK MailSite POP3 Server 5.3.4.0 Ready <[email protected]> + + string response; + readResponse(response, false); + + if (isSuccessResponse(response)) + { + bool authentified = false; + + const authenticationInfos auth = authenticator().requestAuthInfos(); + + // Secured authentication with APOP (if requested and if available) + // + // eg: C: APOP vincent <digest> + // --- S: +OK vincent is a valid mailbox + messageId mid(response); + + if (session().properties().get(sm_infos.propertyPrefix() + "options.apop", false)) + { + if (mid.left().length() && mid.right().length()) + { + // <digest> is the result of MD5 applied to "<message-id>password" + sendRequest("APOP " + auth.username() + " " + + utility::md5(mid.generate() + auth.password()).hex()); + readResponse(response, false); + + if (isSuccessResponse(response)) + { + authentified = true; + } + else + { + if (session().properties().get(sm_infos.propertyPrefix() + + "options.apop.fallback", false) == false) + { + internalDisconnect(); + throw exceptions::authentication_error(response); + } + } + } + else + { + // APOP not supported + if (session().properties().get(sm_infos.propertyPrefix() + + "options.apop.fallback", false) == false) + { + // Can't fallback on basic authentification + internalDisconnect(); + throw exceptions::unsupported_option(); + } + } + } + + if (!authentified) + { + // Basic authentication + // + // eg: C: USER vincent + // --- S: +OK vincent is a valid mailbox + // + // C: PASS couic + // S: +OK vincent's maildrop has 2 messages (320 octets) + + sendRequest("USER " + auth.username()); + readResponse(response, false); + + if (isSuccessResponse(response)) + { + sendRequest("PASS " + auth.password()); + readResponse(response, false); + + if (!isSuccessResponse(response)) + { + internalDisconnect(); + throw exceptions::authentication_error(response); + } + } + else + { + internalDisconnect(); + throw exceptions::authentication_error(response); + } + } + } + else + { + internalDisconnect(); + throw exceptions::connection_greeting_error(response); + } + + m_authentified = true; +} + + +const bool POP3Store::isConnected() const +{ + return (m_socket && m_socket->isConnected() && m_authentified); +} + + +void POP3Store::disconnect() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + internalDisconnect(); +} + + +void POP3Store::internalDisconnect() +{ + for (std::list <POP3Folder*>::iterator it = m_folders.begin() ; + it != m_folders.end() ; ++it) + { + (*it)->onStoreDisconnected(); + } + + m_folders.clear(); + + + sendRequest("QUIT"); + + m_socket->disconnect(); + + delete (m_socket); + m_socket = NULL; + + delete (m_timeoutHandler); + m_timeoutHandler = NULL; + + m_authentified = false; +} + + +void POP3Store::noop() +{ + m_socket->send("NOOP"); + + string response; + readResponse(response, false); + + if (!isSuccessResponse(response)) + throw exceptions::command_error("NOOP", response); +} + + +const bool POP3Store::isSuccessResponse(const string& buffer) +{ + static const string OK("+OK"); + + return (buffer.length() >= 3 && + std::equal(buffer.begin(), buffer.begin() + 3, OK.begin())); +} + + +const bool POP3Store::stripFirstLine(const string& buffer, string& result, string* firstLine) +{ + const string::size_type end = buffer.find('\n'); + + if (end != string::npos) + { + if (firstLine) *firstLine = buffer.substr(0, end); + result = buffer.substr(end + 1); + return (true); + } + else + { + result = buffer; + return (false); + } +} + + +void POP3Store::stripResponseCode(const string& buffer, string& result) +{ + const string::size_type pos = buffer.find_first_of(" \t"); + + if (pos != string::npos) + result = buffer.substr(pos + 1); + else + result = buffer; +} + + +void POP3Store::sendRequest(const string& buffer, const bool end) +{ + m_socket->send(buffer); + if (end) m_socket->send("\r\n"); +} + + +void POP3Store::readResponse(string& buffer, const bool multiLine, + progressionListener* progress) +{ + bool foundTerminator = false; + int current = 0, total = 0; + + if (progress) + progress->start(total); + + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + buffer.clear(); + + string::value_type last1 = '\0', last2 = '\0'; + + for ( ; !foundTerminator ; ) + { +#if 0 // not supported + // Check for possible cancellation + if (progress && progress->cancel()) + throw exceptions::operation_cancelled(); +#endif + + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + throw exceptions::operation_timed_out(); + } + + // Receive data from the socket + string receiveBuffer; + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) // buffer is empty + { + platformDependant::getHandler()->wait(); + continue; + } + + // We have received data: reset the time-out counter + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + // Check for transparent characters: '\n..' becomes '\n.' + const string::value_type first = receiveBuffer[0]; + + if (first == '.' && last2 == '\n' && last1 == '.') + { + receiveBuffer.erase(receiveBuffer.begin()); + } + else if (receiveBuffer.length() >= 2 && first == '.' && + receiveBuffer[1] == '.' && last1 == '\n') + { + receiveBuffer.erase(receiveBuffer.begin()); + } + + for (string::size_type trans ; + string::npos != (trans = receiveBuffer.find("\n..")) ; ) + { + receiveBuffer.replace(trans, 3, "\n."); + } + + last1 = receiveBuffer[receiveBuffer.length() - 1]; + last2 = (receiveBuffer.length() >= 2) ? receiveBuffer[receiveBuffer.length() - 2] : 0; + + // Append the data to the response buffer + buffer += receiveBuffer; + current += receiveBuffer.length(); + + // Check for terminator string (and strip it if present) + foundTerminator = checkTerminator(buffer, multiLine); + + // Notify progression + if (progress) + { + total = std::max(total, current); + progress->progress(current, total); + } + + // If there is an error (-ERR) when executing a command that + // requires a multi-line response, the error response will + // include only one line, so we stop waiting for a multi-line + // terminator and check for a "normal" one. + if (multiLine && !foundTerminator && buffer.length() >= 4 && buffer[0] == '-') + { + foundTerminator = checkTerminator(buffer, false); + } + } + + if (progress) + progress->stop(total); +} + + +void POP3Store::readResponse(utility::outputStream& os, progressionListener* progress, + const int predictedSize) +{ + bool foundTerminator = false; + int current = 0, total = predictedSize; + + string temp; + bool codeDone = false; + + if (progress) + progress->start(total); + + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + string::value_type last1 = '\0', last2 = '\0'; + + for ( ; !foundTerminator ; ) + { +#if 0 // not supported + // Check for possible cancellation + if (progress && progress->cancel()) + throw exceptions::operation_cancelled(); +#endif + + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + throw exceptions::operation_timed_out(); + } + + // Receive data from the socket + string receiveBuffer; + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) // buffer is empty + { + platformDependant::getHandler()->wait(); + continue; + } + + // We have received data: reset the time-out counter + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + // Check for transparent characters: '\n..' becomes '\n.' + const string::value_type first = receiveBuffer[0]; + + if (first == '.' && last2 == '\n' && last1 == '.') + { + receiveBuffer.erase(receiveBuffer.begin()); + } + else if (receiveBuffer.length() >= 2 && first == '.' && + receiveBuffer[1] == '.' && last1 == '\n') + { + receiveBuffer.erase(receiveBuffer.begin()); + } + + for (string::size_type trans ; + string::npos != (trans = receiveBuffer.find("\n..")) ; ) + { + receiveBuffer.replace(trans, 3, "\n."); + } + + last1 = receiveBuffer[receiveBuffer.length() - 1]; + last2 = (receiveBuffer.length() >= 2) ? receiveBuffer[receiveBuffer.length() - 2] : 0; + + // If we don't have extracted the response code yet + if (!codeDone) + { + temp += receiveBuffer; + + string firstLine; + + if (stripFirstLine(temp, temp, &firstLine) == true) + { + if (!isSuccessResponse(firstLine)) + throw exceptions::command_error("?", firstLine); + + receiveBuffer = temp; + temp.clear(); + + codeDone = true; + } + } + + if (codeDone) + { + // Check for terminator string (and strip it if present) + foundTerminator = checkTerminator(receiveBuffer, true); + + // Inject the data into the output stream + os.write(receiveBuffer.data(), receiveBuffer.length()); + current += receiveBuffer.length(); + + // Notify progression + if (progress) + { + total = std::max(total, current); + progress->progress(current, total); + } + } + } + + if (progress) + progress->stop(total); +} + + +const bool POP3Store::checkTerminator(string& buffer, const bool multiLine) +{ + // Multi-line response + if (multiLine) + { + static const string term1("\r\n.\r\n"); + static const string term2("\n.\n"); + + return (checkOneTerminator(buffer, term1) || + checkOneTerminator(buffer, term2)); + } + // Normal response + else + { + static const string term1("\r\n"); + static const string term2("\n"); + + return (checkOneTerminator(buffer, term1) || + checkOneTerminator(buffer, term2)); + } + + return (false); +} + + +const bool POP3Store::checkOneTerminator(string& buffer, const string& term) +{ + if (buffer.length() >= term.length() && + std::equal(buffer.end() - term.length(), buffer.end(), term.begin())) + { + buffer.erase(buffer.end() - term.length(), buffer.end()); + return (true); + } + + return (false); +} + + +void POP3Store::registerFolder(POP3Folder* folder) +{ + m_folders.push_back(folder); +} + + +void POP3Store::unregisterFolder(POP3Folder* folder) +{ + std::list <POP3Folder*>::iterator it = std::find(m_folders.begin(), m_folders.end(), folder); + if (it != m_folders.end()) m_folders.erase(it); +} + + + +// Service infos + +POP3Store::_infos POP3Store::sm_infos; + + +const port_t POP3Store::_infos::defaultPort() const +{ + return (110); +} + + +const string POP3Store::_infos::propertyPrefix() const +{ + return "store.pop3."; +} + + +const std::vector <string> POP3Store::_infos::availableProperties() const +{ + std::vector <string> list; + + // POP3-specific options + list.push_back("options.apop"); + list.push_back("options.apop.fallback"); + + // Common properties + list.push_back("auth.username"); + list.push_back("auth.password"); + + list.push_back("server.address"); + list.push_back("server.port"); + list.push_back("server.socket-factory"); + + list.push_back("timeout.factory"); + + return (list); +} + + +} // messaging +} // vmime diff --git a/src/messaging/POP3Store.hpp b/src/messaging/POP3Store.hpp new file mode 100644 index 00000000..b9d6bd87 --- /dev/null +++ b/src/messaging/POP3Store.hpp @@ -0,0 +1,110 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_POP3STORE_HPP_INCLUDED +#define VMIME_MESSAGING_POP3STORE_HPP_INCLUDED + + +#include "store.hpp" +#include "socket.hpp" +#include "../config.hpp" +#include "timeoutHandler.hpp" +#include "../utility/stream.hpp" + + +namespace vmime { +namespace messaging { + + +/** POP3 store service. + */ + +class POP3Store : public store +{ + friend class POP3Folder; + friend class POP3Message; + +public: + + POP3Store(class session& sess, class authenticator* auth); + ~POP3Store(); + + const string protocolName() const; + + folder* getDefaultFolder(); + folder* getRootFolder(); + folder* getFolder(const folder::path& path); + + static const serviceInfos& infosInstance() { return (sm_infos); } + const serviceInfos& infos() const { return (sm_infos); } + + void connect(); + const bool isConnected() const; + void disconnect(); + + void noop(); + +private: + + static const bool isSuccessResponse(const string& buffer); + static const bool stripFirstLine(const string& buffer, string& result, string* firstLine = NULL); + static void stripResponseCode(const string& buffer, string& result); + + void sendRequest(const string& buffer, const bool end = true); + void readResponse(string& buffer, const bool multiLine, progressionListener* progress = NULL); + void readResponse(utility::outputStream& os, progressionListener* progress = NULL, const int predictedSize = 0); + + static const bool checkTerminator(string& buffer, const bool multiLine); + static const bool checkOneTerminator(string& buffer, const string& term); + + void internalDisconnect(); + + + void registerFolder(POP3Folder* folder); + void unregisterFolder(POP3Folder* folder); + + std::list <POP3Folder*> m_folders; + + + socket* m_socket; + bool m_authentified; + + timeoutHandler* m_timeoutHandler; + + + // Service infos + class _infos : public serviceInfos + { + public: + + const port_t defaultPort() const; + + const string propertyPrefix() const; + const std::vector <string> availableProperties() const; + }; + + static _infos sm_infos; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_POP3STORE_HPP_INCLUDED diff --git a/src/messaging/SMTPTransport.cpp b/src/messaging/SMTPTransport.cpp new file mode 100644 index 00000000..309f3e06 --- /dev/null +++ b/src/messaging/SMTPTransport.cpp @@ -0,0 +1,575 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "SMTPTransport.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" +#include "../encoderB64.hpp" +#include "../message.hpp" +#include "../mailboxList.hpp" +#include "authHelper.hpp" + + +namespace vmime { +namespace messaging { + + +SMTPTransport::SMTPTransport(class session& sess, class authenticator* auth) + : transport(sess, infosInstance(), auth), m_socket(NULL), + m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL) +{ +} + + +SMTPTransport::~SMTPTransport() +{ + if (isConnected()) + disconnect(); + else if (m_socket) + internalDisconnect(); +} + + +const string SMTPTransport::protocolName() const +{ + return "smtp"; +} + + +void SMTPTransport::connect() +{ + if (isConnected()) + throw exceptions::already_connected(); + + const string address = session().properties()[sm_infos.propertyPrefix() + "server.address"]; + const port_t port = session().properties().get(sm_infos.propertyPrefix() + "server.port", sm_infos.defaultPort()); + + // Create the time-out handler + if (session().properties().exists + (sm_infos.propertyPrefix() + "timeout.factory")) + { + timeoutHandlerFactory* tof = platformDependant::getHandler()-> + getTimeoutHandlerFactory(session().properties() + [sm_infos.propertyPrefix() + "timeout.factory"]); + + m_timeoutHandler = tof->create(); + } + + // Create and connect the socket + socketFactory* sf = platformDependant::getHandler()->getSocketFactory + (session().properties().get(sm_infos.propertyPrefix() + "server.socket-factory", string("default"))); + + m_socket = sf->create(); + m_socket->connect(address, port); + + // Connection + // + // eg: C: <connection to server> + // --- S: 220 smtp.domain.com Service ready + + string response; + readResponse(response); + + if (responseCode(response) != 220) + { + internalDisconnect(); + throw exceptions::connection_greeting_error(response); + } + + // Identification + // First, try Extended SMTP (ESMTP) + // + // eg: C: EHLO thismachine.ourdomain.com + // S: 250 OK + + sendRequest("EHLO " + platformDependant::getHandler()->getHostName()); + readResponse(response); + + if (responseCode(response) != 250) + { + // Next, try "Basic" SMTP + // + // eg: C: HELO thismachine.ourdomain.com + // S: 250 OK + + sendRequest("HELO " + platformDependant::getHandler()->getHostName()); + readResponse(response); + + if (responseCode(response) != 250) + { + internalDisconnect(); + throw exceptions::connection_greeting_error(response); + } + + m_extendedSMTP = false; + } + else + { + m_extendedSMTP = true; + } + + // Authentication + if (session().properties().get + (sm_infos.propertyPrefix() + "options.need-authentication", false) == true) + { + if (!m_extendedSMTP) + { + internalDisconnect(); + throw exceptions::command_error("AUTH", "ESMTP not supported."); + } + + const authenticationInfos auth = authenticator().requestAuthInfos(); + bool authentified = false; + + enum AuthMethods + { + First = 0, + CRAM_MD5 = First, + // TODO: more authentication methods... + End + }; + + for (int currentMethod = First ; !authentified ; ++currentMethod) + { + switch (currentMethod) + { + case CRAM_MD5: + { + sendRequest("AUTH CRAM-MD5"); + readResponse(response); + + if (responseCode(response) == 334) + { + encoderB64 base64; + + string challengeB64 = responseText(response); + string challenge, challengeHex; + + { + utility::inputStreamStringAdapter in(challengeB64); + utility::outputStreamStringAdapter out(challenge); + + base64.decode(in, out); + } + + hmac_md5(challenge, auth.password(), challengeHex); + + string decoded = auth.username() + " " + challengeHex; + string encoded; + + { + utility::inputStreamStringAdapter in(decoded); + utility::outputStreamStringAdapter out(encoded); + + base64.encode(in, out); + } + + sendRequest(encoded); + readResponse(response); + + if (responseCode(response) == 235) + { + authentified = true; + } + else + { + internalDisconnect(); + throw exceptions::authentication_error(response); + } + } + + break; + } + case End: + { + // All authentication methods have been tried and + // the server does not understand any. + throw exceptions::authentication_error(response); + } + + } + } + } + + m_authentified = true; +} + + +const bool SMTPTransport::isConnected() const +{ + return (m_socket && m_socket->isConnected() && m_authentified); +} + + +void SMTPTransport::disconnect() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + internalDisconnect(); +} + + +void SMTPTransport::internalDisconnect() +{ + sendRequest("QUIT"); + + m_socket->disconnect(); + + delete (m_socket); + m_socket = NULL; + + delete (m_timeoutHandler); + m_timeoutHandler = NULL; + + m_authentified = false; + m_extendedSMTP = false; +} + + +void SMTPTransport::noop() +{ + m_socket->send("NOOP"); + + string response; + readResponse(response); + + if (responseCode(response) != 250) + throw exceptions::command_error("NOOP", response); +} + + +static void extractMailboxes + (mailboxList& recipients, const addressList& list) +{ + for (addressList::const_iterator it = list.begin() ; + it != list.end() ; ++it) + { + recipients.append((*it)); + } +} + + +void SMTPTransport::send(vmime::message* msg, progressionListener* progress) +{ + // Extract expeditor + mailbox expeditor; + + try + { + const mailboxField& from = dynamic_cast <const mailboxField&> + (msg->header().fields.find(headerField::From)); + expeditor = from.value(); + } + catch (exceptions::no_such_field&) + { + throw exceptions::no_expeditor(); + } + + // Extract recipients + mailboxList recipients; + + try + { + const addressListField& to = dynamic_cast <const addressListField&> + (msg->header().fields.find(headerField::To)); + extractMailboxes(recipients, to.value()); + } + catch (exceptions::no_such_field&) { } + + try + { + const addressListField& cc = dynamic_cast <const addressListField&> + (msg->header().fields.find(headerField::Cc)); + extractMailboxes(recipients, cc.value()); + } + catch (exceptions::no_such_field&) { } + + try + { + const addressListField& bcc = dynamic_cast <const addressListField&> + (msg->header().fields.find(headerField::Bcc)); + extractMailboxes(recipients, bcc.value()); + } + catch (exceptions::no_such_field&) { } + + // Generate the message, "stream" it and delegate the sending + // to the generic send() function. + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + msg->generate(ossAdapter); + + const string& str(oss.str()); + + utility::inputStreamStringAdapter isAdapter(str); + + send(expeditor, recipients, isAdapter, str.length(), progress); +} + + +void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients, + utility::inputStream& is, const utility::stream::size_type size, + progressionListener* progress) +{ + // If no recipient/expeditor was found, throw an exception + if (recipients.empty()) + throw exceptions::no_recipient(); + else if (expeditor.empty()) + throw exceptions::no_expeditor(); + + // Emit the "MAIL" command + string response; + + sendRequest("MAIL FROM: <" + expeditor.email() + ">"); + readResponse(response); + + if (responseCode(response) != 250) + { + internalDisconnect(); + throw exceptions::command_error("MAIL", response); + } + + // Emit a "RCPT TO" command for each recipient + for (mailboxList::const_iterator it = recipients.begin() ; + it != recipients.end() ; ++it) + { + sendRequest("RCPT TO: <" + (*it).email() + ">"); + readResponse(response); + + if (responseCode(response) != 250) + { + internalDisconnect(); + throw exceptions::command_error("RCPT TO", response); + } + } + + // Send the message data + sendRequest("DATA"); + readResponse(response); + + if (responseCode(response) != 354) + { + internalDisconnect(); + throw exceptions::command_error("DATA", response); + } + + int current = 0, total = size; + + if (progress) + progress->start(total); + + char buffer[65536]; + + while (!is.eof()) + { + const int read = is.read(buffer, sizeof(buffer)); + + // Transform '.' into '..' at the beginning of a line + char* start = buffer; + char* end = buffer + read; + char* pos = buffer; + + while ((pos = std::find(pos, end, '.')) != end) + { + if (pos > buffer && *(pos - 1) == '\n') + { + m_socket->sendRaw(start, pos - start); + m_socket->sendRaw(".", 1); + + start = pos; + } + + ++pos; + } + + // Send the remaining data + m_socket->sendRaw(start, end - start); + + current += read; + + // Notify progression + if (progress) + { + total = std::max(total, current); + progress->progress(current, total); + } + } + + if (progress) + progress->stop(total); + + m_socket->sendRaw("\r\n.\r\n", 5); + readResponse(response); + + if (responseCode(response) != 250) + { + internalDisconnect(); + throw exceptions::command_error("DATA", response); + } +} + + +void SMTPTransport::sendRequest(const string& buffer, const bool end) +{ + m_socket->send(buffer); + if (end) m_socket->send("\r\n"); +} + + +const int SMTPTransport::responseCode(const string& response) +{ + int code = 0; + + if (response.length() >= 3) + { + code = (response[0] - '0') * 100 + + (response[1] - '0') * 10 + + (response[2] - '0'); + } + + return (code); +} + + +const string SMTPTransport::responseText(const string& response) +{ + string text; + + std::istringstream iss(response); + std::string line; + + while (std::getline(iss, line)) + { + if (line.length() >= 4) + text += line.substr(4); + else + text += line; + + text += "\n"; + } + + return (text); +} + + +void SMTPTransport::readResponse(string& buffer) +{ + bool foundTerminator = false; + + buffer.clear(); + + for ( ; !foundTerminator ; ) + { + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + throw exceptions::operation_timed_out(); + } + + // Receive data from the socket + string receiveBuffer; + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) // buffer is empty + { + platformDependant::getHandler()->wait(); + continue; + } + + // We have received data: reset the time-out counter + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + // Append the data to the response buffer + buffer += receiveBuffer; + + // Check for terminator string (and strip it if present) + if (buffer.length() >= 2 && buffer[buffer.length() - 1] == '\n') + { + string::size_type p = buffer.length() - 2; + bool end = false; + + for ( ; !end ; --p) + { + if (p == 0 || buffer[p] == '\n') + { + end = true; + + if (p + 4 < buffer.length()) + foundTerminator = true; + } + } + } + } + + // Remove [CR]LF at the end of the response + if (buffer.length() >= 2 && buffer[buffer.length() - 1] == '\n') + { + if (buffer[buffer.length() - 2] == '\r') + buffer.resize(buffer.length() - 2); + else + buffer.resize(buffer.length() - 1); + } +} + + + +// Service infos + +SMTPTransport::_infos SMTPTransport::sm_infos; + + +const port_t SMTPTransport::_infos::defaultPort() const +{ + return (25); +} + + +const string SMTPTransport::_infos::propertyPrefix() const +{ + return "transport.smtp."; +} + + +const std::vector <string> SMTPTransport::_infos::availableProperties() const +{ + std::vector <string> list; + + // SMTP-specific options + list.push_back("options.need-authentication"); + + // Common properties + list.push_back("auth.username"); + list.push_back("auth.password"); + + list.push_back("server.address"); + list.push_back("server.port"); + list.push_back("server.socket-factory"); + + list.push_back("timeout.factory"); + + return (list); +} + + +} // messaging +} // vmime diff --git a/src/messaging/SMTPTransport.hpp b/src/messaging/SMTPTransport.hpp new file mode 100644 index 00000000..fa5abfcf --- /dev/null +++ b/src/messaging/SMTPTransport.hpp @@ -0,0 +1,95 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SMTPTRANSPORT_HPP_INCLUDED +#define VMIME_MESSAGING_SMTPTRANSPORT_HPP_INCLUDED + + +#include "transport.hpp" +#include "socket.hpp" +#include "../config.hpp" +#include "timeoutHandler.hpp" + + +namespace vmime { +namespace messaging { + + +/** SMTP transport service. + */ + +class SMTPTransport : public transport +{ +public: + + SMTPTransport(class session& sess, class authenticator* auth); + ~SMTPTransport(); + + const string protocolName() const; + + static const serviceInfos& infosInstance() { return (sm_infos); } + const serviceInfos& infos() const { return (sm_infos); } + + void connect(); + const bool isConnected() const; + void disconnect(); + + void noop(); + + void send(vmime::message* msg, progressionListener* progress = NULL); + void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, progressionListener* progress = NULL); + +private: + + static const int responseCode(const string& response); + static const string responseText(const string& response); + + void sendRequest(const string& buffer, const bool end = true); + + void readResponse(string& buffer); + + void internalDisconnect(); + + socket* m_socket; + bool m_authentified; + bool m_extendedSMTP; + + timeoutHandler* m_timeoutHandler; + + + // Service infos + class _infos : public serviceInfos + { + public: + + const port_t defaultPort() const; + + const string propertyPrefix() const; + const std::vector <string> availableProperties() const; + }; + + static _infos sm_infos; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SMTPTRANSPORT_HPP_INCLUDED diff --git a/src/messaging/authHelper.cpp b/src/messaging/authHelper.cpp new file mode 100644 index 00000000..05c77d8a --- /dev/null +++ b/src/messaging/authHelper.cpp @@ -0,0 +1,105 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "authHelper.hpp" + +#include "../config.hpp" +#include "../utility/md5.hpp" + + +namespace vmime { +namespace messaging { + + +// +// This code is based on the Sample Code published in the Appendix of +// the RFC-2104: "HMAC: Keyed-Hashing for Message Authentication". +// + +void hmac_md5(const string& text, const string& key, string& hexDigest) +{ + vmime_uint8 digest[16]; + + unsigned char ipad[65]; // inner padding - key XORd with ipad + unsigned char opad[65]; // outer padding - key XORd with opad + + unsigned char tkey[16]; + int tkeyLen; + + // If key is longer than 64 bytes reset it to key = MD5(key) + if (key.length() > 64) + { + utility::md5 keyMD5; + keyMD5.update((vmime_uint8*) key.data(), key.length()); + + std::copy(keyMD5.hash(), keyMD5.hash() + 16, tkey); + tkeyLen = 16; + } + else + { + std::copy(key.begin(), key.end(), tkey); + tkeyLen = key.length(); + } + + // + // the HMAC_MD5 transform looks like: + // + // MD5(K XOR opad, MD5(K XOR ipad, text)) + // + // where K is an n byte key + // ipad is the byte 0x36 repeated 64 times + // + // opad is the byte 0x5c repeated 64 times + // and text is the data being protected + // + + // Start out by storing key in pads + std::fill(ipad, ipad + sizeof(ipad), 0); + std::fill(opad, opad + sizeof(opad), 0); + + std::copy(tkey, tkey + tkeyLen, ipad); + std::copy(tkey, tkey + tkeyLen, opad); + + // XOR key with ipad and opad values + for (int i = 0 ; i < 64 ; ++i) + { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + // Perform inner MD5 + utility::md5 innerMD5; + innerMD5.update(ipad, 64); + innerMD5.update(text); + + std::copy(innerMD5.hash(), innerMD5.hash() + 16, digest); + + // Perform outer MD5 + utility::md5 outerMD5; + outerMD5.update(opad, 64); + outerMD5.update(digest, 16); + + //std::copy(outerMD5.hash(), outerMD5.hash() + 16, digest); + + hexDigest = outerMD5.hex(); +} + + +} // messaging +} // vmime diff --git a/src/messaging/authHelper.hpp b/src/messaging/authHelper.hpp new file mode 100644 index 00000000..1fad3ced --- /dev/null +++ b/src/messaging/authHelper.hpp @@ -0,0 +1,38 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_AUTHHELPER_HPP_INCLUDED +#define VMIME_MESSAGING_AUTHHELPER_HPP_INCLUDED + + +#include "../types.hpp" + + +namespace vmime { +namespace messaging { + + +void hmac_md5(const string& text, const string& key, string& hexDigest); + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_AUTHHELPER_HPP_INCLUDED diff --git a/src/messaging/authenticationInfos.cpp b/src/messaging/authenticationInfos.cpp new file mode 100644 index 00000000..3c5acca7 --- /dev/null +++ b/src/messaging/authenticationInfos.cpp @@ -0,0 +1,40 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "authenticationInfos.hpp" + + +namespace vmime { +namespace messaging { + + +authenticationInfos::authenticationInfos(const string& username, const string& password) + : m_username(username), m_password(password) +{ +} + + +authenticationInfos::authenticationInfos(const authenticationInfos& infos) + : m_username(infos.m_username), m_password(infos.m_password) +{ +} + + +} // messaging +} // vmime diff --git a/src/messaging/authenticationInfos.hpp b/src/messaging/authenticationInfos.hpp new file mode 100644 index 00000000..fd2da2fa --- /dev/null +++ b/src/messaging/authenticationInfos.hpp @@ -0,0 +1,64 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_AUTHENTICATIONINFOS_HPP_INCLUDED +#define VMIME_MESSAGING_AUTHENTICATIONINFOS_HPP_INCLUDED + + +#include "../types.hpp" + + +namespace vmime { +namespace messaging { + + +/** This class encapsulates user credentials. + */ + +class authenticationInfos +{ +public: + + authenticationInfos(const string& username, const string& password); + authenticationInfos(const authenticationInfos& infos); + + /** Return the user account name. + * + * @return account name + */ + const string& username() const { return (m_username); } + + /** Return the user account password. + * + * @return account password + */ + const string& password() const { return (m_password); } + +private: + + string m_username; + string m_password; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_AUTHENTICATIONINFOS_HPP_INCLUDED diff --git a/src/messaging/authenticator.cpp b/src/messaging/authenticator.cpp new file mode 100644 index 00000000..91e488f8 --- /dev/null +++ b/src/messaging/authenticator.cpp @@ -0,0 +1,33 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "authenticator.hpp" + + +namespace vmime { +namespace messaging { + + +authenticator::~authenticator() +{ +} + + +} // messaging +} // vmime diff --git a/src/messaging/authenticator.hpp b/src/messaging/authenticator.hpp new file mode 100644 index 00000000..2faed34c --- /dev/null +++ b/src/messaging/authenticator.hpp @@ -0,0 +1,53 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_AUTHENTICATOR_HPP_INCLUDED +#define VMIME_MESSAGING_AUTHENTICATOR_HPP_INCLUDED + + +#include "authenticationInfos.hpp" + + +namespace vmime { +namespace messaging { + + +/** This class is used to obtain user credentials. + */ + +class authenticator +{ +public: + + virtual ~authenticator(); + + /** Called when the service needs to retrieve user credentials. + * It should return the user account name and password. + * + * @return user credentials (user name and password) + */ + virtual const authenticationInfos requestAuthInfos() const = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_AUTHENTICATOR_HPP_INCLUDED diff --git a/src/messaging/builtinServices.inl b/src/messaging/builtinServices.inl new file mode 100644 index 00000000..f1934ff9 --- /dev/null +++ b/src/messaging/builtinServices.inl @@ -0,0 +1,46 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#define REGISTER_SERVICE(p_class, p_name) \ + vmime::messaging::service::initializer <vmime::messaging::p_class> p_name(#p_name) + + +#if VMIME_BUILTIN_MESSAGING_PROTO_POP3 + #include "POP3Store.hpp" + REGISTER_SERVICE(POP3Store, pop3); +#endif + + +#if VMIME_BUILTIN_MESSAGING_PROTO_SMTP + #include "SMTPTransport.hpp" + REGISTER_SERVICE(SMTPTransport, smtp); +#endif + + +#if VMIME_BUILTIN_MESSAGING_PROTO_IMAP + #include "IMAPStore.hpp" + REGISTER_SERVICE(IMAPStore, imap); +#endif + + +#if VMIME_BUILTIN_MESSAGING_PROTO_MAILDIR + #include "maildirStore.hpp" + REGISTER_SERVICE(maildirStore, maildir); +#endif + diff --git a/src/messaging/defaultAuthenticator.cpp b/src/messaging/defaultAuthenticator.cpp new file mode 100644 index 00000000..c69b8c3e --- /dev/null +++ b/src/messaging/defaultAuthenticator.cpp @@ -0,0 +1,41 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "defaultAuthenticator.hpp" + + +namespace vmime { +namespace messaging { + + +defaultAuthenticator::defaultAuthenticator(const propertySet& props, const string& prefix) + : m_props(props), m_prefix(prefix) +{ +} + + +const authenticationInfos defaultAuthenticator::requestAuthInfos() const +{ + return (authenticationInfos + (m_props[m_prefix + "auth.username"], m_props[m_prefix + "auth.password"])); +} + + +} // messaging +} // vmime diff --git a/src/messaging/defaultAuthenticator.hpp b/src/messaging/defaultAuthenticator.hpp new file mode 100644 index 00000000..9480ec56 --- /dev/null +++ b/src/messaging/defaultAuthenticator.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_DEFAULTAUTHENTICATOR_HPP_INCLUDED +#define VMIME_MESSAGING_DEFAULTAUTHENTICATOR_HPP_INCLUDED + + +#include "authenticator.hpp" +#include "../propertySet.hpp" + + +namespace vmime { +namespace messaging { + + +/** An auhenticator that simply returns the credentials set in the + * session properties (named 'username' and 'password'). + */ + +class defaultAuthenticator : public authenticator +{ +public: + + defaultAuthenticator(const propertySet& props, const string& prefix); + +private: + + const propertySet& m_props; + const string m_prefix; + + const authenticationInfos requestAuthInfos() const; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_DEFAULTAUTHENTICATOR_HPP_INCLUDED diff --git a/src/messaging/events.cpp b/src/messaging/events.cpp new file mode 100644 index 00000000..d117cc82 --- /dev/null +++ b/src/messaging/events.cpp @@ -0,0 +1,110 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "events.hpp" + +#include <algorithm> + + +namespace vmime { +namespace messaging { +namespace events { + + +// +// messageCountEvent +// + +messageCountEvent::messageCountEvent + (class folder* folder, const Types type, const std::vector <int>& nums) + : m_folder(folder), m_type(type) +{ + m_nums.resize(nums.size()); + std::copy(nums.begin(), nums.end(), m_nums.begin()); +} + + +const folder* messageCountEvent::folder() const { return (const_cast <class folder*>(m_folder)); } +const messageCountEvent::Types messageCountEvent::type() const { return (m_type); } +const std::vector <int>& messageCountEvent::numbers() const { return (m_nums); } + + +void messageCountEvent::dispatch(messageCountListener* listener) const +{ + if (m_type == TYPE_ADDED) + listener->messagesAdded(*this); + else + listener->messagesRemoved(*this); +} + + +// +// messageChangedEvent +// + +messageChangedEvent::messageChangedEvent + (class folder* folder, const Types type, const std::vector <int>& nums) + : m_folder(folder), m_type(type) +{ + m_nums.resize(nums.size()); + std::copy(nums.begin(), nums.end(), m_nums.begin()); +} + + +const folder* messageChangedEvent::folder() const { return (const_cast <class folder*>(m_folder)); } +const messageChangedEvent::Types messageChangedEvent::type() const { return (m_type); } +const std::vector <int>& messageChangedEvent::numbers() const { return (m_nums); } + + +void messageChangedEvent::dispatch(messageChangedListener* listener) const +{ + listener->messageChanged(*this); +} + + +// +// folderEvent +// + +folderEvent::folderEvent + (class folder* folder, const Types type, + const utility::path& oldPath, const utility::path& newPath) + : m_folder(folder), m_type(type), m_oldPath(oldPath), m_newPath(newPath) +{ +} + + +const class folder* folderEvent::folder() const { return (m_folder); } +const folderEvent::Types folderEvent::type() const { return (m_type); } + + +void folderEvent::dispatch(class folderListener* listener) const +{ + switch (m_type) + { + case TYPE_CREATED: listener->folderCreated(*this); break; + case TYPE_RENAMED: listener->folderRenamed(*this); break; + case TYPE_DELETED: listener->folderDeleted(*this); break; + } +} + + +} // events +} // messaging +} // vmime diff --git a/src/messaging/events.hpp b/src/messaging/events.hpp new file mode 100644 index 00000000..d51ca2d0 --- /dev/null +++ b/src/messaging/events.hpp @@ -0,0 +1,174 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_EVENTS_HPP_INCLUDED +#define VMIME_MESSAGING_EVENTS_HPP_INCLUDED + + +#include <vector> + +#include "../utility/path.hpp" + + +namespace vmime { +namespace messaging { + +class folder; + +namespace events { + + +// +// messageCountEvent +// + +class messageCountEvent +{ +public: + + enum Types + { + TYPE_ADDED, // new messages + TYPE_REMOVED // expunged messages: renumbering + }; + + + messageCountEvent(class folder* folder, const Types type, const std::vector <int>& nums); + + const class folder* folder() const; + const Types type() const; + const std::vector <int>& numbers() const; + + void dispatch(class messageCountListener* listener) const; + +private: + + class folder* m_folder; + const Types m_type; + std::vector <int> m_nums; +}; + + +class messageCountListener +{ +protected: + + virtual ~messageCountListener() { } + +public: + + virtual void messagesAdded(const messageCountEvent& event) = 0; + virtual void messagesRemoved(const messageCountEvent& event) = 0; +}; + + +// +// messageChangedEvent +// + +class messageChangedEvent +{ +public: + + enum Types + { + TYPE_FLAGS // flags changed + }; + + + messageChangedEvent(class folder* folder, const Types type, const std::vector <int>& nums); + + const class folder* folder() const; + const Types type() const; + const std::vector <int>& numbers() const; + + void dispatch(class messageChangedListener* listener) const; + +private: + + class folder* m_folder; + const Types m_type; + std::vector <int> m_nums; +}; + + +class messageChangedListener +{ +protected: + + virtual ~messageChangedListener() { } + +public: + + virtual void messageChanged(const messageChangedEvent& event) = 0; +}; + + +// +// folderEvent +// + +class folderEvent +{ +public: + + enum Types + { + TYPE_CREATED, // a folder was created + TYPE_DELETED, // a folder was deleted + TYPE_RENAMED // a folder was renamed + }; + + + folderEvent(class folder* folder, const Types type, const utility::path& oldPath, const utility::path& newPath); + + const class folder* folder() const; + const Types type() const; + + void dispatch(class folderListener* listener) const; + +private: + + class folder* m_folder; + const Types m_type; + const utility::path m_oldPath; + const utility::path m_newPath; +}; + + +class folderListener +{ +protected: + + virtual ~folderListener() { } + +public: + + virtual void folderCreated(const folderEvent& event) = 0; + virtual void folderRenamed(const folderEvent& event) = 0; + virtual void folderDeleted(const folderEvent& event) = 0; +}; + + +} // events +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_EVENTS_HPP_INCLUDED diff --git a/src/messaging/folder.cpp b/src/messaging/folder.cpp new file mode 100644 index 00000000..e508389c --- /dev/null +++ b/src/messaging/folder.cpp @@ -0,0 +1,96 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "folder.hpp" + +#include <algorithm> + + +namespace vmime { +namespace messaging { + + +void folder::addMessageChangedListener(events::messageChangedListener* l) +{ + m_messageChangedListeners.push_back(l); +} + + +void folder::removeMessageChangedListener(events::messageChangedListener* l) +{ + std::remove(m_messageChangedListeners.begin(), m_messageChangedListeners.end(), l); +} + + +void folder::notifyMessageChanged(const events::messageChangedEvent& event) +{ + for (std::list <events::messageChangedListener*>::iterator + it = m_messageChangedListeners.begin() ; it != m_messageChangedListeners.end() ; ++it) + { + event.dispatch(*it); + } +} + + +void folder::addMessageCountListener(events::messageCountListener* l) +{ + m_messageCountListeners.push_back(l); +} + + +void folder::removeMessageCountListener(events::messageCountListener* l) +{ + std::remove(m_messageCountListeners.begin(), m_messageCountListeners.end(), l); +} + + +void folder::notifyMessageCount(const events::messageCountEvent& event) +{ + for (std::list <events::messageCountListener*>::iterator + it = m_messageCountListeners.begin() ; it != m_messageCountListeners.end() ; ++it) + { + event.dispatch(*it); + } +} + + +void folder::addFolderListener(events::folderListener* l) +{ + m_folderListeners.push_back(l); +} + + +void folder::removeFolderListener(events::folderListener* l) +{ + std::remove(m_folderListeners.begin(), m_folderListeners.end(), l); +} + + +void folder::notifyFolder(const events::folderEvent& event) +{ + for (std::list <events::folderListener*>::iterator + it = m_folderListeners.begin() ; it != m_folderListeners.end() ; ++it) + { + event.dispatch(*it); + } +} + + +} // messaging +} // vmime diff --git a/src/messaging/folder.hpp b/src/messaging/folder.hpp new file mode 100644 index 00000000..574ac1b0 --- /dev/null +++ b/src/messaging/folder.hpp @@ -0,0 +1,373 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_FOLDER_HPP_INCLUDED +#define VMIME_MESSAGING_FOLDER_HPP_INCLUDED + + +#include <vector> + +#include "../types.hpp" +#include "../dateTime.hpp" +#include "progressionListener.hpp" +#include "message.hpp" +#include "../message.hpp" +#include "events.hpp" +#include "../utility/path.hpp" +#include "../utility/stream.hpp" + + +namespace vmime { +namespace messaging { + + +/** Abstract representation of a folder in a message store. + */ + +class folder +{ +protected: + + folder(const folder&) { } + folder() { } + +public: + + virtual ~folder() { } + + /** Type used for fully qualified path name of a folder. + */ + typedef vmime::utility::path path; + + + /** Open mode. + */ + enum Modes + { + MODE_READ_ONLY, /**< Read-only mode (no modification to folder or messages is possible). */ + MODE_READ_WRITE /**< Full access mode (read and write). */ + }; + + /** Folder types. + */ + enum Types + { + TYPE_CONTAINS_FOLDERS = (1 << 0), /**< Folder can contain folders. */ + TYPE_CONTAINS_MESSAGES = (1 << 1), /**< Folder can contain messages. */ + + TYPE_UNDEFINED = 9999 /**< Used internally (this should not be returned + by the type() function). */ + }; + + /** Folder flags. + */ + enum Flags + { + FLAG_CHILDREN = (1 << 0), /**< Folder contains subfolders. */ + FLAG_NO_OPEN = (1 << 1), /**< Folder cannot be open. */ + + FLAG_UNDEFINED = 9999 /**< Used internally (this should not be returned + by the type() function). */ + }; + + /** Return the type of this folder. + * + * @return folder type (see folder::Types) + */ + virtual const int type() = 0; + + /** Return the flags of this folder. + * + * @return folder flags (see folder::Flags) + */ + virtual const int flags() = 0; + + /** Return the mode in which the folder has been open. + * + * @return folder opening mode (see folder::Modes) + */ + virtual const int mode() const = 0; + + /** Return the name of this folder. + * + * @return folder name + */ + virtual const folder::path::component name() const = 0; + + /** Return the fully qualified path name of this folder. + * + * @return absolute path of the folder + */ + virtual const folder::path fullPath() const = 0; + + /** Open this folder. + * + * @param mode open mode (see folder::Modes) + * @param failIfModeIsNotAvailable if set to false and if the requested mode + * is not available, a more restricted mode will be selected automatically. + * If set to true and if the requested mode is not available, the opening + * will fail. + */ + virtual void open(const int mode, bool failIfModeIsNotAvailable = false) = 0; + + /** Close this folder. + * + * @param expunge if set to true, deleted messages are expunged + */ + virtual void close(const bool expunge) = 0; + + /** Create this folder. + * + * @param type folder type (see folder::Types) + */ + virtual void create(const int type) = 0; + + /** Test whether this folder exists. + * + * @return true if the folder exists, false otherwise + */ + virtual const bool exists() = 0; + + /** Test whether this folder is open. + * + * @return true if the folder is open, false otherwise + */ + virtual const bool isOpen() const = 0; + + /** Get a new reference to a message in this folder. + * + * @param num message sequence number + * @return a new object referencing the specified message + */ + virtual message* getMessage(const int num) = 0; + + /** Get new references to messages in this folder. + * + * @param from sequence number of the first message to get + * @param to sequence number of the last message to get + * @return new objects referencing the specified messages + */ + virtual std::vector <message*> getMessages(const int from = 1, const int to = -1) = 0; + + /** Get new references to messages in this folder. + * + * @param nums sequence numbers of the messages to delete + * @return new objects referencing the specified messages + */ + virtual std::vector <message*> getMessages(const std::vector <int>& nums) = 0; + + /** Return the number of messages in this folder. + * + * @return number of messages in the folder + */ + virtual const int getMessageCount() = 0; + + /** Get a new reference to a sub-folder in this folder. + * + * @param name sub-folder name + * @return a new object referencing the specified folder + */ + virtual folder* getFolder(const folder::path::component& name) = 0; + + /** Get the list of all sub-folders in this folder. + * + * @param recursive if set to true, all the descendant are returned. + * If set to false, only the direct children are returned. + * @return list of sub-folders + */ + virtual std::vector <folder*> getFolders(const bool recursive = false) = 0; + + /** Rename (move) this folder to another location. + * + * @param newPath new path of the folder + */ + virtual void rename(const folder::path& newPath) = 0; + + /** Remove a message in this folder. + * + * @param num sequence number of the message to delete + */ + virtual void deleteMessage(const int num) = 0; + + /** Remove one or more messages from this folder. + * + * @param from sequence number of the first message to delete + * @param to sequence number of the last message to delete + */ + virtual void deleteMessages(const int from = 1, const int to = -1) = 0; + + /** Remove one or more messages from this folder. + * + * @param nums sequence numbers of the messages to delete + */ + virtual void deleteMessages(const std::vector <int>& nums) = 0; + + /** Change the flags for one or more messages in this folder. + * + * @param from sequence number of the first message to modify + * @param to sequence number of the last message to modify + * @param flags set of flags (see message::Flags) + * @param mode indicate how to treat old and new flags (see message::FlagsModes) + */ + virtual void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET) = 0; + + /** Change the flags for one or more messages in this folder. + * + * @param nums sequence numbers of the messages to modify + * @param flags set of flags (see message::Flags) + * @param mode indicate how to treat old and new flags (see message::FlagsModes) + */ + virtual void setMessageFlags(const std::vector <int>& nums, const int flags, const int mode = message::FLAG_MODE_SET) = 0; + + /** Add a message to this folder. + * + * @param msg message to add (data: header + body) + * @param flags flags for the new message + * @param date date/time for the new message (if NULL, the current time is used) + * @param progress progression listener, or NULL if not used + */ + virtual void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL) = 0; + + /** Add a message to this folder. + * + * @param is message to add (data: header + body) + * @param size size of the message to add (in bytes) + * @param flags flags for the new message + * @param date date/time for the new message (if NULL, the current time is used) + * @param progress progression listener, or NULL if not used + */ + virtual void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL) = 0; + + /** Copy a message from this folder to another folder. + * + * @param dest destination folder path + * @param num sequence number of the message to copy + */ + virtual void copyMessage(const folder::path& dest, const int num) = 0; + + /** Copy messages from this folder to another folder. + * + * @param dest destination folder path + * @param from sequence number of the first message to copy + * @param to sequence number of the last message to copy + */ + virtual void copyMessages(const folder::path& dest, const int from = 1, const int to = -1) = 0; + + /** Copy messages from this folder to another folder. + * + * @param dest destination folder path + * @param nums sequence numbers of the messages to copy + */ + virtual void copyMessages(const folder::path& dest, const std::vector <int>& nums) = 0; + + /** Request folder status without opening it. + * + * @param count will receive the number of messages in the folder + * @param unseen will receive the number of unseen messages in the folder + */ + virtual void status(int& count, int& unseen) = 0; + + /** Expunge deleted messages. + */ + virtual void expunge() = 0; + + /** Return a new folder objet referencing the parent folder of this folder. + * + * @return parent folder object + */ + virtual folder* getParent() = 0; + + /** Return a reference to the store to which this folder belongs. + * + * @return the store object to which this folder is attached + */ + virtual const class store& store() const = 0; + + /** Return a reference to the store to which this folder belongs. + * + * @return the store object to which this folder is attached + */ + virtual class store& store() = 0; + + /** Possible fetchable objects. + */ + enum FetchOptions + { + FETCH_ENVELOPE = (1 << 0), /**< Fetch sender, recipients, date, subject. */ + FETCH_STRUCTURE = (1 << 1), /**< Fetch structure (body parts). */ + FETCH_CONTENT_INFO = (1 << 2), /**< Fetch top-level content type. */ + FETCH_FLAGS = (1 << 3), /**< Fetch message flags. */ + FETCH_SIZE = (1 << 4), /**< Fetch message size (exact or estimated). */ + FETCH_FULL_HEADER = (1 << 5), /**< Fetch full RFC-[2]822 header. */ + FETCH_UID = (1 << 6), /**< Fetch unique identifier (protocol specific). */ + + FETCH_CUSTOM = (1 << 16) /**< Reserved for future use. */ + }; + + /** Fetch objects for the specified messages. + * + * @param msg list of message sequence numbers + * @param options objects to fetch (combination of folder::FetchOptions flags) + * @param progress progression listener, or NULL if not used + */ + virtual void fetchMessages(std::vector <message*>& msg, const int options, progressionListener* progress = NULL) = 0; + + /** Fetch objects for the specified message. + * + * @param msg the message + * @param options objects to fetch (combination of folder::FetchOptions flags) + */ + virtual void fetchMessage(message* msg, const int options) = 0; + + /** Return the list of fetchable objects supported by + * the underlying protocol (see folder::FetchOptions). + * + * @return list of supported fetchable objects + */ + virtual const int getFetchCapabilities() const = 0; + + // Event listeners + void addMessageChangedListener(events::messageChangedListener* l); + void removeMessageChangedListener(events::messageChangedListener* l); + + void addMessageCountListener(events::messageCountListener* l); + void removeMessageCountListener(events::messageCountListener* l); + + void addFolderListener(events::folderListener* l); + void removeFolderListener(events::folderListener* l); + +protected: + + void notifyMessageChanged(const events::messageChangedEvent& event); + void notifyMessageCount(const events::messageCountEvent& event); + void notifyFolder(const events::folderEvent& event); + +private: + + std::list <events::messageChangedListener*> m_messageChangedListeners; + std::list <events::messageCountListener*> m_messageCountListeners; + std::list <events::folderListener*> m_folderListeners; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_FOLDER_HPP_INCLUDED diff --git a/src/messaging/maildirFolder.cpp b/src/messaging/maildirFolder.cpp new file mode 100644 index 00000000..903de91d --- /dev/null +++ b/src/messaging/maildirFolder.cpp @@ -0,0 +1,552 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "maildirFolder.hpp" + +#include "maildirStore.hpp" +#include "maildirMessage.hpp" +#include "maildirUtils.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" + + +namespace vmime { +namespace messaging { + + +maildirFolder::maildirFolder(const folder::path& path, maildirStore* store) + : m_store(store), m_path(path), m_name(path.last()), m_mode(-1), m_open(false) +{ + m_store->registerFolder(this); +} + + +maildirFolder::~maildirFolder() +{ + if (m_store) + { + if (m_open) + close(false); + + m_store->unregisterFolder(this); + } + else if (m_open) + { + close(false); + } +} + + +void maildirFolder::onStoreDisconnected() +{ + m_store = NULL; +} + + +const int maildirFolder::mode() const +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + return (m_mode); +} + + +const int maildirFolder::type() +{ + if (m_path.empty()) + return (TYPE_CONTAINS_FOLDERS); + else + return (TYPE_CONTAINS_FOLDERS | TYPE_CONTAINS_MESSAGES); +} + + +const int maildirFolder::flags() +{ + int flags = 0; + + utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); + + utility::auto_ptr <utility::file> rootDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CONTAINER)); + + utility::auto_ptr <utility::fileIterator> it = rootDir->getFiles(); + + while (it->hasMoreElements()) + { + utility::auto_ptr <utility::file> file = it->nextElement(); + + if (maildirUtils::isSubfolderDirectory(*file)) + { + flags |= FLAG_CHILDREN; // Contains at least one sub-folder + break; + } + } + + return (flags); +} + + +const folder::path::component maildirFolder::name() const +{ + return (m_name); +} + + +const folder::path maildirFolder::fullPath() const +{ + return (m_path); +} + + +void maildirFolder::open(const int mode, bool /* failIfModeIsNotAvailable */) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (isOpen()) + throw exceptions::illegal_state("Folder is already open"); + else if (!exists()) + throw exceptions::illegal_state("Folder already exists"); + + m_open = true; + m_mode = mode; +} + + +void maildirFolder::close(const bool expunge) +{ + // TODO +} + + +void maildirFolder::onClose() +{ + // TODO +} + + +void maildirFolder::create(const int type) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (isOpen()) + throw exceptions::illegal_state("Folder is open"); + else if (exists()) + throw exceptions::illegal_state("Folder already exists"); + + // Folder name cannot start with '.' + if (!m_path.empty()) + { + const path::component& comp = m_path.last(); + + const int length = comp.buffer().length(); + int pos = 0; + + while ((pos < length) && (comp.buffer()[pos] == '.')) + ++pos; + + if (pos != 0) + throw exceptions::invalid_folder_name("Name cannot start with '.'"); + } + + // Create directory on file system + try + { + utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); + + utility::auto_ptr <utility::file> rootDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_ROOT)); + + utility::auto_ptr <utility::file> newDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_NEW)); + utility::auto_ptr <utility::file> tmpDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_TMP)); + utility::auto_ptr <utility::file> curDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CUR)); + + rootDir->createDirectory(true); + + newDir->createDirectory(false); + tmpDir->createDirectory(false); + curDir->createDirectory(false); + } + catch (exceptions::filesystem_exception& e) + { + throw exceptions::command_error("CREATE", e.what(), "File system exception"); + } + + // Notify folder created + events::folderEvent event(this, events::folderEvent::TYPE_CREATED, m_path, m_path); + notifyFolder(event); +} + + +const bool maildirFolder::exists() +{ + utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); + + utility::auto_ptr <utility::file> rootDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_ROOT)); + + utility::auto_ptr <utility::file> newDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_NEW)); + utility::auto_ptr <utility::file> tmpDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_TMP)); + utility::auto_ptr <utility::file> curDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CUR)); + + return (rootDir->exists() && rootDir->isDirectory() && + newDir->exists() && newDir->isDirectory() && + tmpDir->exists() && tmpDir->isDirectory() && + curDir->exists() && curDir->isDirectory()); +} + + +const bool maildirFolder::isOpen() const +{ + return (m_open); +} + + +void maildirFolder::scanFolder() +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + try + { + utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); + + utility::auto_ptr <utility::file> newDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_NEW)); + utility::auto_ptr <utility::file> curDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CUR)); + + // Unread messages (new/) + utility::auto_ptr <utility::fileIterator> nit = newDir->getFiles(); + std::vector <utility::path::component> unreadMessageFilenames; + + while (nit->hasMoreElements()) + { + utility::auto_ptr <utility::file> file = nit->nextElement(); + unreadMessageFilenames.push_back(file->fullPath().last()); + } + + // Seen messages (cur/) + utility::auto_ptr <utility::fileIterator> cit = curDir->getFiles(); + std::vector <utility::path::component> messageFilenames; + + while (cit->hasMoreElements()) + { + utility::auto_ptr <utility::file> file = cit->nextElement(); + messageFilenames.push_back(file->fullPath().last()); + } + + // TODO: update m_messageFilenames + // TODO: what to do with files which name has changed? (flag change, message deletion...) + + m_unreadMessageCount = unreadMessageFilenames.size(); + m_messageCount = messageFilenames.size(); + } + catch (exceptions::filesystem_exception&) + { + // Should not happen... + } + + /* + int m_unreadMessageCount; + int m_messageCount; + + std::vector <folder::path::component> m_unreadMessageFilenames; + std::vector <folder::path::component> m_messageFilenames; + + if (0) + { + m_messageFilenames.clear(); + + for (...) + { + m_messageFilenames.push_back(...); + } + } + */ +} + + +message* maildirFolder::getMessage(const int num) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + if (num < 1 || num > m_messageCount) + throw exceptions::message_not_found(); + + return new maildirMessage(this, num); +} + + +std::vector <message*> maildirFolder::getMessages(const int from, const int to) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::vector <message*> v; + + for (int i = from ; i <= to ; ++i) + v.push_back(new maildirMessage(this, i)); + + return (v); +} + + +std::vector <message*> maildirFolder::getMessages(const std::vector <int>& nums) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::vector <message*> v; + + for (std::vector <int>::const_iterator it = nums.begin() ; it != nums.end() ; ++it) + v.push_back(new maildirMessage(this, *it)); + + return (v); +} + + +const int maildirFolder::getMessageCount() +{ + return (m_messageCount); +} + + +folder* maildirFolder::getFolder(const folder::path::component& name) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + return new maildirFolder(m_path / name, m_store); +} + + +std::vector <folder*> maildirFolder::getFolders(const bool recursive) +{ + if (!isOpen() && !m_store) + throw exceptions::illegal_state("Store disconnected"); + + std::vector <folder*> list; + + try + { + listFolders(list, recursive); + } + catch (std::exception&) + { + for (std::vector <folder*>::iterator it = list.begin() ; it != list.end() ; ++it) + delete (*it); + + throw; + } + + return (list); +} + + +void maildirFolder::listFolders(std::vector <folder*>& list, const bool recursive) +{ + try + { + utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); + + utility::auto_ptr <utility::file> rootDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CONTAINER)); + utility::auto_ptr <utility::fileIterator> it = rootDir->getFiles(); + + while (it->hasMoreElements()) + { + utility::auto_ptr <utility::file> file = it->nextElement(); + + if (maildirUtils::isSubfolderDirectory(*file)) + { + const utility::path subPath = m_path / file->fullPath().last(); + maildirFolder* subFolder = new maildirFolder(subPath, m_store); + + list.push_back(subFolder); + + if (recursive) + subFolder->listFolders(list, true); + } + } + } + catch (exceptions::filesystem_exception& e) + { + throw exceptions::command_error("LIST", e.what()); + } +} + + +void maildirFolder::rename(const folder::path& newPath) +{ + // TODO +} + + +void maildirFolder::deleteMessage(const int num) +{ + // TODO +} + + +void maildirFolder::deleteMessages(const int from, const int to) +{ + // TODO +} + + +void maildirFolder::deleteMessages(const std::vector <int>& nums) +{ + // TODO +} + + +void maildirFolder::setMessageFlags + (const int from, const int to, const int flags, const int mode) +{ + // TODO +} + + +void maildirFolder::setMessageFlags + (const std::vector <int>& nums, const int flags, const int mode) +{ + // TODO +} + + +void maildirFolder::addMessage(vmime::message* msg, const int flags, + vmime::datetime* date, progressionListener* progress) +{ + // TODO +} + + +void maildirFolder::addMessage(utility::inputStream& is, const int size, + const int flags, vmime::datetime* date, progressionListener* progress) +{ + // TODO +} + + +void maildirFolder::copyMessage(const folder::path& dest, const int num) +{ + // TODO +} + + +void maildirFolder::copyMessages(const folder::path& dest, const int from, const int to) +{ + // TODO +} + + +void maildirFolder::copyMessages(const folder::path& dest, const std::vector <int>& nums) +{ + // TODO +} + + +void maildirFolder::status(int& count, int& unseen) +{ + const int oldCount = m_messageCount; + + scanFolder(); + + count = m_messageCount; + unseen = m_unreadMessageCount; + + // Notify message count changed (new messages) + if (count > oldCount) + { + std::vector <int> nums; + nums.reserve(count - oldCount); + + for (int i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j) + nums[j] = i; + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list <maildirFolder*>::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == m_path) + { + (*it)->m_messageCount = count; + (*it)->notifyMessageCount(event); + } + } + } +} + + +void maildirFolder::expunge() +{ + // TODO +} + + +folder* maildirFolder::getParent() +{ + return (m_path.empty() ? NULL : new maildirFolder(m_path.parent(), m_store)); +} + + +const class store& maildirFolder::store() const +{ + return (*m_store); +} + + +class store& maildirFolder::store() +{ + return (*m_store); +} + + +void maildirFolder::fetchMessages(std::vector <message*>& msg, + const int options, progressionListener* progress) +{ + // TODO +} + + +void maildirFolder::fetchMessage(message* msg, const int options) +{ + // TODO +} + + +const int maildirFolder::getFetchCapabilities() const +{ + return (FETCH_ENVELOPE | FETCH_STRUCTURE | FETCH_CONTENT_INFO | + FETCH_FLAGS | FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID); +} + + +} // messaging +} // vmime diff --git a/src/messaging/maildirFolder.hpp b/src/messaging/maildirFolder.hpp new file mode 100644 index 00000000..37578157 --- /dev/null +++ b/src/messaging/maildirFolder.hpp @@ -0,0 +1,144 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_MAILDIRFOLDER_HPP_INCLUDED +#define VMIME_MESSAGING_MAILDIRFOLDER_HPP_INCLUDED + + +#include <vector> +#include <map> + +#include "../types.hpp" +#include "folder.hpp" + + +namespace vmime { +namespace messaging { + + +class maildirStore; + + +/** maildir folder implementation. + */ + +class maildirFolder : public folder +{ +protected: + + friend class maildirStore; + friend class maildirMessage; + + + maildirFolder(const folder::path& path, maildirStore* store); + maildirFolder(const maildirFolder&) : folder() { } + + ~maildirFolder(); + +public: + + const int mode() const; + + const int type(); + + const int flags(); + + const folder::path::component name() const; + const folder::path fullPath() const; + + void open(const int mode, bool failIfModeIsNotAvailable = false); + void close(const bool expunge); + void create(const int type); + + const bool exists(); + + const bool isOpen() const; + + message* getMessage(const int num); + std::vector <message*> getMessages(const int from = 1, const int to = -1); + std::vector <message*> getMessages(const std::vector <int>& nums); + const int getMessageCount(); + + folder* getFolder(const folder::path::component& name); + std::vector <folder*> getFolders(const bool recursive = false); + + void rename(const folder::path& newPath); + + void deleteMessage(const int num); + void deleteMessages(const int from = 1, const int to = -1); + void deleteMessages(const std::vector <int>& nums); + + void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET); + void setMessageFlags(const std::vector <int>& nums, const int flags, const int mode = message::FLAG_MODE_SET); + + void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + + void copyMessage(const folder::path& dest, const int num); + void copyMessages(const folder::path& dest, const int from = 1, const int to = -1); + void copyMessages(const folder::path& dest, const std::vector <int>& nums); + + void status(int& count, int& unseen); + + void expunge(); + + folder* getParent(); + + const class store& store() const; + class store& store(); + + + void fetchMessages(std::vector <message*>& msg, const int options, progressionListener* progress = NULL); + void fetchMessage(message* msg, const int options); + + const int getFetchCapabilities() const; + +private: + + maildirStore* m_store; + + folder::path m_path; + folder::path::component m_name; + + int m_mode; + bool m_open; + + int m_unreadMessageCount; + int m_messageCount; + + std::vector <folder::path::component> m_unreadMessageFilenames; + std::vector <folder::path::component> m_messageFilenames; + + void scanFolder(); + + void listFolders(std::vector <folder*>& list, const bool recursive); + + + + void onStoreDisconnected(); + + void onClose(); +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_MAILDIRFOLDER_HPP_INCLUDED diff --git a/src/messaging/maildirMessage.cpp b/src/messaging/maildirMessage.cpp new file mode 100644 index 00000000..c14831c3 --- /dev/null +++ b/src/messaging/maildirMessage.cpp @@ -0,0 +1,28 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "maildirMessage.hpp" + + +namespace vmime { +namespace messaging { + + +} // messaging +} // vmime diff --git a/src/messaging/maildirMessage.hpp b/src/messaging/maildirMessage.hpp new file mode 100644 index 00000000..3ea6afbf --- /dev/null +++ b/src/messaging/maildirMessage.hpp @@ -0,0 +1,78 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_MAILDIRMESSAGE_HPP_INCLUDED +#define VMIME_MESSAGING_MAILDIRMESSAGE_HPP_INCLUDED + + +#include "message.hpp" +#include "folder.hpp" + + +namespace vmime { +namespace messaging { + + +class maildirFolder; + + +/** maildir message implementation. + */ + +class maildirMessage : public message +{ + friend class maildirFolder; + +protected: + + maildirMessage(maildirFolder* folder, const int num); + maildirMessage(const maildirMessage&) : message() { } + + ~maildirMessage(); + +public: + + const int number() const; + + const uid uniqueId() const; + + const int size() const; + + const bool isExpunged() const; + + const class structure& structure() const; + class structure& structure(); + + const class header& header() const; + + const int flags() const; + void setFlags(const int flags, const int mode = FLAG_MODE_SET); + + void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + + void fetchPartHeader(part& p); +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_MAILDIRMESSAGE_HPP_INCLUDED diff --git a/src/messaging/maildirStore.cpp b/src/messaging/maildirStore.cpp new file mode 100644 index 00000000..2e125109 --- /dev/null +++ b/src/messaging/maildirStore.cpp @@ -0,0 +1,165 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "maildirStore.hpp" + +#include "maildirFolder.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" + + +namespace vmime { +namespace messaging { + + +maildirStore::maildirStore(class session& sess, class authenticator* auth) + : store(sess, infosInstance(), auth), m_connected(false) +{ +} + + +maildirStore::~maildirStore() +{ + if (isConnected()) + disconnect(); +} + + +const string maildirStore::protocolName() const +{ + return "maildir"; +} + + +folder* maildirStore::getRootFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new maildirFolder(folder::path(), this); +} + + +folder* maildirStore::getDefaultFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new maildirFolder(folder::path::component("inbox"), this); +} + + +folder* maildirStore::getFolder(const folder::path& path) +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new maildirFolder(path, this); +} + + +void maildirStore::connect() +{ + if (isConnected()) + throw exceptions::already_connected(); + + m_fsPath = platformDependant::getHandler()->getFileSystemFactory()->stringToPath + (session().properties()[infos().propertyPrefix() + "server.path"]); + + m_connected = true; +} + + +const bool maildirStore::isConnected() const +{ + return (m_connected); +} + + +void maildirStore::disconnect() +{ + for (std::list <maildirFolder*>::iterator it = m_folders.begin() ; + it != m_folders.end() ; ++it) + { + (*it)->onStoreDisconnected(); + } + + m_folders.clear(); + + m_connected = false; +} + + +void maildirStore::noop() +{ + // Nothing to do. +} + + +void maildirStore::registerFolder(maildirFolder* folder) +{ + m_folders.push_back(folder); +} + + +void maildirStore::unregisterFolder(maildirFolder* folder) +{ + std::list <maildirFolder*>::iterator it = std::find(m_folders.begin(), m_folders.end(), folder); + if (it != m_folders.end()) m_folders.erase(it); +} + + +const utility::path& maildirStore::getFileSystemPath() const +{ + return (m_fsPath); +} + + + + +// Service infos + +maildirStore::_infos maildirStore::sm_infos; + + +const port_t maildirStore::_infos::defaultPort() const +{ + return (0); +} + + +const string maildirStore::_infos::propertyPrefix() const +{ + return "store.maildir."; +} + + +const std::vector <string> maildirStore::_infos::availableProperties() const +{ + std::vector <string> list; + + list.push_back("server.path"); + + return (list); +} + + +} // messaging +} // vmime diff --git a/src/messaging/maildirStore.hpp b/src/messaging/maildirStore.hpp new file mode 100644 index 00000000..62acb13e --- /dev/null +++ b/src/messaging/maildirStore.hpp @@ -0,0 +1,102 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_MAILDIRSTORE_HPP_INCLUDED +#define VMIME_MESSAGING_MAILDIRSTORE_HPP_INCLUDED + + +#include "store.hpp" +#include "socket.hpp" +#include "folder.hpp" +#include "../config.hpp" + +#include "utility/file.hpp" + +#include <ostream> + + +namespace vmime { +namespace messaging { + + +class maildirFolder; + + +/** maildir store service. + */ + +class maildirStore : public store +{ + friend class maildirFolder; + +public: + + maildirStore(class session& sess, class authenticator* auth); + ~maildirStore(); + + const string protocolName() const; + + folder* getDefaultFolder(); + folder* getRootFolder(); + folder* getFolder(const folder::path& path); + + static const serviceInfos& infosInstance() { return (sm_infos); } + const serviceInfos& infos() const { return (sm_infos); } + + void connect(); + const bool isConnected() const; + void disconnect(); + + void noop(); + + const utility::path& getFileSystemPath() const; + +private: + + void registerFolder(maildirFolder* folder); + void unregisterFolder(maildirFolder* folder); + + + std::list <maildirFolder*> m_folders; + + bool m_connected; + + utility::path m_fsPath; + + + // Service infos + class _infos : public serviceInfos + { + public: + + const port_t defaultPort() const; + + const string propertyPrefix() const; + const std::vector <string> availableProperties() const; + }; + + static _infos sm_infos; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_MAILDIRSTORE_HPP_INCLUDED diff --git a/src/messaging/maildirUtils.cpp b/src/messaging/maildirUtils.cpp new file mode 100644 index 00000000..28df6902 --- /dev/null +++ b/src/messaging/maildirUtils.cpp @@ -0,0 +1,87 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "maildirUtils.hpp" +#include "maildirStore.hpp" + + +namespace vmime { +namespace messaging { + + +const vmime::word maildirUtils::TMP_DIR("tmp"); // ensure reliable delivery (not to be listed) +const vmime::word maildirUtils::CUR_DIR("cur"); // no longer new messages +const vmime::word maildirUtils::NEW_DIR("new"); // unread messages + + +const utility::file::path maildirUtils::getFolderFSPath + (maildirStore* store, const utility::path& folderPath, const FolderFSPathMode mode) +{ + // Root path + utility::file::path path(store->getFileSystemPath()); + const int count = (mode == FOLDER_PATH_CONTAINER ? folderPath.size() : folderPath.size() - 1); + + // Parent folders + for (int i = 0 ; i < count ; ++i) + { + utility::file::path::component comp(folderPath[i]); + + // TODO: may not work with all encodings... + comp.buffer() = "." + comp.buffer() + ".directory"; + + path /= comp; + } + + // Last component + if (folderPath.size() != 0 && + mode != FOLDER_PATH_CONTAINER) + { + path /= folderPath.last(); + + switch (mode) + { + case FOLDER_PATH_ROOT: break; // Nothing to do + case FOLDER_PATH_NEW: path /= NEW_DIR; break; + case FOLDER_PATH_CUR: path /= CUR_DIR; break; + case FOLDER_PATH_TMP: path /= TMP_DIR; break; + case FOLDER_PATH_CONTAINER: break; // Can't happen... + } + } + + return (path); +} + + +const bool maildirUtils::isSubfolderDirectory(const utility::file& file) +{ + // A directory which name does not start with '.' + // is listed as a sub-folder... + if (file.isDirectory() && + file.fullPath().last().buffer().size() >= 1 && + file.fullPath().last().buffer()[0] != '.') + { + return (true); + } + + return (false); +} + + +} // messaging +} // vmime diff --git a/src/messaging/maildirUtils.hpp b/src/messaging/maildirUtils.hpp new file mode 100644 index 00000000..44d36ad3 --- /dev/null +++ b/src/messaging/maildirUtils.hpp @@ -0,0 +1,72 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_MAILDIRUTILS_HPP_INCLUDED +#define VMIME_MESSAGING_MAILDIRUTILS_HPP_INCLUDED + + +#include "../utility/file.hpp" +#include "../utility/path.hpp" + + +namespace vmime { +namespace messaging { + + +class maildirStore; + + +class maildirUtils +{ +public: + + /** Mode for return value of getFolderFSPath(). */ + enum FolderFSPathMode + { + FOLDER_PATH_ROOT, /**< Root folder (eg. ~/Mail/MyFolder) */ + FOLDER_PATH_NEW, /**< Folder containing unread messages (eg. ~/Mail/MyFolder/new) */ + FOLDER_PATH_CUR, /**< Folder containing messages that have been seen (eg. ~/Mail/MyFolder/cur) */ + FOLDER_PATH_TMP, /**< Temporary folder used for reliable delivery (eg. ~/Mail/MyFolder/tmp) */ + FOLDER_PATH_CONTAINER /**< Container for sub-folders (eg. ~/Mail/.MyFolder.directory) */ + }; + + /** Return the path on the filesystem for the folder in specified store. + * + * @param store parent store + * @param folderPath path of the folder + * @param mode type of path to return (see FolderFSPathMode) + * @return filesystem path for the specified folder + */ + static const utility::file::path getFolderFSPath(maildirStore* store, const utility::path& folderPath, const FolderFSPathMode mode); + + static const bool isSubfolderDirectory(const utility::file& file); + +private: + + static const vmime::word TMP_DIR; + static const vmime::word CUR_DIR; + static const vmime::word NEW_DIR; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_MAILDIRUTILS_HPP_INCLUDED diff --git a/src/messaging/message.cpp b/src/messaging/message.cpp new file mode 100644 index 00000000..0bb1aaca --- /dev/null +++ b/src/messaging/message.cpp @@ -0,0 +1,46 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "message.hpp" + + +namespace vmime { +namespace messaging { + + +const part& part::operator[](const int x) const +{ + return (structure()[x]); +} + + +part& part::operator[](const int x) +{ + return (structure()[x]); +} + + +const int part::count() const +{ + return (structure().count()); +} + + +} // messaging +} // vmime diff --git a/src/messaging/message.hpp b/src/messaging/message.hpp new file mode 100644 index 00000000..c07c9ca3 --- /dev/null +++ b/src/messaging/message.hpp @@ -0,0 +1,280 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_MESSAGE_HPP_INCLUDED +#define VMIME_MESSAGING_MESSAGE_HPP_INCLUDED + + +#include "../header.hpp" +#include "progressionListener.hpp" +#include "../utility/stream.hpp" + + +namespace vmime { +namespace messaging { + + +/** A MIME part in a message. + */ + +class part +{ +protected: + + part() { } + part(const part&) { } + + virtual ~part() { } + +public: + + /** Return the structure of this part. + * + * @return structure of the part + */ + virtual const class structure& structure() const = 0; + + /** Return the structure of this part. + * + * @return structure of the part + */ + virtual class structure& structure() = 0; + + /** Return the header section for this part (you must fetch header + * before using this function: see message::fetchPartHeader). + * + * @return header section + */ + virtual const class header& header() const = 0; + + /** Return the media-type of the content in this part. + * + * @return content media type + */ + virtual const mediaType& type() const = 0; + + /** Return the size of this part. + * + * @return size of the part (in bytes) + */ + virtual const int size() const = 0; + + /** Return the part sequence number (index) + * + * @return part number + */ + virtual const int number() const = 0; // begin at 1 + + /** Return the sub-part at the specified position. + * This provide easy access to parts: + * Eg: "message->extract(message->structure()[3][1][2])". + * + * @param x index of the sub-part + * @return sub-part at position 'x' + */ + const part& operator[](const int x) const; + + /** Return the sub-part at the specified position. + * This provide easy access to parts: + * Eg: "message->extract(message->structure()[3][1][2])". + * + * @param x index of the sub-part + * @return sub-part at position 'x' + */ + part& operator[](const int x); + + /** Return the number of sub-parts in this part. + * + * @return number of sub-parts + */ + const int count() const; +}; + + +/** Structure of a MIME part/message. + */ + +class structure +{ +protected: + + structure() { } + structure(const structure&) { } + + virtual ~structure() { } + +public: + + /** Return the part at the specified position. + * + * @param x position + * @return part at position 'x' + */ + virtual const part& operator[](const int x) const = 0; + + /** Return the part at the specified position. + * + * @param x position + * @return part at position 'x' + */ + virtual part& operator[](const int x) = 0; + + /** Return the number of parts in this part. + * + * @return number of parts + */ + virtual const int count() const = 0; +}; + + +/** Abstract representation of a message in a store/transport service. + */ + +class message +{ +protected: + + message() { } + message(const message&) { } + +public: + + virtual ~message() { } + + /** The type for an unique message identifier. + */ + typedef string uid; + + /** Return the MIME structure of the message (must fetch before). + * + * @return MIME structure of the message + */ + virtual const class structure& structure() const = 0; + + /** Return the MIME structure of the message (must fetch before). + * + * @return MIME structure of the message + */ + virtual class structure& structure() = 0; + + /** Return a reference to the header fields of the message (must fetch before). + * + * @return header section of the message + */ + virtual const class header& header() const = 0; + + /** Return the sequence number of this message. This number is + * used to reference the message in the folder. + * + * @return sequence number of the message + */ + virtual const int number() const = 0; + + /** Return the unique identified of this message (must fetch before). + * + * @return UID of the message + */ + virtual const uid uniqueId() const = 0; + + /** Return the size of the message (must fetch before). + * + * @return size of the message (in bytes) + */ + virtual const int size() const = 0; + + /** Check whether this message has been expunged + * (ie: definitively deleted). + * + * @return true if the message is expunged, false otherwise + */ + virtual const bool isExpunged() const = 0; + + /** Possible flags for a message. + */ + enum Flags + { + FLAG_SEEN = (1 << 0), /**< Message has been seen. */ + FLAG_RECENT = (1 << 1), /**< Message has been recently received. */ + FLAG_DELETED = (1 << 2), /**< Message is marked for deletion. */ + FLAG_REPLIED = (1 << 3), /**< User replied to this message. */ + FLAG_MARKED = (1 << 4), /**< Used-defined flag. */ + + FLAG_UNDEFINED = 9999 /**< Used internally (this should not be returned + by the flags() function). */ + }; + + /** Methods for setting the flags. + */ + enum FlagsModes + { + FLAG_MODE_SET, /**< Set (replace) the flags. */ + FLAG_MODE_ADD, /**< Add the flags. */ + FLAG_MODE_REMOVE /**< Remove the flags. */ + }; + + /** Return the flags of this message. + * + * @return flags of the message + */ + virtual const int flags() const = 0; + + /** Set the flags of this message. + * + * @param flags set of flags (see Flags) + * @param mode indicate how to treat old and new flags (see FlagsModes) + */ + virtual void setFlags(const int flags, const int mode = FLAG_MODE_SET) = 0; + + /** Extract the whole message data (header + contents). + * + * WARNING: partial fetch might not be supported by the underlying protocol. + * + * @param os output stream in which to write message data + * @param progress progression listener, or NULL if not used + * @param start index of the first byte to retrieve (used for partial fetch) + * @param length number of bytes to retrieve (used for partial fetch) + */ + + virtual void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const = 0; + + /** Extract the specified (MIME) part of the message (header + contents). + * + * WARNING: partial fetch might not be supported by the underlying protocol. + * + * @param p part to extract + * @param os output stream in which to write part data + * @param progress progression listener, or NULL if not used + * @param start index of the first byte to retrieve (used for partial fetch) + * @param length number of bytes to retrieve (used for partial fetch) + */ + virtual void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const = 0; + + /** Fetch the MIME header for the specified part. + * + * @param p the part for which to fetch the header + */ + virtual void fetchPartHeader(part& p) = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_MESSAGE_HPP_INCLUDED diff --git a/src/messaging/progressionListener.hpp b/src/messaging/progressionListener.hpp new file mode 100644 index 00000000..1e75d862 --- /dev/null +++ b/src/messaging/progressionListener.hpp @@ -0,0 +1,75 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_PROGRESSIONLISTENER_HPP_INCLUDED +#define VMIME_MESSAGING_PROGRESSIONLISTENER_HPP_INCLUDED + + +namespace vmime { +namespace messaging { + + +/** An interface to implement if you want to be notified + * of a progression status by some objects. + */ + +class progressionListener +{ +protected: + + virtual ~progressionListener() { } + +public: + + /** Allow the caller object to cancel the current operation. + * + * @warning WARNING: this is implementation-dependant: the underlying + * messaging protocol may not support this). + * + * @return true to cancel the operation, false otherwise + */ + virtual const bool cancel() const = 0; + + /** Called at the beginning of the operation. + * + * @param predictedTotal predicted amount of units (this has + * no concrete meaning: they are not bytes, nor percentage...) + */ + virtual void start(const int predictedTotal) = 0; + + /** Called during the operation (can be called several times). + * + * @param current current position + * @param currentTotal adjusted total amount of units + */ + virtual void progress(const int current, const int currentTotal) = 0; + + /** Called at the end of the operation. + * + * @param total final total amount of units + */ + virtual void stop(const int total) = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_PROGRESSIONLISTENER_HPP_INCLUDED diff --git a/src/messaging/service.cpp b/src/messaging/service.cpp new file mode 100644 index 00000000..5a55fb4f --- /dev/null +++ b/src/messaging/service.cpp @@ -0,0 +1,44 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "service.hpp" + +#include "defaultAuthenticator.hpp" + + +namespace vmime { +namespace messaging { + + +service::service(class session& sess, const serviceInfos& infos, class authenticator* auth) + : m_deleteAuth(auth == NULL), m_session(sess), m_auth(auth ? auth : + new defaultAuthenticator(sess.properties(), infos.propertyPrefix())) +{ +} + + +service::~service() +{ + if (m_deleteAuth) + delete (m_auth); +} + + +} // messaging +} // vmime diff --git a/src/messaging/service.hpp b/src/messaging/service.hpp new file mode 100644 index 00000000..d4a59c7f --- /dev/null +++ b/src/messaging/service.hpp @@ -0,0 +1,143 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SERVICE_HPP_INCLUDED +#define VMIME_MESSAGING_SERVICE_HPP_INCLUDED + + +#include "../types.hpp" +#include "session.hpp" + +#include "authenticator.hpp" +#include "progressionListener.hpp" + +#include "serviceFactory.hpp" +#include "serviceInfos.hpp" + + +namespace vmime { +namespace messaging { + + +class service +{ +protected: + + service(class session& sess, const serviceInfos& infos, class authenticator* auth); + +public: + + virtual ~service(); + + // Possible service types + enum Type + { + TYPE_STORE = 0, /**< The service is a message store. */ + TYPE_TRANSPORT /**< The service sends messages. */ + }; + + /** Return the type of service. + * + * @return type of service + */ + virtual const Type type() const = 0; + + /** Return the protocol name of this service. + * + * @return protocol name + */ + virtual const string protocolName() const = 0; + + /** Return the session object associated with this service instance. + * + * @return session object + */ + const class session& session() const { return (m_session); } + + /** Return the session object associated with this service instance. + * + * @return session object + */ + class session& session() { return (m_session); } + + /** Return information about this service. + * + * @return information about the service + */ + virtual const serviceInfos& infos() const = 0; + + /** Connect to service. + */ + virtual void connect() = 0; + + /** Disconnect from service. + */ + virtual void disconnect() = 0; + + /** Test whether this service is connected. + * + * @return true if the service is connected, false otherwise + */ + virtual const bool isConnected() const = 0; + + /** Do nothing but ensure the server do not disconnect (for + * example, this can reset the auto-logout timer on the + * server, if one exists). + */ + virtual void noop() = 0; + + /** Return the authenticator object used with this service instance. + * + * @return authenticator object + */ + const class authenticator& authenticator() const { return (*m_auth); } + + /** Return the authenticator object used with this service instance. + * + * @return authenticator object + */ + class authenticator& authenticator() { return (*m_auth); } + + // Basic service registerer + template <class S> + class initializer + { + public: + + initializer(const string& protocol) + { + serviceFactory::getInstance()-> + template registerName <S>(protocol); + } + }; + +private: + + bool m_deleteAuth; + + class session& m_session; + class authenticator* m_auth; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SERVICE_HPP_INCLUDED diff --git a/src/messaging/serviceFactory.cpp b/src/messaging/serviceFactory.cpp new file mode 100644 index 00000000..09bced01 --- /dev/null +++ b/src/messaging/serviceFactory.cpp @@ -0,0 +1,102 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "serviceFactory.hpp" +#include "service.hpp" + +#include "../exception.hpp" +#include "../config.hpp" + +#include "builtinServices.inl" + + +namespace vmime { +namespace messaging { + + +serviceFactory::serviceFactory() +{ +} + + +serviceFactory::~serviceFactory() +{ + for (ProtoMap::iterator it = m_protoMap.begin() ; it != m_protoMap.end() ; ++it) + delete ((*it).second); +} + + +service* serviceFactory::create + (session& sess, const string& protocol, authenticator* auth) +{ + ProtoMap::const_iterator pos = m_protoMap.find(toLower(protocol)); + + if (pos != m_protoMap.end()) + { + return ((*pos).second)->create(sess, auth); + } + else + { + throw exceptions::no_service_available(); + return (NULL); + } +} + + +service* serviceFactory::create + (session& sess, const url& u, authenticator* auth) +{ + service* serv = create(sess, u.protocol(), auth); + + sess.properties()[serv->infos().propertyPrefix() + "server.address"] = u.host(); + + if (u.port() != url::UNSPECIFIED_PORT) + sess.properties()[serv->infos().propertyPrefix() + "server.port"] = u.port(); + + if (!u.path().empty()) + sess.properties()[serv->infos().propertyPrefix() + "server.path"] = u.path(); + + if (!u.username().empty()) + { + sess.properties()[serv->infos().propertyPrefix() + "auth.username"] = u.username(); + sess.properties()[serv->infos().propertyPrefix() + "auth.password"] = u.password(); + } + + return (serv); +} + + +const serviceFactory::registeredService& serviceFactory::operator[] + (const string& protocol) const +{ + ProtoMap::const_iterator pos = m_protoMap.find(toLower(protocol)); + + if (pos != m_protoMap.end()) + { + return *((*pos).second); + } + else + { + throw exceptions::no_service_available(); + } +} + + +} // messaging +} // vmime diff --git a/src/messaging/serviceFactory.hpp b/src/messaging/serviceFactory.hpp new file mode 100644 index 00000000..9aa8e46a --- /dev/null +++ b/src/messaging/serviceFactory.hpp @@ -0,0 +1,207 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SERVICEFACTORY_HPP_INCLUDED +#define VMIME_MESSAGING_SERVICEFACTORY_HPP_INCLUDED + + +#include <map> + +#include "../types.hpp" +#include "../base.hpp" +#include "../utility/singleton.hpp" + +#include "serviceInfos.hpp" +#include "authenticator.hpp" +#include "progressionListener.hpp" +#include "timeoutHandler.hpp" +#include "url.hpp" + + +namespace vmime { +namespace messaging { + + +class service; +class session; + + +/** A factory to create 'service' objects for a specified protocol. + */ + +class serviceFactory : public utility::singleton <serviceFactory> +{ + friend class utility::singleton <serviceFactory>; + +protected: + + serviceFactory(); + ~serviceFactory(); + +public: + + class registeredService + { + friend class serviceFactory; + + protected: + + virtual ~registeredService() { } + + public: + + virtual service* create(session& sess, authenticator* auth) = 0; + + virtual const string& name() const = 0; + virtual const serviceInfos& infos() const = 0; + }; + +private: + + template <class S> + class registeredServiceImpl : public registeredService + { + friend class serviceFactory; + + protected: + + registeredServiceImpl(const string& name) + : m_name(name), m_servInfos(S::infosInstance()) + { + } + + public: + + service* create(session& sess, authenticator* auth) + { + return new S(sess, auth); + } + + const serviceInfos& infos() const + { + return (m_servInfos); + } + + const string& name() const + { + return (m_name); + } + + private: + + const string m_name; + const serviceInfos& m_servInfos; + }; + + typedef std::map <string, registeredService*> ProtoMap; + ProtoMap m_protoMap; + +public: + + template <class S> + void registerName(const string& protocol) + { + const string name = vmime::toLower(protocol); + m_protoMap.insert(ProtoMap::value_type(name, + new registeredServiceImpl <S>(name))); + } + + service* create(session& sess, const string& protocol, authenticator* auth = NULL); + service* create(session& sess, const url& u, authenticator* auth = NULL); + + const registeredService& operator[](const string& protocol) const; + + + class iterator; + + class const_iterator + { + friend class serviceFactory; + + public: + + const_iterator() { } + const_iterator(const const_iterator& it) : m_it(it.m_it) { } + const_iterator(const iterator& it) : m_it(it.m_it) { } + + const_iterator& operator=(const const_iterator& it) { m_it = it.m_it; return (*this); } + + const registeredService& operator*() const { return (*(*m_it).second); } + const registeredService* operator->() const { return ((*m_it).second); } + + const_iterator& operator++() { ++m_it; return (*this); } + const_iterator operator++(int) { return (m_it++); } + + const_iterator& operator--() { --m_it; return (*this); } + const_iterator operator--(int) { return (m_it--); } + + const bool operator==(const const_iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const const_iterator& it) const { return (m_it != it.m_it); } + + private: + + const_iterator(const ProtoMap::const_iterator it) : m_it(it) { } + + ProtoMap::const_iterator m_it; + }; + + class iterator + { + friend class serviceFactory; + friend class serviceFactory::const_iterator; + + public: + + iterator() { } + iterator(const iterator& it) : m_it(it.m_it) { } + + iterator& operator=(const iterator& it) { m_it = it.m_it; return (*this); } + + registeredService& operator*() const { return (*(*m_it).second); } + registeredService* operator->() const { return ((*m_it).second); } + + iterator& operator++() { ++m_it; return (*this); } + iterator operator++(int) { return (m_it++); } + + iterator& operator--() { --m_it; return (*this); } + iterator operator--(int) { return (m_it--); } + + const bool operator==(const iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const iterator& it) const { return (m_it != it.m_it); } + + private: + + iterator(const ProtoMap::iterator it) : m_it(it) { } + + ProtoMap::iterator m_it; + }; + + iterator begin() { return iterator(m_protoMap.begin()); } + iterator end() { return iterator(m_protoMap.end()); } + + const_iterator begin() const { return const_iterator(m_protoMap.begin()); } + const_iterator end() const { return const_iterator(m_protoMap.end()); } +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SERVICEFACTORY_HPP_INCLUDED diff --git a/src/messaging/serviceInfos.hpp b/src/messaging/serviceInfos.hpp new file mode 100644 index 00000000..9da5ab26 --- /dev/null +++ b/src/messaging/serviceInfos.hpp @@ -0,0 +1,75 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SERVICEINFOS_HPP_INCLUDED +#define VMIME_MESSAGING_SERVICEINFOS_HPP_INCLUDED + + +#include <vector> + +#include "../types.hpp" + + +namespace vmime { +namespace messaging { + + +class serviceInfos +{ + friend class serviceFactory; + +protected: + + serviceInfos() { } + serviceInfos(const serviceInfos&) { } + +private: + + serviceInfos& operator=(const serviceInfos&) { return (*this); } + +public: + + virtual ~serviceInfos() { } + + /** Return the default port used for the underlying protocol. + * + * @return default port number + */ + virtual const port_t defaultPort() const = 0; + + /** Return the property prefix used by this service. + * Use this to set/get properties in the session object. + * + * @return property prefix + */ + virtual const string propertyPrefix() const = 0; + + /** Return a list of available properties for this service. + * + * @return list of property names + */ + virtual const std::vector <string> availableProperties() const = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SERVICEINFOS_HPP_INCLUDED diff --git a/src/messaging/session.cpp b/src/messaging/session.cpp new file mode 100644 index 00000000..be1d8206 --- /dev/null +++ b/src/messaging/session.cpp @@ -0,0 +1,88 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "session.hpp" +#include "serviceFactory.hpp" + +#include "store.hpp" +#include "transport.hpp" + + +namespace vmime { +namespace messaging { + + +session::session() +{ +} + + +session::session(const propertySet& props) + : m_props(props) +{ +} + + +session::~session() +{ +} + + +transport* session::getTransport(authenticator* auth) +{ + return (getTransport(m_props["transport.protocol"], auth)); +} + + +transport* session::getTransport(const string& protocol, authenticator* auth) +{ + service* sv = serviceFactory::getInstance()->create(*this, protocol, auth); + + if (sv->type() != service::TYPE_TRANSPORT) + { + delete (sv); + throw exceptions::no_service_available(); + } + + return static_cast<transport*>(sv); +} + + +store* session::getStore(authenticator* auth) +{ + return (getStore(m_props["store.protocol"], auth)); +} + + +store* session::getStore(const string& protocol, authenticator* auth) +{ + service* sv = serviceFactory::getInstance()->create(*this, protocol, auth); + + if (sv->type() != service::TYPE_STORE) + { + delete (sv); + throw exceptions::no_service_available(); + } + + return static_cast<store*>(sv); +} + + +} // messaging +} // vmime diff --git a/src/messaging/session.hpp b/src/messaging/session.hpp new file mode 100644 index 00000000..43bbe0b1 --- /dev/null +++ b/src/messaging/session.hpp @@ -0,0 +1,113 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SESSION_HPP_INCLUDED +#define VMIME_MESSAGING_SESSION_HPP_INCLUDED + + +#include "authenticator.hpp" +#include "progressionListener.hpp" + +#include "../propertySet.hpp" + + +namespace vmime { +namespace messaging { + + +class store; +class transport; + + +/** An object that contains all the information needed + * for connection to a service. + */ + +class session +{ +public: + + session(); + session(const propertySet& props); + + virtual ~session(); + + /** Return a transport service instance for the protocol specified + * in the session properties. + * + * The property "transport.protocol" specify the protocol to use. + * + * @param auth authenticator object to use for the new transport service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new transport service + */ + transport* getTransport(authenticator* auth = NULL); + + /** Return a transport service instance for the specified protocol. + * + * @param protocol transport protocol to use (eg. "smtp") + * @param auth authenticator object to use for the new transport service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new transport service + */ + transport* getTransport(const string& protocol, authenticator* auth = NULL); + + /** Return a transport service instance for the protocol specified + * in the session properties. + * + * The property "store.protocol" specify the protocol to use. + * + * @param auth authenticator object to use for the new store service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new transport service + */ + store* getStore(authenticator* auth = NULL); + + /** Return a store service instance for the specified protocol. + * + * @param protocol store protocol to use (eg. "imap") + * @param auth authenticator object to use for the new store service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new transport service + */ + store* getStore(const string& protocol, authenticator* auth = NULL); + + /** Properties for the session and for the services. + */ + const propertySet& properties() const { return (m_props); } + + /** Properties for the session and for the services. + */ + propertySet& properties() { return (m_props); } + +private: + + propertySet m_props; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SESSION_HPP_INCLUDED diff --git a/src/messaging/simpleAuthenticator.cpp b/src/messaging/simpleAuthenticator.cpp new file mode 100644 index 00000000..557efd63 --- /dev/null +++ b/src/messaging/simpleAuthenticator.cpp @@ -0,0 +1,45 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "simpleAuthenticator.hpp" + + +namespace vmime { +namespace messaging { + + +simpleAuthenticator::simpleAuthenticator() +{ +} + + +simpleAuthenticator::simpleAuthenticator(const string& username, const string& password) + : m_username(username), m_password(password) +{ +} + + +const authenticationInfos simpleAuthenticator::getAuthInfos() const +{ + return (authenticationInfos(m_username, m_password)); +} + + +} // messaging +} // vmime diff --git a/src/messaging/simpleAuthenticator.hpp b/src/messaging/simpleAuthenticator.hpp new file mode 100644 index 00000000..9d19f4c6 --- /dev/null +++ b/src/messaging/simpleAuthenticator.hpp @@ -0,0 +1,59 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SIMPLEAUTHENTICATOR_HPP_INCLUDED +#define VMIME_MESSAGING_SIMPLEAUTHENTICATOR_HPP_INCLUDED + + +#include "authenticator.hpp" + + +namespace vmime { +namespace messaging { + + +class simpleAuthenticator : public authenticator +{ +public: + + simpleAuthenticator(); + simpleAuthenticator(const string& username, const string& password); + +public: + + const string& username() const { return (m_username); } + string& username() { return (m_username); } + + const string& password() const { return (m_password); } + string& password() { return (m_password); } + +private: + + string m_username; + string m_password; + + const authenticationInfos getAuthInfos() const; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SIMPLEAUTHENTICATOR_HPP_INCLUDED diff --git a/src/messaging/socket.hpp b/src/messaging/socket.hpp new file mode 100644 index 00000000..4cf69536 --- /dev/null +++ b/src/messaging/socket.hpp @@ -0,0 +1,98 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SOCKET_HPP_INCLUDED +#define VMIME_MESSAGING_SOCKET_HPP_INCLUDED + + +#include "../base.hpp" + + +namespace vmime { +namespace messaging { + + +class socket +{ +public: + + virtual ~socket() { } + + /** Connect to the specified address and port. + * + * @param address server address (this can be a full qualified domain name + * or an IP address, doesn't matter) + * @param port server port + */ + virtual void connect(const string& address, const port_t port) = 0; + + /** Disconnect from the server. + */ + virtual void disconnect() = 0; + + /** Test whether this socket is connected. + * + * @return true if the socket is connected, false otherwise + */ + virtual const bool isConnected() const = 0; + + /** Receive (text) data from the socket. + * + * @param buffer buffer in which to write received data + */ + virtual void receive(string& buffer) = 0; + + /** Receive (raw) data from the socket. + * + * @param buffer buffer in which to write received data + * @param count maximum number of bytes to receive (size of buffer) + * @return number of bytes received/written into output buffer + */ + virtual const int receiveRaw(char* buffer, const int count) = 0; + + /** Send (text) data to the socket. + * + * @param buffer data to send + */ + virtual void send(const string& buffer) = 0; + + /** Send (raw) data to the socket. + * + * @param buffer data to send + * @param count number of bytes to send (size of buffer) + */ + virtual void sendRaw(const char* buffer, const int count) = 0; +}; + + +class socketFactory +{ +public: + + virtual ~socketFactory() { } + + virtual socket* create() = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SOCKET_HPP_INCLUDED diff --git a/src/messaging/store.hpp b/src/messaging/store.hpp new file mode 100644 index 00000000..687975d3 --- /dev/null +++ b/src/messaging/store.hpp @@ -0,0 +1,75 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_STORE_HPP_INCLUDED +#define VMIME_MESSAGING_STORE_HPP_INCLUDED + + +#include "service.hpp" +#include "folder.hpp" + + +namespace vmime { +namespace messaging { + + +/** A store service. + * Encapsulate protocols that provide access to user's mail drop. + */ + +class store : public service +{ +protected: + + store(class session& sess, const serviceInfos& infos, class authenticator* auth) + : service(sess, infos, auth) { } + +public: + + /** Return the default folder. This is protocol dependant + * and usually is the INBOX folder. + * + * @return default folder + */ + virtual folder* getDefaultFolder() = 0; + + /** Return the root folder. This is protocol dependant + * and usually is the user's mail drop root folder + * + * @return root folder + */ + virtual folder* getRootFolder() = 0; + + /** Return the folder specified by the path. + * + * @param path absolute folder path + * @return folder at the specified path + */ + virtual folder* getFolder(const folder::path& path) = 0; + + + const Type type() const { return (TYPE_STORE); } +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_STORE_HPP_INCLUDED diff --git a/src/messaging/timeoutHandler.hpp b/src/messaging/timeoutHandler.hpp new file mode 100644 index 00000000..a7675b79 --- /dev/null +++ b/src/messaging/timeoutHandler.hpp @@ -0,0 +1,71 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_TIMEOUTHANDLER_HPP_INCLUDED +#define VMIME_MESSAGING_TIMEOUTHANDLER_HPP_INCLUDED + + +namespace vmime { +namespace messaging { + + +/** A class to manage time-out in messaging services. + */ + +class timeoutHandler +{ +public: + + virtual ~timeoutHandler() { } + + /** Called to test if the time limit has been reached. + * + * @return true if the time-out delay is elapsed + */ + virtual const bool isTimeOut() = 0; + + /** Called to reset the time-out counter. + */ + virtual void resetTimeOut() = 0; + + /** Called when the time limit has been reached (when + * isTimeOut() returned true). + * + * @return true to continue (and reset the time-out) + * or false to cancel the current operation + */ + virtual const bool handleTimeOut() = 0; +}; + + +class timeoutHandlerFactory +{ +public: + + virtual ~timeoutHandlerFactory() { } + + virtual timeoutHandler* create() = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_TIMEOUTHANDLER_HPP_INCLUDED diff --git a/src/messaging/transport.hpp b/src/messaging/transport.hpp new file mode 100644 index 00000000..588eb24b --- /dev/null +++ b/src/messaging/transport.hpp @@ -0,0 +1,76 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_TRANSPORT_HPP_INCLUDED +#define VMIME_MESSAGING_TRANSPORT_HPP_INCLUDED + + +#include "service.hpp" +#include "../utility/stream.hpp" + + +namespace vmime { + +class message; +class mailbox; +class mailboxList; + +namespace messaging { + + +/** A transport service. + * Encapsulate protocols that can send messages. + */ + +class transport : public service +{ +protected: + + transport(class session& sess, const serviceInfos& infos, class authenticator* auth) + : service(sess, infos, auth) { } + +public: + + /** Send a message over this transport service. + * + * @param msg message to send + * @param progress progression listener, or NULL if not used + */ + virtual void send(vmime::message* msg, progressionListener* progress = NULL) = 0; + + /** Send a message over this transport service. + * + * @param expeditor expeditor mailbox + * @param recipients list of recipient mailboxes + * @param is input stream provding message data (header + body) + * @param size size of the message data + * @param progress progression listener, or NULL if not used + */ + virtual void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, progressionListener* progress = NULL) = 0; + + + const Type type() const { return (TYPE_TRANSPORT); } +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_TRANSPORT_HPP_INCLUDED diff --git a/src/messaging/url.cpp b/src/messaging/url.cpp new file mode 100644 index 00000000..cd38c656 --- /dev/null +++ b/src/messaging/url.cpp @@ -0,0 +1,226 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "url.hpp" + +#include "parserHelpers.hpp" +#include "urlUtils.hpp" +#include "exception.hpp" + +#include <sstream> + + +namespace vmime { +namespace messaging { + + +// Known protocols +const string url::PROTOCOL_FILE = "file"; +const string url::PROTOCOL_HTTP = "http"; +const string url::PROTOCOL_FTP = "ftp"; + + + +url::url(const string& s) +{ + parse(s); +} + + +url::url(const url& u) +{ + operator=(u); +} + + +url::url(const string& protocol, const string& host, const port_t port, + const string& path, const string& username, const string& password) + : m_protocol(protocol), m_username(username), m_password(password), + m_host(host), m_port(port), m_path(path) +{ +} + + +url& url::operator=(const url& u) +{ + m_protocol = u.m_protocol; + + m_username = u.m_username; + m_password = u.m_password; + + m_host = u.m_host; + m_port = u.m_port; + + m_path = u.m_path; + + return (*this); +} + + +url& url::operator=(const string& s) +{ + parse(s); + + return (*this); +} + + +url::operator string() const +{ + return build(); +} + + +const string url::build() const +{ + std::ostringstream oss; + + oss << m_protocol << "://"; + + if (!m_username.empty()) + { + oss << urlUtils::encode(m_username); + + if (!m_password.empty()) + { + oss << ":"; + oss << urlUtils::encode(m_password); + } + + oss << "@"; + } + + oss << urlUtils::encode(m_host); + + if (m_port != UNSPECIFIED_PORT) + { + oss << ":"; + oss << m_port; + } + + if (!m_path.empty()) + { + oss << "/"; + oss << urlUtils::encode(m_path); + } + + return (oss.str()); +} + + +void url::parse(const string& str) +{ + // Protocol + const string::size_type protoEnd = str.find("://"); + if (protoEnd == string::npos) throw exceptions::malformed_url("No protocol separator"); + + const string proto = + toLower(string(str.begin(), str.begin() + protoEnd)); + + // Username/password + string::size_type slashPos = str.find('/', protoEnd + 3); + if (slashPos == string::npos) slashPos = str.length(); + + string::size_type atPos = str.find('@', protoEnd + 3); + string hostPart; + + string username; + string password; + + if (atPos != string::npos && atPos < slashPos) + { + const string userPart(str.begin() + protoEnd, str.begin() + atPos); + const string::size_type colonPos = userPart.find(':'); + + if (colonPos == string::npos) + { + username = userPart; + } + else + { + username = string(userPart.begin(), userPart.begin() + colonPos); + password = string(userPart.begin() + colonPos + 1, userPart.end()); + } + + hostPart = string(str.begin() + atPos + 1, str.begin() + slashPos); + } + else + { + hostPart = string(str.begin() + protoEnd, str.begin() + slashPos); + } + + // Host/port + const string::size_type colonPos = hostPart.find(':'); + + string host; + string port; + + if (colonPos == string::npos) + { + host = hostPart; + } + else + { + host = string(hostPart.begin(), hostPart.begin() + colonPos); + port = string(hostPart.begin() + colonPos + 1, hostPart.end()); + } + + // Path + string path(str.begin() + slashPos, str.end()); + + if (path == "/") + path.clear(); + + // Some sanity check + if (proto.empty()) + throw exceptions::malformed_url("No protocol specified"); + else if (host.empty() && path.empty()) // Accept empty host (eg. "file:///home/vincent/mydoc") + throw exceptions::malformed_url("No host specified"); + + bool onlyDigit = true; + + for (string::const_iterator it = port.begin() ; + onlyDigit && it != port.end() ; ++it) + { + onlyDigit = isdigit(*it); + } + + if (!onlyDigit) + throw exceptions::malformed_url("Port can only contain digits"); + + std::istringstream iss(port); + port_t portNum = 0; + + iss >> portNum; + + // Now, save URL parts + m_protocol = proto; + + m_username = urlUtils::decode(username); + m_password = urlUtils::decode(password); + + m_host = urlUtils::decode(host); + m_port = portNum; + + m_path = urlUtils::decode(path); +} + + +} // messaging +} // vmime diff --git a/src/messaging/url.hpp b/src/messaging/url.hpp new file mode 100644 index 00000000..8cef2d16 --- /dev/null +++ b/src/messaging/url.hpp @@ -0,0 +1,125 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_URL_HPP_INCLUDED +#define VMIME_MESSAGING_URL_HPP_INCLUDED + + +#include "../types.hpp" +#include "../base.hpp" + + +namespace vmime { +namespace messaging { + + +/** This class represents a Uniform Resource Locator (a pointer + * to a "resource" on the World Wide Web). + */ + +class url +{ +public: + + /** Means "port not specified" (use default port). + */ + static const port_t UNSPECIFIED_PORT = static_cast <port_t>(-1); + + static const string PROTOCOL_FILE; + static const string PROTOCOL_HTTP; + static const string PROTOCOL_FTP; + + + /** Construct an URL from a string (parse the URL components). + * + * @param s full URL string (eg. http://vmime.sourceforge.net:80/download.html + */ + url(const string& s); + + /** Construct an URL from another URL object. + * + * @param u other URL object + */ + url(const url& u); + + /** Construct an URL from the components. + * + * @param protocol protocol (eg. "http", "ftp"...) + * @param host host name (eg. "vmime.sourceforge.net", "123.45.67.89") + * @param port optional port number (eg. 80, 110 or UNSPECIFIED_PORT to mean "default") + * @param path optional full path (eg. "download.html") + * @param username optional user name + * @param password optional user password + */ + url(const string& protocol, const string& host, const port_t port = UNSPECIFIED_PORT, + const string& path = "", const string& username = "", const string& password = ""); + + + const string& protocol() const { return (m_protocol); } + string& protocol() { return (m_protocol); } + + const string& username() const { return (m_username); } + string& username() { return (m_username); } + + const string& password() const { return (m_password); } + string& password() { return (m_password); } + + const string& host() const { return (m_host); } + string& host() { return (m_host); } + + const port_t port() const { return (m_port); } + port_t& port() { return (m_port); } + + const string& path() const { return (m_path); } + string& path() { return (m_path); } + + + /** Build a string URL from this object. + */ + operator string() const; + + url& operator=(const url& u); + url& operator=(const string& s); + +private: + + const string build() const; + void parse(const string& str); + + // Format: + // "protocol://[username[:password]@]host[:port][/path]" + + string m_protocol; + + string m_username; + string m_password; + + string m_host; + + port_t m_port; + + string m_path; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_URL_HPP_INCLUDED diff --git a/src/messaging/urlUtils.cpp b/src/messaging/urlUtils.cpp new file mode 100644 index 00000000..b8e9cb91 --- /dev/null +++ b/src/messaging/urlUtils.cpp @@ -0,0 +1,93 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "urlUtils.hpp" + + +namespace vmime { +namespace messaging { + + +const string urlUtils::encode(const string& s) +{ + string result; + result.reserve(s.length()); + + for (string::const_iterator it = s.begin() ; it != s.end() ; ++it) + { + const char_t c = *it; + + if (isprint(c) && c != '%') + { + result += c; + } + else + { + char hex[4]; + + hex[0] = '%'; + hex[1] = c / 16; + hex[2] = c % 16; + hex[3] = 0; + + result += hex; + } + } + + return (result); +} + + +const string urlUtils::decode(const string& s) +{ + string result; + result.reserve(s.length()); + + for (string::const_iterator it = s.begin() ; it != s.end() ; ) + { + const char_t c = *it; + + switch (c) + { + case '%': + { + const char_t p = (++it != s.end() ? *it : 0); + const char_t q = (++it != s.end() ? *it : 0); + + result += static_cast <string::value_type>(p * 16 + q); + + if (it != s.end()) + ++it; + + break; + } + default: + + result += c; + ++it; + break; + } + } + + return (result); +} + + +} // messaging +} // vmime diff --git a/src/messaging/urlUtils.hpp b/src/messaging/urlUtils.hpp new file mode 100644 index 00000000..135d508e --- /dev/null +++ b/src/messaging/urlUtils.hpp @@ -0,0 +1,51 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_URLUTILS_HPP_INCLUDED +#define VMIME_MESSAGING_URLUTILS_HPP_INCLUDED + + +#include "../types.hpp" +#include "../base.hpp" + + +namespace vmime { +namespace messaging { + + +class urlUtils +{ +public: + + /** Encode extended characters in a URL string (ASCII characters + * are unmodified, other are encoded as '%' followed by hex code). + */ + static const string encode(const string& s); + + /** Decode an hex-encoded URL (see encode()). + */ + static const string decode(const string& s); +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_URLUTILS_HPP_INCLUDED diff --git a/src/options.cpp b/src/options.cpp new file mode 100644 index 00000000..cd630a1d --- /dev/null +++ b/src/options.cpp @@ -0,0 +1,27 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "options.hpp" + + +namespace vmime +{ + + +} // vmime diff --git a/src/options.hpp b/src/options.hpp new file mode 100644 index 00000000..bfc2f243 --- /dev/null +++ b/src/options.hpp @@ -0,0 +1,98 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_OPTIONS_HPP_INCLUDED +#define VMIME_OPTIONS_HPP_INCLUDED + + +#include "base.hpp" +#include "utility/singleton.hpp" + + +namespace vmime +{ + + +/** A class to set global options for VMime. + */ + +class options : public utility::singleton <options> +{ + friend class utility::singleton <options>; + +protected: + + /** Message-related options. + */ + class messageOptions + { + protected: + + friend class options; + + messageOptions() + : m_maxLineLength(lineLengthLimits::convenient) + { + } + + string::size_type m_maxLineLength; + + public: + + const string::size_type& maxLineLength() const { return (m_maxLineLength); } + string::size_type& maxLineLength() { return (m_maxLineLength); } + }; + + /** Multipart-related options. + */ + class multipartOptions + { + protected: + + friend class options; + + multipartOptions() + : m_prologText("This is a multi-part message in MIME format. Your mail reader does not understand MIME message format."), + m_epilogText("") + { + } + + string m_prologText; + string m_epilogText; + + public: + + const string& prologText() const { return (m_prologText); } + string& prologText() { return (m_prologText); } + + const string& epilogText() const { return (m_epilogText); } + string& epilogText() { return (m_epilogText); } + }; + +public: + + multipartOptions multipart; + messageOptions message; +}; + + +} // vmime + + +#endif // VMIME_OPTIONS_HPP_INCLUDED diff --git a/src/parameter.cpp b/src/parameter.cpp new file mode 100644 index 00000000..98111cb0 --- /dev/null +++ b/src/parameter.cpp @@ -0,0 +1,43 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "parameter.hpp" +#include "parameterFactory.hpp" + + +namespace vmime +{ + + +parameter* parameter::clone() const +{ + parameter* p = parameterFactory::getInstance()->create(m_name); + p->copyFrom(*this); + + return (p); +} + + +void parameter::copyFrom(const parameter& param) +{ + m_name = param.m_name; +} + + +} // vmime diff --git a/src/parameter.hpp b/src/parameter.hpp new file mode 100644 index 00000000..57f130ba --- /dev/null +++ b/src/parameter.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PARAMETER_HPP_INCLUDED +#define VMIME_PARAMETER_HPP_INCLUDED + + +#include "component.hpp" + + +namespace vmime +{ + + +class parameter : public component +{ + friend class parameterFactory; // for "parse()" + +public: + + parameter* clone() const; + +protected: + + string m_name; + +public: + + virtual void copyFrom(const parameter& param); + + const string& name() const { return (m_name); } + string& name() { return (m_name); } +}; + + +} // vmime + + +#endif // VMIME_PARAMETER_HPP_INCLUDED diff --git a/src/parameterFactory.cpp b/src/parameterFactory.cpp new file mode 100644 index 00000000..e67a8ee3 --- /dev/null +++ b/src/parameterFactory.cpp @@ -0,0 +1,71 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "parameterFactory.hpp" +#include "exception.hpp" + +#include "textParameter.hpp" +#include "charsetParameter.hpp" +#include "dateParameter.hpp" + + +namespace vmime +{ + + +parameterFactory::parameterFactory() +{ + // Register some default names + registerName <charsetParameter>("charset"); + registerName <dateParameter>("creation-date"); + registerName <dateParameter>("modification-date"); + registerName <dateParameter>("read-date"); +} + + +parameterFactory::~parameterFactory() +{ +} + + +parameter* parameterFactory::create + (const string& name, const string& value) +{ + const string _name = toLower(name); + + NameMap::const_iterator pos = m_nameMap.find(_name); + parameter* param = NULL; + + if (pos != m_nameMap.end()) + { + param = ((*pos).second)(); + } + else + { + param = new textParameter; + } + + param->name() = _name; + if (value != NULL_STRING) param->parse(value); + + return (param); +} + + +} // vmime diff --git a/src/parameterFactory.hpp b/src/parameterFactory.hpp new file mode 100644 index 00000000..8bff7e33 --- /dev/null +++ b/src/parameterFactory.hpp @@ -0,0 +1,73 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PARAMETERFACTORY_HPP_INCLUDED +#define VMIME_PARAMETERFACTORY_HPP_INCLUDED + + +#include "parameter.hpp" +#include "utility/singleton.hpp" + + +namespace vmime +{ + + +class parameterFactory : public utility::singleton <parameterFactory> +{ + friend class utility::singleton <parameterFactory>; + +protected: + + parameterFactory(); + ~parameterFactory(); + + typedef parameter* (*AllocFunc)(void); + typedef std::map <string, AllocFunc> NameMap; + + NameMap m_nameMap; + + template <class TYPE> + class registerer + { + public: + + static parameter* creator() + { + // Allocate a new object + return new TYPE(); + } + }; + +public: + + template <class T> + void registerName(const string& name) + { + m_nameMap.insert(NameMap::value_type(toLower(name), ®isterer<T>::creator)); + } + + parameter* create(const string& name, const string& value = NULL_STRING); +}; + + +} // vmime + + +#endif // VMIME_PARAMETERFACTORY_HPP_INCLUDED diff --git a/src/parameterizedHeaderField.cpp b/src/parameterizedHeaderField.cpp new file mode 100644 index 00000000..ee00cd32 --- /dev/null +++ b/src/parameterizedHeaderField.cpp @@ -0,0 +1,335 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "parameterizedHeaderField.hpp" +#include "parameterFactory.hpp" +#include "text.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +parameterizedHeaderField::parameterizedHeaderField() +{ +} + + +/* + This class handles field contents of the following form: + Field: VALUE; PARAM1="VALUE1"; PARAM2="VALUE2"... + + eg. RFC-1521 + + content := "Content-Type" ":" type "/" subtype *(";" parameter) + + parameter := attribute "=" value + + attribute := token ; case-insensitive + + value := token / quoted-string + + token := 1*<any (ASCII) CHAR except SPACE, CTLs, or tspecials> + + tspecials := "(" / ")" / "<" / ">" / "@" + / "," / ";" / ":" / "\" / <"> + / "/" / "[" / "]" / "?" / "=" + ; Must be in quoted-string, + ; to use within parameter values +*/ + +void parameterizedHeaderField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pstart; + + const string::size_type start = position; + + while (p < pend && *p != ';') ++p; + + parseValue(buffer, start, position + (p - pstart)); + + // If there is one or more parameters following... + if (p < pend) + { + while (*p == ';') + { + // Skip ';' + ++p; + + while (p < pend && isspace(*p)) ++p; + + const string::size_type attrStart = position + (p - pstart); + + while (p < pend && !(*p == ';' || *p == '=')) + ++p; + + if (p >= pend || *p == ';') + { + // Hmmmm... we didn't found an '=' sign. + // This parameter may not be valid so try to advance + // to the next one, if there is one. + while (p < pend && *p != ';') + ++p; + } + else + { + // Extract the attribute name + string::size_type attrEnd = position + (p - pstart); + + while (attrEnd != attrStart && isspace(buffer[attrEnd - 1])) + --attrEnd; + + // Skip '=' + ++p; + + // Skip white-spaces between '=' and the value + while (p < pend && isspace(*p)) ++p; + + // Extract the value + string value; + + // -- this is a quoted-string + if (*p == '"') + { + // Skip '"' + ++p; + + // Extract quoted-string + bool escape = false; + bool stop = false; + + std::ostringstream ss; + string::size_type start = position + (p - pstart); + + for ( ; p < pend && !stop ; ++p) + { + if (escape) + { + escape = false; + start = position + (p - pstart); + } + else + { + switch (*p) + { + case '"': + { + ss << string(buffer.begin() + start, + buffer.begin() + position + (p - pstart)); + + stop = true; + break; + } + case '\\': + { + ss << string(buffer.begin() + start, + buffer.begin() + position + (p - pstart)); + + escape = true; + break; + } + + } + } + } + + if (!stop) + { + ss << string(buffer.begin() + start, + buffer.begin() + position + (p - pstart)); + } + + value = ss.str(); + } + // -- the value is a simple token + else + { + const string::size_type valStart = position + (p - pstart); + + while (p < pend && *p != ';') + ++p; + + string::size_type valEnd = position + (p - pstart); + + while (valEnd != valStart && isspace(buffer[valEnd - 1])) + --valEnd; + + value = string(buffer.begin() + valStart, + buffer.begin() + valEnd); + } + + // Don't allow ill-formed parameters + if (attrStart != attrEnd && value.length()) + { + // Append this parameter to the list + parameters.m_params.push_back(parameterFactory::getInstance()-> + create(string(buffer.begin() + attrStart, + buffer.begin() + attrEnd), value)); + } + + // Skip white-spaces after this parameter + while (p < pend && isspace(*p)) ++p; + } + } + } + + if (newPosition) + *newPosition = end; +} + + +void parameterizedHeaderField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + // Parent header field + headerField::generate(os, maxLineLength, pos, &pos); + + // Value + const string value = generateValue(); + + encodeAndFoldText(os, text(value), maxLineLength - 1, + pos, &pos, encodeAndFoldFlags::none); + + // Parameters + for (std::vector <parameter*>::const_iterator + it = parameters.m_params.begin() ; it != parameters.m_params.end() ; ++it) + { + const parameter& param = **it; + + os << "; "; + pos += 2; + + param.generate(os, maxLineLength, pos, &pos); + } + + if (newLinePos) + *newLinePos = pos; +} + + +void parameterizedHeaderField::copyFrom(const headerField& field) +{ + const parameterizedHeaderField& source = dynamic_cast<const parameterizedHeaderField&>(field); + + parameters.clear(); + + for (std::vector <parameter*>::const_iterator i = source.parameters.m_params.begin() ; + i != source.parameters.m_params.end() ; ++i) + { + parameters.m_params.push_back((*i)->clone()); + } + + headerField::copyFrom(field); +} + + + +////////////////////// +// Params container // +////////////////////// + + +parameterizedHeaderField::paramsContainer::~paramsContainer() +{ + clear(); +} + + +parameter& parameterizedHeaderField::paramsContainer::find(const string& name) const +{ + const string _name = toLower(name); + + std::vector <parameter*>::const_iterator pos = m_params.begin(); + const std::vector <parameter*>::const_iterator end = m_params.end(); + + for ( ; pos != end && (*pos)->name() != _name ; ++pos); + + // No parameter with this name can be found + if (pos == end) + { + throw exceptions::no_such_parameter(name); + } + // Else, return a reference to the existing parameter + else + { + return (**pos); + } +} + + +parameter& parameterizedHeaderField::paramsContainer::get(const string& name) +{ + const string _name = toLower(name); + + std::vector <parameter*>::iterator pos = m_params.begin(); + const std::vector <parameter*>::iterator end = m_params.end(); + + for ( ; pos != end && (*pos)->name() != _name ; ++pos); + + // If no parameter with this name can be found, create a new one + if (pos == end) + { + parameter* param = parameterFactory::getInstance()->create(_name); + m_params.push_back(param); + + // Return a reference to the new parameter + return (*param); + } + // Else, return a reference to the existing parameter + else + { + return (**pos); + } +} + + +// Parameter insertion +void parameterizedHeaderField::paramsContainer::append(const parameter& param) +{ + m_params.push_back(param.clone()); +} + + +void parameterizedHeaderField::paramsContainer::insert(const iterator it, const parameter& param) +{ + m_params.insert(it.m_iterator, param.clone()); +} + + +// Parameter removing +void parameterizedHeaderField::paramsContainer::remove(const iterator it) +{ + delete (*it.m_iterator); + m_params.erase(it.m_iterator); +} + + +void parameterizedHeaderField::paramsContainer::clear() +{ + free_container(m_params); +} + + +} // vmime diff --git a/src/parameterizedHeaderField.hpp b/src/parameterizedHeaderField.hpp new file mode 100644 index 00000000..949aceb4 --- /dev/null +++ b/src/parameterizedHeaderField.hpp @@ -0,0 +1,201 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PARAMETERIZEDHEADERFIELD_HPP_INCLUDED +#define VMIME_PARAMETERIZEDHEADERFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "headerFieldFactory.hpp" +#include "parameter.hpp" +#include "exception.hpp" +#include "parameterFactory.hpp" + + +namespace vmime +{ + + +class parameterizedHeaderField : public headerField +{ + friend class headerFieldFactory::registerer <parameterizedHeaderField>; + +protected: + + parameterizedHeaderField(); + +public: + + void copyFrom(const headerField& field); + + // A sub-class for parameter manipulation + class paramsContainer + { + friend class parameterizedHeaderField; + + protected: + + ~paramsContainer(); + + public: + + // Find the first parameter with the specified name. If no parameter + // is found, an exception is thrown. + parameter& find(const string& name) const; + + // Find the first parameter with the specified name + parameter& get(const string& name); + + // Parameter iterator + class const_iterator; + + class iterator + { + friend class parameterizedHeaderField::paramsContainer::const_iterator; + friend class parameterizedHeaderField::paramsContainer; + + public: + + typedef std::vector <parameter*>::iterator::difference_type difference_type; + + iterator(std::vector <parameter*>::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + parameter& operator*() const { return (**m_iterator); } + parameter* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator operator++(int) { iterator i(*this); ++m_iterator; return (i); } + + iterator& operator--() { --m_iterator; return (*this); } + iterator operator--(int) { iterator i(*this); --m_iterator; return (i); } + + iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + iterator operator-(difference_type x) const { return iterator(m_iterator - x); } + + parameter& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector <parameter*>::iterator m_iterator; + }; + + class const_iterator + { + public: + + typedef std::vector <parameter*>::const_iterator::difference_type difference_type; + + const_iterator(std::vector <parameter*>::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const parameter& operator*() const { return (**m_iterator); } + const parameter* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator operator++(int) { const_iterator i(*this); ++m_iterator; return (i); } + + const_iterator& operator--() { --m_iterator; return (*this); } + const_iterator operator--(int) { const_iterator i(*this); --m_iterator; return (i); } + + const_iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + const_iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + const_iterator operator-(difference_type x) const { return const_iterator(m_iterator - x); } + + const parameter& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector <parameter*>::const_iterator m_iterator; + }; + + public: + + iterator begin() { return (m_params.begin()); } + iterator end() { return (m_params.end()); } + + const_iterator begin() const { return (const_iterator(m_params.begin())); } + const_iterator end() const { return (const_iterator(m_params.end())); } + + // Parameter insertion + void append(const parameter& param); + void insert(const iterator it, const parameter& param); + + // Parameter removing + void remove(const iterator it); + void clear(); + + // Parameter count + const size_t count() const { return (m_params.size()); } + const size_t size() const { return (m_params.size()); } + + parameter& front() { return (*m_params.front()); } + const parameter& front() const { return (*m_params.front()); } + parameter& back() { return (*m_params.back()); } + const parameter& back() const { return (*m_params.back()); } + + protected: + + std::vector <parameter*> m_params; + + } parameters; + + typedef paramsContainer::iterator iterator; + typedef paramsContainer::const_iterator const_iterator; + +protected: + + std::vector <parameter*> m_params; + +protected: + + virtual void parseValue(const string& buffer, const string::size_type position, const string::size_type end) = 0; + virtual const string generateValue() const = 0; + +public: + + using headerField::parse; + using headerField::generate; + + // No need to override these (use "parseValue" and "generateValue" instead). + // For more information, see "defaultParameter.hpp". + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_PARAMETERIZEDHEADERFIELD_HPP_INCLUDED diff --git a/src/parserHelpers.hpp b/src/parserHelpers.hpp new file mode 100644 index 00000000..41a9f13c --- /dev/null +++ b/src/parserHelpers.hpp @@ -0,0 +1,80 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PARSERHELPERS_HPP_INCLUDED +#define VMIME_PARSERHELPERS_HPP_INCLUDED + + +#include "types.hpp" + +#include <algorithm> + + + +namespace vmime +{ + + +inline const bool isspace(const char_t c) +{ + return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); +} + + +inline const bool isdigit(const char_t c) +{ + return (c >= '0' && c <= '9'); +} + + +inline const bool isalpha(const char_t c) +{ + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); +} + + +inline const char_t tolower(const char_t c) +{ + if (c >= 'A' && c <= 'Z') + return ('a' + (c - 'A')); + else + return c; +} + + +// Checks whether a character is in the 7-bit US-ASCII charset + +inline const bool isascii(const char_t c) +{ + return (c <= 127); +} + + +// Checks whether a character has a visual representation + +inline const bool isprint(const char_t c) +{ + return (c >= 0x20 && c <= 0x7E); +} + + +} // vmime + + +#endif // VMIME_PARSERHELPERS_HPP_INCLUDED diff --git a/src/plainTextPart.cpp b/src/plainTextPart.cpp new file mode 100644 index 00000000..141fb271 --- /dev/null +++ b/src/plainTextPart.cpp @@ -0,0 +1,80 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "plainTextPart.hpp" +#include "header.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +const mediaType plainTextPart::type() const +{ + return (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN)); +} + + +const int plainTextPart::getPartCount() const +{ + return (1); +} + + +void plainTextPart::generateIn(bodyPart& /* message */, bodyPart& parent) const +{ + // Create a new part + bodyPart* part = new bodyPart(); + parent.body().parts.append(part); + + // Set header fields + part->header().fields.ContentType() = mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN); + part->header().fields.ContentType().charset() = m_charset; + part->header().fields.ContentTransferEncoding() = encoding(encodingTypes::QUOTED_PRINTABLE); + + // Set contents + part->body().contents() = m_text; +} + + +void plainTextPart::parse(const bodyPart& /* message */, + const bodyPart& /* parent */, const bodyPart& textPart) +{ + m_text = textPart.body().contents(); + + try + { + const contentTypeField& ctf = dynamic_cast<contentTypeField&> + (textPart.header().fields.find(headerField::ContentType)); + + m_charset = ctf.charset(); + } + catch (exceptions::no_such_field) + { + // No "Content-type" field. + } + catch (exceptions::no_such_parameter) + { + // No "charset" parameter. + } +} + + +} // vmime diff --git a/src/plainTextPart.hpp b/src/plainTextPart.hpp new file mode 100644 index 00000000..324d2203 --- /dev/null +++ b/src/plainTextPart.hpp @@ -0,0 +1,58 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PLAINTEXTPART_HPP_INCLUDED +#define VMIME_PLAINTEXTPART_HPP_INCLUDED + + +#include "textPart.hpp" + + +namespace vmime +{ + + +class plainTextPart : public textPart +{ +public: + + const mediaType type() const; + + const class charset& charset() const { return (m_charset); } + class charset& charset() { return (m_charset); } + + const contentHandler& text() const { return (m_text); } + contentHandler& text() { return (m_text); } + +protected: + + contentHandler m_text; + class charset m_charset; + + const int getPartCount() const; + + void generateIn(bodyPart& message, bodyPart& parent) const; + void parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart); +}; + + +} // vmime + + +#endif // VMIME_PLAINTEXTPART_HPP_INCLUDED diff --git a/src/platformDependant.cpp b/src/platformDependant.cpp new file mode 100644 index 00000000..cf1ed8c1 --- /dev/null +++ b/src/platformDependant.cpp @@ -0,0 +1,35 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "platformDependant.hpp" + + +namespace vmime +{ + + +platformDependant::handler* platformDependant::sm_handler = NULL; + + +platformDependant::handler::~handler() +{ +} + + +} // vmime diff --git a/src/platformDependant.hpp b/src/platformDependant.hpp new file mode 100644 index 00000000..920e970b --- /dev/null +++ b/src/platformDependant.hpp @@ -0,0 +1,155 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PLATFORMDEPENDANT_HPP_INCLUDED +#define VMIME_PLATFORMDEPENDANT_HPP_INCLUDED + + +#include "config.hpp" +#include "dateTime.hpp" +#include "exception.hpp" +#include "charset.hpp" + +#if VMIME_HAVE_MESSAGING_FEATURES + #include "messaging/socket.hpp" + #include "messaging/timeoutHandler.hpp" +#endif + +#if VMIME_HAVE_FILESYSTEM_FEATURES + #include "utility/file.hpp" +#endif + + +namespace vmime +{ + + +/** The link between your application and VMime. It offers an interface to + * access platform-dependant objects: sockets, date/time, file system, etc. + */ + +class platformDependant +{ +public: + + class handler + { + public: + + virtual ~handler(); + + /** Return the current UNIX time (Epoch time): the number of + * seconds elapsed since Jan, 1st 1970 00:00. + * + * @return UNIX Epoch time + */ + virtual const unsigned int getUnixTime() const = 0; + + /** Return the current date and time, in the local time zone. + * + * @return current date and time + */ + virtual const datetime getCurrentLocalTime() const = 0; + + /** Return the host name of the system. + * Used when generating message ids. + * + * @return host name + */ + virtual const string getHostName() const = 0; + + /** Return the current process identifier. + * Used when generating random strings (part boundaries or message ids). + * + * @return current process id + */ + virtual const unsigned int getProcessId() const = 0; + + /** Return the charset used on the system. + * + * @return locale charset + */ + virtual const charset getLocaleCharset() const = 0; + + /** This function is called when VMime library is waiting for + * something (for example, it is called when there is no data + * available in a socket). On POSIX-compliant systems, a + * simple call to sched_yield() should suffice. + */ + virtual void wait() const = 0; + +#if VMIME_HAVE_MESSAGING_FEATURES + /** Return a pointer to a socket factory for the specified socket + * type name (this is user-defined, and used for example when you + * want to set up a SSL connection to a server). + * The returned object will not be deleted by VMime, so it can be + * a pointer to a static object. + * + * @param name socket type name (user-dependant): this is usually + * the value of the property "server.socket-factory" set in the + * session object + * @return socket factory + */ + virtual messaging::socketFactory* getSocketFactory(const string& name = "default") const = 0; + + /** Return a pointer to a timeout-handler factory for the specified name. + * The returned object will not be deleted by VMime, so it can be a + * pointer to a static object. + * + * This is used when you want to handle a timeout-mechanism when + * connecting to messaging servers (please read the documentation to + * learn how to use it). If you are not using time-out handlers, you + * can safely return NULL here. + * + * @param name time-out type name + * @return time-out factory + */ + virtual messaging::timeoutHandlerFactory* getTimeoutHandlerFactory(const string& name = "default") const = 0; +#endif +#if VMIME_HAVE_FILESYSTEM_FEATURES + virtual utility::fileSystemFactory* getFileSystemFactory() const = 0; +#endif + }; + + + template <class TYPE> + static void setHandler() + { + delete (sm_handler); + sm_handler = new TYPE; + } + + static const handler* const getHandler() + { + if (!sm_handler) + throw exceptions::no_platform_dependant_handler(); + + return (sm_handler); + } + +private: + + static handler* sm_handler; +}; + + +} // vmime + + +#endif // VMIME_PLATFORMDEPENDANT_HPP_INCLUDED diff --git a/src/propertySet.cpp b/src/propertySet.cpp new file mode 100644 index 00000000..664a2b43 --- /dev/null +++ b/src/propertySet.cpp @@ -0,0 +1,218 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "propertySet.hpp" + + +namespace vmime +{ + + +propertySet::propertySet() +{ +} + + +propertySet::propertySet(const string& props) +{ + parse(props); +} + + +propertySet::propertySet(const propertySet& set) +{ + for (std::list <property*>::const_iterator it = set.m_props.begin() ; it != set.m_props.end() ; ++it) + m_props.push_back(new property(**it)); +} + + +propertySet::~propertySet() +{ + empty(); +} + + +propertySet& propertySet::operator=(const propertySet& set) +{ + empty(); + + for (std::list <property*>::const_iterator it = set.m_props.begin() ; it != set.m_props.end() ; ++it) + m_props.push_back(new property(**it)); + + return (*this); +} + + +void propertySet::set(const string& props) +{ + parse(props); +} + + +void propertySet::empty() +{ + free_container(m_props); +} + + +void propertySet::clear(const string& name) +{ + std::list <property*>::iterator it = std::find_if + (m_props.begin(), m_props.end(), propFinder(name)); + + if (it != m_props.end()) + { + delete (*it); + m_props.erase(it); + } +} + + +void propertySet::parse(const string& props) +{ + const string::const_iterator end = props.end(); + string::const_iterator pos = props.begin(); + + for ( ; pos != end ; ) + { + // Skip white-spaces + for ( ; pos != end && isspace(*pos) ; ++pos); + + if (pos != end) + { + if (*pos == ';') + { + ++pos; + continue; + } + + // Extract the property name + const string::const_iterator optStart = pos; + + for ( ; pos != end && *pos != '=' ; ++pos); + + string::const_iterator optEnd = pos; + + for ( ; optEnd != optStart && isspace(*(optEnd - 1)) ; --optEnd); + + const string option(optStart, optEnd); + string value = "1"; + + if (pos != end) + { + ++pos; // skip '=' + + // Extract the value + for ( ; pos != end && isspace(*pos) ; ++pos); + + if (pos != end) + { + // A quoted-string + if (*pos == '"' || *pos == '\'') + { + value.reserve(50); + + const std::string::value_type quoteChar = *pos; + bool theEnd = false; + bool escape = false; + + for ( ; (pos != end) && !theEnd ; ++pos) + { + if (escape) + { + value += *pos; + escape = false; + } + else + { + if (*pos == '\\') + escape = true; + else if (*pos == quoteChar) + theEnd = true; + else + value += *pos; + } + } + + if (pos != end) + ++pos; + } + // Simple value + else + { + const string::const_iterator valStart = pos; + + for ( ; pos != end && !isspace(*pos) ; ++pos); + + value = string(valStart, pos); + } + + // Advance to the next ';' + for ( ; pos != end && (*pos != ';') ; ++pos); + + if (pos != end) + ++pos; // skip ';' + } + } + + m_props.push_back(new property(option, value)); + } + } +} + + +template <> +void propertySet::property::set(const string& value) +{ + m_value = value; +} + + +template <> +void propertySet::property::set(const bool& value) +{ + m_value = value ? "true" : "false"; +} + + +template <> +const string propertySet::property::get() const +{ + return (m_value); +} + + +template <> +const bool propertySet::property::get() const +{ + if (toLower(m_value) == "true") + return true; + else + { + int val = 0; + + std::istringstream iss(m_value); + iss >> val; + + return (!iss.fail() && val != 0); + } +} + + +} // vmime diff --git a/src/propertySet.hpp b/src/propertySet.hpp new file mode 100644 index 00000000..eb0ee7d7 --- /dev/null +++ b/src/propertySet.hpp @@ -0,0 +1,340 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PROPERTY_HPP_INCLUDED +#define VMIME_PROPERTY_HPP_INCLUDED + + +#include <list> +#include <functional> +#include <algorithm> +#include <sstream> + +#include "base.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +class propertySet +{ +private: + + class property + { + public: + + property(const string& name, const string& value) : m_name(name), m_value(value) { } + property(const string& name) : m_name(name) { } + property(const property& prop) : m_name(prop.m_name), m_value(prop.m_value) { } + + const string& name() const { return (m_name); } + const string& value() const { return (m_value); } + + template <class TYPE> void set(const TYPE& value); + template <class TYPE> const TYPE get() const; + + private: + + string m_name; + string m_value; + }; + + + class propertyProxy + { + public: + + propertyProxy(const string& name, propertySet* set) + : m_name(name), m_set(set) + { + } + + template <class TYPE> + propertyProxy& operator=(const TYPE& value) + { + m_set->set(m_name, value); + return (*this); + } + + template <class TYPE> + void set(const TYPE& value) + { + m_set->set(m_name, value); + } + + template <class TYPE> + const TYPE get() const + { + return (m_set->get <TYPE>(m_name)); + } + + operator string() const + { + return (m_set->get <string>(m_name)); + } + + private: + + const string m_name; + propertySet* m_set; + }; + + class constPropertyProxy + { + public: + + constPropertyProxy(const string& name, const propertySet* set) + : m_name(name), m_set(set) + { + } + + template <class TYPE> + const TYPE get() const + { + return (m_set->get <TYPE>(m_name)); + } + + operator string() const + { + return (m_set->get <string>(m_name)); + } + + private: + + const string m_name; + const propertySet* m_set; + }; + +public: + + propertySet(); + propertySet(const string& props); + propertySet(const propertySet& set); + + ~propertySet(); + + propertySet& operator=(const propertySet& set); + + void set(const string& props); + + void empty(); + + void clear(const string& name); + + + const bool exists(const string& name) const + { + return (find(name) != NULL); + } + + template <class TYPE> + const TYPE get(const string& name) const + { + const property* const prop = find(name); + if (!prop) throw exceptions::no_such_property(name); + + return (prop->get <TYPE>()); + } + + template <class TYPE> + const TYPE get(const string& name, const TYPE defaultValue) const + { + const property* const prop = find(name); + return (prop ? prop->get <TYPE>() : defaultValue); + } + + template <class TYPE> + void set(const string& name, const TYPE& value) + { + findOrCreate(name)->set(value); + } + + propertyProxy operator[](const string& name) + { + return (propertyProxy(name, this)); + } + + const constPropertyProxy operator[](const string& name) const + { + return (constPropertyProxy(name, this)); + } + +private: + + void parse(const string& props); + + + class propFinder : public std::unary_function <property*, bool> + { + public: + + propFinder(const string& name) : m_name(toLower(name)) { } + + const bool operator()(property* const p) const + { + return (toLower(p->name()) == m_name); + } + + private: + + const std::string m_name; + }; + + property* find(const string& name) const + { + std::list <property*>::const_iterator it = std::find_if + (m_props.begin(), m_props.end(), propFinder(name)); + + return (it != m_props.end() ? *it : NULL); + } + + property* findOrCreate(const string& name) + { + std::list <property*>::const_iterator it = std::find_if + (m_props.begin(), m_props.end(), propFinder(name)); + + if (it != m_props.end()) + { + return (*it); + } + else + { + property* prop = new property(name, ""); + m_props.push_back(prop); + return (prop); + } + } + + typedef std::list <property*> list_type; + list_type m_props; + +public: + + class iterator; + + class const_iterator + { + friend class propertySet; + + public: + + const_iterator() { } + const_iterator(const const_iterator& it) : m_it(it.m_it) { } + const_iterator(const iterator& it) : m_it(it.m_it) { } + + const_iterator& operator=(const const_iterator& it) { m_it = it.m_it; return (*this); } + + const property& operator*() const { return (**m_it); } + const property* operator->() const { return (*m_it); } + + const_iterator& operator++() { ++m_it; return (*this); } + const_iterator operator++(int) { return (m_it++); } + + const_iterator& operator--() { --m_it; return (*this); } + const_iterator operator--(int) { return (m_it--); } + + const bool operator==(const const_iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const const_iterator& it) const { return (m_it != it.m_it); } + + private: + + const_iterator(const list_type::const_iterator it) : m_it(it) { } + + list_type::const_iterator m_it; + }; + + class iterator + { + friend class propertySet; + friend class propertySet::const_iterator; + + public: + + iterator() { } + iterator(const iterator& it) : m_it(it.m_it) { } + + iterator& operator=(const iterator& it) { m_it = it.m_it; return (*this); } + + property& operator*() const { return (**m_it); } + property* operator->() const { return (*m_it); } + + iterator& operator++() { ++m_it; return (*this); } + iterator operator++(int) { return (m_it++); } + + iterator& operator--() { --m_it; return (*this); } + iterator operator--(int) { return (m_it--); } + + const bool operator==(const iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const iterator& it) const { return (m_it != it.m_it); } + + private: + + iterator(const list_type::iterator it) : m_it(it) { } + + list_type::iterator m_it; + }; + + iterator begin() { return iterator(m_props.begin()); } + iterator end() { return iterator(m_props.end()); } + + const_iterator begin() const { return const_iterator(m_props.begin()); } + const_iterator end() const { return const_iterator(m_props.end()); } +}; + + + +template <class TYPE> +void propertySet::property::set(const TYPE& value) +{ + std::ostringstream oss; + oss << value; + + m_value = oss.str(); +} + + +template <class TYPE> +const TYPE propertySet::property::get() const +{ + TYPE val = TYPE(); + + std::istringstream iss(m_value); + iss >> val; + + if (iss.fail()) + throw exceptions::invalid_property_type(); + + return (val); +} + + +template <> void propertySet::property::set(const string& value); +template <> void propertySet::property::set(const bool& value); + +template <> const string propertySet::property::get() const; +template <> const bool propertySet::property::get() const; + + +} // vmime + + +#endif // VMIME_PROPERTY_HPP_INCLUDED diff --git a/src/relayField.cpp b/src/relayField.cpp new file mode 100644 index 00000000..b7959547 --- /dev/null +++ b/src/relayField.cpp @@ -0,0 +1,239 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "relayField.hpp" +#include "text.hpp" +#include "parserHelpers.hpp" + +#include <sstream> + + +namespace vmime +{ + + +relayField::relayField() +{ +} + + +/* + + RFC #2822: + + received = "Received" ":" ; one per relay + ["from" domain] ; sending host + ["by" domain] ; receiving host + ["via" atom] ; physical path + *("with" atom) ; link/mail protocol + ["id" msg-id] ; receiver msg id + ["for" addr-spec] ; initial form +*/ + +void relayField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pend - 1; + + // Find the beginning of the date part + while (p >= pstart && *p != ';') + --p; + + if (p >= pstart) + { + // Parse the date/time part + m_date.parse(buffer, position + (p - pstart) + 1, end); + + // Parse the components + std::istringstream iss(string + (buffer.begin() + position, buffer.begin() + position + (p - pstart))); + + string word; + std::vector <string> previous; + + enum Parts + { + Part_None, + Part_From, // The "from" part + Part_By, // The "by" part + Part_Via, // The "via" part + Part_With, // One "with" part + Part_Id, // The "id" part + Part_For, // The "for" part + Part_End + }; + + Parts part = Part_None; + bool cont = true; + bool inComment = false; + + while (cont) + { + Parts newPart = Part_None; + + if (cont = (iss >> word)) + { + // A little hack for handling comments + if (inComment) + { + string::size_type par = word.find(')'); + + if (par != string::npos) + { + previous.push_back(string(word.begin(), word.begin() + par + 1)); + word.erase(word.begin(), word.begin() + par + 1); + inComment = false; + } + } + + bool keyword = false; + + if (!inComment) + { + if (isStringEqualNoCase(word, "from", 4)) + { + newPart = Part_From; + keyword = true; + } + else if (isStringEqualNoCase(word, "by", 2)) + { + newPart = Part_By; + keyword = true; + } + else if (isStringEqualNoCase(word, "via", 2)) + { + newPart = Part_Via; + keyword = true; + } + else if (isStringEqualNoCase(word, "with", 2)) + { + newPart = Part_With; + keyword = true; + } + else if (isStringEqualNoCase(word, "id", 2)) + { + newPart = Part_Id; + keyword = true; + } + else if (isStringEqualNoCase(word, "for", 2)) + { + newPart = Part_For; + keyword = true; + } + } + + if (!keyword) + { + if (word.find('(') != string::npos) + inComment = true; + + previous.push_back(word); + } + } + + if (!cont || newPart != Part_None) + { + if (part != Part_None) + { + std::ostringstream value; + + for (std::vector <string>::const_iterator + it = previous.begin() ; it != previous.end() ; ++it) + { + if (it != previous.begin()) value << " "; + value << *it; + } + + switch (part) + { + case Part_From: m_from = value.str(); break; + case Part_By: m_by = value.str(); break; + case Part_Via: m_via = value.str(); break; + case Part_With: m_with.push_back(value.str()); break; + case Part_Id: m_id = value.str(); break; + case Part_For: m_for = value.str(); break; + default: break; // Should never happen... + } + } + + previous.clear(); + part = newPart; + } + } + } + + if (newPosition) + *newPosition = end; +} + + +void relayField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + std::ostringstream oss; + int count = 0; + + if (m_from.length()) oss << (count++ > 0 ? " " : "") << "from " << m_from; + if (m_by.length()) oss << (count++ > 0 ? " " : "") << "by " << m_by; + if (m_via.length()) oss << (count++ > 0 ? " " : "") << "via " << m_via; + + for (std::vector <string>::const_iterator + it = m_with.begin() ; it != m_with.end() ; ++it) + { + oss << (count++ > 0 ? " " : "") << "with " << *it; + } + + if (m_id.length()) oss << (count++ > 0 ? " " : "") << "id " << m_id; + if (m_for.length()) oss << (count++ > 0 ? " " : "") << "for " << m_for; + + oss << "; " << m_date.generate(); + + string result(oss.str()); + + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + encodeAndFoldText(os, text(result), maxLineLength, + pos, newLinePos, encodeAndFoldFlags::forceNoEncoding); +} + + +void relayField::copyFrom(const headerField& field) +{ + const relayField& source = dynamic_cast<const relayField&>(field); + + m_from = source.m_from; + m_via = source.m_via; + m_by = source.m_by; + m_id = source.m_id; + m_for = source.m_for; + + m_with.resize(source.m_with.size()); + std::copy(source.m_with.begin(), source.m_with.end(), m_with.begin()); + + m_date = source.m_date; + + headerField::copyFrom(field); +} + + +} // vmime diff --git a/src/relayField.hpp b/src/relayField.hpp new file mode 100644 index 00000000..f24be873 --- /dev/null +++ b/src/relayField.hpp @@ -0,0 +1,93 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_RELAYFIELD_HPP_INCLUDED +#define VMIME_RELAYFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "dateTime.hpp" + + +namespace vmime +{ + + +class relayField : public headerField +{ + friend class headerFieldFactory::registerer <relayField>; + +protected: + + relayField(); + +public: + + void copyFrom(const headerField& field); + + const string& from() const { return (m_from); } + string& from() { return (m_from); } + + const string& via() const { return (m_via); } + string& via() { return (m_via); } + + const string& by() const { return (m_by); } + string& by() { return (m_by); } + + const string& id() const { return (m_id); } + string& id() { return (m_id); } + + const string& for_() const { return (m_for); } + string& for_() { return (m_for); } + + const datetime& date() const { return (m_date); } + datetime& date() { return (m_date); } + + const std::vector <string>& with() const { return (m_with); } + std::vector <string>& with() { return (m_with); } + +protected: + + string m_from; + string m_via; + string m_by; + string m_id; + string m_for; + std::vector <string> m_with; + + datetime m_date; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_RELAYFIELD_HPP_INCLUDED diff --git a/src/text.cpp b/src/text.cpp new file mode 100644 index 00000000..db993136 --- /dev/null +++ b/src/text.cpp @@ -0,0 +1,240 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "text.hpp" + + +namespace vmime +{ + + +text::text() +{ +} + + +text::text(const text& t) +{ + operator=(t); +} + + +text::text(const string& t, const charset& ch) +{ + makeWordsFromText(t, ch, *this); +} + + +text::text(const string& t) +{ + makeWordsFromText(t, charset::getLocaleCharset(), *this); +} + + +text::text(const word& w) +{ + append(w); +} + + +text::~text() +{ + clear(); +} + + +#if VMIME_WIDE_CHAR_SUPPORT + +const wstring text::getDecodedText() const +{ + wstring out; + + for (std::vector <word*>::const_iterator i = m_words.begin() ; i != m_words.end() ; ++i) + { + out += (*i)->getDecodedText(); + } + + return (out); +} + +#endif + + +void text::append(const word& w) +{ + m_words.push_back(new word(w)); +} + + +void text::insert(const iterator it, const word& w) +{ + m_words.insert(it.m_iterator, new word(w)); +} + + +void text::clear() +{ + free_container(m_words); + + m_words.clear(); +} + + +void text::remove(const iterator it) +{ + delete (*it.m_iterator); + m_words.erase(it.m_iterator); +} + + +text& text::operator=(const text& t) +{ + clear(); + + for (std::vector <word*>::const_iterator i = t.m_words.begin() ; i != t.m_words.end() ; ++i) + m_words.push_back(new word(**i)); + + return (*this); +} + + +const bool text::operator==(const text& t) const +{ + if (size() == t.size()) + { + bool equal = false; + + std::vector <word*>::const_iterator i = m_words.begin(); + std::vector <word*>::const_iterator j = t.m_words.begin(); + + for ( ; equal && i != m_words.end() ; ++i, ++j) + equal = (*i == *j); + + return (equal); + } + + return (false); +} + + +const bool text::operator!=(const text& t) const +{ + return !(*this == t); +} + + +/** Return the text converted into the specified charset. + * The encoded-words are decoded and then converted in the + * destination charset. + * + * @param dest output charset + * @return text decoded in the specified charset + */ + +const string text::getConvertedText(const charset& dest) const +{ + string out; + + for (std::vector <word*>::const_iterator i = m_words.begin() ; i != m_words.end() ; ++i) + { + out += (*i)->getConvertedText(dest); + } + + return (out); +} + + +/** Check whether the list of encoded-words is empty. + * + * @return true if the list contains no encoded-word, false otherwise + */ + +const bool text::empty() const +{ + return (m_words.size() == 0); +} + + +/** Return the number of encoded-words in the list. + * + * @return number of encoded-words + */ + +const size_t text::count() const +{ + return (m_words.size()); +} + + +/** Return the number of encoded-words in the list. + * + * @return number of encoded-words + */ + +const size_t text::size() const +{ + return (m_words.size()); +} + + +/** Return the first encoded-word of the list. + * + * @return first encoded-word + */ + +word& text::front() +{ + return (*m_words.front()); +} + + +/** Return the first encoded-word of the list. + * + * @return first encoded-word + */ + +const word& text::front() const +{ + return (*m_words.front()); +} + + +/** Return the last encoded-word of the list. + * + * @return last encoded-word + */ + +word& text::back() +{ + return (*m_words.back()); +} + + +/** Return the last encoded-word of the list. + * + * @return last encoded-word + */ + +const word& text::back() const +{ + return (*m_words.back()); +} + + +} // vmime diff --git a/src/text.hpp b/src/text.hpp new file mode 100644 index 00000000..e6339ab9 --- /dev/null +++ b/src/text.hpp @@ -0,0 +1,176 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TEXT_HPP_INCLUDED +#define VMIME_TEXT_HPP_INCLUDED + + +#include "base.hpp" +#include "word.hpp" + + +namespace vmime +{ + + +/** A class representing a list of encoded-words, as defined + * in RFC-2047 (basic type). + */ + +class text +{ +public: + + text(); + text(const text& t); + text(const string& t, const charset& ch); + explicit text(const string& t); + explicit text(const word& w); + ~text(); + +public: + + text& operator=(const text& t); + + const bool operator==(const text& t) const; + const bool operator!=(const text& t) const; + + // Words iterator + class const_iterator; + + class iterator + { + friend class text::const_iterator; + friend class text; + + public: + + typedef std::vector <word*>::iterator::difference_type difference_type; + + iterator(std::vector <word*>::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + word& operator*() const { return (**m_iterator); } + word* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator operator++(int) { iterator i(*this); ++m_iterator; return (i); } + + iterator& operator--() { --m_iterator; return (*this); } + iterator operator--(int) { iterator i(*this); --m_iterator; return (i); } + + iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + iterator operator+(difference_type x) const { return iterator(m_iterator + x); } + iterator operator-(difference_type x) const { return iterator(m_iterator - x); } + + word& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector <word*>::iterator m_iterator; + }; + + class const_iterator + { + public: + + typedef std::vector <word*>::const_iterator::difference_type difference_type; + + const_iterator(std::vector <word*>::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const word& operator*() const { return (**m_iterator); } + const word* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator operator++(int) { const_iterator i(*this); ++m_iterator; return (i); } + + const_iterator& operator--() { --m_iterator; return (*this); } + const_iterator operator--(int) { const_iterator i(*this); --m_iterator; return (i); } + + const_iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + const_iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + const_iterator operator+(difference_type x) const { return const_iterator(m_iterator + x); } + const_iterator operator-(difference_type x) const { return const_iterator(m_iterator - x); } + + const word& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector <word*>::const_iterator m_iterator; + }; + + + iterator begin() { return (m_words.begin()); } + iterator end() { return (m_words.end()); } + + const_iterator begin() const { return (const_iterator(m_words.begin())); } + const_iterator end() const { return (const_iterator(m_words.end())); } + + const word& operator[](const std::vector <word*>::size_type x) const { return (*m_words[x]); } + word& operator[](const std::vector <word*>::size_type x) { return (*m_words[x]); } + + // Word manipulation + void append(const word& w); + void insert(const iterator it, const word& w); + + void clear(); + void remove(const iterator it); + + // Word count + const bool empty() const; + const size_t count() const; + const size_t size() const; + + word& front(); + const word& front() const; + word& back(); + const word& back() const; + + // Decoding +#if VMIME_WIDE_CHAR_SUPPORT + const wstring getDecodedText() const; +#endif + const string getConvertedText(const charset& dest) const; + +protected: + + std::vector <word*> m_words; +}; + + +} // vmime + + +#endif // VMIME_TEXT_HPP_INCLUDED diff --git a/src/textField.cpp b/src/textField.cpp new file mode 100644 index 00000000..0a316af9 --- /dev/null +++ b/src/textField.cpp @@ -0,0 +1,69 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "textField.hpp" + + +namespace vmime +{ + + +textField::textField() +{ +} + + +void textField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + decodeAndUnfoldText(buffer.begin() + position, buffer.begin() + end, m_text); + + if (newPosition) + *newPosition = end; +} + + +void textField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + encodeAndFoldText(os, m_text, maxLineLength, pos, newLinePos, encodeAndFoldFlags::none); +} + + +void textField::copyFrom(const headerField& field) +{ + const textField& source = dynamic_cast<const textField&>(field); + m_text = source.m_text; + + headerField::copyFrom(field); +} + + +textField& textField::operator=(const text& value) +{ + m_text = value; + return (*this); +} + + +} // vmime diff --git a/src/textField.hpp b/src/textField.hpp new file mode 100644 index 00000000..1e672202 --- /dev/null +++ b/src/textField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TEXTFIELD_HPP_INCLUDED +#define VMIME_TEXTFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "text.hpp" + + +namespace vmime +{ + + +class textField : public headerField +{ + friend class headerFieldFactory::registerer <textField>; + +protected: + + textField(); + +public: + + void copyFrom(const headerField& field); + + textField& operator=(const text& value); + + const text& value() const { return (m_text); } + text& value() { return (m_text); } + +protected: + + text m_text; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_TEXTFIELD_HPP_INCLUDED diff --git a/src/textParameter.cpp b/src/textParameter.cpp new file mode 100644 index 00000000..402d8d1f --- /dev/null +++ b/src/textParameter.cpp @@ -0,0 +1,50 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "textParameter.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +void textParameter::parseValue(const string& buffer, const string::size_type position, + const string::size_type end) +{ + m_value = string(buffer.begin() + position, buffer.begin() + end); +} + + +const string textParameter::generateValue() const +{ + return (m_value); +} + + +void textParameter::copyFrom(const parameter& param) +{ + const textParameter& source = dynamic_cast<const textParameter&>(param); + m_value = source.m_value; + + defaultParameter::copyFrom(param); +} + + +} // vmime diff --git a/src/textParameter.hpp b/src/textParameter.hpp new file mode 100644 index 00000000..a3332a84 --- /dev/null +++ b/src/textParameter.hpp @@ -0,0 +1,54 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TEXTPARAMETER_HPP_INCLUDED +#define VMIME_TEXTPARAMETER_HPP_INCLUDED + + +#include "defaultParameter.hpp" + + +namespace vmime +{ + + +class textParameter : public defaultParameter +{ +protected: + + string m_value; + +public: + + void copyFrom(const parameter& param); + + const string& value() const { return (m_value); } + string& value() { return (m_value); } + +protected: + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_TEXTPARAMETER_HPP_INCLUDED diff --git a/src/textPart.hpp b/src/textPart.hpp new file mode 100644 index 00000000..ed07f0b7 --- /dev/null +++ b/src/textPart.hpp @@ -0,0 +1,66 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TEXTPART_HPP_INCLUDED +#define VMIME_TEXTPART_HPP_INCLUDED + + +#include "bodyPart.hpp" + +#include "mediaType.hpp" +#include "charset.hpp" +#include "contentHandler.hpp" + + +namespace vmime +{ + + +class textPart +{ + friend class textPartFactory; + friend class messageBuilder; // for generateIn, getPartCount + friend class messageParser; // for parse + +public: + + virtual ~textPart() { } + + + virtual const mediaType type() const = 0; + + virtual const class charset& charset() const = 0; + virtual class charset& charset() = 0; + + virtual const contentHandler& text() const = 0; + virtual contentHandler& text() = 0; + +protected: + + virtual const int getPartCount() const = 0; + + virtual void generateIn(bodyPart& message, bodyPart& parent) const = 0; + virtual void parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart) = 0; +}; + + +} // vmime + + +#endif // VMIME_TEXTPART_HPP_INCLUDED diff --git a/src/textPartFactory.cpp b/src/textPartFactory.cpp new file mode 100644 index 00000000..c8fc37eb --- /dev/null +++ b/src/textPartFactory.cpp @@ -0,0 +1,60 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "textPartFactory.hpp" +#include "exception.hpp" + + +#include "plainTextPart.hpp" +#include "htmlTextPart.hpp" + + +namespace vmime +{ + + +textPartFactory::textPartFactory() +{ + // Register some default names + registerType <plainTextPart>(mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN)); + registerType <htmlTextPart>(mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML)); +} + + +textPartFactory::~textPartFactory() +{ +} + + +textPart* textPartFactory::create(const mediaType& type) +{ + NameMap::const_iterator pos = m_nameMap.find(type.generate()); + + if (pos != m_nameMap.end()) + { + return ((*pos).second)(); + } + else + { + throw exceptions::no_factory_available(); + } +} + + +} // vmime diff --git a/src/textPartFactory.hpp b/src/textPartFactory.hpp new file mode 100644 index 00000000..15969c93 --- /dev/null +++ b/src/textPartFactory.hpp @@ -0,0 +1,74 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TEXTPARTFACTORY_HPP_INCLUDED +#define VMIME_TEXTPARTFACTORY_HPP_INCLUDED + + +#include "textPart.hpp" +#include "mediaType.hpp" +#include "utility/singleton.hpp" + + +namespace vmime +{ + + +class textPartFactory : public utility::singleton <textPartFactory> +{ + friend class utility::singleton <textPartFactory>; + +protected: + + textPartFactory(); + ~textPartFactory(); + + typedef textPart* (*AllocFunc)(void); + typedef std::map <string, AllocFunc> NameMap; + + NameMap m_nameMap; + + template <class TYPE> + class registerer + { + public: + + static textPart* creator() + { + // Allocate a new object + return new TYPE(); + } + }; + +public: + + template <class T> + void registerType(const mediaType& type) + { + m_nameMap.insert(NameMap::value_type(toLower(type.generate()), ®isterer<T>::creator)); + } + + textPart* create(const mediaType& type); +}; + + +} // vmime + + +#endif // VMIME_TEXTPARTFACTORY_HPP_INCLUDED diff --git a/src/types.hpp b/src/types.hpp new file mode 100644 index 00000000..fa37c6dc --- /dev/null +++ b/src/types.hpp @@ -0,0 +1,43 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TYPES_HPP_INCLUDED +#define VMIME_TYPES_HPP_INCLUDED + + +#include <string> +#include <limits> + +#include "config.hpp" + + +namespace vmime +{ + typedef std::string string; +#if VMIME_WIDE_CHAR_SUPPORT + typedef std::wstring wstring; +#endif + + typedef unsigned short port_t; + + typedef int char_t; +} + + +#endif // VMIME_TYPES_HPP_INCLUDED diff --git a/src/utility/file.hpp b/src/utility/file.hpp new file mode 100644 index 00000000..f4d63bc2 --- /dev/null +++ b/src/utility/file.hpp @@ -0,0 +1,218 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_FILE_HPP_INCLUDED +#define VMIME_UTILITY_FILE_HPP_INCLUDED + + +#include "path.hpp" +#include "../config.hpp" + + +#if VMIME_HAVE_FILESYSTEM_FEATURES + + +namespace vmime { +namespace utility { + + +class file; + + +/** File list iterator (see file::getFiles). + */ + +class fileIterator +{ +public: + + virtual ~fileIterator() { } + + /** Check whether the cursor has reach the end of the list. + * + * @return true if you can call nextElement(), or false + * if no more file is available + */ + virtual const bool hasMoreElements() const = 0; + + /** Return the next file in the list. + * + * @return next file or NULL + */ + virtual file* nextElement() = 0; +}; + + +// TODO: fileWriter + +class fileWriter +{ +public: + + virtual ~fileWriter() { } + + virtual utility::outputStream* getOutputStream() = 0; +}; + + +// TODO: fileReader + +class fileReader +{ +public: + + virtual ~fileReader() { } + + virtual utility::inputStream* getInputStream() = 0; +}; + + +/** Abstract representation of a file or directory. + */ + +class file +{ +public: + + typedef utility::path path; + typedef long length_type; + + + virtual ~file() { } + + + /** Create the file pointed by this file object. + */ + virtual void createFile() = 0; + + /** Create the directory pointed by this file object. + * + * @param createAll if set to true, recursively create all + * parent directories if they do not exist + */ + virtual void createDirectory(const bool createAll = false) = 0; + + /** Test whether this is a file. + * + * @return true if this is a file, false otherwise + */ + virtual const bool isFile() const = 0; + + /** Test whether this is a directory. + * + * @return true if this is a directory, false otherwise + */ + virtual const bool isDirectory() const = 0; + + /** Test whether this file is readible. + * + * @return true if we can read this file, false otherwise + */ + virtual const bool canRead() const = 0; + + /** Test whether this file is writeable. + * + * @return true if we can write to this file, false otherwise + */ + virtual const bool canWrite() const = 0; + + /** Return the length of this file. + * + * @return file size (in bytes) + */ + virtual const length_type length() = 0; + + /** Return the full path of this file/directory. + * + * @return full path of the file + */ + virtual const path& fullPath() const = 0; + + /** Test whether this file/directory exists. + * + * @return true if the file exists, false otherwise + */ + virtual const bool exists() const = 0; + + /** Return the parent directory of this file/directory. + * + * @return parent directory (or NULL if root) + */ + virtual const file* getParent() const = 0; + + /** Rename the file/directory. + * + * @param newName full path of the new file + */ + virtual void rename(const path& newName) = 0; + + /** Deletes this file/directory. + */ + virtual void remove() = 0; + + + // TODO virtual fileWriter* getFileWriter() = 0; + // TODO virtual fileReader* getFileReader() = 0; + + /** Enumerate files contained in this directory. + * + * @return file iterator to enumerate files + * @throw exceptions::not_a_directory if this is not a directory + */ + virtual fileIterator* getFiles() const; +}; + + +class fileSystemFactory +{ +public: + + virtual ~fileSystemFactory() { } + + /** Create a new file object from the specified path. + * + * @param path full path (absolute) of the file + * @return new file object for the path + */ + virtual file* create(const file::path& path) = 0; + + /** Parse a path contained in a string. + * + * @param str string containing a path in a system-dependant representation + * @return path object (abstract representation) + */ + virtual file::path stringToPath(const string& str) = 0; + + /** Return the system-dependant string representation for the specified path. + * + * @param path abstract representation of the path + * @return string representation of the path + */ + virtual string pathToString(const file::path& path) = 0; +}; + + +} // utility +} // vmime + + +#endif // VMIME_HAVE_FILESYSTEM_FEATURES + + +#endif // VMIME_UTILITY_FILE_HPP_INCLUDED diff --git a/src/utility/md5.cpp b/src/utility/md5.cpp new file mode 100644 index 00000000..e181a5d0 --- /dev/null +++ b/src/utility/md5.cpp @@ -0,0 +1,331 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// +// Derived from cryptoapi implementation, originally based on the +// public domain implementation written by Colin Plumb in 1993. +// +// Copyright (C) Cryptoapi developers. +// +// Algorithm Copyright: +// +// Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +// rights reserved. +// +// License to copy and use this software is granted provided that it +// is identified as the "RSA Data Security, Inc. MD5 Message-Digest +// Algorithm" in all material mentioning or referencing this software +// or this function. +// +// License is also granted to make and use derivative works provided +// that such works are identified as "derived from the RSA Data +// Security, Inc. MD5 Message-Digest Algorithm" in all material +// mentioning or referencing the derived work. +// +// RSA Data Security, Inc. makes no representations concerning either +// the merchantability of this software or the suitability of this +// software forany particular purpose. It is provided "as is" +// without express or implied warranty of any kind. +// These notices must be retained in any copies of any part of this +// documentation and/or software. + +#include "md5.hpp" + + +namespace vmime { +namespace utility { + + +md5::md5() + : m_finalized(false) +{ + init(); +} + + +md5::md5(const vmime_uint8* const in, const unsigned long length) + : m_finalized(false) +{ + init(); + update(in, length); +} + + +md5::md5(const string& in) + : m_finalized(false) +{ + init(); + update((vmime_uint8*) in.c_str(), in.length()); +} + + +void md5::init() +{ + m_hash[0] = 0x67452301; + m_hash[1] = 0xefcdab89; + m_hash[2] = 0x98badcfe; + m_hash[3] = 0x10325476; + + m_byteCount = 0; +} + + +static void copyUint8Array(vmime_uint8* dest, const vmime_uint8* src, unsigned long count) +{ + for ( ; count >= 4 ; count -= 4, dest += 4, src += 4) + { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; + } + + for ( ; count ; --count, ++dest, ++src) + dest[0] = src[0]; +} + + +void md5::update(const string& in) +{ + update((vmime_uint8*) in.c_str(), in.length()); +} + + +void md5::update(const vmime_uint8* data, unsigned long len) +{ + if (m_finalized) + return; + + const unsigned long avail = 64 - (m_byteCount & 0x3f); + + m_byteCount += len; + + if (avail > len) + { + copyUint8Array(m_block + (64 - avail), data, len); + return; + } + + copyUint8Array(m_block + (64 - avail), data, avail); + transformHelper(); + + data += avail; + len -= avail; + + while (len >= 64) + { + copyUint8Array(m_block, data, 64); + transformHelper(); + + data += 64; + len -= 64; + } + + copyUint8Array(m_block, data, len); +} + + +void md5::finalize() +{ + const long offset = m_byteCount & 0x3f; + + vmime_uint8* p = m_block + offset; + long padding = 56 - (offset + 1); + + *p++ = 0x80; + + if (padding < 0) + { + memset(p, 0x00, padding + 8); + transformHelper(); + p = m_block; + padding = 56; + } + + memset(p, 0, padding); + + ((vmime_uint32*) m_block)[14] = (m_byteCount << 3); + ((vmime_uint32*) m_block)[15] = (m_byteCount >> 29); + +#if VMIME_BYTE_ORDER_BIG_ENDIAN + swapUint32Array((vmime_uint32*) m_block, (64 - 8) / 4); +#endif + + transform(); + +#if VMIME_BYTE_ORDER_BIG_ENDIAN + swapUint32Array((vmime_uint32*) m_hash, 4); +#endif + + m_finalized = true; +} + + +static inline vmime_uint32 swapUint32(const vmime_uint32 D) +{ + return ((D << 24) | ((D << 8) & 0x00FF0000) | ((D >> 8) & 0x0000FF00) | (D >> 24)); +} + + +static inline void swapUint32Array(vmime_uint32* buf, unsigned long words) +{ + for ( ; words >= 4 ; words -= 4, buf += 4) + { + buf[0] = swapUint32(buf[0]); + buf[1] = swapUint32(buf[1]); + buf[2] = swapUint32(buf[2]); + buf[3] = swapUint32(buf[3]); + } + + for ( ; words ; --words, ++buf) + buf[0] = swapUint32(buf[0]); +} + + +void md5::transformHelper() +{ +#if VMIME_BYTE_ORDER_BIG_ENDIAN + swapUint32Array((vmime_uint32*) m_block, 64 / 4); +#endif + transform(); +} + + +void md5::transform() +{ + const vmime_uint32* const in = (vmime_uint32*) m_block; + + vmime_uint32 a = m_hash[0]; + vmime_uint32 b = m_hash[1]; + vmime_uint32 c = m_hash[2]; + vmime_uint32 d = m_hash[3]; + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define MD5STEP(f, w, x, y, z, in, s) \ + (w += f(x, y, z) + in, w = (w<<s | w>>(32-s)) + x) + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + m_hash[0] += a; + m_hash[1] += b; + m_hash[2] += c; + m_hash[3] += d; +} + + +const string md5::hex() +{ + if (!m_finalized) + finalize(); + + static const unsigned char hex[] = "0123456789abcdef"; + + std::ostringstream oss; + const vmime_uint8* const digest = (vmime_uint8*) m_hash; + + for (int i = 0 ; i < 16 ; ++i) + { + oss << hex[(digest[i] & 0xf0) >> 4]; + oss << hex[(digest[i] & 0x0f)]; + } + + return (oss.str()); +} + + +const vmime_uint8* md5::hash() +{ + if (!m_finalized) + finalize(); + + return ((vmime_uint8*) m_hash); +} + + +} // utility +} // vmime diff --git a/src/utility/md5.hpp b/src/utility/md5.hpp new file mode 100644 index 00000000..dc9bb384 --- /dev/null +++ b/src/utility/md5.hpp @@ -0,0 +1,68 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_MD5_HPP_INCLUDED +#define VMIME_UTILITY_MD5_HPP_INCLUDED + + +#include "base.hpp" +#include "config.hpp" + + +namespace vmime { +namespace utility { + + +class md5 +{ +public: + + md5(); + md5(const vmime_uint8* const in, const unsigned long length); + md5(const string& in); + +public: + + const string hex(); + const vmime_uint8* hash(); + + void update(const vmime_uint8* data, unsigned long len); + void update(const string& in); + +protected: + + void init(); + void transformHelper(); + void transform(); + void finalize(); + + vmime_uint32 m_hash[4]; + + unsigned long m_byteCount; + vmime_uint8 m_block[64]; + + bool m_finalized; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_MD5_HPP_INCLUDED diff --git a/src/utility/path.cpp b/src/utility/path.cpp new file mode 100644 index 00000000..477f29dd --- /dev/null +++ b/src/utility/path.cpp @@ -0,0 +1,196 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "path.hpp" + +#include <algorithm> + + +namespace vmime { +namespace utility { + + +path::path() +{ +} + + +path::path(const component& c) +{ + m_list.push_back(c); +} + + +path::path(const path& p) +{ + m_list.resize(p.m_list.size()); + std::copy(p.m_list.begin(), p.m_list.end(), m_list.begin()); +} + + +path::path(const string& s) +{ + m_list.push_back(component(s)); +} + + +path path::operator/(const path& p) const +{ + path pr(*this); + pr /= p; + + return (pr); +} + + +path path::operator/(const component& c) const +{ + path pr(*this); + pr /= c; + + return (pr); +} + + +path& path::operator/=(const path& p) +{ + const list::size_type size = m_list.size(); + + m_list.resize(size + p.m_list.size()); + std::copy(p.m_list.begin(), p.m_list.end(), m_list.begin() + size); + + return (*this); +} + + +path& path::operator/=(const component& c) +{ + m_list.push_back(c); + return (*this); +} + + +path path::parent() const +{ + path p; + + if (!empty()) + { + p.m_list.resize(m_list.size() - 1); + std::copy(m_list.begin(), m_list.end() - 1, p.m_list.begin()); + } + + return (p); +} + + +path& path::operator=(const path& p) +{ + m_list.resize(p.m_list.size()); + std::copy(p.m_list.begin(), p.m_list.end(), m_list.begin()); + + return (*this); +} + + +path& path::operator=(const component& c) +{ + m_list.resize(1); + m_list[0] = c; + + return (*this); +} + + +const bool path::operator==(const path& p) const +{ + if (m_list.size() != p.m_list.size()) + return (false); + + list::const_iterator i = m_list.begin(); + list::const_iterator j = p.m_list.begin(); + + bool equal = true; + + for ( ; equal && i != m_list.end() ; ++i, ++j) + //equal = (*i == *j); + equal = ((*i).buffer() == (*j).buffer()); + + return (equal); +} + + +const bool path::operator!=(const path& p) const +{ + return (!(*this == p)); +} + + +const bool path::empty() const +{ + return (m_list.empty()); +} + + +const path::component path::last() const +{ + return (empty() ? component("") : m_list[m_list.size() - 1]); +} + + +path::component& path::last() +{ + return (m_list[m_list.size() - 1]); +} + + +const int path::size() const +{ + return (m_list.size()); +} + + +const path::component& path::operator[](const int x) const +{ + return (m_list[x]); +} + + +path::component& path::operator[](const int x) +{ + return (m_list[x]); +} + + +const bool path::isDirectParentOf(const path& p) const +{ + if (p.size() != size() + 1) + return (false); + + bool equal = true; + + for (int i = 0 ; equal && i < size() ; ++i) + equal = (m_list[i] == p.m_list[i]); + + return (equal); +} + + +} // utility +} // vmime diff --git a/src/utility/path.hpp b/src/utility/path.hpp new file mode 100644 index 00000000..bc980889 --- /dev/null +++ b/src/utility/path.hpp @@ -0,0 +1,124 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_PATH_HPP_INCLUDED +#define VMIME_UTILITY_PATH_HPP_INCLUDED + + +#include <vector> + +#include "../types.hpp" +#include "../word.hpp" + + +namespace vmime { +namespace utility { + + +/** Abstract representation of a path (filesystem, mailbox, etc). + */ + +class path +{ +public: + + typedef vmime::word component; + typedef std::vector <component> list; + + // Construct a path + path(); + path(const component& c); + path(const path& p); + path(const string& s); + + // Append a component to a path + path operator/(const path& p) const; + path operator/(const component& c) const; + + path& operator/=(const path& p); + path& operator/=(const component& c); + + // Return the parent path + path parent() const; + + // Assignment + path& operator=(const path& p); + path& operator=(const component& c); + + // Path comparison + const bool operator==(const path& p) const; + const bool operator!=(const path& p) const; + + /** Test whether this path is empty (root). + * + * @return true if the path is empty (no components = root) + */ + const bool empty() const; + + /** Return the last component of this path (const version). + * + * @return last component + */ + const component last() const; + + /** Return the last component of this path (non-const version). + * + * @return last component + */ + component& last(); + + /** Return the number of components in this path. + * + * @return number of components + */ + const int size() const; + + /** Return the specified component of the path (const version). + * + * @param x index of the component + * @return component at the specified index + */ + const component& operator[](const int x) const; + + /** Return the specified component of the path (non-const version). + * + * @param x index of the component + * @return component at the specified index + */ + component& operator[](const int x); + + /** Test whether this path is a direct parent of another one. + * + * @param p other path + * @return true if the specified path is a child (direct or + * indirect) of this path, false otherwise + */ + const bool isDirectParentOf(const path& p) const; + +private: + + list m_list; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_PATH_HPP_INCLUDED diff --git a/src/utility/random.cpp b/src/utility/random.cpp new file mode 100644 index 00000000..6896d83c --- /dev/null +++ b/src/utility/random.cpp @@ -0,0 +1,59 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "random.hpp" +#include "platformDependant.hpp" + +#include <ctime> + + +namespace vmime { +namespace utility { + + +unsigned int random::m_next(static_cast<unsigned int>(::std::time(NULL))); + + +const unsigned int random::next() +{ + // Park and Miller's minimal standard generator: + // xn+1 = (a * xn + b) mod c + // xn+1 = (16807 * xn) mod (2^31 - 1) + static const unsigned long a = 16807; + static const unsigned long c = (1 << ((sizeof(int) << 3) - 1)); + + m_next = static_cast<unsigned int>((a * m_next) % c); + return (m_next); +} + + +const unsigned int random::time() +{ + return (platformDependant::getHandler()->getUnixTime()); +} + + +const unsigned int random::process() +{ + return (platformDependant::getHandler()->getProcessId()); +} + + +} // utility +} // vmime diff --git a/src/utility/random.hpp b/src/utility/random.hpp new file mode 100644 index 00000000..0d9dd92a --- /dev/null +++ b/src/utility/random.hpp @@ -0,0 +1,62 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_RANDOM_HPP_INCLUDED +#define VMIME_UTILITY_RANDOM_HPP_INCLUDED + + +namespace vmime { +namespace utility { + + +class random +{ +public: + + /** Return a new random number. + * + * @return random number + */ + static const unsigned int next(); + + /** Return the current time as a number (may be used to + * build "random" strings). + * + * @return time as a number + */ + static const unsigned int time(); + + /** Return the current process number (may be user to + * build "random" strings). + * + * @return process number + */ + static const unsigned int process(); + +protected: + + static unsigned int m_next; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_RANDOM_HPP_INCLUDED diff --git a/src/utility/singleton.cpp b/src/utility/singleton.cpp new file mode 100644 index 00000000..c960a64a --- /dev/null +++ b/src/utility/singleton.cpp @@ -0,0 +1,53 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "singleton.hpp" + + +namespace vmime { +namespace utility { + + +singletonManager::singletonManager() +{ +} + + +singletonManager::~singletonManager() +{ + for (std::list <abstractSingleton*>::iterator it = m_list.begin() ; it != m_list.end() ; ++it) + delete (*it); +} + + +singletonManager* singletonManager::getInstance() +{ + static singletonManager inst; + return (&inst); +} + + +void singletonManager::manage(abstractSingleton* s) +{ + m_list.push_back(s); +} + + +} // utility +} // vmime diff --git a/src/utility/singleton.hpp b/src/utility/singleton.hpp new file mode 100644 index 00000000..33def75b --- /dev/null +++ b/src/utility/singleton.hpp @@ -0,0 +1,92 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_SINGLETON_HPP_INCLUDED +#define VMIME_UTILITY_SINGLETON_HPP_INCLUDED + + +#include <list> + + +namespace vmime { +namespace utility { + + +// Singleton abstract base class. + +class abstractSingleton +{ + friend class singletonManager; + +protected: + + abstractSingleton() { } + virtual ~abstractSingleton() { } +}; + + +// Singleton manager +// (for automatic clean-up of all instanciated singletons). + +class singletonManager +{ +public: + + static singletonManager* getInstance(); + + void manage(abstractSingleton* s); + +private: + + singletonManager(); + ~singletonManager(); + + std::list <abstractSingleton*> m_list; +}; + + +// A singleton template. + +template <class TYPE> +class singleton : public abstractSingleton +{ +protected: + + singleton() { } + ~singleton() { } + +public: + + static TYPE* getInstance() + { + static TYPE* inst = NULL; + + if (!inst) + singletonManager::getInstance()->manage(inst = new TYPE()); + + return (inst); + } +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_SINGLETON_HPP_INCLUDED diff --git a/src/utility/smartPtr.hpp b/src/utility/smartPtr.hpp new file mode 100644 index 00000000..9905ae2f --- /dev/null +++ b/src/utility/smartPtr.hpp @@ -0,0 +1,166 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_SMARTPTR_HPP_INCLUDED +#define VMIME_UTILITY_SMARTPTR_HPP_INCLUDED + + +namespace vmime { +namespace utility { + + +/** Simple auto-delete pointer. + */ + +template <class T> +class auto_ptr +{ +private: + + T* const m_ptr; + +public: + + auto_ptr(T* const ptr) : m_ptr(ptr) { } + ~auto_ptr() { delete (m_ptr); } + + operator T*() { return (m_ptr); } + + T* const operator ->() { return (m_ptr); } + T& operator *() { return (*m_ptr); } +}; + + +/** Smart auto-delete, referencable and copiable pointer. + */ + +template <class T> +class smart_ptr +{ +private: + + struct data + { + int refCount; + T* ptr; + }; + + data* m_data; + + + typedef std::map <T*, data*> MapType; + static MapType sm_map; + +public: + + smart_ptr() : m_data(NULL) { } + smart_ptr(T* const ptr) : m_data(NULL) { if (ptr) { attach(ptr); } } + smart_ptr(smart_ptr& ptr) : m_data(NULL) { if (ptr.m_data) { attach(ptr); } } + + ~smart_ptr() { detach(); } + + smart_ptr& operator=(smart_ptr& ptr) + { + attach(ptr); + return (*this); + } + + smart_ptr& operator=(T* const ptr) + { + if (!ptr) + detach(); + else + attach(ptr); + + return (*this); + } + + operator T*() { return (m_data ? m_data->ptr : NULL); } + operator const T*() { return (m_data ? m_data->ptr : NULL); } + + T& operator *() { return (*(m_data->ptr)); } + T* operator ->() { return (m_data->ptr); } + + const T* const ptr() const { return (m_data ? m_data->ptr : NULL); } + T* const ptr() { return (m_data ? m_data->ptr : NULL); } + +private: + + void detach() + { + if (m_data) + { + if (m_data->refCount == 1) + { + typename MapType::iterator it = sm_map.find(m_data->ptr); + if (it != sm_map.end()) sm_map.erase(it); + + delete (m_data->ptr); + delete (m_data); + } + else + { + m_data->refCount--; + } + + m_data = NULL; + } + } + + void attach(T* const ptr) + { + detach(); + + typename MapType::iterator it = sm_map.find(ptr); + + if (it != sm_map.end()) + { + (*it).second->refCount++; + } + else + { + m_data = new data; + m_data->refCount = 1; + m_data->ptr = ptr; + + sm_map.insert(typename MapType::value_type(ptr, m_data)); + } + } + + void attach(smart_ptr <T>& ptr) + { + data* newData = ptr.m_data; + if (newData) newData->refCount++; + + detach(); + + m_data = newData; + } +}; + + +template <class T> +typename smart_ptr <T>::MapType smart_ptr <T>::sm_map; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_SMARTPTR_HPP_INCLUDED diff --git a/src/utility/stream.cpp b/src/utility/stream.cpp new file mode 100644 index 00000000..06d4ba27 --- /dev/null +++ b/src/utility/stream.cpp @@ -0,0 +1,257 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "stream.hpp" +#include "stringProxy.hpp" + +#include <algorithm> // for std::copy +#include <iterator> // for std::back_inserter + + +namespace vmime { +namespace utility { + + +// Helpers + +outputStream& operator<<(outputStream& os, const stream::value_type c) +{ + os.write(&c, 1); + return (os); +} + + +outputStream& operator<<(outputStream& os, const string& str) +{ + os.write(str.data(), str.length()); + return (os); +} + + +const stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os) +{ + stream::value_type buffer[65536]; + stream::size_type total = 0; + + while (!is.eof()) + { + const stream::size_type read = is.read(buffer, sizeof(buffer)); + + if (read != 0) + { + os.write(buffer, read); + total += read; + } + } + + return (total); +} + + + +// outputStreamAdapter + +outputStreamAdapter::outputStreamAdapter(std::ostream& os) + : m_stream(os) +{ +} + + +void outputStreamAdapter::write + (const value_type* const data, const size_type count) +{ + m_stream.write(data, count); +} + + + +// outputStreamStringAdapter + +outputStreamStringAdapter::outputStreamStringAdapter(string& buffer) + : m_buffer(buffer) +{ + m_buffer.clear(); +} + + +void outputStreamStringAdapter::write(const value_type* const data, const size_type count) +{ + // TODO: better way? + std::copy(data, data + count, std::back_inserter(m_buffer)); +} + + + +// inputStreamAdapter + +inputStreamAdapter::inputStreamAdapter(std::istream& is) + : m_stream(is) +{ +} + + +const bool inputStreamAdapter::eof() const +{ + return (m_stream.eof()); +} + + +void inputStreamAdapter::reset() +{ + m_stream.seekg(0, std::ios::beg); + m_stream.clear(); +} + + +const stream::size_type inputStreamAdapter::read + (value_type* const data, const size_type count) +{ + m_stream.read(data, count); + return (m_stream.gcount()); +} + + + +// inputStreamStringAdapter + +inputStreamStringAdapter::inputStreamStringAdapter(const string& buffer) + : m_buffer(buffer), m_begin(0), m_end(buffer.length()), m_pos(0) +{ +} + + +inputStreamStringAdapter::inputStreamStringAdapter(const string& buffer, + const string::size_type begin, const string::size_type end) + : m_buffer(buffer), m_begin(begin), m_end(end), m_pos(begin) +{ +} + + +const bool inputStreamStringAdapter::eof() const +{ + return (m_pos >= m_end); +} + + +void inputStreamStringAdapter::reset() +{ + m_pos = m_begin; +} + + +const stream::size_type inputStreamStringAdapter::read + (value_type* const data, const size_type count) +{ + if (m_pos + count >= m_end) + { + const size_type remaining = m_end - m_pos; + + std::copy(m_buffer.begin() + m_pos, m_buffer.end(), data); + m_pos = m_end; + return (remaining); + } + else + { + std::copy(m_buffer.begin() + m_pos, m_buffer.begin() + m_pos + count, data); + m_pos += count; + return (count); + } +} + + + +// inputStreamStringProxyAdapter + +inputStreamStringProxyAdapter::inputStreamStringProxyAdapter(const stringProxy& buffer) + : m_buffer(buffer), m_pos(0) +{ +} + + +const bool inputStreamStringProxyAdapter::eof() const +{ + return (m_pos >= m_buffer.length()); +} + + +void inputStreamStringProxyAdapter::reset() +{ + m_pos = 0; +} + + +const stream::size_type inputStreamStringProxyAdapter::read + (value_type* const data, const size_type count) +{ + const size_type remaining = m_buffer.length() - m_pos; + + if (count > remaining) + { + std::copy(m_buffer.it_begin() + m_pos, m_buffer.it_end(), data); + m_pos = m_buffer.length(); + return (remaining); + } + else + { + std::copy(m_buffer.it_begin() + m_pos, m_buffer.it_begin() + m_pos + count, data); + m_pos += count; + return (count); + } +} + + + +// inputStreamPointerAdapter + +inputStreamPointerAdapter::inputStreamPointerAdapter(std::istream* is, const bool own) + : m_stream(is), m_own(own) +{ +} + + +inputStreamPointerAdapter::~inputStreamPointerAdapter() +{ + if (m_own) + delete (m_stream); +} + + +const bool inputStreamPointerAdapter::eof() const +{ + return (m_stream->eof()); +} + + +void inputStreamPointerAdapter::reset() +{ + m_stream->seekg(0, std::ios::beg); + m_stream->clear(); +} + + +const stream::size_type inputStreamPointerAdapter::read + (value_type* const data, const size_type count) +{ + m_stream->read(data, count); + return (m_stream->gcount()); +} + + +} // utility +} // vmime diff --git a/src/utility/stream.hpp b/src/utility/stream.hpp new file mode 100644 index 00000000..8f8de54c --- /dev/null +++ b/src/utility/stream.hpp @@ -0,0 +1,263 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_STREAM_HPP_INCLUDED +#define VMIME_UTILITY_STREAM_HPP_INCLUDED + + +#include <istream> +#include <ostream> + +#include "../types.hpp" + + +namespace vmime { +namespace utility { + + +class stringProxy; + + +/** Base class for input/output stream. + */ + +class stream +{ +public: + + virtual ~stream() { } + + /** Type used to read/write one byte in the stream. + */ + typedef string::value_type value_type; + + /** Type used for lengths in streams. + */ + typedef string::size_type size_type; +}; + + + +/** Simple output stream. + */ + +class outputStream : public stream +{ +public: + + /** Write data to the stream. + * + * @param data buffer containing data to write + * @param count number of bytes to write + */ + virtual void write(const value_type* const data, const size_type count) = 0; +}; + + + +/** Simple input stream. + */ + +class inputStream : public stream +{ +public: + + /** Test for end of stream (no more data to read). + * + * @return true if we have reached the end of stream, false otherwise + */ + virtual const bool eof() const = 0; + + /** Set the read pointer to the beginning of the stream. + * + * @warning WARNING: this may not work for all stream types. + */ + virtual void reset() = 0; + + /** Read data from the stream. + * + * @param data will receive the data read + * @param count maximum number of bytes to read + * @return number of bytes read + */ + virtual const size_type read(value_type* const data, const size_type count) = 0; +}; + + + +// Helpers functions + +outputStream& operator<<(outputStream& os, const string& str); +outputStream& operator<<(outputStream& os, const stream::value_type c); + + +template <int N> +outputStream& operator<<(outputStream& os, const char (&str)[N]) +{ + os.write(str, N - 1); + return (os); +} + + +/** Copy data from one stream into another stream using a buffered method. + * + * @param is input stream (source data) + * @param os output stream (destination for data) + * @return number of bytes copied + */ + +const stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os); + + + +// Adapters + + +/** An adapter class for C++ standard output streams. + */ + +class outputStreamAdapter : public outputStream +{ +public: + + /** @param os output stream to wrap + */ + outputStreamAdapter(std::ostream& os); + + void write(const value_type* const data, const size_type count); + +private: + + std::ostream& m_stream; +}; + + +/** An adapter class for string output. + */ + +class outputStreamStringAdapter : public outputStream +{ +public: + + outputStreamStringAdapter(string& buffer); + + void write(const value_type* const data, const size_type count); + +private: + + string& m_buffer; +}; + + +/** An adapter class for C++ standard input streams. + */ + +class inputStreamAdapter : public inputStream +{ +public: + + /** @param is input stream to wrap + */ + inputStreamAdapter(std::istream& is); + + const bool eof() const; + void reset(); + const size_type read(value_type* const data, const size_type count); + +private: + + std::istream& m_stream; +}; + + +/** An adapter class for string input. + */ + +class inputStreamStringAdapter : public inputStream +{ +public: + + inputStreamStringAdapter(const string& buffer); + inputStreamStringAdapter(const string& buffer, const string::size_type begin, const string::size_type end); + + const bool eof() const; + void reset(); + const size_type read(value_type* const data, const size_type count); + +private: + + const string m_buffer; // do _NOT_ keep a reference... + const string::size_type m_begin; + const string::size_type m_end; + string::size_type m_pos; +}; + + +/** An adapter class for stringProxy input. + */ + +class inputStreamStringProxyAdapter : public inputStream +{ +public: + + /** @param buffer stringProxy object to wrap + */ + inputStreamStringProxyAdapter(const stringProxy& buffer); + + const bool eof() const; + void reset(); + const size_type read(value_type* const data, const size_type count); + +private: + + const stringProxy& m_buffer; + string::size_type m_pos; +}; + + +/** An adapter class for pointer to C++ standard input stream. + */ + +class inputStreamPointerAdapter : public inputStream +{ +public: + + /** @param is input stream to wrap + * @param own if set to 'true', the pointer will be deleted when + * this object is destroyed + */ + inputStreamPointerAdapter(std::istream* is, const bool own = true); + ~inputStreamPointerAdapter(); + + const bool eof() const; + void reset(); + const size_type read(value_type* const data, const size_type count); + +private: + + std::istream* m_stream; + const bool m_own; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_STREAM_HPP_INCLUDED diff --git a/src/utility/stringProxy.cpp b/src/utility/stringProxy.cpp new file mode 100644 index 00000000..f79e8a58 --- /dev/null +++ b/src/utility/stringProxy.cpp @@ -0,0 +1,131 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "stringProxy.hpp" + +#include <iterator> +#include <algorithm> + + +namespace vmime { +namespace utility { + + +stringProxy::stringProxy() + : m_start(0), m_end(0) +{ +} + + +stringProxy::stringProxy(const stringProxy& s) + : m_buffer(s.m_buffer), m_start(s.m_start), m_end(s.m_end) +{ +} + + +stringProxy::stringProxy(const string_type& s, const size_type start, const size_type end) + : m_buffer(s), m_start(start), + m_end(end == std::numeric_limits <size_type>::max() ? s.length() : end) +{ +} + + +void stringProxy::set(const string_type& s, const size_type start, const size_type end) +{ + m_buffer = s; + m_start = start; + + if (end == std::numeric_limits <size_type>::max()) + m_end = s.length(); + else + m_end = end; +} + + +void stringProxy::detach() +{ + m_buffer.clear(); + m_start = m_end = 0; +} + + +stringProxy& stringProxy::operator=(const stringProxy& s) +{ + m_buffer = s.m_buffer; + m_start = s.m_start; + m_end = s.m_end; + + return (*this); +} + + +stringProxy& stringProxy::operator=(const string_type& s) +{ + m_buffer = s; + m_start = 0; + m_end = s.length(); + + return (*this); +} + + +void stringProxy::extract(outputStream& os, const size_type start, const size_type end) const +{ + if (end == std::numeric_limits <size_type>::max()) + os.write(m_buffer.data() + m_start + start, m_end - start - m_start); + else + os.write(m_buffer.data() + m_start + start, end - start - m_start); +} + + +const stringProxy::size_type stringProxy::length() const +{ + return (m_end - m_start); +} + + +const stringProxy::size_type stringProxy::start() const +{ + return (m_start); +} + + +const stringProxy::size_type stringProxy::end() const +{ + return (m_end); +} + + +std::ostream& operator<<(std::ostream& os, const stringProxy& s) +{ + outputStreamAdapter adapter(os); + s.extract(adapter); + return (os); +} + + +outputStream& operator<<(outputStream& os, const stringProxy& s) +{ + s.extract(os); + return (os); +} + + +} // utility +} // vmime diff --git a/src/utility/stringProxy.hpp b/src/utility/stringProxy.hpp new file mode 100644 index 00000000..e8001aed --- /dev/null +++ b/src/utility/stringProxy.hpp @@ -0,0 +1,90 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_STRINGPROXY_HPP_INCLUDED +#define VMIME_UTILITY_STRINGPROXY_HPP_INCLUDED + + +#include <limits> + +#include "../types.hpp" +#include "stream.hpp" + + +namespace vmime { +namespace utility { + + +/** This class is a proxy for the string class. This takes + * advantage of the COW (copy-on-write) system that might + * be used in "std::string" implementation. + */ + +class stringProxy +{ +public: + + typedef string::size_type size_type; + typedef string string_type; + + + // Consruction + stringProxy(); + stringProxy(const stringProxy& s); + stringProxy(const string_type& s, const size_type start = 0, const size_type end = std::numeric_limits <size_type>::max()); + + // Assignment + void set(const string_type& s, const size_type start = 0, const size_type end = std::numeric_limits <size_type>::max()); + void detach(); + + stringProxy& operator=(const stringProxy& s); + stringProxy& operator=(const string_type& s); + + // Extract some portion (or whole) of the string + // and output it into a stream. + void extract(outputStream& os, const size_type start = 0, const size_type end = std::numeric_limits <size_type>::max()) const; + + // Return the "virtual" length of the string + const size_type length() const; + + // Return the boundaries of the "virtual" string + const size_type start() const; + const size_type end() const; + + string::const_iterator it_begin() const { return (m_buffer.begin() + m_start); } + string::const_iterator it_end() const { return (m_buffer.begin() + m_end); } + +private: + + string_type m_buffer; + + size_type m_start; + size_type m_end; +}; + + +std::ostream& operator<<(std::ostream& os, const stringProxy& s); +outputStream& operator<<(outputStream& os, const stringProxy& s); + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_STRINGPROXY_HPP_INCLUDED diff --git a/src/vmime b/src/vmime new file mode 100644 index 00000000..4953d8fd --- /dev/null +++ b/src/vmime @@ -0,0 +1,93 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_INCLUDED +#define VMIME_INCLUDED + + +// Configuration +#include "config.hpp" + +// Base definitions +#include "base.hpp" +#include "exception.hpp" +#include "options.hpp" +#include "platformDependant.hpp" + +// Base components +#include "dateTime.hpp" +#include "message.hpp" +#include "bodyPart.hpp" +#include "charset.hpp" +#include "text.hpp" +#include "encoding.hpp" +#include "disposition.hpp" +#include "mailbox.hpp" +#include "mailboxGroup.hpp" +#include "mailboxList.hpp" +#include "addressList.hpp" +#include "mediaType.hpp" +#include "messageId.hpp" + +// Message components +#include "message.hpp" + +// Header fields +#include "headerFieldFactory.hpp" +#include "mailboxField.hpp" +#include "defaultField.hpp" +#include "addressListField.hpp" +#include "parameterizedHeaderField.hpp" +#include "relayField.hpp" + +// Encoders +#include "encoderFactory.hpp" + +// Message builder/parser +#include "messageBuilder.hpp" +#include "messageParser.hpp" + +#include "fileAttachment.hpp" +#include "defaultAttachment.hpp" + +#include "plainTextPart.hpp" +#include "htmlTextPart.hpp" + +// Property set +#include "propertySet.hpp" + +// Messaging features +#if VMIME_HAVE_MESSAGING_FEATURES + #include "messaging/socket.hpp" + + #include "messaging/service.hpp" + #include "messaging/store.hpp" + #include "messaging/transport.hpp" + + #include "messaging/session.hpp" + #include "messaging/authenticator.hpp" + #include "messaging/defaultAuthenticator.hpp" + #include "messaging/simpleAuthenticator.hpp" + + #include "messaging/folder.hpp" + #include "messaging/message.hpp" +#endif // VMIME_HAVE_MESSAGING_FEATURES + + +#endif // VMIME_INCLUDED diff --git a/src/word.cpp b/src/word.cpp new file mode 100644 index 00000000..6578ec5f --- /dev/null +++ b/src/word.cpp @@ -0,0 +1,102 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "word.hpp" + + +namespace vmime +{ + + +word::word() + : m_charset(charset::getLocaleCharset()) +{ +} + + +word::word(const word& w) + : m_buffer(w.m_buffer), m_charset(w.m_charset) +{ +} + + +word::word(const string& buffer) // Defaults to locale charset + : m_buffer(buffer), m_charset(charset::getLocaleCharset()) +{ +} + + +word::word(const string& buffer, const class charset& charset) + : m_buffer(buffer), m_charset(charset) +{ +} + + +#if VMIME_WIDE_CHAR_SUPPORT + +const wstring word::getDecodedText() const +{ + wstring out; + + charset::decode(m_buffer, out, m_charset); + + return (out); +} + +#endif + + +word& word::operator=(const word& w) +{ + m_buffer = w.m_buffer; + m_charset = w.m_charset; + return (*this); +} + + +word& word::operator=(const string& s) +{ + m_buffer = s; + return (*this); +} + + +const bool word::operator==(const word& w) const +{ + return (m_charset == w.m_charset && m_buffer == w.m_buffer); +} + + +const bool word::operator!=(const word& w) const +{ + return (m_charset != w.m_charset || m_buffer != w.m_buffer); +} + + +const string word::getConvertedText(const class charset& dest) const +{ + string out; + + charset::convert(m_buffer, out, m_charset, dest); + + return (out); +} + + +} // vmime diff --git a/src/word.hpp b/src/word.hpp new file mode 100644 index 00000000..e9321942 --- /dev/null +++ b/src/word.hpp @@ -0,0 +1,74 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_WORD_HPP_INCLUDED +#define VMIME_WORD_HPP_INCLUDED + + +#include "charset.hpp" + + +namespace vmime +{ + + +/** A class that encapsulates an encoded-word (RFC-2047): + * some text encoded into one specified charset. + */ + +class word +{ +public: + + word(); + word(const word& w); + word(const string& buffer); // Defaults to locale charset + word(const string& buffer, const class charset& charset); + + const string& buffer() const { return (m_buffer); } + string& buffer() { return (m_buffer); } + + const class charset& charset() const { return (m_charset); } + class charset& charset() { return (m_charset); } + + + word& operator=(const word& w); + word& operator=(const string& s); + + const bool operator==(const word& w) const; + const bool operator!=(const word& w) const; + +#if VMIME_WIDE_CHAR_SUPPORT + const wstring getDecodedText() const; +#endif + const string getConvertedText(const class charset& dest) const; + +protected: + + // The "m_buffer" of this word holds the data, and this data is encoded + // in the specified "m_charset". + string m_buffer; + class charset m_charset; +}; + + +} // vmime + + +#endif // VMIME_WORD_HPP_INCLUDED |