/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

#include "common.h"

#ifdef WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x500
#endif
#endif

#include "utils.h"

#ifdef WIN32

#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <Rpc.h>
#include <shlobj.h>
#include <psapi.h>
#include <openssl/applink.c>

#else
#include <arpa/inet.h>
#endif

#ifndef WIN32
#include <config.h>
#include <pwd.h>
#include <uuid/uuid.h>
#endif

#ifndef WIN32
#include <unistd.h>
#include <dirent.h>
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <string.h>

#include <glib.h>
#include <glib/gstdio.h>
#include <searpc-utils.h>

#include <jansson.h>

#ifndef WIN32
#include <utime.h>
#endif

#if defined __linux__ || defined __APPLE__
#include <sys/xattr.h>
#endif

#if defined __FreeBSD__ || defined __NetBSD__
#include <sys/extattr.h>
#endif

#include <zlib.h>

#include "log.h"



void
rawdata_to_hex (const unsigned char *rawdata, char *hex_str, int n_bytes)
{
    static const char hex[] = "0123456789abcdef";
    int i;

    for (i = 0; i < n_bytes; i++) {
        unsigned int val = *rawdata++;
        *hex_str++ = hex[val >> 4];
        *hex_str++ = hex[val & 0xf];
    }
    *hex_str = '\0';
}

static unsigned hexval(char c)
{
    if (c >= '0' && c <= '9')
        return c - '0';
    if (c >= 'a' && c <= 'f')
        return c - 'a' + 10;
    if (c >= 'A' && c <= 'F')
        return c - 'A' + 10;
    return ~0;
}

int
hex_to_rawdata (const char *hex_str, unsigned char *rawdata, int n_bytes)
{
    int i;
    for (i = 0; i < n_bytes; i++) {
        unsigned int val = (hexval(hex_str[0]) << 4) | hexval(hex_str[1]);
        if (val & ~0xff)
            return -1;
        *rawdata++ = val;
        hex_str += 2;
    }
    return 0;
}

size_t
ccnet_strlcpy (char *dest, const char *src, size_t size)
{
    size_t ret = strlen(src);

    if (size) {
        size_t len = (ret >= size) ? size - 1 : ret;
        memcpy(dest, src, len);
        dest[len] = '\0';
    }
    return ret;
}


int
checkdir (const char *dir)
{
    SeafStat st;

#ifdef WIN32
    /* remove trailing '\\' */
    char *path = g_strdup(dir);
    char *p = (char *)path + strlen(path) - 1;
    while (*p == '\\' || *p == '/') *p-- = '\0';
    if ((seaf_stat(dir, &st) < 0) || !S_ISDIR(st.st_mode)) {
        g_free (path);
        return -1;
    }
    g_free (path);
    return 0;
#else
    if ((seaf_stat(dir, &st) < 0) || !S_ISDIR(st.st_mode))
        return -1;
    return 0;
#endif
}

int
checkdir_with_mkdir (const char *dir)
{
    int ret;
#ifdef WIN32
    char *path = g_strdup(dir);
    char *p = (char *)path + strlen(path) - 1;
    while (*p == '\\' || *p == '/') *p-- = '\0';
    ret = g_mkdir_with_parents(path, 0755);
    g_free (path);
    return ret;
#elif defined __APPLE__
    char *dir_nfd = g_utf8_normalize (dir, -1, G_NORMALIZE_NFD);
    ret = g_mkdir_with_parents(dir_nfd, 0755);
    g_free (dir_nfd);
    return ret;
#else
    return g_mkdir_with_parents(dir, 0755);
#endif
}

int
objstore_mkdir (const char *base)
{
    int ret;
    int i, j, len;
    static const char hex[] = "0123456789abcdef";
    char subdir[SEAF_PATH_MAX];

    if ( (ret = checkdir_with_mkdir(base)) < 0)
        return ret;

    len = strlen(base);
    memcpy(subdir, base, len);
    subdir[len] = G_DIR_SEPARATOR;
    subdir[len+3] = '\0';

    for (i = 0; i < 16; i++) {
        subdir[len+1] = hex[i];
        for (j = 0; j < 16; j++) {
            subdir[len+2] = hex[j];
            if ( (ret = checkdir_with_mkdir(subdir)) < 0)
                return ret;
        }
    }
    return 0;
}

void
objstore_get_path (char *path, const char *base, const char *obj_id)
{
    int len;

    len = strlen(base);
    memcpy(path, base, len);
    path[len] = G_DIR_SEPARATOR;
    path[len+1] = obj_id[0];
    path[len+2] = obj_id[1];
    path[len+3] = G_DIR_SEPARATOR;
    strcpy(path+len+4, obj_id+2);
}

#ifdef WIN32

/* UNIX epoch expressed in Windows time, the unit is 100 nanoseconds.
 * See http://msdn.microsoft.com/en-us/library/ms724228
 */
#define UNIX_EPOCH 116444736000000000ULL

__time64_t
file_time_to_unix_time (FILETIME *ftime)
{
    guint64 win_time, unix_time;

    win_time = (guint64)ftime->dwLowDateTime + (((guint64)ftime->dwHighDateTime)<<32);
    unix_time = (win_time - UNIX_EPOCH)/10000000;

    return (__time64_t)unix_time;
}

static int
get_utc_file_time_fd (int fd, __time64_t *mtime, __time64_t *ctime)
{
    HANDLE handle;
    FILETIME write_time, create_time;

    handle = (HANDLE)_get_osfhandle (fd);
    if (handle == INVALID_HANDLE_VALUE) {
        g_warning ("Failed to get handle from fd: %lu.\n", GetLastError());
        return -1;
    }

    if (!GetFileTime (handle, &create_time, NULL, &write_time)) {
        g_warning ("Failed to get file time: %lu.\n", GetLastError());
        return -1;
    }

    *mtime = file_time_to_unix_time (&write_time);
    *ctime = file_time_to_unix_time (&create_time);

    return 0;
}

#define EPOCH_DIFF 11644473600ULL

inline static void
unix_time_to_file_time (guint64 unix_time, FILETIME *ftime)
{
    guint64 win_time;

    win_time = (unix_time + EPOCH_DIFF) * 10000000;
    ftime->dwLowDateTime = win_time & 0xFFFFFFFF;
    ftime->dwHighDateTime = (win_time >> 32) & 0xFFFFFFFF;
}

static int
set_utc_file_time (const char *path, const wchar_t *wpath, guint64 mtime)
{
    HANDLE handle;
    FILETIME write_time;

    handle = CreateFileW (wpath,
                          GENERIC_WRITE,
                          FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                          NULL,
                          OPEN_EXISTING,
                          FILE_FLAG_BACKUP_SEMANTICS,
                          NULL);
    if (handle == INVALID_HANDLE_VALUE) {
        g_warning ("Failed to open %s: %lu.\n", path, GetLastError());
        return -1;
    }

    unix_time_to_file_time (mtime, &write_time);

    if (!SetFileTime (handle, NULL, NULL, &write_time)) {
        g_warning ("Failed to set file time for %s: %lu.\n", path, GetLastError());
        CloseHandle (handle);
        return -1;
    }
    CloseHandle (handle);

    return 0;
}

wchar_t *
win32_long_path (const char *path)
{
    char *long_path, *p;
    wchar_t *long_path_w;

    if (strncmp(path, "//", 2) == 0)
        long_path = g_strconcat ("\\\\?\\UNC\\", path + 2, NULL);
    else
        long_path = g_strconcat ("\\\\?\\", path, NULL);
    for (p = long_path; *p != 0; ++p)
        if (*p == '/')
            *p = '\\';

    long_path_w = g_utf8_to_utf16 (long_path, -1, NULL, NULL, NULL);

    g_free (long_path);
    return long_path_w;
}

/* Convert a (possible) 8.3 format path to long path */
wchar_t *
win32_83_path_to_long_path (const char *worktree, const wchar_t *path, int path_len)
{
    wchar_t *worktree_w = g_utf8_to_utf16 (worktree, -1, NULL, NULL, NULL);
    int wt_len;
    wchar_t *p;
    wchar_t *fullpath_w = NULL;
    wchar_t *fullpath_long = NULL;
    wchar_t *ret = NULL;
    char *fullpath;

    for (p = worktree_w; *p != L'\0'; ++p)
        if (*p == L'/')
            *p = L'\\';

    wt_len = wcslen(worktree_w);

    fullpath_w = g_new0 (wchar_t, wt_len + path_len + 6);
    wcscpy (fullpath_w, L"\\\\?\\");
    wcscat (fullpath_w, worktree_w);
    wcscat (fullpath_w, L"\\");
    wcsncat (fullpath_w, path, path_len);

    fullpath_long = g_new0 (wchar_t, SEAF_PATH_MAX);

    DWORD n = GetLongPathNameW (fullpath_w, fullpath_long, SEAF_PATH_MAX);
    if (n == 0) {
        /* Failed. */
        fullpath = g_utf16_to_utf8 (fullpath_w, -1, NULL, NULL, NULL);
        g_free (fullpath);

        goto out;
    } else if (n > SEAF_PATH_MAX) {
        /* In this case n is the necessary length for the buf. */
        g_free (fullpath_long);
        fullpath_long = g_new0 (wchar_t, n);

        if (GetLongPathNameW (fullpath_w, fullpath_long, n) != (n - 1)) {
            fullpath = g_utf16_to_utf8 (fullpath_w, -1, NULL, NULL, NULL);
            g_free (fullpath);

            goto out;
        }
    }

    /* Remove "\\?\worktree\" from the beginning. */
    ret = wcsdup (fullpath_long + wt_len + 5);

out:
    g_free (worktree_w);
    g_free (fullpath_w);
    g_free (fullpath_long);

    return ret;
}

static int
windows_error_to_errno (DWORD error)
{
    switch (error) {
    case ERROR_FILE_NOT_FOUND:
    case ERROR_PATH_NOT_FOUND:
        return ENOENT;
    case ERROR_ALREADY_EXISTS:
        return EEXIST;
    case ERROR_ACCESS_DENIED:
    case ERROR_SHARING_VIOLATION:
        return EACCES;
    case ERROR_DIR_NOT_EMPTY:
        return ENOTEMPTY;
    default:
        return 0;
    }
}

#endif

int
seaf_stat (const char *path, SeafStat *st)
{
#ifdef WIN32
    wchar_t *wpath = win32_long_path (path);
    WIN32_FILE_ATTRIBUTE_DATA attrs;
    int ret = 0;

    if (!GetFileAttributesExW (wpath, GetFileExInfoStandard, &attrs)) {
        ret = -1;
        errno = windows_error_to_errno (GetLastError());
        goto out;
    }

    memset (st, 0, sizeof(SeafStat));

    if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        st->st_mode = S_IFDIR;
    else
        st->st_mode = S_IFREG;

    st->st_atime = file_time_to_unix_time (&attrs.ftLastAccessTime);
    st->st_ctime = file_time_to_unix_time (&attrs.ftCreationTime);
    st->st_mtime = file_time_to_unix_time (&attrs.ftLastWriteTime);

    st->st_size = ((((__int64)attrs.nFileSizeHigh)<<32) + attrs.nFileSizeLow);

out:
    g_free (wpath);

    return ret;
#else
    return stat (path, st);
#endif
}

int
seaf_fstat (int fd, SeafStat *st)
{
#ifdef WIN32
    if (_fstat64 (fd, st) < 0)
        return -1;

    if (get_utc_file_time_fd (fd, &st->st_mtime, &st->st_ctime) < 0)
        return -1;

    return 0;
#else
    return fstat (fd, st);
#endif
}

#ifdef WIN32

void
seaf_stat_from_find_data (WIN32_FIND_DATAW *fdata, SeafStat *st)
{
    memset (st, 0, sizeof(SeafStat));

    if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        st->st_mode = S_IFDIR;
    else
        st->st_mode = S_IFREG;

    st->st_atime = file_time_to_unix_time (&fdata->ftLastAccessTime);
    st->st_ctime = file_time_to_unix_time (&fdata->ftCreationTime);
    st->st_mtime = file_time_to_unix_time (&fdata->ftLastWriteTime);

    st->st_size = ((((__int64)fdata->nFileSizeHigh)<<32) + fdata->nFileSizeLow);
}

#endif

int
seaf_set_file_time (const char *path, guint64 mtime)
{
#ifndef WIN32
    struct stat st;
    struct utimbuf times;

    if (stat (path, &st) < 0) {
        g_warning ("Failed to stat %s: %s.\n", path, strerror(errno));
        return -1;
    }

    times.actime = st.st_atime;
    times.modtime = (time_t)mtime;

    return utime (path, &times);
#else
    wchar_t *wpath = win32_long_path (path);
    int ret = 0;

    if (set_utc_file_time (path, wpath, mtime) < 0)
        ret = -1;

    g_free (wpath);
    return ret;
#endif
}

int
seaf_util_unlink (const char *path)
{
    int ret = 0;
#ifdef WIN32
    wchar_t *wpath = win32_long_path (path);

    if (!DeleteFileW (wpath)) {
        ret = -1;
        errno = windows_error_to_errno (GetLastError());
    }

    g_free (wpath);
    return ret;
#elif defined __APPLE__
    char *path_nfd = g_utf8_normalize (path, -1, G_NORMALIZE_NFD);
    ret = unlink (path_nfd);
    g_free (path_nfd);
    return ret;
#else
    return unlink (path);
#endif
}

int
seaf_util_rmdir (const char *path)
{
    int ret = 0;
#ifdef WIN32
    wchar_t *wpath = win32_long_path (path);

    if (!RemoveDirectoryW (wpath)) {
        ret = -1;
        errno = windows_error_to_errno (GetLastError());
    }

    g_free (wpath);
    return ret;
#elif defined __APPLE__
    char *path_nfd = g_utf8_normalize (path, -1, G_NORMALIZE_NFD);
    ret = rmdir (path_nfd);
    g_free (path_nfd);
    return ret;
#else
    return rmdir (path);
#endif
}

int
seaf_util_mkdir (const char *path, mode_t mode)
{
    int ret = 0;
#ifdef WIN32
    wchar_t *wpath = win32_long_path (path);

    if (!CreateDirectoryW (wpath, NULL)) {
        ret = -1;
        errno = windows_error_to_errno (GetLastError());
    }

    g_free (wpath);
    return ret;
#elif defined __APPLE__
    char *path_nfd = g_utf8_normalize (path, -1, G_NORMALIZE_NFD);
    ret = mkdir (path_nfd, mode);
    g_free (path_nfd);
    return ret;
#else
    return mkdir (path, mode);
#endif
}

int
seaf_util_open (const char *path, int flags)
{
#ifdef WIN32
    wchar_t *wpath;
    DWORD access = 0;
    HANDLE handle;
    int fd;

    access |= GENERIC_READ;
    if (flags & (O_WRONLY | O_RDWR))
        access |= GENERIC_WRITE;

    wpath = win32_long_path (path);

    handle = CreateFileW (wpath,
                          access,
                          FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                          NULL,
                          OPEN_EXISTING,
                          0,
                          NULL);
    if (handle == INVALID_HANDLE_VALUE) {
        errno = windows_error_to_errno (GetLastError());
        g_free (wpath);
        return -1;
    }

    fd = _open_osfhandle ((intptr_t)handle, 0);

    g_free (wpath);
    return fd;
#elif defined __APPLE__
    int ret = 0;
    char *path_nfd = g_utf8_normalize (path, -1, G_NORMALIZE_NFD);
    ret = open (path_nfd, flags);
    g_free (path_nfd);
    return ret;
#else
    return open (path, flags);
#endif
}

int
seaf_util_create (const char *path, int flags, mode_t mode)
{
#ifdef WIN32
    wchar_t *wpath;
    DWORD access = 0;
    HANDLE handle;
    int fd;

    access |= GENERIC_READ;
    if (flags & (O_WRONLY | O_RDWR))
        access |= GENERIC_WRITE;

    wpath = win32_long_path (path);

    handle = CreateFileW (wpath,
                          access,
                          FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                          NULL,
                          CREATE_ALWAYS,
                          0,
                          NULL);
    if (handle == INVALID_HANDLE_VALUE) {
        errno = windows_error_to_errno (GetLastError());
        g_free (wpath);
        return -1;
    }

    fd = _open_osfhandle ((intptr_t)handle, 0);

    g_free (wpath);
    return fd;
#elif defined __APPLE__
    int ret = 0;
    char *path_nfd = g_utf8_normalize (path, -1, G_NORMALIZE_NFD);
    ret = open (path_nfd, flags, mode);
    g_free (path_nfd);
    return ret;
#else
    return open (path, flags, mode);
#endif
}

int
seaf_util_rename (const char *oldpath, const char *newpath)
{
    int ret = 0;
#ifdef WIN32
    wchar_t *oldpathw = win32_long_path (oldpath);
    wchar_t *newpathw = win32_long_path (newpath);

    if (!MoveFileExW (oldpathw, newpathw, MOVEFILE_REPLACE_EXISTING)) {
        ret = -1;
        errno = windows_error_to_errno (GetLastError());
    }

    g_free (oldpathw);
    g_free (newpathw);
    return ret;
#elif defined __APPLE__
    char *oldpath_nfd = g_utf8_normalize (oldpath, -1, G_NORMALIZE_NFD);
    char *newpath_nfd = g_utf8_normalize (newpath, -1, G_NORMALIZE_NFD);
    ret = rename (oldpath_nfd, newpath_nfd);
    g_free (oldpath_nfd);
    g_free (newpath_nfd);
    return ret;
#else
    return rename (oldpath, newpath);
#endif
}

gboolean
seaf_util_exists (const char *path)
{
#ifdef WIN32
    wchar_t *wpath = win32_long_path (path);
    DWORD attrs;
    gboolean ret;

    attrs = GetFileAttributesW (wpath);
    ret = (attrs != INVALID_FILE_ATTRIBUTES);

    g_free (wpath);
    return ret;
#elif defined __APPLE__
    int ret = 0;
    char *path_nfd = g_utf8_normalize (path, -1, G_NORMALIZE_NFD);
    ret = access (path_nfd, F_OK);
    g_free (path_nfd);
    return (ret == 0);
#else
    return (access (path, F_OK) == 0);
#endif
}

gint64
seaf_util_lseek (int fd, gint64 offset, int whence)
{
#ifdef WIN32
    return _lseeki64 (fd, offset, whence);
#else
    return lseek (fd, offset, whence);
#endif
}

#ifdef WIN32

int
traverse_directory_win32 (wchar_t *path_w,
                          DirentCallback callback,
                          void *user_data)
{
    WIN32_FIND_DATAW fdata;
    HANDLE handle;
    wchar_t *pattern;
    char *path;
    int path_len_w;
    DWORD error;
    gboolean stop;
    int ret = 0;

    path = g_utf16_to_utf8 (path_w, -1, NULL, NULL, NULL);
    if (!path)
        return -1;

    path_len_w = wcslen(path_w);

    pattern = g_new0 (wchar_t, (path_len_w + 3));
    wcscpy (pattern, path_w);
    wcscat (pattern, L"\\*");

    handle = FindFirstFileW (pattern, &fdata);
    if (handle == INVALID_HANDLE_VALUE) {
        g_warning ("FindFirstFile failed %s: %lu.\n",
                   path, GetLastError());
        ret = -1;
        goto out;
    }

    do {
        if (wcscmp (fdata.cFileName, L".") == 0 ||
            wcscmp (fdata.cFileName, L"..") == 0)
            continue;

        ++ret;

        stop = FALSE;
        if (callback (path_w, &fdata, user_data, &stop) < 0) {
            ret = -1;
            FindClose (handle);
            goto out;
        }
        if (stop) {
            FindClose (handle);
            goto out;
        }
    } while (FindNextFileW (handle, &fdata) != 0);

    error = GetLastError();
    if (error != ERROR_NO_MORE_FILES) {
        g_warning ("FindNextFile failed %s: %lu.\n",
                   path, error);
        ret = -1;
    }

    FindClose (handle);

out:
    g_free (path);
    g_free (pattern);
    return ret;
}

#endif

#ifdef WIN32
static inline gboolean
has_trailing_space_or_period (const char *path)
{
    int len = strlen(path);
    if (path[len - 1] == ' ' || path[len - 1] == '.') {
        return TRUE;
    }

    return FALSE;
}
#endif

gboolean
should_ignore_on_checkout (const char *file_path, IgnoreReason *ignore_reason)
{
    gboolean ret = FALSE;

#ifdef WIN32
    static char illegals[] = {'\\', ':', '*', '?', '"', '<', '>', '|', '\b', '\t'};
    char **components = g_strsplit (file_path, "/", -1);
    int n_comps = g_strv_length (components);
    int j = 0;
    char *file_name;
    int i;
    char c;

    for (; j < n_comps; ++j) {
        file_name = components[j];

        if (g_strcmp0(file_name, ".") == 0 || g_strcmp0(file_name, "..") == 0) {
            if (ignore_reason)
                *ignore_reason = IGNORE_REASON_INVALID_CHARACTER;
            ret = TRUE;
            goto out;
        }

        if (has_trailing_space_or_period (file_name)) {
            /* Ignore files/dir whose path has trailing spaces. It would cause
             * problem on windows. */
            /* g_debug ("ignore '%s' which contains trailing space in path\n", path); */
            ret = TRUE;
            if (ignore_reason)
                *ignore_reason = IGNORE_REASON_END_SPACE_PERIOD;
            goto out;
        }

        for (i = 0; i < G_N_ELEMENTS(illegals); i++) {
            if (strchr (file_name, illegals[i])) {
                ret = TRUE;
                if (ignore_reason)
                    *ignore_reason = IGNORE_REASON_INVALID_CHARACTER;
                goto out;
            }
        }

        for (c = 1; c <= 31; c++) {
            if (strchr (file_name, c)) {
                ret = TRUE;
                if (ignore_reason)
                    *ignore_reason = IGNORE_REASON_INVALID_CHARACTER;
                goto out;
            }
        }
    }

out:
    g_strfreev (components);
    return ret;
#else
    char **components = g_strsplit (file_path, "/", -1);
    int n_comps = g_strv_length (components);
    int j = 0;
    char *file_name;

    for (; j < n_comps; ++j) {
        file_name = components[j];
        if (g_strcmp0(file_name, ".") == 0 || g_strcmp0(file_name, "..") == 0) {
            ret = TRUE;
            if (ignore_reason)
                *ignore_reason = IGNORE_REASON_INVALID_CHARACTER;
            break;
        }
    }
    g_strfreev (components);
    return ret;
#endif
}

ssize_t						/* Read "n" bytes from a descriptor. */
readn(int fd, void *vptr, size_t n)
{
	size_t	nleft;
	ssize_t	nread;
	char	*ptr;

	ptr = vptr;
	nleft = n;
	while (nleft > 0) {
		if ( (nread = read(fd, ptr, nleft)) < 0) {
			if (errno == EINTR)
				nread = 0;		/* and call read() again */
			else
				return(-1);
		} else if (nread == 0)
			break;				/* EOF */

		nleft -= nread;
		ptr   += nread;
	}
	return(n - nleft);		/* return >= 0 */
}

ssize_t						/* Write "n" bytes to a descriptor. */
writen(int fd, const void *vptr, size_t n)
{
	size_t		nleft;
	ssize_t		nwritten;
	const char	*ptr;

	ptr = vptr;
	nleft = n;
	while (nleft > 0) {
		if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
			if (nwritten < 0 && errno == EINTR)
				nwritten = 0;		/* and call write() again */
			else
				return(-1);			/* error */
		}

		nleft -= nwritten;
		ptr   += nwritten;
	}
	return(n);
}


ssize_t						/* Read "n" bytes from a descriptor. */
recvn(evutil_socket_t fd, void *vptr, size_t n)
{
	size_t	nleft;
	ssize_t	nread;
	char	*ptr;

	ptr = vptr;
	nleft = n;
	while (nleft > 0) {
#ifndef WIN32
        if ( (nread = read(fd, ptr, nleft)) < 0)
#else
        if ( (nread = recv(fd, ptr, nleft, 0)) < 0)
#endif
        {
			if (errno == EINTR)
				nread = 0;		/* and call read() again */
			else
				return(-1);
		} else if (nread == 0)
			break;				/* EOF */

		nleft -= nread;
		ptr   += nread;
	}
	return(n - nleft);		/* return >= 0 */
}

ssize_t						/* Write "n" bytes to a descriptor. */
sendn(evutil_socket_t fd, const void *vptr, size_t n)
{
	size_t		nleft;
	ssize_t		nwritten;
	const char	*ptr;

	ptr = vptr;
	nleft = n;
	while (nleft > 0) {
#ifndef WIN32
        if ( (nwritten = write(fd, ptr, nleft)) <= 0)
#else
        if ( (nwritten = send(fd, ptr, nleft, 0)) <= 0)
#endif
        {
			if (nwritten < 0 && errno == EINTR)
				nwritten = 0;		/* and call write() again */
			else
				return(-1);			/* error */
		}

		nleft -= nwritten;
		ptr   += nwritten;
	}
	return(n);
}

#ifdef WIN32
static SOCKET pg_serv_sock = INVALID_SOCKET;
static struct sockaddr_in pg_serv_addr;

/* seaf_pipe() should only be called in the main loop,
 * since it accesses the static global socket.
 */
int
seaf_pipe (seaf_pipe_t handles[2])
{
    int len = sizeof( pg_serv_addr );

    handles[0] = handles[1] = INVALID_SOCKET;

    if (pg_serv_sock == INVALID_SOCKET) {
        if ((pg_serv_sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
            seaf_warning("seaf_pipe failed to create socket: %d\n", WSAGetLastError());
            return -1;
        }

        memset(&pg_serv_addr, 0, sizeof(pg_serv_addr));
        pg_serv_addr.sin_family = AF_INET;
        pg_serv_addr.sin_port = htons(0);
        pg_serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

        if (bind(pg_serv_sock, (SOCKADDR *)&pg_serv_addr, len) == SOCKET_ERROR) {
            seaf_warning("seaf_pipe failed to bind: %d\n", WSAGetLastError());
            closesocket(pg_serv_sock);
            pg_serv_sock = INVALID_SOCKET;
            return -1;
        }

        if (listen(pg_serv_sock, SOMAXCONN) == SOCKET_ERROR) {
            seaf_warning("seaf_pipe failed to listen: %d\n", WSAGetLastError());
            closesocket(pg_serv_sock);
            pg_serv_sock = INVALID_SOCKET;
            return -1;
        }

        struct sockaddr_in tmp_addr;
        int tmp_len = sizeof(tmp_addr);
        if (getsockname(pg_serv_sock, (SOCKADDR *)&tmp_addr, &tmp_len) == SOCKET_ERROR) {
            seaf_warning("seaf_pipe failed to getsockname: %d\n", WSAGetLastError());
            closesocket(pg_serv_sock);
            pg_serv_sock = INVALID_SOCKET;
            return -1;
        }
        pg_serv_addr.sin_port = tmp_addr.sin_port;
    }

    if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
        seaf_warning("seaf_pipe failed to create socket 2: %d\n", WSAGetLastError());
        closesocket(pg_serv_sock);
        pg_serv_sock = INVALID_SOCKET;
        return -1;
    }

    if (connect(handles[1], (SOCKADDR *)&pg_serv_addr, len) == SOCKET_ERROR)
    {
        seaf_warning("seaf_pipe failed to connect socket: %d\n", WSAGetLastError());
        closesocket(handles[1]);
        handles[1] = INVALID_SOCKET;
        closesocket(pg_serv_sock);
        pg_serv_sock = INVALID_SOCKET;
        return -1;
    }

    struct sockaddr_in client_addr;
    int client_len = sizeof(client_addr);
    if ((handles[0] = accept(pg_serv_sock, (SOCKADDR *)&client_addr, &client_len)) == INVALID_SOCKET)
    {
        seaf_warning("seaf_pipe failed to accept socket: %d\n", WSAGetLastError());
        closesocket(handles[1]);
        handles[1] = INVALID_SOCKET;
        closesocket(pg_serv_sock);
        pg_serv_sock = INVALID_SOCKET;
        return -1;
    }

    return 0;
}

int
seaf_pipe_read (seaf_pipe_t fd, char *buf, int len)
{
    return recv (fd, buf, len, 0);
}

int
seaf_pipe_write (seaf_pipe_t fd, const char *buf, int len)
{
    return send (fd, buf, len, 0);
}

int
seaf_pipe_close (seaf_pipe_t fd)
{
    return closesocket (fd);
}

ssize_t seaf_pipe_readn (seaf_pipe_t fd, void *vptr, size_t n)
{
    return recvn (fd, vptr, n);
}

ssize_t seaf_pipe_writen (seaf_pipe_t fd, const void *vptr, size_t n)
{
    return sendn (fd, vptr, n);
}

#else

int
seaf_pipe (seaf_pipe_t handles[2])
{
    return pipe (handles);
}

int
seaf_pipe_read (seaf_pipe_t fd, char *buf, int len)
{
    return read (fd, buf, len);
}

int
seaf_pipe_write (seaf_pipe_t fd, const char *buf, int len)
{
    return write (fd, buf, len);
}

int
seaf_pipe_close (seaf_pipe_t fd)
{
    return close (fd);
}

ssize_t seaf_pipe_readn (seaf_pipe_t fd, void *vptr, size_t n)
{
    return readn (fd, vptr, n);
}

ssize_t seaf_pipe_writen (seaf_pipe_t fd, const void *vptr, size_t n)
{
    return writen (fd, vptr, n);
}

#endif

int copy_fd (int ifd, int ofd)
{
    while (1) {
        char buffer[8192];
        ssize_t len = readn (ifd, buffer, sizeof(buffer));
        if (!len)
            break;
        if (len < 0) {
            close (ifd);
            return -1;
        }
        if (writen (ofd, buffer, len) < 0) {
            close (ofd);
            return -1;
        }
    }
    close(ifd);
    return 0;
}

int copy_file (const char *dst, const char *src, int mode)
{
    int fdi, fdo, status;

    if ((fdi = g_open (src, O_RDONLY | O_BINARY, 0)) < 0)
        return fdi;

    fdo = g_open (dst, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, mode);
    if (fdo < 0 && errno == EEXIST) {
        close (fdi);
        return 0;
    } else if (fdo < 0){
        close (fdi);
        return -1;
    }

    status = copy_fd (fdi, fdo);
    if (close (fdo) != 0)
        return -1;

    return status;
}

char*
ccnet_expand_path (const char *src)
{
#ifdef WIN32
    char new_path[SEAF_PATH_MAX + 1];
    char *p = new_path;
    const char *q = src;

    memset(new_path, 0, sizeof(new_path));
    if (*src == '~') {
        const char *home = g_get_home_dir();
        memcpy(new_path, home, strlen(home));
        p += strlen(new_path);
        q++;
    }
    memcpy(p, q, strlen(q));

    /* delete the charactor '\' or '/' at the end of the path
     * because the function stat faied to deal with directory names
     * with '\' or '/' in the end */
    p = new_path + strlen(new_path) - 1;
    while(*p == '\\' || *p == '/') *p-- = '\0';

    return strdup (new_path);
#else
    const char *next_in, *ntoken;
    char new_path[SEAF_PATH_MAX + 1];
    char *next_out;
    int len;

   /* special cases */
    if (!src || *src == '\0')
        return NULL;
    if (strlen(src) > SEAF_PATH_MAX)
        return NULL;

    next_in = src;
    next_out = new_path;
    *next_out = '\0';

    if (*src == '~') {
        /* handle src start with '~' or '~<user>' like '~plt' */
        struct passwd *pw = NULL;

        for ( ; *next_in != '/' && *next_in != '\0'; next_in++) ;
        
        len = next_in - src;
        if (len == 1) {
            pw = getpwuid (geteuid());
        } else {
            /* copy '~<user>' to new_path */
            memcpy (new_path, src, len);
            new_path[len] = '\0';
            pw = getpwnam (new_path + 1);
        }
        if (pw == NULL)
            return NULL;
       
        len = strlen (pw->pw_dir);
        memcpy (new_path, pw->pw_dir, len);
        next_out = new_path + len;
        *next_out = '\0';

        if (*next_in == '\0')
            return strdup (new_path);
    } else if (*src != '/') {
        getcwd (new_path, SEAF_PATH_MAX);
        for ( ; *next_out; next_out++) ; /* to '\0' */
    }
    
    while (*next_in != '\0') {
        /* move ntoken to the next not '/' char  */
        for (ntoken = next_in; *ntoken == '/'; ntoken++) ;

        for (next_in = ntoken; *next_in != '/' 
                 && *next_in != '\0'; next_in++) ;
 
        len = next_in - ntoken;

        if (len == 0) {
            /* the path ends with '/', keep it */
            *next_out++ = '/';
            *next_out = '\0';
            break;
        }

        if (len == 2 && ntoken[0] == '.' && ntoken[1] == '.') 
        {
            /* '..' */
            for (; next_out > new_path && *next_out != '/'; next_out--)
                ;
            *next_out = '\0';
        } else if (ntoken[0] != '.' || len != 1) {
            /* not '.' */
            *next_out++ = '/';
            memcpy (next_out, ntoken, len);
            next_out += len;
            *next_out = '\0';
        }
    }

    /* the final special case */
    if (new_path[0] == '\0') {
        new_path[0] = '/';
        new_path[1] = '\0';
    }
    return strdup (new_path);
#endif
}


int
calculate_sha1 (unsigned char *sha1, const char *msg, int len)
{
    GChecksum *c;
    gsize cs_len = 20;

    if (len < 0)
        len = strlen(msg);

    c = g_checksum_new (G_CHECKSUM_SHA1);
    g_checksum_update(c, (const unsigned char *)msg, len);    
    g_checksum_get_digest (c, sha1, &cs_len);
    g_checksum_free (c);
    return 0;
}

uint32_t
ccnet_sha1_hash (const void *v)
{
    /* 31 bit hash function */
    const unsigned char *p = v;
    uint32_t h = 0;
    int i;

    for (i = 0; i < 20; i++)
        h = (h << 5) - h + p[i];

    return h;
}

int
ccnet_sha1_equal (const void *v1,
                  const void *v2)
{
    const unsigned char *p1 = v1;
    const unsigned char *p2 = v2;
    int i;

    for (i = 0; i < 20; i++)
        if (p1[i] != p2[i])
            return 0;
    
    return 1;
}

#ifndef WIN32
char* gen_uuid ()
{
    char *uuid_str = g_malloc (37);
    uuid_t uuid;

    uuid_generate (uuid);
    uuid_unparse_lower (uuid, uuid_str);

    return uuid_str;
}

void gen_uuid_inplace (char *buf)
{
    uuid_t uuid;

    uuid_generate (uuid);
    uuid_unparse_lower (uuid, buf);
}

gboolean
is_uuid_valid (const char *uuid_str)
{
    uuid_t uuid;

    if (!uuid_str)
        return FALSE;

    if (uuid_parse (uuid_str, uuid) < 0)
        return FALSE;
    return TRUE;
}

#else
char* gen_uuid ()
{
    char *uuid_str = g_malloc (37);
    unsigned char *str = NULL;
    UUID uuid;

    UuidCreate(&uuid);
    UuidToStringA(&uuid, &str);
    memcpy(uuid_str, str, 37);
    RpcStringFreeA(&str);
    return uuid_str;
}

void gen_uuid_inplace (char *buf)
{
    unsigned char *str = NULL;
    UUID uuid;

    UuidCreate(&uuid);
    UuidToStringA(&uuid, &str);
    memcpy(buf, str, 37);
    RpcStringFreeA(&str);
}

gboolean
is_uuid_valid (const char *uuid_str)
{
    if (!uuid_str)
        return FALSE;

    UUID uuid;
    if (UuidFromStringA((unsigned char *)uuid_str, &uuid) != RPC_S_OK)
        return FALSE;
    return TRUE;
}

#endif

gboolean
is_object_id_valid (const char *obj_id)
{
    if (!obj_id)
        return FALSE;

    int len = strlen(obj_id);
    int i;
    char c;

    if (len != 40)
        return FALSE;

    for (i = 0; i < len; ++i) {
        c = obj_id[i];
        if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))
            continue;
        return FALSE;
    }

    return TRUE;
}

char** strsplit_by_space (char *string, int *length)
{
    char *remainder, *s;
    int size = 8, num = 0, done = 0;
    char **array;
    
    if (string == NULL || string[0] == '\0') {
        if (length != NULL) {
          *length = 0;
        }
        return NULL;
    }

    array = malloc (sizeof(char *) * size);
    if (array == NULL) {
      return NULL;
    }
    
    remainder = string;
    while (!done) {
        for (s = remainder; *s != ' ' && *s != '\0'; ++s) ;

        if (*s == '\0')
            done = 1;
        else
            *s = '\0';

        array[num++] = remainder;
        if (!done && num == size) {
            size <<= 1;
            char** tmp = realloc (array, sizeof(char *) * size);
            if (tmp == NULL) {
              free(array);
              return NULL;
            }
            array = tmp;
        }

        remainder = s + 1;
    }
    
    if (length != NULL) {
      *length = num;
    }

    return array;
}

char** strsplit_by_char (char *string, int *length, char c)
{
    char *remainder, *s;
    int size = 8, num = 0, done = 0;
    char **array;
    
    if (string == NULL || string[0] == '\0') {
        *length = 0;
        return NULL;
    }

    array = malloc (sizeof(char *) * size);
    if (array == NULL) {
      return NULL;
    }
    
    remainder = string;
    while (!done) {
        for (s = remainder; *s != c && *s != '\0'; ++s) ;

        if (*s == '\0')
            done = 1;
        else
            *s = '\0';

        array[num++] = remainder;
        if (!done && num == size) {
            size <<= 1;
            char** tmp = realloc (array, sizeof(char *) * size);
            if (tmp == NULL) {
              free(array);
              return NULL;
            }
            array = tmp;
        }

        remainder = s + 1;
    }
    
    if (length != NULL) {
      *length = num;
    }

    return array;
}

char* strjoin_n (const char *seperator, int argc, char **argv)
{
    GString *buf;
    int i;
    char *str;

    if (argc == 0)
        return NULL;
    
    buf = g_string_new (argv[0]);
    for (i = 1; i < argc; ++i) {
        g_string_append (buf, seperator);
        g_string_append (buf, argv[i]);
    }

    str = buf->str;
    g_string_free (buf, FALSE);
    return str;
}


gboolean is_ipaddr_valid (const char *ip)
{
    unsigned char buf[sizeof(struct in6_addr)];

    if (evutil_inet_pton(AF_INET, ip, buf) == 1)
        return TRUE;

    if (evutil_inet_pton(AF_INET6, ip, buf) == 1)
        return TRUE;
    
    return FALSE;
}

void parse_key_value_pairs (char *string, KeyValueFunc func, void *data)
{
    char *line = string, *next, *space;
    char *key, *value;

    while (*line) {
        /* handle empty line */
        if (*line == '\n') {
            ++line;
            continue;
        }

        for (next = line; *next != '\n' && *next; ++next) ;
        *next = '\0';
        
        for (space = line; space < next && *space != ' '; ++space) ;
        if (*space != ' ') {
            g_warning ("Bad key value format: %s\n", line);
            return;
        }
        *space = '\0';
        key = line;
        value = space + 1;
        
        func (data, key, value);

        line = next + 1;
    }
}

void parse_key_value_pairs2 (char *string, KeyValueFunc2 func, void *data)
{
    char *line = string, *next, *space;
    char *key, *value;

    while (*line) {
        /* handle empty line */
        if (*line == '\n') {
            ++line;
            continue;
        }

        for (next = line; *next != '\n' && *next; ++next) ;
        *next = '\0';
        
        for (space = line; space < next && *space != ' '; ++space) ;
        if (*space != ' ') {
            g_warning ("Bad key value format: %s\n", line);
            return;
        }
        *space = '\0';
        key = line;
        value = space + 1;
        
        if (func(data, key, value) == FALSE)
            break;

        line = next + 1;
    }
}

/**
 * handle the empty string problem.
 */
gchar* 
ccnet_key_file_get_string (GKeyFile *keyf,
                           const char *category,
                           const char *key)
{
    gchar *v;

    if (!g_key_file_has_key (keyf, category, key, NULL))
        return NULL;

    v = g_key_file_get_string (keyf, category, key, NULL);
    if (v != NULL && v[0] == '\0') {
        g_free(v);
        return NULL;
    }

    return v;
}

/**
 * string_list_is_exists:
 * @str_list: 
 * @string: a C string or %NULL
 *
 * Check whether @string is in @str_list.
 *
 * returns: %TRUE if @string is in str_list, %FALSE otherwise
 */
gboolean
string_list_is_exists (GList *str_list, const char *string)
{
    GList *ptr;
    for (ptr = str_list; ptr; ptr = ptr->next) {
        if (g_strcmp0(string, ptr->data) == 0)
            return TRUE;
    }
    return FALSE;
}

/**
 * string_list_append:
 * @str_list: 
 * @string: a C string (can't be %NULL
 *
 * Append @string to @str_list if it is in the list.
 *
 * returns: the new start of the list
 */
GList*
string_list_append (GList *str_list, const char *string)
{
    g_return_val_if_fail (string != NULL, str_list);

    if (string_list_is_exists(str_list, string))
        return str_list;

    str_list = g_list_append (str_list, g_strdup(string));
    return str_list;
}

GList *
string_list_append_sorted (GList *str_list, const char *string)
{
    g_return_val_if_fail (string != NULL, str_list);

    if (string_list_is_exists(str_list, string))
        return str_list;

    str_list = g_list_insert_sorted_with_data (str_list, g_strdup(string),
                                 (GCompareDataFunc)g_strcmp0, NULL);
    return str_list;
}


GList *
string_list_remove (GList *str_list, const char *string)
{
    g_return_val_if_fail (string != NULL, str_list);

    GList *ptr;

    for (ptr = str_list; ptr; ptr = ptr->next) {
        if (strcmp((char *)ptr->data, string) == 0) {
            g_free (ptr->data);
            return g_list_delete_link (str_list, ptr);
        }
    }
    return str_list;
}


void
string_list_free (GList *str_list)
{
    GList *ptr = str_list;

    while (ptr) {
        g_free (ptr->data);
        ptr = ptr->next;
    }

    g_list_free (str_list);
}


void
string_list_join (GList *str_list, GString *str, const char *seperator)
{
    GList *ptr;
    if (!str_list)
        return;

    ptr = str_list;
    g_string_append (str, ptr->data);

    for (ptr = ptr->next; ptr; ptr = ptr->next) {
        g_string_append (str, seperator);
        g_string_append (str, (char *)ptr->data);
    }
}

GList *
string_list_parse (const char *list_in_str, const char *seperator)
{
    if (!list_in_str)
        return NULL;

    GList *list = NULL;
    char **array = g_strsplit (list_in_str, seperator, 0);
    char **ptr;

    for (ptr = array; *ptr; ptr++) {
        list = g_list_prepend (list, g_strdup(*ptr));
    }
    list = g_list_reverse (list);
    
    g_strfreev (array);
    return list;
}

GList *
string_list_parse_sorted (const char *list_in_str, const char *seperator)
{
    GList *list = string_list_parse (list_in_str, seperator);

    return g_list_sort (list, (GCompareFunc)g_strcmp0);
}

gboolean
string_list_sorted_is_equal (GList *list1, GList *list2)
{
    GList *ptr1 = list1, *ptr2 = list2;

    while (ptr1 && ptr2) {
        if (g_strcmp0(ptr1->data, ptr2->data) != 0)
            break;

        ptr1 = ptr1->next;
        ptr2 = ptr2->next;
    }

    if (!ptr1 && !ptr2)
        return TRUE;
    return FALSE;
}

char **
ncopy_string_array (char **orig, int n)
{
    char **ret = g_malloc (sizeof(char *) * n);
    int i = 0;

    for (; i < n; i++)
        ret[i] = g_strdup(orig[i]);
    return ret;
}

void
nfree_string_array (char **array, int n)
{
    int i = 0;

    for (; i < n; i++)
        g_free (array[i]);
    g_free (array);
}

gint64
get_current_time()
{
    GTimeVal tv;
    gint64 t;

    g_get_current_time (&tv);
    t = tv.tv_sec * (gint64)1000000 + tv.tv_usec;
    return t;
}

/* convert locale specific input to utf8 encoded string  */
char *ccnet_locale_to_utf8 (const gchar *src)
{
    if (!src)
        return NULL;

    gsize bytes_read = 0;
    gsize bytes_written = 0;
    GError *error = NULL;
    gchar *dst = NULL;

    dst = g_locale_to_utf8
        (src,                   /* locale specific string */
         strlen(src),           /* len of src */
         &bytes_read,           /* length processed */
         &bytes_written,        /* output length */
         &error);

    if (error) {
        return NULL;
    }

    return dst;
}

/* convert utf8 input to locale specific string  */
char *ccnet_locale_from_utf8 (const gchar *src)
{
    if (!src)
        return NULL;

    gsize bytes_read = 0;
    gsize bytes_written = 0;
    GError *error = NULL;
    gchar *dst = NULL;

    dst = g_locale_from_utf8
        (src,                   /* locale specific string */
         strlen(src),           /* len of src */
         &bytes_read,           /* length processed */
         &bytes_written,        /* output length */
         &error);

    if (error) {
        return NULL;
    }

    return dst;
}

#ifdef WIN32

static HANDLE
get_process_handle (const char *process_name_in)
{
    char name[256];
    if (strstr(process_name_in, ".exe")) {
        snprintf (name, sizeof(name), "%s", process_name_in);
    } else {
        snprintf (name, sizeof(name), "%s.exe", process_name_in);
    }

    DWORD aProcesses[1024], cbNeeded, cProcesses;

    if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
        return NULL;

    /* Calculate how many process identifiers were returned. */
    cProcesses = cbNeeded / sizeof(DWORD);

    HANDLE hProcess;
    HMODULE hMod;
    char process_name[SEAF_PATH_MAX];
    unsigned int i;

    for (i = 0; i < cProcesses; i++) {
        if(aProcesses[i] == 0)
            continue;
        hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, aProcesses[i]);
        if (!hProcess)
            continue;
            
        if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) {
            GetModuleBaseNameA(hProcess, hMod, process_name, 
                              sizeof(process_name)/sizeof(char));
        }

        if (strcasecmp(process_name, name) == 0)
            return hProcess;
        else {
            CloseHandle(hProcess);
        }
    }
    /* Not found */
    return NULL;
}

int count_process (const char *process_name_in)
{
    char name[SEAF_PATH_MAX];
    char process_name[SEAF_PATH_MAX];
    DWORD aProcesses[1024], cbNeeded, cProcesses;
    HANDLE hProcess;
    HMODULE hMods[1024];
    int count = 0;
    int i, j;
    
    if (strstr(process_name_in, ".exe")) {
        snprintf (name, sizeof(name), "%s", process_name_in);
    } else {
        snprintf (name, sizeof(name), "%s.exe", process_name_in);
    }

    if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) {
        return 0;
    }

    /* Calculate how many process identifiers were returned. */
    cProcesses = cbNeeded / sizeof(DWORD);

    for (i = 0; i < cProcesses; i++) {
        if(aProcesses[i] == 0)
            continue;
        hProcess = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i]);
        if (!hProcess) {
            continue;
        }
            
        if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
            for (j = 0; j < cbNeeded / sizeof(HMODULE); j++) {
                if (GetModuleBaseNameA(hProcess, hMods[j], process_name,
                                      sizeof(process_name))) {
                    if (strcasecmp(process_name, name) == 0)
                        count++;
                }
            } 
        }

        CloseHandle(hProcess);
    }
    
    return count;
}

gboolean
process_is_running (const char *process_name)
{
    HANDLE proc_handle = get_process_handle(process_name);

    if (proc_handle) {
        CloseHandle(proc_handle);
        return TRUE;
    } else {
        return FALSE;
    }
}

int
win32_kill_process (const char *process_name)
{
    HANDLE proc_handle = get_process_handle(process_name);

    if (proc_handle) {
        TerminateProcess(proc_handle, 0);
        CloseHandle(proc_handle);
        return 0;
    } else {
        return -1;
    }
}

int
win32_spawn_process (char *cmdline_in, char *working_directory_in)
{
    if (!cmdline_in)
        return -1;

    wchar_t *cmdline_w = NULL;
    wchar_t *working_directory_w = NULL;

    cmdline_w = wchar_from_utf8 (cmdline_in);
    if (!cmdline_in) {
        g_warning ("failed to convert cmdline_in");
        return -1;
    }
    
    if (working_directory_in) {
        working_directory_w = wchar_from_utf8 (working_directory_in);
        if (!working_directory_w) {
            g_warning ("failed to convert working_directory_in");
            return -1;
        }
    }

    STARTUPINFOW si;
    PROCESS_INFORMATION pi;
    unsigned flags;
    BOOL success;

    /* we want to execute seafile without crreating a console window */
    flags = CREATE_NO_WINDOW;

    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESTDHANDLES | STARTF_FORCEOFFFEEDBACK;
    si.hStdInput = (HANDLE) _get_osfhandle(0);
    si.hStdOutput = (HANDLE) _get_osfhandle(1);
    si.hStdError = (HANDLE) _get_osfhandle(2);
    
    memset(&pi, 0, sizeof(pi));

    success = CreateProcessW (NULL, cmdline_w, NULL, NULL, TRUE, flags,
                              NULL, working_directory_w, &si, &pi);
    free (cmdline_w);
    if (working_directory_w) free (working_directory_w);
    
    if (!success) {
        g_warning ("failed to fork_process: GLE=%lu\n", GetLastError());
        return -1;
    }

    /* close the handle of thread so that the process object can be freed by
     * system
     */
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    return 0;
}

char *
wchar_to_utf8 (const wchar_t *wch)
{
    if (wch == NULL) {
        return NULL;
    }

    char *utf8 = NULL;
    int bufsize, len;

    bufsize = WideCharToMultiByte
        (CP_UTF8,               /* multibyte code page */
         0,                     /* flags */
         wch,                   /* src */
         -1,                    /* src len, -1 for all includes \0 */
         utf8,                  /* dst */
         0,                     /* dst buf len */
         NULL,                  /* default char */
         NULL);                 /* BOOL flag indicates default char is used */

    if (bufsize <= 0) {
        g_warning ("failed to convert a string from wchar to utf8 0");
        return NULL;
    }

    utf8 = g_malloc(bufsize);
    len = WideCharToMultiByte
        (CP_UTF8,               /* multibyte code page */
         0,                     /* flags */
         wch,                   /* src */
         -1,                    /* src len, -1 for all includes \0 */
         utf8,                  /* dst */
         bufsize,               /* dst buf len */
         NULL,                  /* default char */
         NULL);                 /* BOOL flag indicates default char is used */

    if (len != bufsize) {
        g_free (utf8);
        g_warning ("failed to convert a string from wchar to utf8");
        return NULL;
    }

    return utf8;
}

wchar_t *
wchar_from_utf8 (const char *utf8)
{
    if (utf8 == NULL) {
        return NULL;
    }

    wchar_t *wch = NULL;
    int bufsize, len;

    bufsize = MultiByteToWideChar
        (CP_UTF8,               /* multibyte code page */
         0,                     /* flags */
         utf8,                  /* src */
         -1,                    /* src len, -1 for all includes \0 */
         wch,                   /* dst */
         0);                    /* dst buf len */

    if (bufsize <= 0) {
        g_warning ("failed to convert a string from wchar to utf8 0");
        return NULL;
    }

    wch = g_malloc (bufsize * sizeof(wchar_t));
    len = MultiByteToWideChar
        (CP_UTF8,               /* multibyte code page */
         0,                     /* flags */
         utf8,                  /* src */
         -1,                    /* src len, -1 for all includes \0 */
         wch,                   /* dst */
         bufsize);              /* dst buf len */

    if (len != bufsize) {
        g_free (wch);
        g_warning ("failed to convert a string from utf8 to wchar");
        return NULL;
    }

    return wch;
}

#endif  /* ifdef WIN32 */

#ifdef __linux__
/* read the link of /proc/123/exe and compare with `process_name' */
static int
find_process_in_dirent(struct dirent *dir, const char *process_name)
{
    char path[512];
    /* fisrst construct a path like /proc/123/exe */
    if (sprintf (path, "/proc/%s/exe", dir->d_name) < 0) {
        return -1;
    }

    char buf[SEAF_PATH_MAX];
    /* get the full path of exe */
    ssize_t l = readlink(path, buf, SEAF_PATH_MAX);

    if (l < 0)
        return -1;
    buf[l] = '\0';

    /* get the base name of exe */
    char *base = g_path_get_basename(buf);
    int ret = strcmp(base, process_name);
    g_free(base);

    if (ret == 0)
        return atoi(dir->d_name);
    else
        return -1;
}

/* read the /proc fs to determine whether some process is running */
gboolean process_is_running (const char *process_name)
{
    DIR *proc_dir = opendir("/proc");
    if (!proc_dir) {
        fprintf (stderr, "failed to open /proc/ dir\n");
        return FALSE;
    }

    struct dirent *subdir = NULL;
    while ((subdir = readdir(proc_dir))) {
        char first = subdir->d_name[0];
        /* /proc/[1-9][0-9]* */
        if (first > '9' || first < '1')
            continue;
        int pid = find_process_in_dirent(subdir, process_name);
        if (pid > 0) {
            closedir(proc_dir);
            return TRUE;
        }
    }

    closedir(proc_dir);
    return FALSE;
}

int count_process(const char *process_name)
{
    int count = 0;
    DIR *proc_dir = opendir("/proc");
    if (!proc_dir) {
        g_warning ("failed to open /proc/ :%s\n", strerror(errno));
        return FALSE;
    }

    struct dirent *subdir = NULL;
    while ((subdir = readdir(proc_dir))) {
        char first = subdir->d_name[0];
        /* /proc/[1-9][0-9]* */
        if (first > '9' || first < '1')
            continue;
        if (find_process_in_dirent(subdir, process_name) > 0) {
            count++;
        }
    }

    closedir (proc_dir);
    return count;
}

#endif

#ifdef __APPLE__
gboolean process_is_running (const char *process_name)
{
    //TODO
    return FALSE;
}
#endif

char*
ccnet_object_type_from_id (const char *object_id)
{
    char *ptr;

    if ( !(ptr = strchr(object_id, '/')) )
        return NULL;

    return g_strndup(object_id, ptr - object_id);
}


#ifdef WIN32
/**
 * In Win32 we need to use _stat64 for files larger than 2GB. _stat64 needs
 * the `path' argument in gbk encoding.
 */
    #define STAT_STRUCT struct __stat64
    #define STAT_FUNC win_stat64_utf8

static inline int
win_stat64_utf8 (char *path_utf8, STAT_STRUCT *sb)
{
    wchar_t *path_w = wchar_from_utf8 (path_utf8);
    int result = _wstat64 (path_w, sb);
    free (path_w);
    return result;
}

#else
    #define STAT_STRUCT struct stat
    #define STAT_FUNC stat
#endif

static gint64
calc_recursively (const char *path, GError **calc_error)
{
    gint64 sum = 0;

    GError *error = NULL;
    GDir *folder = g_dir_open(path, 0, &error);
    if (!folder) {
        g_set_error (calc_error, CCNET_DOMAIN, 0,
                     "g_open() dir %s failed:%s\n", path, error->message);
        return -1;
    }

    const char *name = NULL;
    while ((name = g_dir_read_name(folder)) != NULL) {
        STAT_STRUCT sb;
        char *full_path= g_build_filename (path, name, NULL);
        if (STAT_FUNC(full_path, &sb) < 0) {
            g_set_error (calc_error, CCNET_DOMAIN, 0, "failed to stat on %s: %s\n",
                         full_path, strerror(errno));
            g_free(full_path);
            g_dir_close(folder);
            return -1;
        }

        if (S_ISDIR(sb.st_mode)) {
            gint64 size = calc_recursively(full_path, calc_error);
            if (size < 0) {
                g_free (full_path);
                g_dir_close (folder);
                return -1;
            }
            sum += size;
            g_free(full_path);
        } else if (S_ISREG(sb.st_mode)) {
            sum += sb.st_size;
            g_free(full_path);
        }
    }

    g_dir_close (folder);
    return sum;
}

gint64
ccnet_calc_directory_size (const char *path, GError **error)
{
    return calc_recursively (path, error);
}

#ifdef WIN32
/*
 * strtok_r code directly from glibc.git /string/strtok_r.c since windows
 * doesn't have it.
 */
char *
strtok_r(char *s, const char *delim, char **save_ptr)
{
    char *token;
    
    if(s == NULL)
        s = *save_ptr;
    
    /* Scan leading delimiters.  */
    s += strspn(s, delim);
    if(*s == '\0') {
        *save_ptr = s;
        return NULL;
    }
    
    /* Find the end of the token.  */
    token = s;
    s = strpbrk(token, delim);
    
    if(s == NULL) {
        /* This token finishes the string.  */
        *save_ptr = strchr(token, '\0');
    } else {
        /* Terminate the token and make *SAVE_PTR point past it.  */
        *s = '\0';
        *save_ptr = s + 1;
    }
    
    return token;
}
#endif

/* JSON related utils. For compatibility with json-glib. */

const char *
json_object_get_string_member (json_t *object, const char *key)
{
    json_t *string = json_object_get (object, key);
    if (!string)
        return NULL;
    return json_string_value (string);
}

gboolean
json_object_has_member (json_t *object, const char *key)
{
    return (json_object_get (object, key) != NULL);
}

gint64
json_object_get_int_member (json_t *object, const char *key)
{
    json_t *integer = json_object_get (object, key);
    return json_integer_value (integer);
}

void
json_object_set_string_member (json_t *object, const char *key, const char *value)
{
    json_object_set_new (object, key, json_string (value));
}

void
json_object_set_int_member (json_t *object, const char *key, gint64 value)
{
    json_object_set_new (object, key, json_integer (value));
}

void
clean_utf8_data (char *data, int len)
{
    const char *s, *e;
    char *p;
    gboolean is_valid;

    s = data;
    p = data;

    while ((s - data) != len) {
        is_valid = g_utf8_validate (s, len - (s - data), &e);
        if (is_valid)
            break;

        if (s != e)
            p += (e - s);
        *p = '?';
        ++p;
        s = e + 1;
    }
}

char *
normalize_utf8_path (const char *path)
{
    if (!g_utf8_validate (path, -1, NULL))
        return NULL;
    return g_utf8_normalize (path, -1, G_NORMALIZE_NFC);
}

/* zlib related wrapper functions. */

#define ZLIB_BUF_SIZE 16384

int
seaf_compress (guint8 *input, int inlen, guint8 **output, int *outlen)
{
    int ret;
    unsigned have;
    z_stream strm;
    guint8 out[ZLIB_BUF_SIZE];
    GByteArray *barray;

    if (inlen == 0)
        return -1;

    /* allocate deflate state */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
    if (ret != Z_OK) {
        g_warning ("deflateInit failed.\n");
        return -1;
    }

    strm.avail_in = inlen;
    strm.next_in = input;
    barray = g_byte_array_new ();

    do {
        strm.avail_out = ZLIB_BUF_SIZE;
        strm.next_out = out;
        ret = deflate(&strm, Z_FINISH);    /* no bad return value */
        have = ZLIB_BUF_SIZE - strm.avail_out;
        g_byte_array_append (barray, out, have);
    } while (ret != Z_STREAM_END);

    *outlen = barray->len;
    *output = g_byte_array_free (barray, FALSE);

    /* clean up and return */
    (void)deflateEnd(&strm);
    return 0;
}

int
seaf_decompress (guint8 *input, int inlen, guint8 **output, int *outlen)
{
    int ret;
    unsigned have;
    z_stream strm;
    unsigned char out[ZLIB_BUF_SIZE];
    GByteArray *barray;

    if (inlen == 0) {
        g_warning ("Empty input for zlib, invalid.\n");
        return -1;
    }

    /* allocate inflate state */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = 0;
    strm.next_in = Z_NULL;
    ret = inflateInit(&strm);
    if (ret != Z_OK) {
        g_warning ("inflateInit failed.\n");
        return -1;
    }

    strm.avail_in = inlen;
    strm.next_in = input;
    barray = g_byte_array_new ();

    do {
        strm.avail_out = ZLIB_BUF_SIZE;
        strm.next_out = out;
        ret = inflate(&strm, Z_NO_FLUSH);
        if (ret < 0) {
            g_warning ("Failed to inflate.\n");
            goto out;
        }
        have = ZLIB_BUF_SIZE - strm.avail_out;
        g_byte_array_append (barray, out, have);
    } while (ret != Z_STREAM_END);

out:
    /* clean up and return */
    (void)inflateEnd(&strm);

    if (ret == Z_STREAM_END) {
        *outlen = barray->len;
        *output = g_byte_array_free (barray, FALSE);
        return 0;
    } else {
        g_byte_array_free (barray, TRUE);
        return -1;
    }
}

char*
format_dir_path (const char *path)
{
    int path_len = strlen (path);
    char *rpath;
    if (path[0] != '/') {
        rpath = g_strconcat ("/", path, NULL);
        path_len++;
    } else {
        rpath = g_strdup (path);
    }
    while (path_len > 1 && rpath[path_len-1] == '/') {
        rpath[path_len-1] = '\0';
        path_len--;
    }

    return rpath;
}

gboolean
is_empty_string (const char *str)
{
    return !str || strcmp (str, "") == 0;
}

gboolean
is_permission_valid (const char *perm)
{
    if (is_empty_string (perm)) {
        return FALSE;
    }

    return strcmp (perm, "r") == 0 || strcmp (perm, "rw") == 0;
}

gboolean
is_eml_file (const char *path)
{
    int len = strlen(path);
    const char *ext;

    if (len < 5)
        return FALSE;
    ext = &path[len-4];
    return (strcasecmp (ext, ".eml") == 0);
}

char *
canonical_server_url (const char *url_in)
{
    char *url = g_strdup(url_in);
    int len = strlen(url);

    if (url[len - 1] == '/')
        url[len - 1] = 0;

    return url;
}

#ifdef __APPLE__
static gboolean
case_conflict_recursive (const char *worktree, const char *path, char **conflict_path, GHashTable *no_case_conflict_hash)
{
    if (!path || g_strcmp0 (path, ".") == 0) {
        return FALSE;
    }

    if (g_hash_table_lookup (no_case_conflict_hash, path)) {
        return FALSE;
    }

    SeafStat st;
    gboolean ret = FALSE;
    int fd = -1;
    char *full_path = g_build_path ("/", worktree, path, NULL);
    char *no_conflict_path = NULL;

    if (seaf_stat (full_path, &st) < 0) {
        char *sub_path = g_path_get_dirname (path);
        ret = case_conflict_recursive (worktree, sub_path, conflict_path, no_case_conflict_hash);
        g_free (sub_path);
        goto out;
    }

    int len = strlen (path);
    fd = open (full_path, O_RDONLY);
    if (fd < 0) {
        goto out;
    }
    char buffer[SEAF_PATH_MAX];
    if (fcntl (fd, F_GETPATH, buffer) < 0) {
        goto out;
    }
    int offset = strlen (buffer) - len;
    if (strcasecmp (buffer + offset, path) == 0 &&
        strcmp (buffer + offset, path) != 0) {
        if (conflict_path) {
            *conflict_path = g_strdup(path);
        }
        ret = TRUE;
        goto out;
    } else {
        no_conflict_path = g_strdup (path);
        g_hash_table_insert (no_case_conflict_hash, no_conflict_path, no_conflict_path);
    }
    
out:
    if (fd >= 0)
        close (fd);
    g_free (full_path);
    return ret;
}
#elif defined WIN32
static gboolean
case_conflict_recursive (const char *worktree, const char *path, char **conflict_path, GHashTable *no_case_conflict_hash)
{
    if (!path || g_strcmp0 (path, ".") == 0) {
        return FALSE;
    }

    if (g_hash_table_lookup (no_case_conflict_hash, path)) {
        return FALSE;
    }

    SeafStat st;
    gboolean ret = FALSE;
    int fd = -1;
    char *full_path = g_build_path ("/", worktree, path, NULL);
    wchar_t *wpath = win32_long_path (full_path);
    char *no_conflict_path = NULL;

    if (seaf_stat (full_path, &st) < 0) {
        char *sub_path = g_path_get_dirname (path);
        ret = case_conflict_recursive (worktree, sub_path, conflict_path, no_case_conflict_hash);
        if (!ret) {
            no_conflict_path = g_strdup (sub_path);
            g_hash_table_insert (no_case_conflict_hash, no_conflict_path, no_conflict_path);
        }
        g_free (sub_path);
        goto out;
    }

    HANDLE handle;
    WIN32_FIND_DATAW fdata;
    handle = FindFirstFileW (wpath, &fdata);
    if (handle == INVALID_HANDLE_VALUE) {
        seaf_warning ("Checkinng path case, FindFirstFile failed %s: %lu.\n", full_path, GetLastError());
        goto out;
    }
    char *real_name = g_utf16_to_utf8 (fdata.cFileName, -1, NULL, NULL, NULL);
    int offset = strlen (path) - strlen(real_name);
    if (strcasecmp (path + offset, real_name) == 0 &&
        strcmp (path + offset, real_name) != 0) {
        if (conflict_path) {
            *conflict_path = g_strdup(path);
        }
        ret = TRUE;
        g_free (real_name);
        FindClose (handle);
        goto out;
    }
    g_free (real_name);
    FindClose (handle);

    char *sub_path = g_path_get_dirname (path);
    ret = case_conflict_recursive (worktree, sub_path, conflict_path, no_case_conflict_hash);
    g_free (sub_path);

out:
    g_free (full_path);
    g_free (wpath);
    return ret;
}
#else
static gboolean
case_conflict_recursive (const char *worktree, const char *path, char **conflict_path, GHashTable *no_case_conflict_hash)
{
    return FALSE;
}
#endif

gboolean
is_path_case_conflict (const char *worktree, const char *path, char **conflict_path, GHashTable *no_case_conflict_hash)
{
    if (strlen(path) >= SEAF_PATH_MAX) {
        return FALSE;
    }
    if (case_conflict_recursive (worktree, path, conflict_path, no_case_conflict_hash))
        return TRUE;
#ifdef WIN32
    char *no_conflict_path = g_strdup (path);
    g_hash_table_insert (no_case_conflict_hash, no_conflict_path, no_conflict_path);
#endif
    return FALSE;
}

ssize_t
seaf_getxattr (const char *path, const char *name, void *value, size_t size)
{
#ifdef WIN32
    /* On Windows, use alternate data streams for xattrs. */

    char *stream_path;
    wchar_t *w_stream_path;
    HANDLE handle;
    LARGE_INTEGER stream_size;
    DWORD n_to_read, n_read;
    ssize_t ret;

    stream_path = g_strconcat (path, ":", name, NULL);
    w_stream_path = win32_long_path (stream_path);
    g_free (stream_path);

    handle = CreateFileW (w_stream_path,
                          GENERIC_READ,
                          FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                          NULL,
                          OPEN_EXISTING,
                          0,
                          NULL);
    if (handle == INVALID_HANDLE_VALUE) {
        errno = windows_error_to_errno (GetLastError());
        ret = -1;
        goto out;
    }

    if (!GetFileSizeEx (handle, &stream_size)) {
        errno = windows_error_to_errno (GetLastError());
        ret = -1;
        goto out;
    }

    n_to_read = MIN (stream_size.LowPart, size);
    if (!ReadFile (handle, value, n_to_read, &n_read, NULL)) {
        errno = windows_error_to_errno (GetLastError());
        ret = -1;
        goto out;
    }

    ret = (ssize_t)n_read;

out:
    g_free (w_stream_path);
    if (handle != INVALID_HANDLE_VALUE)
        CloseHandle(handle);
    return ret;
#endif

#ifdef __linux__
    return getxattr (path, name, value, size);
#endif

#ifdef __APPLE__
    return getxattr (path, name, value, size, 0, 0);
#endif

#if defined __FreeBSD__ || defined __NetBSD__
    return extattr_get_file (path, EXTATTR_NAMESPACE_USER, name, value, size);
#endif

    return -1;
}

int
seaf_setxattr (const char *path, const char *name, const void *value, size_t size)
{
#ifdef WIN32
    char *stream_path;
    wchar_t *w_stream_path;
    HANDLE handle;
    DWORD n_written;
    ssize_t ret = 0;

    stream_path = g_strconcat (path, ":", name, NULL);
    w_stream_path = win32_long_path (stream_path);
    g_free (stream_path);

    handle = CreateFileW (w_stream_path,
                          GENERIC_WRITE,
                          FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                          NULL,
                          OPEN_ALWAYS,
                          0,
                          NULL);
    if (handle == INVALID_HANDLE_VALUE) {
        errno = windows_error_to_errno (GetLastError());
        ret = -1;
        goto out;
    }

    if (!WriteFile (handle, value, (DWORD)size, &n_written, NULL)) {
        errno = windows_error_to_errno (GetLastError());
        ret = -1;
    }

out:
    g_free (w_stream_path);
    if (handle != INVALID_HANDLE_VALUE)
        CloseHandle(handle);
    return ret;
#endif

#ifdef __linux__
    return setxattr (path, name, value, size, 0);
#endif

#ifdef __APPLE__
    return setxattr (path, name, value, size, 0, 0);
#endif

#if defined __FreeBSD__ || defined __NetBSD__
    return extattr_set_file (path, EXTATTR_NAMESPACE_USER, name, value, size);
#endif

    return -1;
}

int
seaf_removexattr (const char *path, const char *name)
{
#ifdef WIN32
    char *stream_path;
    wchar_t *w_stream_path;
    int ret = 0;

    stream_path = g_strconcat (path, ":", name, NULL);
    w_stream_path = win32_long_path (stream_path);
    g_free (stream_path);

    if (!DeleteFileW (w_stream_path)) {
        errno = windows_error_to_errno (GetLastError());
        ret = -1;
    }

    g_free (w_stream_path);
    return ret;
#else

#ifdef __APPLE__
    return removexattr (path, name, 0);
#else
    return removexattr (path, name);
#endif

#endif
}
