//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002 Vincent Richard <vincent@vmime.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 3 of
// the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Linking this library statically or dynamically with other modules is making
// a combined work based on this library. Thus, the terms and conditions of
// the GNU General Public License cover the whole combination.
//
#include "vmime/config.hpp"
#if VMIME_PLATFORM_IS_POSIX
#include "vmime/platforms/posix/posixHandler.hpp"
#include "vmime/platforms/posix/posixCriticalSection.hpp"
#include "vmime/utility/stringUtils.hpp"
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <locale.h>
#include <langinfo.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#if VMIME_HAVE_SYSCALL
# include <sys/syscall.h>
#endif
#include <netdb.h>
#include <string.h>
#include <cassert>
#include <cstdlib>
/*
#ifdef _POSIX_PRIORITY_SCHEDULING
#include <sched.h>
#endif // _POSIX_PRIORITY_SCHEDULING
*/
namespace vmime {
namespace platforms {
namespace posix {
posixHandler::posixHandler() {
#if VMIME_HAVE_MESSAGING_FEATURES
m_socketFactory = make_shared <posixSocketFactory>();
#endif
#if VMIME_HAVE_FILESYSTEM_FEATURES
m_fileSysFactory = make_shared <posixFileSystemFactory>();
m_childProcFactory = make_shared <posixChildProcessFactory>();
#endif
}
posixHandler::~posixHandler() {
}
unsigned long posixHandler::getUnixTime() const {
return static_cast <unsigned long>(::time(NULL));
}
const vmime::datetime posixHandler::getCurrentLocalTime() const {
const time_t t(::time(NULL));
// Get the local time
#if VMIME_HAVE_LOCALTIME_R
tm local;
::localtime_r(&t, &local);
#else
tm local = *::localtime(&t); // WARNING: this is not thread-safe!
#endif
// Get the UTC time
#if VMIME_HAVE_GMTIME_R
tm gmt;
::gmtime_r(&t, &gmt);
#else
tm gmt = *::gmtime(&t); // WARNING: this is not thread-safe!
#endif
// "A negative value for tm_isdst causes mktime() to attempt
// to determine whether Daylight Saving Time is in effect
// for the specified time."
local.tm_isdst = -1;
gmt.tm_isdst = -1;
// Calculate the difference (in seconds)
const time_t diff = ::mktime(&local) - ::mktime(&gmt);
// Return the date
return vmime::datetime(
local.tm_year + 1900, local.tm_mon + 1, local.tm_mday,
local.tm_hour, local.tm_min, local.tm_sec, static_cast <int>(diff / 60)
);
}
const vmime::charset posixHandler::getLocalCharset() const {
// Note: nl_langinfo() might be affected by calls to setlocale()
// in a multithread environment. There is not MT-safe alternative
// to nl_langinfo().
auto codeset = ::nl_langinfo(CODESET);
if (codeset) {
return vmime::charset(codeset);
}
return vmime::charset();
}
static inline bool isAcceptableHostname(const vmime::string& str) {
// At least, try to find something better than "localhost"
if (utility::stringUtils::isStringEqualNoCase(str, "localhost", 9) ||
utility::stringUtils::isStringEqualNoCase(str, "localhost.localdomain", 21)) {
return false;
}
// Anything else will be OK, as long as it is a valid hostname
return utility::stringUtils::isValidHostname(str);
}
const vmime::string posixHandler::getHostName() const {
char hostname[256];
::gethostname(hostname, sizeof(hostname));
hostname[sizeof(hostname)-1] = '\0';
// Try to get official canonical name of this host
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // either IPV4 or IPV6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
struct addrinfo* info;
if (getaddrinfo(hostname, "http", &hints, &info) == 0) {
// First, try to get a Fully-Qualified Domain Name (FQDN)
for (struct addrinfo* p = info ; p != NULL ; p = p->ai_next) {
if (p->ai_canonname) {
const string hn(p->ai_canonname);
if (utility::stringUtils::isValidFQDN(hn)) {
freeaddrinfo(info);
return hn;
}
}
}
// Then, try to find an acceptable host name
for (struct addrinfo* p = info ; p != NULL ; p = p->ai_next) {
if (p->ai_canonname) {
const string hn(p->ai_canonname);
if (isAcceptableHostname(hn)) {
freeaddrinfo(info);
return hn;
}
}
}
freeaddrinfo(info);
}
if (::strlen(hostname) == 0 || !isAcceptableHostname(hostname)) {
::strcpy(hostname, "localhost.localdomain");
}
return hostname;
}
unsigned int posixHandler::getProcessId() const {
return ::getpid();
}
unsigned int posixHandler::getThreadId() const {
#if VMIME_HAVE_GETTID
return static_cast <unsigned int>(::gettid());
#elif VMIME_HAVE_SYSCALL && VMIME_HAVE_SYSCALL_GETTID
return static_cast <unsigned int>(::syscall(SYS_gettid));
#elif VMIME_HAVE_GETTHRID // OpenBSD
return static_cast <unsigned int>(::getthrid());
#else
#error We have no implementation of getThreadId() for this platform!
#endif
}
#if VMIME_HAVE_MESSAGING_FEATURES
shared_ptr <vmime::net::socketFactory> posixHandler::getSocketFactory() {
return m_socketFactory;
}
#endif
#if VMIME_HAVE_FILESYSTEM_FEATURES
shared_ptr <vmime::utility::fileSystemFactory> posixHandler::getFileSystemFactory() {
return m_fileSysFactory;
}
shared_ptr <vmime::utility::childProcessFactory> posixHandler::getChildProcessFactory() {
return m_childProcFactory;
}
#endif
void posixHandler::generateRandomBytes(unsigned char* buffer, const unsigned int count) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd != -1) {
read(fd, buffer, count);
close(fd);
} else { // fallback
for (unsigned int i = 0 ; i < count ; ++i) {
buffer[i] = static_cast <unsigned char>(rand() % 255);
}
}
}
shared_ptr <utility::sync::criticalSection> posixHandler::createCriticalSection() {
return make_shared <posixCriticalSection>();
}
} // posix
} // platforms
} // vmime
#endif // VMIME_PLATFORM_IS_POSIX