You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pEpEngine/src/platform_unix.c

533 lines
12 KiB
C

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

// This file is under GNU General Public License 3.0
// see LICENSE.txt
#define _POSIX_C_SOURCE 200809L
#ifdef ANDROID
#ifndef __LP64__
#include <time64.h>
#endif
#endif
#include <stdbool.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <glob.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <regex.h>
#include "platform_unix.h"
#include "dynamic_api.h"
#define MAX_PATH 1024
#ifndef LOCAL_DB_FILENAME
#define OLD_LOCAL_DB_FILENAME ".pEp_management.db"
#define OLD_KEYS_DB_FILENAME ".pEp_keys.db"
#define LOCAL_DB_FILENAME "management.db"
#define KEYS_DB_FILENAME "keys.db"
#endif
#define SYSTEM_DB_FILENAME "system.db"
#ifdef ANDROID
#include <uuid.h>
/* FIXME : timegm will miss when linking for x86_64 on android, when supported */
#ifndef __LP64__
time_t timegm(struct tm* const t) {
static const time_t kTimeMax = ~(1L << (sizeof(time_t) * CHAR_BIT - 1));
static const time_t kTimeMin = (1L << (sizeof(time_t) * CHAR_BIT - 1));
time64_t result = timegm64(t);
if (result < kTimeMin || result > kTimeMax)
return -1;
return result;
}
#endif
char *stpncpy(char *dst, const char *src, size_t n)
{
if (n != 0) {
char *d = dst;
const char *s = src;
dst = &dst[n];
do {
if ((*d++ = *s++) == 0) {
dst = d - 1;
/* NUL pad the remaining n-1 bytes */
while (--n != 0)
*d++ = 0;
break;
}
} while (--n != 0);
}
return (dst);
}
char *stpcpy(char *dst, const char *src)
{
for (;; ++dst, ++src) {
*dst = *src;
if (*dst == 0)
break;
}
return dst;
}
/*
long int random(void)
{
static bool seeded = false;
static unsigned short xsubi[3];
if(!seeded)
{
const long long t = (long long)time(NULL);
xsubi[0] = (unsigned short)t;
xsubi[1] = (unsigned short)(t>>16);
xsubi[2] = (unsigned short)(t>>32);
seeded = true;
}
return nrand48(xsubi);
} */
const char *android_system_db(void)
{
static char buffer[MAX_PATH];
static bool done = false;
if (!done) {
char *tw_env;
if(tw_env = getenv("TRUSTWORDS")){
char *p = stpncpy(buffer, tw_env, MAX_PATH);
ssize_t len = MAX_PATH - (p - buffer) - 2;
if (len < strlen(SYSTEM_DB_FILENAME)) {
assert(0);
return NULL;
}
*p++ = '/';
strncpy(p, SYSTEM_DB_FILENAME, len);
done = true;
}else{
return NULL;
}
}
return buffer;
}
void uuid_generate_random(pEpUUID out)
{
uuid_t *uuid;
uuid_rc_t rc_create;
size_t size = sizeof(uuid_string_t);
void *_out = out;
if ((rc_create = uuid_create(&uuid)) != UUID_RC_OK ||
uuid_make(uuid, UUID_MAKE_V1) != UUID_RC_OK ||
uuid_export(uuid, UUID_FMT_BIN, &_out, &size) != UUID_RC_OK)
{
memset(out, 0, sizeof(pEpUUID));
}
if (rc_create == UUID_RC_OK)
{
uuid_destroy(uuid);
}
}
void uuid_unparse_upper(pEpUUID uu, uuid_string_t out)
{
uuid_t *uuid;
uuid_rc_t rc_create;
size_t size = sizeof(uuid_string_t);
void *_out = out;
if ((rc_create = uuid_create(&uuid)) != UUID_RC_OK ||
uuid_import(uuid, UUID_FMT_BIN, uu, sizeof(pEpUUID)) != UUID_RC_OK ||
uuid_export(uuid, UUID_FMT_STR, &_out, &size) != UUID_RC_OK)
{
memset(out, 0, sizeof(uuid_string_t));
}
else
{
out[sizeof(uuid_string_t) - 1] = 0;
}
if (rc_create == UUID_RC_OK)
{
uuid_destroy(uuid);
}
}
#endif
#if !defined(BSD) && !defined(__APPLE__)
size_t strlcpy(char* dst, const char* src, size_t size) {
size_t retval = strlen(src);
size_t size_to_copy = (retval < size ? retval : size - 1);
// strlcpy doc says src and dst not allowed to overlap, as
// it's undefined. So this is acceptable:
memcpy((void*)dst, (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
dst[size_to_copy] = '\0';
return retval;
}
size_t strlcat(char* dst, const char* src, size_t size) {
size_t start_len = strnlen(dst, size);
if (start_len == size)
return size; // no copy, no null termination in size bytes, according to spec
size_t add_len = strlen(src);
size_t retval = start_len + add_len;
size_t size_to_copy = (retval < size ? add_len : (size - start_len) - 1);
// strlcat doc says src and dst not allowed to overlap, as
// it's undefined. So this is acceptable:
memcpy((void*)(dst + start_len), (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
dst[start_len + size_to_copy] = '\0';
return retval;
}
char *strnstr(const char *big, const char *little, size_t len) {
if (big == NULL || little == NULL)
return NULL;
if (*little == '\0')
return (char*)big;
const char* curr_big = big;
size_t little_len = strlen(little);
size_t remaining = len;
const char* retval = NULL;
for (remaining = len; remaining >= little_len && *curr_big != '\0'; remaining--, curr_big++) {
// find first-char match
if (*curr_big != *little) {
continue;
}
retval = curr_big;
const char* inner_big = retval + 1;
const char* curr_little = little + 1;
int j;
for (j = 1; j < little_len; j++, inner_big++, curr_little++) {
if (*inner_big != *curr_little) {
retval = NULL;
break;
}
}
if (retval)
break;
}
return (char*)retval;
}
// #ifdef USE_NETPGP
// // FIXME: This may cause problems - this is a quick compatibility fix for netpgp code
// int regnexec(const regex_t* preg, const char* string,
// size_t len, size_t nmatch, regmatch_t pmatch[], int eflags) {
// return regexec(preg, string, nmatch, pmatch, eflags);
// }
// #endif
#endif
static char *_stradd(char **first, const char *second)
{
assert(first && *first && second);
if (!(first && *first && second))
return NULL;
size_t len1 = strlen(*first);
size_t len2 = strlen(second);
size_t size = len1 + len2 + 1;
char *_first = realloc(*first, size);
assert(_first);
if (!_first)
return NULL;
*first = _first;
strlcat(*first, second, size);
return *first;
}
static void _empty(char **p)
{
free(*p);
*p = NULL;
}
static void _move(const char *o, const char *ext, const char *n)
{
assert(o && ext && n);
if (!(o && ext && n))
return;
char *_old = strdup(o);
assert(_old);
if (!_old)
return;
char *r = _stradd(&_old, ext);
if (!r) {
free(_old);
return;
}
char *_new = strdup(n);
assert(_new);
if (!_new) {
free(_old);
return;
}
r = _stradd(&_new, ext);
if (r)
rename(_old, _new);
free(_old);
free(_new);
}
#ifndef NDEBUG
static const char *_per_user_directory(int reset)
#else
static const char *_per_user_directory(void)
#endif
{
static char *path = NULL;
#ifdef NDEBUG
if (path)
return path;
#else
if (path && !reset)
return path;
else if (path) {
free(path);
path = NULL;
}
#endif
const char *home = NULL;
#ifndef NDEBUG
home = getenv("PEP_HOME");
if (!home)
#endif
home = getenv("HOME");
assert(home);
if (!home)
return NULL;
path = strdup(home);
assert(path);
if (!path)
return NULL;
char *_path = _stradd(&path, "/");
if (!_path)
goto error;
_path = _stradd(&path, PER_USER_DIRECTORY);
if (!_path)
goto error;
return path;
error:
_empty(&path);
return NULL;
}
#ifdef NDEBUG
const char *unix_local_db(void)
#else
const char *unix_local_db(int reset)
#endif
{
static char *path = NULL;
#ifdef NDEBUG
if (path)
#else
if (path && !reset)
#endif
return path;
const char* pathret = NULL;
#ifndef NDEBUG
pathret = _per_user_directory(reset);
#else
pathret = _per_user_directory();
#endif
if (!pathret)
return NULL;
path = strdup(pathret);
assert(path);
if (!path)
return NULL;
char *path_c = NULL;
char *old_path = NULL;
char *old_path_c = NULL;
struct stat dir;
int r = stat(path, &dir);
if (r) {
if (errno == ENOENT) {
// directory does not yet exist
r = mkdir(path, 0700);
if (r)
goto error;
}
else {
goto error;
}
}
char *_path = _stradd(&path, "/");
if (!_path)
goto error;
// make a copy of this path in case we need to move files
path_c = strdup(path);
assert(path_c);
if (!path_c)
goto error;
_path = _stradd(&path, LOCAL_DB_FILENAME);
if (!_path)
goto error;
struct stat file;
r = stat(path, &file);
if (r) {
if (errno == ENOENT) {
// we do not have management.db yet, let's test if we need to move
// one with the old name
const char *home = NULL;
#ifndef NDEBUG
home = getenv("PEP_HOME");
if (!home)
#endif
home = getenv("HOME");
// we were already checking for HOME existing, so this is only a
// safeguard
assert(home);
old_path = strdup(home);
assert(old_path);
if (!old_path)
goto error;
char *_old_path = _stradd(&old_path, "/");
if (!_old_path)
goto error;
old_path_c = strdup(old_path);
assert(old_path_c);
if (!old_path_c)
goto error;
_old_path = _stradd(&old_path, OLD_LOCAL_DB_FILENAME);
if (!_old_path)
goto error;
struct stat old;
r = stat(old_path, &old);
if (r == 0) {
// old file existing, new file not yet existing, move
rename(old_path, path);
// if required move associated files, too
_move(old_path, "-shm", path);
_move(old_path, "-wal", path);
// move keys database
_old_path = _stradd(&old_path_c, OLD_KEYS_DB_FILENAME);
if (!_old_path)
goto error;
_path = _stradd(&path_c, KEYS_DB_FILENAME);
if (!_path)
goto error;
rename(old_path_c, path_c);
// if required move associated files, too
_move(old_path_c, "-shm", path_c);
_move(old_path_c, "-wal", path_c);
}
}
else {
goto error;
}
}
goto the_end;
error:
_empty(&path);
the_end:
free(path_c);
free(old_path);
free(old_path_c);
return path;
}
DYNAMIC_API const char *per_user_directory(void) {
#ifdef NDEBUG
return _per_user_directory();
#else
return _per_user_directory(false);
#endif
}
DYNAMIC_API const char *per_machine_directory(void)
{
return PER_MACHINE_DIRECTORY;
}
const char *unix_system_db(void)
{
static char *path = NULL;
if (path)
return path;
path = strdup(per_machine_directory());
assert(path);
if (!path)
return NULL;
char *_path = _stradd(&path, "/");
if (!_path)
goto error;
_path = _stradd(&path, SYSTEM_DB_FILENAME);
if (!_path)
goto error;
return path;
error:
_empty(&path);
return NULL;
}