Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,28 @@ jobs:
make install
make test

build-windows:
name: build-windows (mingw, openssl, icu)
runs-on: windows-latest
steps:
- name: git-config
run: git config --global core.autocrlf input
- name: install-deps
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
pacboy: cmake:p cppunit:p gcc:p gsasl:p icu:p ninja:p openssl:p pkgconf:p
- name: clone-vmime
uses: actions/checkout@v4
- name: buildall
shell: msys2 {0}
run: |
set -ex
cmake . -G Ninja -DVMIME_HAVE_MESSAGING_PROTO_SENDMAIL:BOOL=OFF -DVMIME_HAVE_TLS_SUPPORT:BOOL=ON -DVMIME_TLS_SUPPORT_LIB:STRING=openssl -DVMIME_CHARSETCONV_LIB:STRING=icu -DVMIME_BUILD_TESTS=ON -DVMIME_BUILD_SAMPLES=ON
cmake --build .
ctest --output-on-failure

book:
name: book (pdf)
runs-on: ubuntu-26.04
Expand Down
14 changes: 14 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,18 @@ IF(VMIME_BUILD_TESTS)
${CPPUNIT_LIBRARY}
)

# On Windows, embed a manifest in test executables to use UTF-8 as the
# ANSI code page, as some tests expect the local charset to be UTF-8
SET(VMIME_TESTS_RESOURCE_FILES "")

IF(MSVC)
# Merged by the linker with the manifest it generates
SET(VMIME_TESTS_RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tests/utf8.manifest)
ELSEIF(MINGW)
# Replaces the default manifest added by the toolchain
SET(VMIME_TESTS_RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tests/utf8.rc)
ENDIF()

# Build one file for each test
FOREACH(VMIME_TEST_SRC_FILE ${VMIME_TESTS_SRC_FILES})

Expand All @@ -282,6 +294,7 @@ IF(VMIME_BUILD_TESTS)
ADD_EXECUTABLE(
${VMIME_TEST_NAME}
${VMIME_TEST_SRC_FILE}
${VMIME_TESTS_RESOURCE_FILES}
)

TARGET_LINK_LIBRARIES(
Expand All @@ -306,6 +319,7 @@ IF(VMIME_BUILD_TESTS)
ADD_EXECUTABLE(
"run-tests"
${VMIME_TESTS_SRC_FILES}
${VMIME_TESTS_RESOURCE_FILES}
)

TARGET_LINK_LIBRARIES(
Expand Down
14 changes: 14 additions & 0 deletions src/vmime/platforms/posix/posixFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <dirent.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "vmime/exception.hpp"
Expand Down Expand Up @@ -673,6 +674,19 @@ bool posixFileSystemFactory::isValidPath(const vmime::utility::file::path& path)
}


const vmime::utility::file::path posixFileSystemFactory::getTemporaryDirectoryPath() const {

// Directory specified by the user (POSIX), or default one
const char* dir = ::getenv("TMPDIR");

if (!dir || !*dir) {
dir = "/tmp";
}

return stringToPathImpl(dir);
}


void posixFileSystemFactory::reportError(const vmime::utility::path& path, const int err) {

vmime::string desc;
Expand Down
2 changes: 2 additions & 0 deletions src/vmime/platforms/posix/posixFile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ class posixFileSystemFactory : public vmime::utility::fileSystemFactory {
bool isValidPathComponent(const vmime::utility::file::path::component& comp) const;
bool isValidPath(const vmime::utility::file::path& path) const;

const vmime::utility::file::path getTemporaryDirectoryPath() const;

static void reportError(const vmime::utility::path& path, const int err);
};

Expand Down
24 changes: 22 additions & 2 deletions src/vmime/platforms/windows/windowsFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,20 @@ bool windowsFileSystemFactory::isValidPath(const vmime::utility::file::path& pat
}


const vmime::utility::file::path windowsFileSystemFactory::getTemporaryDirectoryPath() const {

char buffer[MAX_PATH + 1];
const DWORD length = GetTempPathA(static_cast <DWORD>(sizeof(buffer)), buffer);

if (length == 0 || length > sizeof(buffer)) {
reportError(vmime::utility::file::path(), GetLastError());
}

// Trailing backslash is ignored
return stringToPathImpl(vmime::string(buffer, length));
}


void windowsFileSystemFactory::reportError(const vmime::utility::path& path, const int err) {

vmime::string desc;
Expand Down Expand Up @@ -408,7 +422,12 @@ void windowsFile::rename(const path& newName) {

void windowsFile::remove() {

if (!DeleteFile(m_nativePath.c_str())) {
// DeleteFile() does not remove directories
const BOOL removed = isDirectory()
? RemoveDirectory(m_nativePath.c_str())
: DeleteFile(m_nativePath.c_str());

if (!removed) {
windowsFileSystemFactory::reportError(m_path, GetLastError());
}
}
Expand All @@ -428,7 +447,8 @@ shared_ptr <vmime::utility::fileReader> windowsFile::getFileReader() {

shared_ptr <vmime::utility::fileIterator> windowsFile::getFiles() const {

return make_shared <windowsFileIterator>(m_path, m_nativePath);
// FindFirstFile() expects a pattern, else it finds the directory itself
return make_shared <windowsFileIterator>(m_path, m_nativePath + "\\*");
}


Expand Down
2 changes: 2 additions & 0 deletions src/vmime/platforms/windows/windowsFile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class windowsFileSystemFactory : public vmime::utility::fileSystemFactory {
bool firstComponent) const;
bool isValidPath(const vmime::utility::file::path& path) const;

const vmime::utility::file::path getTemporaryDirectoryPath() const;

static void reportError(const vmime::utility::path& path, const int err);
};

Expand Down
10 changes: 8 additions & 2 deletions src/vmime/platforms/windows/windowsHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@

#if VMIME_HAVE_MLANG
# include <mlang.h>

// Defined here, as IID_IMultiLanguage is missing from MinGW libraries
static const CLSID VMIME_CLSID_CMultiLanguage =
{ 0x275c23e2, 0x3747, 0x11d0, { 0x9f, 0xea, 0x00, 0xaa, 0x00, 0x3f, 0x86, 0x46 } };
static const IID VMIME_IID_IMultiLanguage =
{ 0x275c23e1, 0x3747, 0x11d0, { 0x9f, 0xea, 0x00, 0xaa, 0x00, 0x3f, 0x86, 0x46 } };
#endif


Expand Down Expand Up @@ -132,10 +138,10 @@ const vmime::charset windowsHandler::getLocalCharset() const {
IMultiLanguage* pMultiLanguage;

CoCreateInstance(
CLSID_CMultiLanguage,
VMIME_CLSID_CMultiLanguage,
NULL,
CLSCTX_INPROC_SERVER,
IID_IMultiLanguage,
VMIME_IID_IMultiLanguage,
(void**) &pMultiLanguage
);

Expand Down
14 changes: 14 additions & 0 deletions src/vmime/utility/file.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@


#include "vmime/config.hpp"
#include "vmime/exception.hpp"

#include "vmime/utility/path.hpp"
#include "vmime/utility/stream.hpp"
Expand Down Expand Up @@ -251,6 +252,19 @@ class VMIME_EXPORT fileSystemFactory : public object {
* @return true if the path is valid, false otherwise
*/
virtual bool isValidPath(const file::path& path) const = 0;

/** Return the path of the directory to use for temporary files.
*
* @return path of the system temporary directory
* @throw exceptions::filesystem_exception if the temporary directory
* cannot be determined, or if not supported on this platform
*/
virtual const file::path getTemporaryDirectoryPath() const {

throw exceptions::filesystem_exception(
"Temporary directory is not supported on this platform", file::path()
);
}
};


Expand Down
23 changes: 18 additions & 5 deletions tests/net/maildir/maildirStoreTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ const fpath operator/(const fpath& path, const std::string& c) {
}


// Delimiter between message id and flags in file names,
// same as in maildirUtils::buildFilename()
#if VMIME_PLATFORM_IS_WINDOWS
# define TEST_FLAGS_DELIMITER "-" // ':' is not allowed in file names
#else
# define TEST_FLAGS_DELIMITER ":"
#endif


/** Test messages */
static const vmime::string TEST_MESSAGE_1 =
"From: <test@vmime.org>\r\n"
Expand Down Expand Up @@ -92,7 +101,7 @@ static const vmime::string TEST_MAILDIR_KMAIL[] = { // directories to create
};

static const vmime::string TEST_MAILDIRFILES_KMAIL[] = { // files to create and their contents
"/.Folder.directory/.SubFolder.directory/SubSubFolder2/cur/1043236113.351.EmqD:S", TEST_MESSAGE_1,
"/.Folder.directory/.SubFolder.directory/SubSubFolder2/cur/1043236113.351.EmqD" TEST_FLAGS_DELIMITER "S", TEST_MESSAGE_1,
"*" // end
};

Expand Down Expand Up @@ -127,7 +136,7 @@ static const vmime::string TEST_MAILDIRFILES_COURIER[] = { // files to create a
"/.Folder.SubFolder/maildirfolder", "",
"/.Folder.SubFolder.SubSubFolder1/maildirfolder", "",
"/.Folder.SubFolder.SubSubFolder2/maildirfolder", "",
"/.Folder.SubFolder.SubSubFolder2/cur/1043236113.351.EmqD:S", TEST_MESSAGE_1,
"/.Folder.SubFolder.SubSubFolder2/cur/1043236113.351.EmqD" TEST_FLAGS_DELIMITER "S", TEST_MESSAGE_1,
"/.Folder2/maildirfolder", "",
"*" // end
};
Expand Down Expand Up @@ -167,8 +176,11 @@ VMIME_TEST_SUITE_BEGIN(maildirStoreTest)

maildirStoreTest() {

vmime::shared_ptr <vmime::utility::fileSystemFactory> fsf =
vmime::platform::getHandler()->getFileSystemFactory();

// Temporary directory
m_tempPath = fspath() / fspathc("tmp") // Use /tmp
m_tempPath = fsf->getTemporaryDirectoryPath()
/ fspathc("vmime" + vmime::utility::stringUtils::toString(std::time(NULL))
+ vmime::utility::stringUtils::toString(std::rand()));
}
Expand Down Expand Up @@ -500,8 +512,9 @@ VMIME_TEST_SUITE_BEGIN(maildirStoreTest)
vmime::shared_ptr <vmime::utility::fileSystemFactory> fsf =
vmime::platform::getHandler()->getFileSystemFactory();

vmime::utility::url url(std::string("maildir://localhost")
+ fsf->pathToString(m_tempPath));
// Path is set as is, as it may not start with a separator (eg. on Windows)
vmime::utility::url url("maildir", "localhost"); // maildir://localhost
url.setPath(fsf->pathToString(m_tempPath));

return url;
}
Expand Down
9 changes: 6 additions & 3 deletions tests/parser/fileContentHandlerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,16 @@ VMIME_TEST_SUITE_BEGIN(fileContentHandlerTest)
testDataDecoded = "ABCDEFGHIJKLMNOPQRSTUVWXYZ \x12\x34\x56\x78\x90 abcdefghijklmnopqrstuvwxyz0123456789";
testDataEncoded = "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogEjRWeJAgYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5";

std::ostringstream testFilePath;
testFilePath << "/tmp/vmime_test_" << (rand() % 999999999);
std::ostringstream testFileName;
testFileName << "vmime_test_" << (rand() % 999999999);

vmime::shared_ptr <vmime::utility::fileSystemFactory> fsf =
vmime::platform::getHandler()->getFileSystemFactory();

testFile = fsf->create(fsf->stringToPath(testFilePath.str()));
testFile = fsf->create(
fsf->getTemporaryDirectoryPath()
/ vmime::utility::file::path::component(testFileName.str())
);
testFile->createFile();
testFile->getFileWriter()->getOutputStream()->write(testDataEncoded.data(), testDataEncoded.length());
}
Expand Down
7 changes: 6 additions & 1 deletion tests/parser/textTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,12 @@ VMIME_TEST_SUITE_BEGIN(textTest)
vmime::word("Non-quotable text \xc3\xa9")
.generate(ctx, os, 0, NULL, vmime::text::QUOTE_IF_POSSIBLE, NULL);

VASSERT_EQ("3", "=?UTF-8?Q?Non-quotable_text_=C3=A9?=", cleanGeneratedWords(str));
// Local charset name may be "UTF-8" or "utf-8", depending on the platform
VASSERT_EQ(
"3",
"=?" + vmime::charset::getLocalCharset().getName() + "?Q?Non-quotable_text_=C3=A9?=",
cleanGeneratedWords(str)
);
}

void testWordGenerateSpecialCharsets() {
Expand Down
3 changes: 2 additions & 1 deletion tests/testUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,8 @@ const vmime::string toHex(const vmime::string str) {


// Set the global C and C++ locale to a locale which uses UTF-8 encoding,
// as some tests expect the local charset to be UTF-8
// as some tests expect the local charset to be UTF-8 (on Windows, the local
// charset is the ANSI code page instead, which is set by tests/utf8.manifest)
static void setUTF8Locale() {

// Try well-known UTF-8 locales, then fall back to the user-configured locale
Expand Down
22 changes: 22 additions & 0 deletions tests/utf8.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 and later -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- Use UTF-8 as the ANSI code page (Windows 10 version 1903 and later) -->
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
</windowsSettings>
</application>
</assembly>
6 changes: 6 additions & 0 deletions tests/utf8.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Application manifest for test executables on Windows (MinGW): use UTF-8 as
// the ANSI code page, which is used as the local charset by windowsHandler,
// as some tests expect the local charset to be UTF-8.
// This replaces the default manifest added by the MinGW toolchain.

1 24 "utf8.manifest" // CREATEPROCESS_MANIFEST_RESOURCE_ID, RT_MANIFEST
Loading