/*
* tools.c
*
* This file is part of msmtp, an SMTP client.
*
* Copyright (C) 2004, 2005, 2006, 2007
* Martin Lambers <marlam@marlam.de>
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#ifdef W32_NATIVE
# include <windows.h>
# include <io.h>
# include <lmcons.h>
# include <sys/locking.h>
# include <limits.h>
#else
# include <pwd.h>
#endif
#include "gettext.h"
#include "xalloc.h"
#include "tools.h"
/*
* get_prgname()
*
* see tools.h
*/
const char *get_prgname(const char *argv0)
{
const char *prgname;
if (argv0)
{
prgname = strrchr(argv0, PATH_SEP);
if (!prgname)
{
prgname = argv0;
}
else
{
prgname++;
}
}
else
{
prgname = "";
}
return prgname;
}
/*
* get_sysconfdir()
*
* see tools.h
*/
char *get_sysconfdir(void)
{
#ifdef W32_NATIVE
BYTE sysconfdir[MAX_PATH + 1];
HKEY hkey;
DWORD len;
DWORD type;
long l;
l = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\"
"Shell Folders", 0, KEY_READ, &hkey);
if (l != ERROR_SUCCESS)
{
return xstrdup("C:");
}
len = MAX_PATH;
l = RegQueryValueEx(hkey, "Common AppData", NULL, &type, sysconfdir, &len);
if (l != ERROR_SUCCESS || len >= MAX_PATH)
{
if (l != ERROR_SUCCESS || len >= MAX_PATH)
{
return xstrdup("C:");
}
}
RegCloseKey(hkey);
return xstrdup((char *)sysconfdir);
#else /* UNIX or DJGPP */
#ifdef SYSCONFDIR
return xstrdup(SYSCONFDIR);
#else
return xstrdup("/etc");
#endif
#endif
}
/*
* get_username()
*
* see tools.h
*/
char *get_username(void)
{
char *username;
#ifdef W32_NATIVE
DWORD size = UNLEN + 1;
TCHAR buf[UNLEN + 1];
#elif defined DJGPP
#else /* UNIX */
struct passwd *pw;
#endif
username = getenv("USER");
if (username)
{
username = xstrdup(username);
}
else
{
username = getenv("LOGNAME");
if (username)
{
username = xstrdup(username);
}
else
{
#ifdef W32_NATIVE
if (GetUserName(buf, &size))
{
username = xstrdup((char *)buf);
}
else
{
/* last resort */
username = xstrdup("unknown");
}
#elif defined DJGPP
/* DJGPP's getlogin() checks USER, then LOGNAME, and then uses
* "dosuser" as a last resort. We already checked USER and LOGNAME
* and choose "unknown" as a last resort to be consistent with the
* other systems. */
username = xstrdup("unknown");
#else /* UNIX */
username = getlogin();
if (username)
{
username = xstrdup(username);
}
else
{
pw = getpwuid(getuid());
if (pw && pw->pw_name)
{
username = xstrdup(pw->pw_name);
}
else
{
/* last resort */
username = xstrdup("unknown");
}
}
#endif
}
}
return username;
}
/*
* get_homedir()
*
* see tools.h
*/
char *get_homedir(void)
{
#ifdef W32_NATIVE
char *home;
BYTE homebuf[MAX_PATH + 1];
HKEY hkey;
DWORD len;
DWORD type;
long l;
if ((home = getenv("HOME")))
{
home = xstrdup(home);
}
else
{
home = NULL;
l = RegOpenKeyEx(HKEY_CURRENT_USER,
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\"
"Shell Folders", 0, KEY_READ, &hkey);
if (l == ERROR_SUCCESS)
{
len = MAX_PATH;
l = RegQueryValueEx(hkey, "AppData", NULL, &type, homebuf, &len);
if (l == ERROR_SUCCESS && len < MAX_PATH)
{
RegCloseKey(hkey);
home = xstrdup((char *)homebuf);
}
}
if (!home)
{
home = xstrdup("C:");
}
}
return home;
#elif defined DJGPP
char *home;
if ((home = getenv("HOME")))
{
home = xstrdup(home);
}
else
{
home = xstrdup("C:");
}
return home;
#else /* UNIX */
char *home;
struct passwd *pw;
if ((home = getenv("HOME")))
{
home = xstrdup(home);
}
else
{
pw = getpwuid(getuid());
if (pw && pw->pw_dir)
{
home = xstrdup(pw->pw_dir);
}
else
{
home = xstrdup("");
}
}
return home;
#endif
}
/*
* get_filename()
*
* see tools.h
*/
char *get_filename(const char *directory, const char *name)
{
char *path;
size_t dirlen;
dirlen = strlen(directory);
path = xmalloc((dirlen + strlen(name) + 2) * sizeof(char));
strcpy(path, directory);
if (dirlen == 0 || path[dirlen - 1] != PATH_SEP)
{
path[dirlen++] = PATH_SEP;
}
strcpy(path + dirlen, name);
return path;
}
/*
* expand_tilde()
*
* see tools.h
*/
char *expand_tilde(const char *filename)
{
char *new_filename;
size_t homedirlen;
if (filename[0] == '~')
{
new_filename = get_homedir();
homedirlen = strlen(new_filename);
new_filename = xrealloc(new_filename,
(homedirlen + strlen(filename)) * sizeof(char));
strcpy(new_filename + homedirlen, filename + 1);
return new_filename;
}
else
{
return xstrdup(filename);
}
}
/*
* check_secure()
*
* see tools.h
*/
int check_secure(const char *pathname)
{
#if defined W32_NATIVE || defined DJGPP || defined __CYGWIN__
return 0;
#else /* UNIX */
struct stat statbuf;
if (stat(pathname, &statbuf) < 0)
{
return 3;
}
if (statbuf.st_uid != geteuid())
{
return 1;
}
if (statbuf.st_mode & (S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP
| S_IROTH | S_IWOTH | S_IXOTH))
{
return 2;
}
return 0;
#endif /* UNIX */
}
/*
* [DJGPP and Windows only] mkstemp_unlink()
*
* This function does on DOS/Windows what mkstemp() followed by unlink() do on
* UNIX.
*
* 1. unlink() on DOS and Windows is not POSIX conformant: it does not wait
* until the last file descriptor is closed before unlinking the file.
* Instead, it fails (Windows) or may even mess up the file system (DOS).
* 2. Windows does not have mkstemp.
* 3. If a file is opened with O_TEMPORARY on Windows or DOS, it will be deleted
* after the last file descriptor is closed. This is what this function does.
*
* Return value: file descriptor, or -1 on error (errno will be set).
*/
#if defined(W32_NATIVE) || defined(DJGPP)
int mkstemp_unlink(char *template)
{
size_t templatelen;
char *X;
int i;
int try;
int ret;
const char alnum[]
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
templatelen = strlen(template);
if (templatelen < 6)
{
errno = EINVAL;
return -1;
}
X = template + templatelen - 6;
if (strcmp(X, "XXXXXX") != 0)
{
errno = EINVAL;
return -1;
}
srand((unsigned int)time(NULL));
/* We have 62^6 possible filenames. We try 62^2=3844 times. */
ret = -1;
for (try = 0; ret == -1 && try < 3844; try++)
{
for (i = 0; i < 6; i++)
{
X[i] = alnum[rand() % 36];
}
#ifdef W32_NATIVE
ret = _open(template, _O_CREAT | _O_EXCL | _O_RDWR
| _O_TEMPORARY | _O_BINARY,
_S_IREAD | _S_IWRITE);
#else /* DJGPP */
ret = open(template, O_CREAT | O_EXCL | O_RDWR
| O_TEMPORARY | _O_BINARY,
S_IRUSR | S_IWUSR);
#endif /* DJGPP */
}
return ret;
}
#endif /* W32_NATIVE or DJGPP */
/*
* tempfile()
*
* see tools.h
*/
FILE *tempfile(const char *base)
{
FILE *f;
size_t baselen;
const char *dir;
size_t dirlen;
char *template = NULL;
size_t templatelen;
int fd = -1;
int saved_errno;
if (!base || (*base == '\0'))
{
base = "tmp";
}
/* the directory for the temp file */
if (!(dir = getenv("TMPDIR")))
{
/* system dependent default location */
#ifdef W32_NATIVE
/* there is no registry key for this (?) */
if (!(dir = getenv("TEMP")))
{
if (!(dir = getenv("TMP")))
{
dir = "C:";
}
}
#elif defined DJGPP
dir = "C:";
#else /* UNIX */
#ifdef P_tmpdir
dir = P_tmpdir;
#else
dir = "/tmp";
#endif
#endif /* UNIX */
}
dirlen = strlen(dir);
/* the proposed file name */
baselen = strlen(base);
#ifdef DJGPP
/* shorten the base to two characters because of 8.3 filenames */
if (baselen > 2)
{
baselen = 2;
}
#endif
/* build the template */
templatelen = dirlen + 1 + baselen + 6;
template = xmalloc((templatelen + 1) * sizeof(char));
strncpy(template, dir, dirlen);
if (dirlen == 0 || template[dirlen - 1] != PATH_SEP)
{
template[dirlen++] = PATH_SEP;
}
/* template is long enough */
strncpy(template + dirlen, base, baselen);
strcpy(template + dirlen + baselen, "XXXXXX");
/* create the file */
#if defined(W32_NATIVE) || defined(DJGPP)
if ((fd = mkstemp_unlink(template)) == -1)
#else /* UNIX */
if ((fd = mkstemp(template)) == -1)
#endif /* UNIX */
{
goto error_exit;
}
/* UNIX only: set the permissions (not every mkstemp() sets them to 0600)
* and unlink the file so that it gets deleted when the caller closes it */
#ifndef DJGPP
#ifndef W32_NATIVE
if (fchmod(fd, S_IRUSR | S_IWUSR) == -1)
{
goto error_exit;
}
if (unlink(template) != 0)
{
goto error_exit;
}
#endif /* not W32_NATIVE */
#endif /* not DJGPP */
/* get the stream from the filedescriptor */
if (!(f = fdopen(fd, "w+")))
{
goto error_exit;
}
free(template);
return f;
error_exit:
saved_errno = errno;
if (fd >= 0)
{
close(fd);
}
if (template)
{
(void)remove(template);
free(template);
}
errno = saved_errno;
return NULL;
}
/*
* lock_file()
*
* see tools.h
*/
/* Helper function that sleeps for the tenth of a second */
static void sleep_tenth_second(void)
{
#ifdef W32_NATIVE
Sleep(100);
#elif defined DJGPP
usleep(100000);
#else /* POSIX */
struct timespec tenth_second = { 0, 100000000 };
nanosleep(&tenth_second, NULL);
#endif
}
int lock_file(FILE *f, int lock_type, int timeout)
{
int fd;
int lock_success;
int tenth_seconds;
#ifndef W32_NATIVE
struct flock lock;
#endif /* not W32_NATIVE */
fd = fileno(f);
#ifndef W32_NATIVE
lock.l_type = (lock_type == TOOLS_LOCK_WRITE) ? F_WRLCK : F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
#endif /* not W32_NATIVE */
tenth_seconds = 0;
for (;;)
{
errno = 0;
#ifdef W32_NATIVE
lock_success = (_locking(fd, _LK_NBLCK, LONG_MAX) != -1);
#else /* UNIX, DJGPP */
lock_success = (fcntl(fd, F_SETLK, &lock) != -1);
#endif
if (lock_success || (errno != EACCES && errno != EAGAIN)
|| tenth_seconds / 10 >= timeout)
{
break;
}
else
{
sleep_tenth_second();
tenth_seconds++;
}
}
return (lock_success ? 0 : (tenth_seconds / 10 >= timeout ? 1 : 2));
}
/*
* string_replace()
*
* see tools.h
*/
char *string_replace(char *str, const char *s, const char *r)
{
char *p, *new_str;
size_t next_pos = 0;
size_t slen = strlen(s);
size_t rlen = strlen(r);
while ((p = strstr(str + next_pos, s)))
{
new_str = xmalloc((strlen(str) + rlen - 1) * sizeof(char));
strncpy(new_str, str, (size_t)(p - str));
strcpy(new_str + (size_t)(p - str), r);
strcpy(new_str + (size_t)(p - str) + rlen,
str + (size_t)(p - str) + slen);
next_pos = (size_t)(p - str) + rlen;
free(str);
str = new_str;
}
return str;
}
syntax highlighted by Code2HTML, v. 0.9.1