diff --git a/SConstruct b/SConstruct index 11443e9c..40bcdb2b 100644 --- a/SConstruct +++ b/SConstruct @@ -1124,6 +1124,8 @@ AM_CONFIG_HEADER([config.h]) AM_MAINTAINER_MODE +VMIME_ADDITIONAL_DEFINES="" + # # Check compilers, processors, etc @@ -1429,6 +1431,16 @@ x*) ;; esac + +# +# Detect some platform-specific stuff +# + +if test "x$VMIME_DETECT_PLATFORM" = "xwindows"; then + AC_CHECK_HEADER(mlang.h, [VMIME_ADDITIONAL_DEFINES="$VMIME_ADDITIONAL_DEFINES HAVE_MLANG_H"]) +fi + + """) @@ -1615,10 +1627,21 @@ typedef unsigned ${VMIME_TYPE_INT32} vmime_uint32; + " $VMIME_BUILTIN_PLATFORM_" + p + " \n") configure_in.write(""" - -#endif // VMIME_CONFIG_HPP_INCLUDED " > vmime/config.hpp + +# Additional defines +echo "// Additional defines" >> vmime/config.hpp + +for d in $VMIME_ADDITIONAL_DEFINES ; do + echo "#define VMIME_$d 1" >> vmime/config.hpp +done + + +echo "" >> vmime/config.hpp +echo "#endif // VMIME_CONFIG_HPP_INCLUDED" >> vmime/config.hpp + + AC_MSG_RESULT([ +=================+ | CONFIGURATION | diff --git a/src/platforms/windows/windowsHandler.cpp b/src/platforms/windows/windowsHandler.cpp index 6b84f59f..bbdcc1b6 100644 --- a/src/platforms/windows/windowsHandler.cpp +++ b/src/platforms/windows/windowsHandler.cpp @@ -18,11 +18,17 @@ // #include "vmime/platforms/windows/windowsHandler.hpp" +#include "vmime/config.hpp" #include #include #include -#include +#include // for winnls.h + +#ifdef VMIME_HAVE_MLANG_H +# include +#endif + namespace vmime { namespace platforms { @@ -62,7 +68,7 @@ const vmime::datetime windowsHandler::getCurrentLocalTime() const const time_t t(::time(NULL)); // Get the local time -#ifdef _REENTRANT +#if defined(_REENTRANT) && defined(localtime_r) tm local; ::localtime_r(&t, &local); #else @@ -70,7 +76,7 @@ const vmime::datetime windowsHandler::getCurrentLocalTime() const #endif // Get the UTC time -#ifdef _REENTRANT +#if defined(_REENTRANT) && defined(gmtime_r) tm gmt; ::gmtime_r(&t, &gmt); #else @@ -94,8 +100,9 @@ const vmime::datetime windowsHandler::getCurrentLocalTime() const const vmime::charset windowsHandler::getLocaleCharset() const { +#ifdef VMIME_HAVE_MLANG_H char szCharset[256]; - + CoInitialize(NULL); { IMultiLanguage* pMultiLanguage; @@ -111,15 +118,66 @@ const vmime::charset windowsHandler::getLocaleCharset() const pMultiLanguage->GetCodePageInfo(codePage, &cpInfo); int nLengthW = lstrlenW(cpInfo.wszBodyCharset) + 1; - + WideCharToMultiByte(codePage, 0, cpInfo.wszBodyCharset, nLengthW, szCharset, sizeof(szCharset), NULL, NULL ); - + pMultiLanguage->Release(); - } + } CoUninitialize(); return vmime::charset(szCharset); +#else // VMIME_HAVE_MLANG_H + vmime::string ch = vmime::charsets::ISO8859_1; // default + + switch (GetACP()) + { + case 437: ch = vmime::charsets::CP_437; break; + case 737: ch = vmime::charsets::CP_737; break; + case 775: ch = vmime::charsets::CP_775; break; + case 850: ch = vmime::charsets::CP_850; break; + case 852: ch = vmime::charsets::CP_852; break; + case 853: ch = vmime::charsets::CP_853; break; + case 855: ch = vmime::charsets::CP_855; break; + case 857: ch = vmime::charsets::CP_857; break; + case 858: ch = vmime::charsets::CP_858; break; + case 860: ch = vmime::charsets::CP_860; break; + case 861: ch = vmime::charsets::CP_861; break; + case 862: ch = vmime::charsets::CP_862; break; + case 863: ch = vmime::charsets::CP_863; break; + case 864: ch = vmime::charsets::CP_864; break; + case 865: ch = vmime::charsets::CP_865; break; + case 866: ch = vmime::charsets::CP_866; break; + case 869: ch = vmime::charsets::CP_869; break; + case 874: ch = vmime::charsets::CP_874; break; + + case 1125: ch = vmime::charsets::CP_1125; break; + case 1250: ch = vmime::charsets::CP_1250; break; + case 1251: ch = vmime::charsets::CP_1251; break; + case 1252: ch = vmime::charsets::CP_1252; break; + case 1253: ch = vmime::charsets::CP_1253; break; + case 1254: ch = vmime::charsets::CP_1254; break; + case 1255: ch = vmime::charsets::CP_1255; break; + case 1256: ch = vmime::charsets::CP_1256; break; + case 1257: ch = vmime::charsets::CP_1257; break; + + case 28591: ch = vmime::charsets::ISO8859_1; break; + case 28592: ch = vmime::charsets::ISO8859_2; break; + case 28593: ch = vmime::charsets::ISO8859_3; break; + case 28594: ch = vmime::charsets::ISO8859_4; break; + case 28595: ch = vmime::charsets::ISO8859_5; break; + case 28596: ch = vmime::charsets::ISO8859_6; break; + case 28597: ch = vmime::charsets::ISO8859_7; break; + case 28598: ch = vmime::charsets::ISO8859_8; break; + case 28599: ch = vmime::charsets::ISO8859_9; break; + case 28605: ch = vmime::charsets::ISO8859_15; break; + + case 65000: ch = vmime::charsets::UTF_7; break; + case 65001: ch = vmime::charsets::UTF_8; break; + } + + return (vmime::charset(ch)); +#endif } diff --git a/src/utility/url.cpp b/src/utility/url.cpp index 0c0a5799..919a3610 100644 --- a/src/utility/url.cpp +++ b/src/utility/url.cpp @@ -181,12 +181,12 @@ void url::parse(const string& str) if (colonPos == string::npos) { - host = hostPart; + host = utility::stringUtils::trim(hostPart); } else { - host = string(hostPart.begin(), hostPart.begin() + colonPos); - port = string(hostPart.begin() + colonPos + 1, hostPart.end()); + host = utility::stringUtils::trim(string(hostPart.begin(), hostPart.begin() + colonPos)); + port = utility::stringUtils::trim(string(hostPart.begin() + colonPos + 1, hostPart.end())); } // Path @@ -198,8 +198,12 @@ void url::parse(const string& str) // 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"); + else if (host.empty()) + { + // Accept empty host (eg. "file:///home/vincent/mydoc") + if (proto != PROTOCOL_FILE) + throw exceptions::malformed_url("No host specified"); + } bool onlyDigit = true; @@ -217,6 +221,9 @@ void url::parse(const string& str) iss >> portNum; + if (portNum == 0) + portNum = UNSPECIFIED_PORT; + // Now, save URL parts m_protocol = proto; diff --git a/src/utility/urlUtils.cpp b/src/utility/urlUtils.cpp index 6099c19b..70a988cf 100644 --- a/src/utility/urlUtils.cpp +++ b/src/utility/urlUtils.cpp @@ -34,17 +34,18 @@ const string urlUtils::encode(const string& s) { const char_t c = *it; - if (parserHelpers::isPrint(c) && c != '%') + if (parserHelpers::isPrint(c) && !parserHelpers::isSpace(c) && c != '%') { result += c; } else { char hex[4]; + const unsigned char k = static_cast (c); hex[0] = '%'; - hex[1] = c / 16; - hex[2] = c % 16; + hex[1] = "0123456789ABCDEF"[k / 16]; + hex[2] = "0123456789ABCDEF"[k % 16]; hex[3] = 0; result += hex; @@ -71,7 +72,35 @@ const string urlUtils::decode(const string& s) const char_t p = (++it != s.end() ? *it : 0); const char_t q = (++it != s.end() ? *it : 0); - result += static_cast (p * 16 + q); + unsigned char r = 0; + + switch (p) + { + case 0: r = '?'; break; + case 'a': case 'A': r = 10; break; + case 'b': case 'B': r = 11; break; + case 'c': case 'C': r = 12; break; + case 'd': case 'D': r = 13; break; + case 'e': case 'E': r = 14; break; + case 'f': case 'F': r = 15; break; + default: r = p - '0'; break; + } + + r *= 16; + + switch (q) + { + case 0: r = '?'; break; + case 'a': case 'A': r += 10; break; + case 'b': case 'B': r += 11; break; + case 'c': case 'C': r += 12; break; + case 'd': case 'D': r += 13; break; + case 'e': case 'E': r += 14; break; + case 'f': case 'F': r += 15; break; + default: r += q - '0'; break; + } + + result += static_cast (r); if (it != s.end()) ++it; diff --git a/tests/utility/urlTest.cpp b/tests/utility/urlTest.cpp new file mode 100644 index 00000000..cb139987 --- /dev/null +++ b/tests/utility/urlTest.cpp @@ -0,0 +1,209 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2005 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 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 "../lib/unit++/unit++.h" + +#include +#include + +#include "vmime/vmime.hpp" +#include "vmime/platforms/posix/posixHandler.hpp" + +#include "vmime/utility/url.hpp" +#include "vmime/utility/urlUtils.hpp" + +using namespace unitpp; + + +namespace +{ + class urlTest : public suite + { + static const bool parseHelper(vmime::utility::url& u, const vmime::string& str) + { + try + { + u = vmime::utility::url(str); + } + catch (vmime::exceptions::malformed_url) + { + return false; + } + + return true; + } + + + void testParse1() + { + // Test some valid constructions + vmime::utility::url u1("", ""); + + assert_eq("1.1", true, parseHelper(u1, "protocol://user:password@host:12345/path/")); + assert_eq("1.2", "protocol", u1.getProtocol()); + assert_eq("1.3", "user", u1.getUsername()); + assert_eq("1.4", "password", u1.getPassword()); + assert_eq("1.5", "host", u1.getHost()); + assert_eq("1.6", 12345, u1.getPort()); + assert_eq("1.7", "/path/", u1.getPath()); + + vmime::utility::url u2("", ""); + + assert_eq("2.1", true, parseHelper(u2, "protocol://user@host:12345/path/")); + assert_eq("2.2", "protocol", u2.getProtocol()); + assert_eq("2.3", "user", u2.getUsername()); + assert_eq("2.4", "", u2.getPassword()); + assert_eq("2.5", "host", u2.getHost()); + assert_eq("2.6", 12345, u2.getPort()); + assert_eq("2.7", "/path/", u2.getPath()); + + vmime::utility::url u3("", ""); + + assert_eq("3.1", true, parseHelper(u3, "protocol://host:12345/path/")); + assert_eq("3.2", "protocol", u3.getProtocol()); + assert_eq("3.3", "", u3.getUsername()); + assert_eq("3.4", "", u3.getPassword()); + assert_eq("3.5", "host", u3.getHost()); + assert_eq("3.6", 12345, u3.getPort()); + assert_eq("3.7", "/path/", u3.getPath()); + + vmime::utility::url u4("", ""); + + assert_eq("4.1", true, parseHelper(u4, "protocol://host/path/")); + assert_eq("4.2", "protocol", u4.getProtocol()); + assert_eq("4.3", "", u4.getUsername()); + assert_eq("4.4", "", u4.getPassword()); + assert_eq("4.5", "host", u4.getHost()); + assert_eq("4.6", vmime::utility::url::UNSPECIFIED_PORT, u4.getPort()); + assert_eq("4.7", "/path/", u4.getPath()); + + vmime::utility::url u5("", ""); + + assert_eq("5.1", true, parseHelper(u5, "protocol://host/")); + assert_eq("5.2", "protocol", u5.getProtocol()); + assert_eq("5.3", "", u5.getUsername()); + assert_eq("5.4", "", u5.getPassword()); + assert_eq("5.5", "host", u5.getHost()); + assert_eq("5.6", vmime::utility::url::UNSPECIFIED_PORT, u4.getPort()); + assert_eq("5.7", "", u5.getPath()); + + vmime::utility::url u6("", ""); + + assert_eq("6.1", true, parseHelper(u4, "protocol://host/path/file")); + assert_eq("6.2", "protocol", u4.getProtocol()); + assert_eq("6.3", "", u4.getUsername()); + assert_eq("6.4", "", u4.getPassword()); + assert_eq("6.5", "host", u4.getHost()); + assert_eq("6.6", vmime::utility::url::UNSPECIFIED_PORT, u4.getPort()); + assert_eq("6.7", "/path/file", u4.getPath()); + } + + void testParse2() + { + // Now, test some ill-formed URLs + + // -- missing protocol + vmime::utility::url u1("", ""); + assert_eq("1", false, parseHelper(u1, "://host")); + + // -- port can contain only digits + vmime::utility::url u2("", ""); + assert_eq("2", false, parseHelper(u2, "proto://host:abc123")); + + // -- no host specified + vmime::utility::url u3("", ""); + assert_eq("3", false, parseHelper(u3, "proto:///path")); + + // -- no protocol separator (://) + vmime::utility::url u4("", ""); + assert_eq("4", false, parseHelper(u4, "protohost/path")); + } + + void testParse3() + { + // Test decoding + vmime::utility::url u1("", ""); + + assert_eq("1.1", true, parseHelper(u1, "pro%12to://user%34:pass%56word@ho%78st:12345/pa%abth/")); + assert_eq("1.2", "pro%12to", u1.getProtocol()); // protocol should not be decoded + assert_eq("1.3", "user\x34", u1.getUsername()); + assert_eq("1.4", "pass\x56word", u1.getPassword()); + assert_eq("1.5", "ho\x78st", u1.getHost()); + assert_eq("1.6", 12345, u1.getPort()); + assert_eq("1.7", "/pa\xabth/", u1.getPath()); + } + + void testGenerate() + { + vmime::utility::url u1("proto", "host", 12345, "path", "user", "password"); + assert_eq("1", "proto://user:password@host:12345/path", + static_cast (u1)); + + // TODO: more tests + } + + void testUtilsEncode() + { + assert_eq("1", "%01", vmime::utility::urlUtils::encode("\x01")); + assert_eq("2", "%20", vmime::utility::urlUtils::encode(" ")); + assert_eq("3", "%FF", vmime::utility::urlUtils::encode("\xff")); + assert_eq("4", "a", vmime::utility::urlUtils::encode("a")); + } + + void testUtilsDecode() + { + for (int i = 0 ; i < 255 ; ++i) + { + std::ostringstream ossTest; + ossTest << "%" << "0123456789ABCDEF"[i / 16] + << "0123456789ABCDEF"[i % 16]; + + std::ostringstream ossNum; + ossNum << i; + + vmime::string res; + res += static_cast (i); + + assert_eq(ossNum.str(), res, + vmime::utility::urlUtils::decode(ossTest.str())); + } + + } + + public: + + urlTest() : suite("vmime::utility::url") + { + // VMime initialization + vmime::platformDependant::setHandler(); + + add("Parse1", testcase(this, "Parse1", &urlTest::testParse1)); + add("Parse2", testcase(this, "Parse2", &urlTest::testParse2)); + add("Parse3", testcase(this, "Parse3", &urlTest::testParse3)); + add("Generate", testcase(this, "Generate", &urlTest::testGenerate)); + add("UtilsEncode", testcase(this, "UtilsEncode", &urlTest::testUtilsEncode)); + add("UtilsDecode", testcase(this, "UtilsDecode", &urlTest::testUtilsDecode)); + + suite::main().add("vmime::utility::url", this); + } + + }; + + urlTest* theTest = new urlTest(); +}