From 670d7c2630b1f067bf8cc57418567843fae0fd28 Mon Sep 17 00:00:00 2001 From: dockes Date: Wed, 16 Mar 2005 07:35:11 +0000 Subject: [PATCH] initial import from bincimap-1.3.3 --- src/bincimapmime/Makefile | 29 + src/bincimapmime/address.cc | 88 +++ src/bincimapmime/address.h | 52 ++ src/bincimapmime/config.h | 0 src/bincimapmime/convert.cc | 131 +++++ src/bincimapmime/convert.h | 318 +++++++++++ src/bincimapmime/depot.h | 0 src/bincimapmime/iodevice.cc | 317 +++++++++++ src/bincimapmime/iodevice.h | 389 +++++++++++++ src/bincimapmime/iofactory.cc | 85 +++ src/bincimapmime/iofactory.h | 68 +++ src/bincimapmime/mime-getpart.cc | 94 ++++ src/bincimapmime/mime-inputsource.h | 166 ++++++ src/bincimapmime/mime-parsefull.cc | 666 +++++++++++++++++++++++ src/bincimapmime/mime-parseonlyheader.cc | 171 ++++++ src/bincimapmime/mime-printbody.cc | 74 +++ src/bincimapmime/mime-printdoc.cc | 70 +++ src/bincimapmime/mime-printheader.cc | 198 +++++++ src/bincimapmime/mime-utils.h | 50 ++ src/bincimapmime/mime.cc | 157 ++++++ src/bincimapmime/mime.h | 143 +++++ src/bincimapmime/session.h | 0 22 files changed, 3266 insertions(+) create mode 100644 src/bincimapmime/Makefile create mode 100644 src/bincimapmime/address.cc create mode 100644 src/bincimapmime/address.h create mode 100644 src/bincimapmime/config.h create mode 100644 src/bincimapmime/convert.cc create mode 100644 src/bincimapmime/convert.h create mode 100644 src/bincimapmime/depot.h create mode 100644 src/bincimapmime/iodevice.cc create mode 100644 src/bincimapmime/iodevice.h create mode 100644 src/bincimapmime/iofactory.cc create mode 100644 src/bincimapmime/iofactory.h create mode 100644 src/bincimapmime/mime-getpart.cc create mode 100644 src/bincimapmime/mime-inputsource.h create mode 100644 src/bincimapmime/mime-parsefull.cc create mode 100644 src/bincimapmime/mime-parseonlyheader.cc create mode 100644 src/bincimapmime/mime-printbody.cc create mode 100644 src/bincimapmime/mime-printdoc.cc create mode 100644 src/bincimapmime/mime-printheader.cc create mode 100644 src/bincimapmime/mime-utils.h create mode 100644 src/bincimapmime/mime.cc create mode 100644 src/bincimapmime/mime.h create mode 100644 src/bincimapmime/session.h diff --git a/src/bincimapmime/Makefile b/src/bincimapmime/Makefile new file mode 100644 index 00000000..feb16708 --- /dev/null +++ b/src/bincimapmime/Makefile @@ -0,0 +1,29 @@ +include ../mk/sysconf + +LIBS = libmime.a +all: $(LIBS) + +SRCS = mime-getpart.cc mime-parsefull.cc mime-parseonlyheader.cc \ + mime-printbody.cc mime-printdoc.cc mime-printheader.cc mime.cc \ + convert.cc iodevice.cc iofactory.cc + +OBJS = mime-getpart.o mime-parsefull.o mime-parseonlyheader.o \ + mime-printbody.o mime-printdoc.o mime-printheader.o mime.o \ + convert.o iodevice.o iofactory.o + +libmime.a : $(OBJS) + ar ru libmime.a $(OBJS) + +# $(CXX) $(CXXFLAGS) -c $< +.cc .o : + $(CXX) $(CXXFLAGS) -c $< + + +clean: + rm -f *.o $(LIBS) + +alldeps:depend +depend: + $(CXX) $(CXXFLAGS) -M $(SRCS) > alldeps +include alldeps + diff --git a/src/bincimapmime/address.cc b/src/bincimapmime/address.cc new file mode 100644 index 00000000..ba9fd0b8 --- /dev/null +++ b/src/bincimapmime/address.cc @@ -0,0 +1,88 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * address.cc + * + * Description: + * Implementation of the Address class. + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "address.h" +#include "convert.h" +#include + +using namespace ::std; +using namespace Binc; + +//------------------------------------------------------------------------ +Address::Address(const string &name, const string &addr) +{ + string::size_type pos = addr.find('@'); + this->name = name; + if (pos != string::npos) { + this->local = addr.substr(0, pos); + this->host = addr.substr(pos + 1); + } else this->local = addr; +} + +//------------------------------------------------------------------------ +Address::Address(const string &wholeaddress) +{ + string::size_type start = wholeaddress.find('<'); + string addr; + if (start != string::npos) + addr = wholeaddress.substr(start + 1); + else + addr = wholeaddress; + + trim(addr, "<>"); + + if (start != string::npos) + name = wholeaddress.substr(0, start); + else + name = ""; + trim(name); + trim(name, "\""); + + start = addr.find('@'); + local = addr.substr(0, start); + host = addr.substr(start + 1); + + trim(local); + trim(host); + trim(name); +} + +//------------------------------------------------------------------------ +string Address::toParenList(void) const +{ + string tmp = "("; + tmp += name == "" ? "NIL" : toImapString(name); + tmp += " NIL "; + tmp += local == "" ? "\"\"" : toImapString(local); + tmp += " "; + tmp += host == "" ? "\"\"" : toImapString(host); + tmp += ")"; + + return tmp; +} diff --git a/src/bincimapmime/address.h b/src/bincimapmime/address.h new file mode 100644 index 00000000..69f6db05 --- /dev/null +++ b/src/bincimapmime/address.h @@ -0,0 +1,52 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * src/mailbox/address.h + * + * Description: + * Declaration of the Address class. + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef address_h_included +#define address_h_included +#include + +namespace Binc { + + //------------------------------------------------------------------------ + class Address { + public: + std::string name; + std::string local; + std::string host; + + //-- + std::string toParenList(void) const; + + //-- + Address(const std::string &name, const std::string &addr); + Address(const std::string &wholeaddr); + }; +} + +#endif diff --git a/src/bincimapmime/config.h b/src/bincimapmime/config.h new file mode 100644 index 00000000..e69de29b diff --git a/src/bincimapmime/convert.cc b/src/bincimapmime/convert.cc new file mode 100644 index 00000000..c2d877cc --- /dev/null +++ b/src/bincimapmime/convert.cc @@ -0,0 +1,131 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * convert.cc + * + * Description: + * Implementation of miscellaneous convertion functions. + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "convert.h" +#include + +using namespace ::std; +using namespace Binc; + +//------------------------------------------------------------------------ +BincStream::BincStream(void) +{ +} + +//------------------------------------------------------------------------ +BincStream::~BincStream(void) +{ + clear(); +} + +//------------------------------------------------------------------------ +string BincStream::popString(unsigned int size) +{ + if (size > nstr.length()) + size = nstr.length(); + string tmp = nstr.substr(0, size); + nstr = nstr.substr(size); + return tmp; +} + +//------------------------------------------------------------------------ +char BincStream::popChar(void) +{ + if (nstr.length() == 0) + return '\0'; + + char c = nstr[0]; + nstr = nstr.substr(1); + return c; +} + +//------------------------------------------------------------------------ +void BincStream::unpopChar(char c) +{ + nstr = c + nstr; +} + +//------------------------------------------------------------------------ +void BincStream::unpopStr(const string &s) +{ + nstr = s + nstr; +} + +//------------------------------------------------------------------------ +const string &BincStream::str(void) const +{ + return nstr; +} + +//------------------------------------------------------------------------ +void BincStream::clear(void) +{ + nstr = ""; +} + +//------------------------------------------------------------------------ +unsigned int BincStream::getSize(void) const +{ + return (unsigned int) nstr.length(); +} + +//------------------------------------------------------------------------ +BincStream &BincStream::operator << (std::ostream&(*)(std::ostream&)) +{ + nstr += "\r\n"; + return *this; +} + +//------------------------------------------------------------------------ +BincStream &BincStream::operator << (const string &t) +{ + nstr += t; + return *this; +} + +//------------------------------------------------------------------------ +BincStream &BincStream::operator << (int t) +{ + nstr += toString(t); + return *this; +} + +//------------------------------------------------------------------------ +BincStream &BincStream::operator << (unsigned int t) +{ + nstr += toString(t); + return *this; +} + +//------------------------------------------------------------------------ +BincStream &BincStream::operator << (char t) +{ + nstr += t; + return *this; +} diff --git a/src/bincimapmime/convert.h b/src/bincimapmime/convert.h new file mode 100644 index 00000000..d69d7a99 --- /dev/null +++ b/src/bincimapmime/convert.h @@ -0,0 +1,318 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * src/util/convert.h + * + * Description: + * Declaration of miscellaneous convertion functions. + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef convert_h_included +#define convert_h_included +#include +#include +#include +#include + +#include +#include + +#include "address.h" +#include "depot.h" + +namespace Binc { + + //---------------------------------------------------------------------- + inline std::string toString(int i_in) + { + char intbuf[16]; + snprintf(intbuf, sizeof(intbuf), "%d", i_in); + return std::string(intbuf); + } + + //---------------------------------------------------------------------- + inline std::string toString(unsigned int i_in) + { + char intbuf[16]; + snprintf(intbuf, sizeof(intbuf), "%u", i_in); + return std::string(intbuf); + } + + //---------------------------------------------------------------------- + inline std::string toString(unsigned long i_in) + { + char longbuf[40]; + snprintf(longbuf, sizeof(longbuf), "%lu", i_in); + return std::string(longbuf); + } + + //---------------------------------------------------------------------- + inline std::string toString(const char *i_in) + { + return std::string(i_in); + } + + //---------------------------------------------------------------------- + inline int atoi(const std::string &s_in) + { + return ::atoi(s_in.c_str()); + } + + //---------------------------------------------------------------------- + inline std::string toHex(const std::string &s) + { + const char hexchars[] = "0123456789abcdef"; + std::string tmp; + for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { + unsigned char c = (unsigned char)*i; + tmp += hexchars[((c & 0xf0) >> 4)]; + tmp += hexchars[c & 0x0f]; + } + + return tmp; + } + + //---------------------------------------------------------------------- + inline std::string fromHex(const std::string &s) + { + const char hexchars[] = "0123456789abcdef"; + std::string tmp; + for (std::string::const_iterator i = s.begin(); + i != s.end() && i + 1 != s.end(); i += 2) { + int n; + unsigned char c = *i; + unsigned char d = *(i + 1); + + char *t; + if ((t = strchr(hexchars, c)) == 0) + return "out of range"; + n = (t - hexchars) << 4; + + + if ((t = strchr(hexchars, d)) == 0) + return "out of range"; + n += (t - hexchars); + + if (n >= 0 && n <= 255) + tmp += (char) n; + else + return "out of range"; + } + + return tmp; + } + + //---------------------------------------------------------------------- + inline std::string toImapString(const std::string &s_in) + { + for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) { + unsigned char c = (unsigned char)*i; + if (c <= 31 || c >= 127 || c == '\"' || c == '\\') + return "{" + toString(s_in.length()) + "}\r\n" + s_in; + } + + return "\"" + s_in + "\""; + } + + //---------------------------------------------------------------------- + inline void uppercase(std::string &input) + { + for (std::string::iterator i = input.begin(); i != input.end(); ++i) + *i = toupper(*i); + } + + //---------------------------------------------------------------------- + inline void lowercase(std::string &input) + { + for (std::string::iterator i = input.begin(); i != input.end(); ++i) + *i = tolower(*i); + } + + //---------------------------------------------------------------------- + inline void chomp(std::string &s_in, const std::string &chars = " \t\r\n") + { + int n = s_in.length(); + while (n > 1 && chars.find(s_in[n - 1]) != std::string::npos) + s_in.resize(n-- - 1); + } + + //---------------------------------------------------------------------- + inline void trim(std::string &s_in, const std::string &chars = " \t\r\n") + { + while (s_in != "" && chars.find(s_in[0]) != std::string::npos) + s_in = s_in.substr(1); + chomp(s_in, chars); + } + + //---------------------------------------------------------------------- + inline const std::string unfold(const std::string &a, + bool removecomment = true) + { + std::string tmp; + bool incomment = false; + bool inquotes = false; + for (std::string::const_iterator i = a.begin(); i != a.end(); ++i) { + unsigned char c = (unsigned char)*i; + if (!inquotes && removecomment) { + if (c == '(') { + incomment = true; + tmp += " "; + } else if (c == ')') { + incomment = false; + } else if (c != 0x0a && c != 0x0d) { + tmp += *i; + } + } else if (c != 0x0a && c != 0x0d) { + tmp += *i; + } + + if (!incomment) { + if (*i == '\"') + inquotes = !inquotes; + } + } + + trim(tmp); + return tmp; + } + + //---------------------------------------------------------------------- + inline void split(const std::string &s_in, const std::string &delim, + std::vector &dest, bool skipempty = true) + { + std::string token; + for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) { + if (delim.find(*i) != std::string::npos) { + if (!skipempty || token != "") + dest.push_back(token); + token = ""; + } else + token += *i; + } + + if (token != "") + dest.push_back(token); + } + + //---------------------------------------------------------------------- + inline void splitAddr(const std::string &s_in, + std::vector &dest, bool skipempty = true) + { + static const std::string delim = ","; + std::string token; + bool inquote = false; + for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) { + if (inquote && *i == '\"') inquote = false; + else if (!inquote && *i == '\"') inquote = true; + + if (!inquote && delim.find(*i) != std::string::npos) { + if (!skipempty || token != "") + dest.push_back(token); + token = ""; + } else + token += *i; + } + if (token != "") + dest.push_back(token); + } + + //---------------------------------------------------------------------- + inline std::string toCanonMailbox(const std::string &s_in) + { + if (s_in.find("..") != std::string::npos) return ""; + + if (s_in.length() >= 5) { + std::string a = s_in.substr(0, 5); + uppercase(a); + return a == "INBOX" ? + a + (s_in.length() > 5 ? s_in.substr(5) : "") : s_in; + } + + return s_in; + } + + //------------------------------------------------------------------------ + inline std::string toRegex(const std::string &s_in, char delimiter) + { + std::string regex = "^"; + for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) { + if (*i == '.' || *i == '[' || *i == ']' || *i == '{' || *i == '}' || + *i == '(' || *i == ')' || *i == '^' || *i == '$' || *i == '?' || + *i == '+' || *i == '\\') { + regex += "\\"; + regex += *i; + } else if (*i == '*') + regex += ".*?"; + else if (*i == '%') { + regex += "[^\\"; + regex += delimiter; + regex += "]*?"; + } else regex += *i; + } + + if (regex[regex.length() - 1] == '?') + regex[regex.length() - 1] = '$'; + else + regex += "$"; + + return regex; + } + + //------------------------------------------------------------------------ + class BincStream { + private: + std::string nstr; + + public: + + //-- + BincStream &operator << (std::ostream&(*)(std::ostream&)); + BincStream &operator << (const std::string &t); + BincStream &operator << (unsigned int t); + BincStream &operator << (int t); + BincStream &operator << (char t); + + //-- + std::string popString(unsigned int size); + + //-- + char popChar(void); + void unpopChar(char c); + void unpopStr(const std::string &s); + + //-- + const std::string &str(void) const; + + //-- + unsigned int getSize(void) const; + + //-- + void clear(void); + + //-- + BincStream(void); + ~BincStream(void); + }; +} + +#endif diff --git a/src/bincimapmime/depot.h b/src/bincimapmime/depot.h new file mode 100644 index 00000000..e69de29b diff --git a/src/bincimapmime/iodevice.cc b/src/bincimapmime/iodevice.cc new file mode 100644 index 00000000..df3b8400 --- /dev/null +++ b/src/bincimapmime/iodevice.cc @@ -0,0 +1,317 @@ +/*-*-mode:c++-*-*/ +/* -------------------------------------------------------------------- + * Filename: + * src/iodevice.cc + * + * Description: + * Implementation of the IODevice class. + * -------------------------------------------------------------------- + * Copyright 2002, 2003 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#include "iodevice.h" +#include "convert.h" // BincStream +#include "session.h" // getEnv/hasEnv + +#include +#include + +using namespace ::std; +using namespace ::Binc; + +//------------------------------------------------------------------------ +IODevice::IODevice(int f) : flags(f | IsEnabled), + maxInputBufferSize(0), + maxOutputBufferSize(0), + timeout(0), + readCount(0), writeCount(0), + outputLevel(0), outputLevelLimit(0), + error(Unknown), errorString("Unknown error"), + dumpfd(0) +{ +} + +//------------------------------------------------------------------------ +IODevice::~IODevice(void) +{ +} + +//------------------------------------------------------------------------ +IODevice &IODevice::operator <<(ostream &(*source)(ostream &)) +{ + if (!(flags & IsEnabled) || outputLevel > outputLevelLimit) + return *this; + + static std::ostream &(*endl_funcptr)(ostream &) = endl; + + if (source != endl_funcptr) + return *this; + + outputBuffer << "\r\n"; + + if (dumpfd) + ::write(dumpfd, "\r\n", 2); + + if (flags & FlushesOnEndl) + flush(); + else if (flags & HasOutputLimit) + if (outputBuffer.getSize() > maxOutputBufferSize) + flush(); + + return *this; +} + +//------------------------------------------------------------------------ +bool IODevice::canRead(void) const +{ + return false; +} + +//------------------------------------------------------------------------ +void IODevice::clear() +{ + if (!(flags & IsEnabled)) + return; + + inputBuffer.clear(); + outputBuffer.clear(); +} + +//------------------------------------------------------------------------ +bool IODevice::flush() +{ + if (!(flags & IsEnabled)) + return true; + + WriteResult writeResult = WriteWait; + do { + unsigned int s = outputBuffer.getSize(); + if (s == 0) + break; + + if (!waitForWrite()) + return false; + + writeResult = write(); + if (writeResult == WriteError) + return false; + + writeCount += s - outputBuffer.getSize(); + } while (outputBuffer.getSize() > 0 && writeResult == WriteWait); + + outputBuffer.clear(); + return true; +} + +//------------------------------------------------------------------------ +void IODevice::setFlags(unsigned int f) +{ + flags |= f; +} + +//------------------------------------------------------------------------ +void IODevice::clearFlags(unsigned int f) +{ + flags &= ~f; +} + +//------------------------------------------------------------------------ +void IODevice::setMaxInputBufferSize(unsigned int max) +{ + maxInputBufferSize = max; +} + +//------------------------------------------------------------------------ +void IODevice::setMaxOutputBufferSize(unsigned int max) +{ + maxOutputBufferSize = max; +} + +//------------------------------------------------------------------------ +void IODevice::setTimeout(unsigned int t) +{ + timeout = t; + + if (t) + flags |= HasTimeout; + else + flags &= ~HasTimeout; +} + +//------------------------------------------------------------------------ +unsigned int IODevice::getTimeout(void) const +{ + return timeout; +} + +//------------------------------------------------------------------------ +void IODevice::setOutputLevel(unsigned int level) +{ + outputLevel = level; +} + +//------------------------------------------------------------------------ +unsigned int IODevice::getOutputLevel(void) const +{ + return outputLevel; +} + +//------------------------------------------------------------------------ +void IODevice::setOutputLevelLimit(unsigned int level) +{ + outputLevelLimit = level; +} + +//------------------------------------------------------------------------ +unsigned int IODevice::getOutputLevelLimit(void) const +{ + return outputLevelLimit; +} + +//------------------------------------------------------------------------ +bool IODevice::readStr(string *dest, unsigned int max) +{ + // If max is 0, fill the input buffer once only if it's empty. + if (!max && inputBuffer.getSize() == 0 && !fillInputBuffer()) + return false; + + // If max is != 0, wait until we have max. + while (max && inputBuffer.getSize() < max) { + if (!fillInputBuffer()) + return false; + } + + unsigned int bytesToRead = max ? max : inputBuffer.getSize(); + *dest += inputBuffer.str().substr(0, bytesToRead); + if (dumpfd) { + ::write(dumpfd, inputBuffer.str().substr(0, bytesToRead).c_str(), + bytesToRead); + } + + inputBuffer.popString(bytesToRead); + readCount += bytesToRead; + return true; +} + +//------------------------------------------------------------------------ +bool IODevice::readChar(char *dest) +{ + if (inputBuffer.getSize() == 0 && !fillInputBuffer()) + return false; + + char c = inputBuffer.popChar(); + if (dest) + *dest = c; + if (dumpfd) + ::write(dumpfd, &c, 1); + + ++readCount; + return true; +} + +//------------------------------------------------------------------------ +void IODevice::unreadChar(char c) +{ + inputBuffer.unpopChar(c); +} + +//------------------------------------------------------------------------ +void IODevice::unreadStr(const string &s) +{ + inputBuffer.unpopStr(s); +} + +//------------------------------------------------------------------------ +bool IODevice::skipTo(char c) +{ + char dest = '\0'; + do { + if (!readChar(&dest)) + return false; + if (dumpfd) + ::write(dumpfd, &dest, 1); + } while (c != dest); + + return true; +} + +//------------------------------------------------------------------------ +string IODevice::service(void) const +{ + return "nul"; +} + +//------------------------------------------------------------------------ +bool IODevice::waitForWrite(void) const +{ + return false; +} + +//------------------------------------------------------------------------ +bool IODevice::waitForRead(void) const +{ + return false; +} + +//------------------------------------------------------------------------ +IODevice::WriteResult IODevice::write(void) +{ + return WriteError; +} + +//------------------------------------------------------------------------ +bool IODevice::fillInputBuffer(void) +{ + return false; +} + +//------------------------------------------------------------------------ +IODevice::Error IODevice::getLastError(void) const +{ + return error; +} + +//------------------------------------------------------------------------ +string IODevice::getLastErrorString(void) const +{ + return errorString; +} + +//------------------------------------------------------------------------ +unsigned int IODevice::getReadCount(void) const +{ + return readCount; +} + +//------------------------------------------------------------------------ +unsigned int IODevice::getWriteCount(void) const +{ + return writeCount; +} + +//------------------------------------------------------------------------ +void IODevice::enableProtocolDumping(void) +{ + BincStream ss; + ss << "/tmp/bincimap-dump-" << (int) time(0) << "-" + << Session::getInstance().getIP() << "-XXXXXX"; + char *safename = strdup(ss.str().c_str()); + dumpfd = mkstemp(safename); + if (dumpfd == -1) + dumpfd = 0; + delete safename; +} diff --git a/src/bincimapmime/iodevice.h b/src/bincimapmime/iodevice.h new file mode 100644 index 00000000..b48a43b9 --- /dev/null +++ b/src/bincimapmime/iodevice.h @@ -0,0 +1,389 @@ +/*-*-mode:c++-*-*/ +/* -------------------------------------------------------------------- + * Filename: + * src/iodevice.h + * + * Description: + * Declaration of the IODevice class. + * -------------------------------------------------------------------- + * Copyright 2002, 2003 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifndef iodevice_h_included +#define iodevice_h_included +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "convert.h" // BincStream +#include +#include // ::write + +namespace Binc { + /*! + \class IODevice + \brief The IODevice class provides a framework for reading and + writing to device. + + Implement new devices by inheriting this class and overloading all + virtual methods. + + service() returns the service that the specific device is used + for. Two values are "log" and "client". + + \sa IOFactory, MultilogDevice, SyslogDevice, StdIODevice, SSLDevice + */ + class IODevice { + public: + /*! + Standard options for an IODevice. + */ + enum Flags { + None = 0, + FlushesOnEndl = 1 << 0, + HasInputLimit = 1 << 1, + HasOutputLimit = 1 << 2, + IsEnabled = 1 << 3, + HasTimeout = 1 << 4 + }; + + /*! + Errors from when an operation returned false. + */ + enum Error { + Unknown, + Timeout + }; + + /*! + Constructs an invalid IODevice. + + Instances of IODevice perform no operations, and all boolean + functions always return false. This constructor is only useful + if called from a subclass that reimplements all virtual methods. + */ + IODevice(int f = 0); + + /*! + Destructs an IODevice; does nothing. + */ + virtual ~IODevice(void); + + /*! + Clears all data in the input and output buffers. + */ + void clear(void); + + /*! + Sets one or more flags. + \param f A bitwise OR of flags from the Flags enum. + */ + void setFlags(unsigned int f); + + /*! + Clears one or more flags. + \param f A bitwise OR of flags from the Flags enum. + */ + void clearFlags(unsigned int f); + + /*! + Sets the maximum allowed input buffer size. If this size is + non-zero and exceeded, reading from the device will fail. This + functionality is used to prevent clients from forcing this class + to consume so much memory that the program crashes. + + Setting the max input buffer size to 0 disables the input size + limit. + + \param max The maximum input buffer size in bytes. + */ + void setMaxInputBufferSize(unsigned int max); + + /*! + Sets the maximum allowed output buffer size. If this size is + non-zero and exceeded, flush() is called implicitly. + + Setting the max output buffer size to 0 disables the output size + limit. This is generally discouraged. + + As a contrast to setMaxInputBufferSize(), this function is used + to bundle up consequent write calls, allowing more efficient use + of the underlying device as larger blocks of data are written at + a time. + + \param max The maximum output buffer size in bytes. + */ + void setMaxOutputBufferSize(unsigned int max); + + /*! + Sets the device's internal timeout in seconds. This timeout is + used both when waiting for data to read and for waiting for the + ability to write. + + If this timeout is exceeded, the read or write function that + triggered the timeout will fail. + + Setting the timeout to 0 disables the timeout. + + \param t The timeout in seconds. + \sa getTimeout() + */ + void setTimeout(unsigned int t); + + /*! + Returns the timeout in seconds, or 0 if there is no timeout. + + \sa setTimeout() + */ + unsigned int getTimeout(void) const; + + /*! + Sets the output level for the following write operations on this + device. + + The output level is a number which gives the following write + operations a priority. You can use setOutputLevelLimit() to + filter the write operations valid for different operating modes. + This enables you to have certain write operations ignored. + + For instance, if the output level is set to 0, then "Hello" is + written, and the output level is set to 1, followed by writing + "Daisy", the output level limit value will decive wether only + "Hello" is written, or if also "Daisy" is written. + + A low value of the level gives higher priority, and a high level + will give low priority. The default value is 0, and write + operations that are done with output level 0 are never ignored. + + \param level The output level + \sa getOutputLevel(), setOutputLevelLimit() + */ + void setOutputLevel(unsigned int level); + + /*! + Returns the current output level. + + \sa setOutputLevel() + */ + unsigned int getOutputLevel(void) const; + + /*! + Sets the current output level limit. Write operations with a + level higher than the output level limit are ignored. + + \param level The output level limit + \sa setOutputLevel() + */ + void setOutputLevelLimit(unsigned int level); + + /*! + Returns the current output level limit. + + \sa setOutputLevelLimit() + */ + unsigned int getOutputLevelLimit(void) const; + + /*! + Returns the number of bytes that have been read from this device + since it was created. + */ + unsigned int getReadCount(void) const; + + /*! + Returns the number of bytes that have been written to this + device since it was created. + */ + unsigned int getWriteCount(void) const; + + void enableProtocolDumping(void); + + /*! + Writes data to the device. Depending on the value of the max + output buffer size, the data may not be written immediately. + + \sa setMaxOutputBufferSize() + */ + template IODevice &operator <<(const T &source); + + /*! + Writes data to the device. This function specializes on standard + ostream derivates, such as std::endl. + */ + IODevice &operator <<(std::ostream &(*source)(std::ostream &)); + + /*! + Returns true if data can be read from the device; otherwise + returns false. + */ + virtual bool canRead(void) const; + + /*! + Reads data from the device, and stores this in a string. Returns + true on success; otherwise returns false. + + \param dest The incoming data is stored in this string. + \param max No more than this number of bytes is read from the + device. + */ + bool readStr(std::string *dest, unsigned int max = 0); + + /*! + Reads exactly one byte from the device and stores this in a + char. Returns true on success; otherwise returns false. + + \param dest The incoming byte is stored in this char. + */ + bool readChar(char *dest = 0); + + /*! + FIXME: add docs + */ + void unreadChar(char c); + + /*! + FIXME: add docs + */ + void unreadStr(const std::string &s); + + /*! + Reads characters from the device, until and including one + certain character is found. All read characters are discarded. + + This function can be used to skip to the beginning of a line, + with the terminating character being '\n'. + + \param The certain character. + */ + bool skipTo(char c); + + /*! + Flushes the output buffer. Writes all data in the output buffer + to the device. + */ + bool flush(void); + + /*! + Returns the type of error that most recently occurred. + */ + Error getLastError(void) const; + + /*! + Returns a human readable description of the error that most + recently occurred. If no known error has occurred, this method + returns "Unknown error". + */ + std::string getLastErrorString(void) const; + + /*! + Returns the type of service provided by this device. Two valid + return values are "client" and "log". + */ + virtual std::string service(void) const; + + protected: + /*! + Waits until data can be written to the device. If the timeout is + 0, this function waits indefinitely. Otherwise, it waits until + the timeout has expired. + + If this function returns true, data can be written to the + device; otherwise, getLastError() must be checked to determine + whether a timeout occurred or whether an error with the device + prevents further writing. + */ + virtual bool waitForWrite(void) const; + + /*! + Waits until data can be read from the device. + + \sa waitForWrite() + */ + virtual bool waitForRead(void) const; + + /*! + Types of results from a write. + */ + enum WriteResult { + WriteWait = 0, + WriteDone = 1 << 0, + WriteError = 1 << 1 + }; + + /*! + Writes as much data as possible to the device. If some but not + all data was written, returns WriteWait. If all data was + written, returns WriteDone. If an error occurred, returns + WriteError. + */ + virtual WriteResult write(void); + + /*! + Reads data from the device, and stores it in the input buffer. + Returns true on success; otherwise returns false. + + This method will fail if there is no more data available, if a + timeout occurred or if an error with the device prevents more + data from being read. + + The number of bytes read from the device is undefined. + */ + virtual bool fillInputBuffer(void); + + BincStream inputBuffer; + BincStream outputBuffer; + + protected: + unsigned int flags; + unsigned int maxInputBufferSize; + unsigned int maxOutputBufferSize; + + unsigned int timeout; + + unsigned int readCount; + unsigned int writeCount; + + unsigned int outputLevel; + unsigned int outputLevelLimit; + + Error error; + std::string errorString; + + int dumpfd; + }; + + //---------------------------------------------------------------------- + template IODevice &IODevice::operator <<(const T &source) + { + if ((flags & IsEnabled) && outputLevel <= outputLevelLimit) { + outputBuffer << source; + + if (dumpfd) { + BincStream ss; + ss << source; + ::write(dumpfd, ss.str().c_str(), ss.getSize()); + } + + if (flags & HasInputLimit) + if (outputBuffer.getSize() > maxOutputBufferSize) + flush(); + } + + return *this; + } +} + +#endif diff --git a/src/bincimapmime/iofactory.cc b/src/bincimapmime/iofactory.cc new file mode 100644 index 00000000..96d873b8 --- /dev/null +++ b/src/bincimapmime/iofactory.cc @@ -0,0 +1,85 @@ +/*-*-mode:c++-*-*/ +/* -------------------------------------------------------------------- + * Filename: + * src/iofactory.cc + * + * Description: + * Implementation of the IOFactory class. + * -------------------------------------------------------------------- + * Copyright 2002, 2003 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#include "iofactory.h" +#include "iodevice.h" + +using namespace ::Binc; +using namespace ::std; + +//------------------------------------------------------------------------ +IOFactory::IOFactory(void) +{ +} + +//------------------------------------------------------------------------ +IOFactory::~IOFactory(void) +{ +} + +//------------------------------------------------------------------------ +IOFactory &IOFactory::getInstance(void) +{ + static IOFactory ioFactory; + return ioFactory; +} + +//------------------------------------------------------------------------ +void IOFactory::addDevice(IODevice *dev) +{ + IODevice *ioDevice = IOFactory::getInstance().devices[dev->service()]; + + // FIXME: Delete correct object. Now, only IODevice's destructor is + // called, and only IODevice's memory is freed. + if (ioDevice) + delete ioDevice; + + IOFactory::getInstance().devices[dev->service()] = dev; +} + +//------------------------------------------------------------------------ +IODevice &IOFactory::getClient(void) +{ + static IODevice nulDevice; + + IOFactory &ioFactory = IOFactory::getInstance(); + + if (ioFactory.devices.find("client") != ioFactory.devices.end()) + return *ioFactory.devices["client"]; + + return nulDevice; +} + +//------------------------------------------------------------------------ +IODevice &IOFactory::getLogger(void) +{ + static IODevice nulDevice; + + IOFactory &ioFactory = IOFactory::getInstance(); + + if (ioFactory.devices.find("log") != ioFactory.devices.end()) + return *ioFactory.devices["log"]; + return nulDevice; +} diff --git a/src/bincimapmime/iofactory.h b/src/bincimapmime/iofactory.h new file mode 100644 index 00000000..12f3164d --- /dev/null +++ b/src/bincimapmime/iofactory.h @@ -0,0 +1,68 @@ +/*-*-mode:c++-*-*/ +/* -------------------------------------------------------------------- + * Filename: + * src/iofactory.h + * + * Description: + * Declaration of the IOFactory class. + * -------------------------------------------------------------------- + * Copyright 2002, 2003 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifndef IOFACTORY_H_INCLUDED +#define IOFACTORY_H_INCLUDED +#include +#include + +namespace Binc { + class IODevice; + class IOFactory { + public: + ~IOFactory(void); + + static void addDevice(IODevice *dev); + static IOFactory &getInstance(void); + static IODevice &getClient(void); + static IODevice &getLogger(void); + + private: + IOFactory(void); + + std::map devices; + }; +} + +#define bincClient \ + IOFactory::getClient() + +#if !defined (DEBUG) +#define bincError if (false) std::cout +#define bincWarning if (false) std::cout +#define bincDebug if (false) std::cout +#else +#define bincError \ + IOFactory::getLogger().setOutputLevel(0);IOFactory::getLogger() +#define bincWarning \ + IOFactory::getLogger().setOutputLevel(2);IOFactory::getLogger() +#define bincDebug \ + IOFactory::getLogger().setOutputLevel(3);IOFactory::getLogger() +#endif + +#define bincInfo \ + IOFactory::getLogger().setOutputLevel(1);IOFactory::getLogger() + +#endif diff --git a/src/bincimapmime/mime-getpart.cc b/src/bincimapmime/mime-getpart.cc new file mode 100644 index 00000000..a0cd2cc6 --- /dev/null +++ b/src/bincimapmime/mime-getpart.cc @@ -0,0 +1,94 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * mime-getpart.cc + * + * Description: + * Implementation of main mime parser components + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "mime.h" +#include "convert.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::std; + +//------------------------------------------------------------------------ +const Binc::MimePart *Binc::MimePart::getPart(const string &findpart, + string genpart, FetchType fetchType) const +{ + if (findpart == genpart) + return this; + + if (isMultipart()) { + if (members.size() != 0) { + vector::const_iterator i = members.begin(); + int part = 1; + while (i != members.end()) { + BincStream ss; + + ss << genpart; + if (genpart != "") + ss << "."; + ss << part; + + const MimePart *m; + if ((m = (*i).getPart(findpart, ss.str())) != 0) { + if (fetchType == FetchHeader && m->isMessageRFC822()) + m = &m->members[0]; + + return m; + } + + ++i; + ++part; + } + } + } else if (isMessageRFC822()) { + if (members.size() == 1) { + const MimePart *m = members[0].getPart(findpart, genpart); + return m; + } else { + return 0; + } + } else { + // Singlepart + if (genpart != "") + genpart += "."; + genpart += "1"; + + if (findpart == genpart) + return this; + } + + return 0; +} diff --git a/src/bincimapmime/mime-inputsource.h b/src/bincimapmime/mime-inputsource.h new file mode 100644 index 00000000..da79760e --- /dev/null +++ b/src/bincimapmime/mime-inputsource.h @@ -0,0 +1,166 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * src/mime-inputsource.h + * + * Description: + * The base class of the MIME input source + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifndef mime_inputsource_h_included +#define mime_inputsource_h_included + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +namespace Binc { + + class MimeInputSource { + public: + inline MimeInputSource(int fd, unsigned int start = 0); + virtual inline ~MimeInputSource(void); + + virtual inline bool fillInputBuffer(void); + virtual inline void reset(void); + + inline void seek(unsigned int offset); + inline bool getChar(char *c); + inline void ungetChar(void); + inline int getFileDescriptor(void) const; + + inline unsigned int getOffset(void) const; + + private: + int fd; + char data[16384]; + unsigned int offset; + unsigned int tail; + unsigned int head; + unsigned int start; + char lastChar; + }; + + inline MimeInputSource::MimeInputSource(int fd, unsigned int start) + { + this->fd = fd; + this->start = start; + offset = 0; + tail = 0; + head = 0; + lastChar = '\0'; + memset(data, '\0', sizeof(data)); + + seek(start); + } + + inline MimeInputSource::~MimeInputSource(void) + { + } + + inline bool MimeInputSource::fillInputBuffer(void) + { + char raw[4096]; + ssize_t nbytes = read(fd, raw, sizeof(raw)); + if (nbytes <= 0) { + // FIXME: If ferror(crlffile) we should log this. + return false; + } + + for (ssize_t i = 0; i < nbytes; ++i) { + const char c = raw[i]; + if (c == '\r') { + if (lastChar == '\r') { + data[tail++ & (0x4000-1)] = '\r'; + data[tail++ & (0x4000-1)] = '\n'; + } + } else if (c == '\n') { + data[tail++ & (0x4000-1)] = '\r'; + data[tail++ & (0x4000-1)] = '\n'; + } else { + if (lastChar == '\r') { + data[tail++ & (0x4000-1)] = '\r'; + data[tail++ & (0x4000-1)] = '\n'; + } + + data[tail++ & (0x4000-1)] = c; + } + + lastChar = c; + } + + return true; + } + + inline void MimeInputSource::reset(void) + { + offset = head = tail = 0; + lastChar = '\0'; + + if (fd != -1) + lseek(fd, 0, SEEK_SET); + } + + inline void MimeInputSource::seek(unsigned int seekToOffset) + { + if (offset > seekToOffset) + reset(); + + char c; + int n = 0; + while (seekToOffset > offset) { + if (!getChar(&c)) + break; + ++n; + } + } + + inline bool MimeInputSource::getChar(char *c) + { + if (head == tail && !fillInputBuffer()) + return false; + + *c = data[head++ & (0x4000-1)]; + ++offset; + return true; + } + + inline void MimeInputSource::ungetChar() + { + --head; + --offset; + } + + inline int MimeInputSource::getFileDescriptor(void) const + { + return fd; + } + + inline unsigned int MimeInputSource::getOffset(void) const + { + return offset; + } +} + +extern Binc::MimeInputSource *mimeSource; + +#endif diff --git a/src/bincimapmime/mime-parsefull.cc b/src/bincimapmime/mime-parsefull.cc new file mode 100644 index 00000000..9adc2dc5 --- /dev/null +++ b/src/bincimapmime/mime-parsefull.cc @@ -0,0 +1,666 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * mime-parsefull.cc + * + * Description: + * Implementation of main mime parser components + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "mime.h" +#include "mime-utils.h" +#include "mime-inputsource.h" +#include "convert.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +Binc::MimeInputSource *mimeSource = 0; + +using namespace ::std; + +//------------------------------------------------------------------------ +void Binc::MimeDocument::parseFull(int fd) const +{ + if (allIsParsed) + return; + + allIsParsed = true; + + if (!mimeSource || mimeSource->getFileDescriptor() != fd) { + delete mimeSource; + mimeSource = new MimeInputSource(fd); + } else { + mimeSource->reset(); + } + + headerstartoffsetcrlf = 0; + headerlength = 0; + bodystartoffsetcrlf = 0; + bodylength = 0; + size = 0; + messagerfc822 = false; + multipart = false; + + int bsize = 0; + string bound; + MimePart::parseFull(bound, bsize); + + // eat any trailing junk to get the correct size + char c; + while (mimeSource->getChar(&c)); + + size = mimeSource->getOffset(); +} + +//------------------------------------------------------------------------ +static bool parseOneHeaderLine(Binc::Header *header, unsigned int *nlines) +{ + using namespace ::Binc; + char c; + bool eof = false; + char cqueue[4]; + string name; + string content; + + while (mimeSource->getChar(&c)) { + // If we encounter a \r before we got to the first ':', then + // rewind back to the start of the line and assume we're at the + // start of the body. + if (c == '\r') { + for (int i = 0; i < (int) name.length() + 1; ++i) + mimeSource->ungetChar(); + return false; + } + + // A colon marks the end of the header name + if (c == ':') break; + + // Otherwise add to the header name + name += c; + } + + cqueue[0] = '\0'; + cqueue[1] = '\0'; + cqueue[2] = '\0'; + cqueue[3] = '\0'; + + // Read until the end of the header. + bool endOfHeaders = false; + while (!endOfHeaders) { + if (!mimeSource->getChar(&c)) { + eof = true; + break; + } + + if (c == '\n') ++*nlines; + + for (int i = 0; i < 3; ++i) + cqueue[i] = cqueue[i + 1]; + cqueue[3] = c; + + if (strncmp(cqueue, "\r\n\r\n", 4) == 0) { + endOfHeaders = true; + break; + } + + // If the last character was a newline, and the first now is not + // whitespace, then rewind one character and store the current + // key,value pair. + if (cqueue[2] == '\n' && c != ' ' && c != '\t') { + if (content.length() > 2) + content.resize(content.length() - 2); + + trim(content); + header->add(name, content); + + if (c != '\r') { + mimeSource->ungetChar(); + if (c == '\n') --*nlines; + return true; + } + + mimeSource->getChar(&c); + return false; + } + + content += c; + } + + if (name != "") { + if (content.length() > 2) + content.resize(content.length() - 2); + header->add(name, content); + } + + return !(eof || endOfHeaders); +} + +//------------------------------------------------------------------------ +static void parseHeader(Binc::Header *header, unsigned int *nlines) +{ + while (parseOneHeaderLine(header, nlines)) + { } +} + +//------------------------------------------------------------------------ +static void analyzeHeader(Binc::Header *header, bool *multipart, + bool *messagerfc822, string *subtype, + string *boundary) +{ + using namespace ::Binc; + + // Do simple parsing of headers to determine the + // type of message (multipart,messagerfc822 etc) + HeaderItem ctype; + if (header->getFirstHeader("content-type", ctype)) { + vector types; + split(ctype.getValue(), ";", types); + + if (types.size() > 0) { + // first element should describe content type + string tmp = types[0]; + trim(tmp); + vector v; + split(tmp, "/", v); + string key, value; + + key = (v.size() > 0) ? v[0] : "text"; + value = (v.size() > 1) ? v[1] : "plain"; + lowercase(key); + + if (key == "multipart") { + *multipart = true; + lowercase(value); + *subtype = value; + } else if (key == "message") { + lowercase(value); + if (value == "rfc822") + *messagerfc822 = true; + } + } + + for (vector::const_iterator i = types.begin(); + i != types.end(); ++i) { + string element = *i; + trim(element); + + if (element.find("=") != string::npos) { + string::size_type pos = element.find('='); + string key = element.substr(0, pos); + string value = element.substr(pos + 1); + + lowercase(key); + trim(key); + + if (key == "boundary") { + trim(value, " \""); + *boundary = value; + } + } + } + } +} + +static void parseMessageRFC822(vector *members, + bool *foundendofpart, + unsigned int *bodylength, + unsigned int *nbodylines, + const string &toboundary) +{ + using namespace ::Binc; + + // message rfc822 means a completely enclosed mime document. we + // call the parser recursively, and pass on the boundary string + // that we got. when parse() finds this boundary, it returns 0. if + // it finds the end boundary (boundary + "--"), it returns != 0. + MimePart m; + + unsigned int bodystartoffsetcrlf = mimeSource->getOffset(); + + // parsefull returns the number of bytes that need to be removed + // from the body because of the terminating boundary string. + int bsize = 0; + if (m.parseFull(toboundary, bsize)) + *foundendofpart = true; + + // make sure bodylength doesn't overflow + *bodylength = mimeSource->getOffset(); + if (*bodylength >= bodystartoffsetcrlf) { + *bodylength -= bodystartoffsetcrlf; + if (*bodylength >= (unsigned int) bsize) { + *bodylength -= (unsigned int) bsize; + } else { + *bodylength = 0; + } + } else { + *bodylength = 0; + } + + *nbodylines += m.getNofLines(); + + members->push_back(m); +} + +static bool skipUntilBoundary(const string &delimiter, + unsigned int *nlines, bool *eof) +{ + int endpos = delimiter.length(); + char *delimiterqueue = 0; + int delimiterpos = 0; + const char *delimiterStr = delimiter.c_str(); + if (delimiter != "") { + delimiterqueue = new char[endpos]; + memset(delimiterqueue, 0, endpos); + } + + // first, skip to the first delimiter string. Anything between the + // header and the first delimiter string is simply ignored (it's + // usually a text message intended for non-mime clients) + char c; + + bool foundBoundary = false; + for (;;) { + if (!mimeSource->getChar(&c)) { + *eof = true; + break; + } + + if (c == '\n') + ++*nlines; + + // if there is no delimiter, we just read until the end of the + // file. + if (!delimiterqueue) + continue; + + delimiterqueue[delimiterpos++ % endpos] = c; + + if (compareStringToQueue(delimiterStr, delimiterqueue, + delimiterpos, endpos)) { + foundBoundary = true; + break; + } + } + + delete [] delimiterqueue; + delimiterqueue = 0; + + return foundBoundary; +} + + +static void parseMultipart(const string &boundary, + const string &toboundary, + bool *eof, + unsigned int *nlines, + int *boundarysize, + bool *foundendofpart, + unsigned int *bodylength, + vector *members) +{ + using namespace ::Binc; + unsigned int bodystartoffsetcrlf = mimeSource->getOffset(); + + // multipart parsing starts with skipping to the first + // boundary. then we call parse() for all parts. the last parse() + // command will return a code indicating that it found the last + // boundary of this multipart. Note that the first boundary does + // not have to start with CRLF. + string delimiter = "--" + boundary; + + skipUntilBoundary(delimiter, nlines, eof); + + if (!eof) + *boundarysize = delimiter.size(); + + // Read two more characters. This may be CRLF, it may be "--" and + // it may be any other two characters. + char a; + if (!mimeSource->getChar(&a)) + *eof = true; + + if (a == '\n') + ++*nlines; + + char b; + if (!mimeSource->getChar(&b)) + *eof = true; + + if (b == '\n') + ++*nlines; + + // If we find two dashes after the boundary, then this is the end + // of boundary marker. + if (!*eof) { + if (a == '-' && b == '-') { + *foundendofpart = true; + *boundarysize += 2; + + if (!mimeSource->getChar(&a)) + *eof = true; + + if (a == '\n') + ++*nlines; + + if (!mimeSource->getChar(&b)) + *eof = true; + + if (b == '\n') + ++*nlines; + } + + if (a == '\r' && b == '\n') { + // This exception is to handle a special case where the + // delimiter of one part is not followed by CRLF, but + // immediately followed by a CRLF prefixed delimiter. + if (!mimeSource->getChar(&a) || !mimeSource->getChar(&b)) + *eof = true; + else if (a == '-' && b == '-') { + mimeSource->ungetChar(); + mimeSource->ungetChar(); + mimeSource->ungetChar(); + mimeSource->ungetChar(); + } else { + mimeSource->ungetChar(); + mimeSource->ungetChar(); + } + + *boundarysize += 2; + } else { + mimeSource->ungetChar(); + mimeSource->ungetChar(); + } + } + + // read all mime parts. + if (!*foundendofpart && !*eof) { + bool quit = false; + do { + MimePart m; + + // If parseFull returns != 0, then it encountered the multipart's + // final boundary. + int bsize = 0; + if (m.parseFull(boundary, bsize)) { + quit = true; + *boundarysize = bsize; + } + + members->push_back(m); + + } while (!quit); + } + + if (!*foundendofpart && !*eof) { + // multipart parsing starts with skipping to the first + // boundary. then we call parse() for all parts. the last parse() + // command will return a code indicating that it found the last + // boundary of this multipart. Note that the first boundary does + // not have to start with CRLF. + string delimiter = "\r\n--" + toboundary; + + skipUntilBoundary(delimiter, nlines, eof); + + if (!*eof) + *boundarysize = delimiter.size(); + + // Read two more characters. This may be CRLF, it may be "--" and + // it may be any other two characters. + char a = '\0'; + if (!mimeSource->getChar(&a)) + *eof = true; + + if (a == '\n') + ++*nlines; + + char b = '\0'; + if (!mimeSource->getChar(&b)) + *eof = true; + + if (b == '\n') + ++*nlines; + + // If we find two dashes after the boundary, then this is the end + // of boundary marker. + if (!*eof) { + if (a == '-' && b == '-') { + *foundendofpart = true; + *boundarysize += 2; + + if (!mimeSource->getChar(&a)) + *eof = true; + + if (a == '\n') + ++*nlines; + + if (!mimeSource->getChar(&b)) + *eof = true; + + if (b == '\n') + ++*nlines; + } + + if (a == '\r' && b == '\n') { + // This exception is to handle a special case where the + // delimiter of one part is not followed by CRLF, but + // immediately followed by a CRLF prefixed delimiter. + if (!mimeSource->getChar(&a) || !mimeSource->getChar(&b)) + *eof = true; + else if (a == '-' && b == '-') { + mimeSource->ungetChar(); + mimeSource->ungetChar(); + mimeSource->ungetChar(); + mimeSource->ungetChar(); + } else { + mimeSource->ungetChar(); + mimeSource->ungetChar(); + } + + *boundarysize += 2; + } else { + mimeSource->ungetChar(); + mimeSource->ungetChar(); + } + } + } + + // make sure bodylength doesn't overflow + *bodylength = mimeSource->getOffset(); + if (*bodylength >= bodystartoffsetcrlf) { + *bodylength -= bodystartoffsetcrlf; + if (*bodylength >= (unsigned int) *boundarysize) { + *bodylength -= (unsigned int) *boundarysize; + } else { + *bodylength = 0; + } + } else { + *bodylength = 0; + } +} + +static void parseSinglePart(const string &toboundary, + int *boundarysize, + unsigned int *nbodylines, + unsigned int *nlines, + bool *eof, bool *foundendofpart, + unsigned int *bodylength) +{ + using namespace ::Binc; + unsigned int bodystartoffsetcrlf = mimeSource->getOffset(); + + // If toboundary is empty, then we read until the end of the + // file. Otherwise we will read until we encounter toboundary. + string _toboundary; + if (toboundary != "") { + _toboundary = "\r\n--"; + _toboundary += toboundary; + } + + // if (skipUntilBoundary(_toboundary, nlines, eof)) + // *boundarysize = _toboundary.length(); + + char *boundaryqueue = 0; + int endpos = _toboundary.length(); + if (toboundary != "") { + boundaryqueue = new char[endpos]; + memset(boundaryqueue, 0, endpos); + } + int boundarypos = 0; + + *boundarysize = 0; + + const char *_toboundaryStr = _toboundary.c_str(); + string line; + bool toboundaryIsEmpty = (toboundary == ""); + char c; + while (mimeSource->getChar(&c)) { + if (c == '\n') { ++*nbodylines; ++*nlines; } + + if (toboundaryIsEmpty) + continue; + + // find boundary + boundaryqueue[boundarypos++ % endpos] = c; + + if (compareStringToQueue(_toboundaryStr, boundaryqueue, + boundarypos, endpos)) { + *boundarysize = _toboundary.length(); + break; + } + } + + delete [] boundaryqueue; + + if (toboundary != "") { + char a; + if (!mimeSource->getChar(&a)) + *eof = true; + + if (a == '\n') + ++*nlines; + char b; + if (!mimeSource->getChar(&b)) + *eof = true; + + if (b == '\n') + ++*nlines; + + if (a == '-' && b == '-') { + *boundarysize += 2; + *foundendofpart = true; + if (!mimeSource->getChar(&a)) + *eof = true; + + if (a == '\n') + ++*nlines; + + if (!mimeSource->getChar(&b)) + *eof = true; + + if (b == '\n') + ++*nlines; + } + + if (a == '\r' && b == '\n') { + // This exception is to handle a special case where the + // delimiter of one part is not followed by CRLF, but + // immediately followed by a CRLF prefixed delimiter. + if (!mimeSource->getChar(&a) || !mimeSource->getChar(&b)) + *eof = true; + else if (a == '-' && b == '-') { + mimeSource->ungetChar(); + mimeSource->ungetChar(); + mimeSource->ungetChar(); + mimeSource->ungetChar(); + } else { + mimeSource->ungetChar(); + mimeSource->ungetChar(); + } + + *boundarysize += 2; + } else { + mimeSource->ungetChar(); + mimeSource->ungetChar(); + } + } + + // make sure bodylength doesn't overflow + *bodylength = mimeSource->getOffset(); + if (*bodylength >= bodystartoffsetcrlf) { + *bodylength -= bodystartoffsetcrlf; + if (*bodylength >= (unsigned int) *boundarysize) { + *bodylength -= (unsigned int) *boundarysize; + } else { + *bodylength = 0; + } + } else { + *bodylength = 0; + } + +} + +//------------------------------------------------------------------------ +int Binc::MimePart::parseFull(const string &toboundary, + int &boundarysize) const +{ + headerstartoffsetcrlf = mimeSource->getOffset(); + + // Parse the header of this mime part. + parseHeader(&h, &nlines); + + // Headerlength includes the seperating CRLF. Body starts after the + // CRLF. + headerlength = mimeSource->getOffset() - headerstartoffsetcrlf; + bodystartoffsetcrlf = mimeSource->getOffset(); + bodylength = 0; + + // Determine the type of mime part by looking at fields in the + // header. + analyzeHeader(&h, &multipart, &messagerfc822, &subtype, &boundary); + + bool eof = false; + bool foundendofpart = false; + + if (messagerfc822) { + parseMessageRFC822(&members, &foundendofpart, &bodylength, + &nbodylines, toboundary); + + } else if (multipart) { + parseMultipart(boundary, toboundary, &eof, &nlines, &boundarysize, + &foundendofpart, &bodylength, + &members); + } else { + parseSinglePart(toboundary, &boundarysize, &nbodylines, &nlines, + &eof, &foundendofpart, &bodylength); + } + + return (eof || foundendofpart) ? 1 : 0; +} diff --git a/src/bincimapmime/mime-parseonlyheader.cc b/src/bincimapmime/mime-parseonlyheader.cc new file mode 100644 index 00000000..59fc4840 --- /dev/null +++ b/src/bincimapmime/mime-parseonlyheader.cc @@ -0,0 +1,171 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * mime-parseonlyheader.cc + * + * Description: + * Implementation of main mime parser components + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "mime.h" +#include "mime-utils.h" +#include "mime-inputsource.h" +#include "convert.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::std; + +//------------------------------------------------------------------------ +void Binc::MimeDocument::parseOnlyHeader(int fd) const +{ + if (allIsParsed || headerIsParsed) + return; + + headerIsParsed = true; + + if (!mimeSource || mimeSource->getFileDescriptor() != fd) { + delete mimeSource; + mimeSource = new MimeInputSource(fd); + } else { + mimeSource->reset(); + } + + + headerstartoffsetcrlf = 0; + headerlength = 0; + bodystartoffsetcrlf = 0; + bodylength = 0; + messagerfc822 = false; + multipart = false; + + nlines = 0; + nbodylines = 0; + + MimePart::parseOnlyHeader(""); +} + +//------------------------------------------------------------------------ +int Binc::MimePart::parseOnlyHeader(const string &toboundary) const +{ + string name; + string content; + char cqueue[4]; + memset(cqueue, 0, sizeof(cqueue)); + + headerstartoffsetcrlf = mimeSource->getOffset(); + + bool quit = false; + char c = '\0'; + + while (!quit) { + // read name + while (1) { + if (!mimeSource->getChar(&c)) { + quit = true; + break; + } + + if (c == '\n') ++nlines; + if (c == ':') break; + if (c == '\n') { + for (int i = name.length() - 1; i >= 0; --i) + mimeSource->ungetChar(); + + quit = true; + name = ""; + break; + } + + name += c; + + if (name.length() == 2 && name.substr(0, 2) == "\r\n") { + name = ""; + quit = true; + break; + } + } + + if (name.length() == 1 && name[0] == '\r') { + name = ""; + break; + } + + if (quit) break; + + while (!quit) { + if (!mimeSource->getChar(&c)) { + quit = true; + break; + } + + if (c == '\n') ++nlines; + + for (int i = 0; i < 3; ++i) + cqueue[i] = cqueue[i + 1]; + cqueue[3] = c; + + if (strncmp(cqueue, "\r\n\r\n", 4) == 0) { + quit = true; + break; + } + + if (cqueue[2] == '\n') { + + // guess the mime rfc says what can not appear on the beginning + // of a line. + if (!isspace(cqueue[3])) { + if (content.length() > 2) + content.resize(content.length() - 2); + + trim(content); + h.add(name, content); + + name = c; + content = ""; + break; + } + } + + content += c; + } + } + + if (name != "") { + if (content.length() > 2) + content.resize(content.length() - 2); + h.add(name, content); + } + + headerlength = mimeSource->getOffset() - headerstartoffsetcrlf; + + return 1; +} diff --git a/src/bincimapmime/mime-printbody.cc b/src/bincimapmime/mime-printbody.cc new file mode 100644 index 00000000..36daa8f1 --- /dev/null +++ b/src/bincimapmime/mime-printbody.cc @@ -0,0 +1,74 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * mime-printbody.cc + * + * Description: + * Implementation of main mime parser components + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "mime.h" +#include "mime-utils.h" +#include "mime-inputsource.h" + +#include "convert.h" +#include "iodevice.h" +#include "iofactory.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::std; + +//------------------------------------------------------------------------ +void Binc::MimePart::printBody(int fd, IODevice &output, + unsigned int startoffset, + unsigned int length) const +{ + if (!mimeSource || mimeSource->getFileDescriptor() != fd) { + delete mimeSource; + mimeSource = new MimeInputSource(fd); + } + + mimeSource->reset(); + mimeSource->seek(bodystartoffsetcrlf + startoffset); + + if (startoffset + length > bodylength) + length = bodylength - startoffset; + + char c = '\0'; + for (unsigned int i = 0; i < length; ++i) { + if (!mimeSource->getChar(&c)) + break; + + output << (char)c; + } +} diff --git a/src/bincimapmime/mime-printdoc.cc b/src/bincimapmime/mime-printdoc.cc new file mode 100644 index 00000000..24b6f3fc --- /dev/null +++ b/src/bincimapmime/mime-printdoc.cc @@ -0,0 +1,70 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * mime-printdoc.cc + * + * Description: + * Implementation of main mime parser components + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "mime.h" +#include "mime-utils.h" +#include "mime-inputsource.h" +#include "convert.h" +#include "iodevice.h" +#include "iofactory.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::std; + +//------------------------------------------------------------------------ +void Binc::MimePart::printDoc(int fd, IODevice &output, + unsigned int startoffset, + unsigned int length) const +{ + if (!mimeSource || mimeSource->getFileDescriptor() != fd) { + delete mimeSource; + mimeSource = new MimeInputSource(fd); + } + + mimeSource->reset(); + mimeSource->seek(headerstartoffsetcrlf); + + char c; + for (unsigned int i = 0; i < length; ++i) { + if (!mimeSource->getChar(&c)) + break; + + output << (char)c; + } +} diff --git a/src/bincimapmime/mime-printheader.cc b/src/bincimapmime/mime-printheader.cc new file mode 100644 index 00000000..0d1c0ba2 --- /dev/null +++ b/src/bincimapmime/mime-printheader.cc @@ -0,0 +1,198 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * mime-printheader.cc + * + * Description: + * Implementation of main mime parser components + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "mime.h" +#include "mime-utils.h" +#include "mime-inputsource.h" +#include "convert.h" +#include "iodevice.h" +#include "iofactory.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::std; + +//------------------------------------------------------------------------ +void Binc::MimePart::printHeader(int fd, IODevice &output, + vector headers, bool includeheaders, + unsigned int startoffset, + unsigned int length, string &store) const +{ + if (!mimeSource || mimeSource->getFileDescriptor() != fd) { + delete mimeSource; + mimeSource = new MimeInputSource(fd); + } + + mimeSource->seek(headerstartoffsetcrlf); + + string name; + string content; + char cqueue[2]; + memset(cqueue, 0, sizeof(cqueue)); + + bool quit = false; + char c = '\0'; + + unsigned int wrotebytes = 0; + unsigned int processedbytes = 0; + bool hasHeaderSeparator = false; + + while (!quit) { + // read name + while (1) { + // allow EOF to end the header + if (!mimeSource->getChar(&c)) { + quit = true; + break; + } + + // assume this character is part of the header name. + name += c; + + // break on the first colon + if (c == ':') + break; + + // break if a '\n' turned up. + if (c == '\n') { + // end of headers detected + if (name == "\r\n") { + hasHeaderSeparator = true; + quit = true; + break; + } + + // put all data back in the buffer to the beginning of this + // line. + for (int i = name.length(); i >= 0; --i) + mimeSource->ungetChar(); + + // abort printing of header. note that in this case, the + // headers will not end with a seperate \r\n. + quit = true; + name = ""; + break; + } + } + + if (quit) break; + + // at this point, we have a name, that is - the start of a + // header. we'll read until the end of the header. + while (!quit) { + // allow EOF to end the header. + if (!mimeSource->getChar(&c)) { + quit = true; + break; + } + + if (c == '\n') ++nlines; + + // make a fifo queue of the last 4 characters. + cqueue[0] = cqueue[1]; + cqueue[1] = c; + + // print header + if (cqueue[0] == '\n' && cqueue[1] != '\t' && cqueue[1] != ' ') { + // it wasn't a space, so put it back as it is most likely + // the start of a header name. in any case it terminates the + // content part of this header. + mimeSource->ungetChar(); + + string lowername = name; + lowercase(lowername); + trim(lowername, ": \t"); + bool foundMatch = false; + for (vector::const_iterator i = headers.begin(); + i != headers.end(); ++i) { + string nametmp = *i; + lowercase(nametmp); + if (nametmp == lowername) { + foundMatch = true; + break; + } + } + + if (foundMatch == includeheaders || headers.size() == 0) { + string out = name + content; + for (string::const_iterator i = out.begin(); i != out.end(); ++i) + if (processedbytes >= startoffset && wrotebytes < length) { + if (processedbytes >= startoffset) { + store += *i; + ++wrotebytes; + } + } else + ++processedbytes; + } + + // move on to the next header + content = ""; + name = ""; + break; + } + + content += c; + } + } + + if (name != "") { + string lowername = name; + lowercase(lowername); + trim(lowername, ": \t"); + bool foundMatch = false; + for (vector::const_iterator i = headers.begin(); + i != headers.end(); ++i) { + string nametmp = *i; + lowercase(nametmp); + if (nametmp == lowername) { + foundMatch = true; + break; + } + } + + if (hasHeaderSeparator || foundMatch == includeheaders || headers.size() == 0) { + string out = name + content; + for (string::const_iterator i = out.begin(); i != out.end(); ++i) + if (processedbytes >= startoffset && wrotebytes < length) { + store += *i; + ++wrotebytes; + } else + ++processedbytes; + } + } +} diff --git a/src/bincimapmime/mime-utils.h b/src/bincimapmime/mime-utils.h new file mode 100644 index 00000000..5bf8ca9c --- /dev/null +++ b/src/bincimapmime/mime-utils.h @@ -0,0 +1,50 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * mime.cc + * + * Description: + * Implementation of main mime parser components + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifndef mime_utils_h_included +#define mime_utils_h_included + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +using namespace ::std; + +inline bool compareStringToQueue(const char *s_in, char *bqueue, + int pos, int size) +{ + for (int i = 0; i < size; ++i) + if (s_in[i] != bqueue[(pos + i) % size]) + return false; + + return true; +} + +#endif diff --git a/src/bincimapmime/mime.cc b/src/bincimapmime/mime.cc new file mode 100644 index 00000000..c1764030 --- /dev/null +++ b/src/bincimapmime/mime.cc @@ -0,0 +1,157 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * mime.cc + * + * Description: + * Implementation of main mime parser components + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "mime.h" +#include "convert.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::std; + +//------------------------------------------------------------------------ +Binc::MimeDocument::MimeDocument(void) : MimePart() +{ + allIsParsed = false; + headerIsParsed = false; +} + +//------------------------------------------------------------------------ +Binc::MimeDocument::~MimeDocument(void) +{ +} + +//------------------------------------------------------------------------ +void Binc::MimeDocument::clear(void) const +{ + members.clear(); + h.clear(); + headerIsParsed = false; + allIsParsed = false; +} + +//------------------------------------------------------------------------ +void Binc::MimePart::clear(void) const +{ + members.clear(); + h.clear(); +} + +//------------------------------------------------------------------------ +Binc::MimePart::MimePart(void) +{ + size = 0; + messagerfc822 = false; + multipart = false; + + nlines = 0; + nbodylines = 0; +} + +//------------------------------------------------------------------------ +Binc::MimePart::~MimePart(void) +{ +} + +//------------------------------------------------------------------------ +Binc::HeaderItem::HeaderItem(void) +{ +} + +//------------------------------------------------------------------------ +Binc::HeaderItem::HeaderItem(const string &key, const string &value) +{ + this->key = key; + this->value = value; +} + +//------------------------------------------------------------------------ +Binc::Header::Header(void) +{ +} + +//------------------------------------------------------------------------ +Binc::Header::~Header(void) +{ +} + +//------------------------------------------------------------------------ +bool Binc::Header::getFirstHeader(const string &key, HeaderItem &dest) const +{ + string k = key; + lowercase(k); + + for (vector::const_iterator i = content.begin(); + i != content.end(); ++i) { + string tmp = (*i).getKey(); + lowercase(tmp); + + if (tmp == k) { + dest = *i; + return true; + } + } + return false; +} + +//------------------------------------------------------------------------ +bool Binc::Header::getAllHeaders(const string &key, vector &dest) const +{ + string k = key; + lowercase(k); + + for (vector::const_iterator i = content.begin(); + i != content.end(); ++i) { + string tmp = (*i).getKey(); + lowercase(tmp); + if (tmp == k) + dest.push_back(*i); + } + + return (dest.size() != 0); +} + +//------------------------------------------------------------------------ +void Binc::Header::clear(void) const +{ + content.clear(); +} + +//------------------------------------------------------------------------ +void Binc::Header::add(const string &key, const string &value) +{ + content.push_back(HeaderItem(key, value)); +} diff --git a/src/bincimapmime/mime.h b/src/bincimapmime/mime.h new file mode 100644 index 00000000..7350c225 --- /dev/null +++ b/src/bincimapmime/mime.h @@ -0,0 +1,143 @@ +/* -*- Mode: c++; -*- */ +/* -------------------------------------------------------------------- + * Filename: + * src/parsers/mime/mime.h + * + * Description: + * Declaration of main mime parser components + * -------------------------------------------------------------------- + * Copyright 2002-2004 Andreas Aardal Hanssen + * + * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * -------------------------------------------------------------------- + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef mime_h_included +#define mime_h_included +#include +#include +#include +#include + +namespace Binc { + //---------------------------------------------------------------------- + class HeaderItem { + private: + mutable std::string key; + mutable std::string value; + + public: + inline const std::string &getKey(void) const { return key; } + inline const std::string &getValue(void) const { return value; } + + //-- + HeaderItem(void); + HeaderItem(const std::string &key, const std::string &value); + }; + + //---------------------------------------------------------------------- + class Header { + private: + mutable std::vector content; + + public: + bool getFirstHeader(const std::string &key, HeaderItem &dest) const; + bool getAllHeaders(const std::string &key, std::vector &dest) const; + void add(const std::string &name, const std::string &content); + void clear(void) const; + + //-- + Header(void); + ~Header(void); + }; + + //---------------------------------------------------------------------- + class IODevice; + class MimeDocument; + class MimePart { + protected: + public: + mutable bool multipart; + mutable bool messagerfc822; + mutable std::string subtype; + mutable std::string boundary; + + mutable unsigned int headerstartoffsetcrlf; + mutable unsigned int headerlength; + + mutable unsigned int bodystartoffsetcrlf; + mutable unsigned int bodylength; + mutable unsigned int nlines; + mutable unsigned int nbodylines; + mutable unsigned int size; + + public: + enum FetchType { + FetchBody, + FetchHeader, + FetchMime + }; + + mutable Header h; + + mutable std::vector members; + + inline const std::string &getSubType(void) const { return subtype; } + inline bool isMultipart(void) const { return multipart; } + inline bool isMessageRFC822(void) const { return messagerfc822; } + inline unsigned int getSize(void) const { return bodylength; } + inline unsigned int getNofLines(void) const { return nlines; } + inline unsigned int getNofBodyLines(void) const { return nbodylines; } + inline unsigned int getBodyLength(void) const { return bodylength; } + inline unsigned int getBodyStartOffset(void) const { return bodystartoffsetcrlf; } + + void printBody(int fd, Binc::IODevice &output, unsigned int startoffset, unsigned int length) const; + void printHeader(int fd, Binc::IODevice &output, std::vector headers, bool includeheaders, unsigned int startoffset, unsigned int length, std::string &storage) const; + void printDoc(int fd, Binc::IODevice &output, unsigned int startoffset, unsigned int length) const; + virtual void clear(void) const; + + const MimePart *getPart(const std::string &findpart, std::string genpart, FetchType fetchType = FetchBody) const; + virtual int parseOnlyHeader(const std::string &toboundary) const; + virtual int parseFull(const std::string &toboundary, int &boundarysize) const; + + MimePart(void); + virtual ~MimePart(void); + }; + + //---------------------------------------------------------------------- + class MimeDocument : public MimePart { + private: + mutable bool headerIsParsed; + mutable bool allIsParsed; + + public: + void parseOnlyHeader(int fd) const; + void parseFull(int fd) const; + void clear(void) const; + + inline bool isHeaderParsed(void) { return headerIsParsed; } + inline bool isAllParsed(void) { return allIsParsed; } + + //-- + MimeDocument(void); + ~MimeDocument(void); + }; + +}; + +#endif diff --git a/src/bincimapmime/session.h b/src/bincimapmime/session.h new file mode 100644 index 00000000..e69de29b