diff --git a/src/assuan-support.c b/src/assuan-support.c index 419adb41..8c331c68 100644 --- a/src/assuan-support.c +++ b/src/assuan-support.c @@ -1,314 +1,314 @@ /* assuan-support.c - Assuan wrappers * Copyright (C) 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "assuan.h" #include "gpgme.h" #include "ath.h" #include "priv-io.h" #include "debug.h" struct assuan_malloc_hooks _gpgme_assuan_malloc_hooks = { malloc, realloc, free }; int _gpgme_assuan_log_cb (assuan_context_t ctx, void *hook, unsigned int cat, const char *msg) { (void)ctx; (void)hook; (void)cat; if (msg == NULL) return 1; - _gpgme_debug (DEBUG_ASSUAN, "%s", msg); + _gpgme_debug (DEBUG_ASSUAN, -1, NULL, NULL, NULL, "%s", msg); return 0; } static void my_usleep (assuan_context_t ctx, unsigned int usec) { /* FIXME: Add to ath. */ __assuan_usleep (ctx, usec); } /* Create a pipe with an inheritable end. */ static int my_pipe (assuan_context_t ctx, assuan_fd_t fds[2], int inherit_idx) { int res; int gfds[2]; (void)ctx; res = _gpgme_io_pipe (gfds, inherit_idx); /* For now... */ fds[0] = (assuan_fd_t) gfds[0]; fds[1] = (assuan_fd_t) gfds[1]; return res; } /* Close the given file descriptor, created with _assuan_pipe or one of the socket functions. */ static int my_close (assuan_context_t ctx, assuan_fd_t fd) { (void)ctx; return _gpgme_io_close ((int) fd); } static gpgme_ssize_t my_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) { (void)ctx; return _gpgme_io_read ((int) fd, buffer, size); } static gpgme_ssize_t my_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size) { (void)ctx; return _gpgme_io_write ((int) fd, buffer, size); } static int my_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { (void)ctx; #ifdef HAVE_W32_SYSTEM (void)fd; (void)msg; (void)flags; gpg_err_set_errno (ENOSYS); return -1; #else return _gpgme_io_recvmsg ((int) fd, msg, flags); #endif } static int my_sendmsg (assuan_context_t ctx, assuan_fd_t fd, const assuan_msghdr_t msg, int flags) { (void)ctx; #ifdef HAVE_W32_SYSTEM (void)fd; (void)msg; (void)flags; gpg_err_set_errno (ENOSYS); return -1; #else return _gpgme_io_sendmsg ((int) fd, msg, flags); #endif } /* If NAME is NULL, don't exec, just fork. FD_CHILD_LIST is modified to reflect the value of the FD in the peer process (on Windows). */ static int my_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char **argv, assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { int err; struct spawn_fd_item_s *fd_items; int i; (void)ctx; (void)flags; assert (name); if (! name) { gpg_err_set_errno (ENOSYS); return -1; } i = 0; if (fd_child_list) { while (fd_child_list[i] != ASSUAN_INVALID_FD) i++; } /* fd_in, fd_out, terminator */ i += 3; fd_items = calloc (i, sizeof (struct spawn_fd_item_s)); if (! fd_items) return -1; i = 0; if (fd_child_list) { while (fd_child_list[i] != ASSUAN_INVALID_FD) { fd_items[i].fd = (int) fd_child_list[i]; fd_items[i].dup_to = -1; i++; } } if (fd_in != ASSUAN_INVALID_FD) { fd_items[i].fd = (int) fd_in; fd_items[i].dup_to = 0; i++; } if (fd_out != ASSUAN_INVALID_FD) { fd_items[i].fd = (int) fd_out; fd_items[i].dup_to = 1; i++; } fd_items[i].fd = -1; fd_items[i].dup_to = -1; err = _gpgme_io_spawn (name, (char*const*)argv, (IOSPAWN_FLAG_NOCLOSE | IOSPAWN_FLAG_DETACHED), fd_items, atfork, atforkvalue, r_pid); if (! err) { i = 0; if (fd_child_list) { while (fd_child_list[i] != ASSUAN_INVALID_FD) { fd_child_list[i] = (assuan_fd_t) fd_items[i].peer_name; i++; } } } free (fd_items); return err; } /* If action is 0, like waitpid. If action is 1, just release the PID? */ static pid_t my_waitpid (assuan_context_t ctx, pid_t pid, int nowait, int *status, int options) { (void)ctx; #ifdef HAVE_W32_SYSTEM (void)nowait; (void)status; (void)options; (void)pid; /* Just a number without a kernel object. */ #else /* We can't just release the PID, a waitpid is mandatory. But NOWAIT in POSIX systems just means the caller already did the waitpid for this child. */ if (! nowait) return _gpgme_ath_waitpid (pid, status, options); #endif return 0; } static int my_socketpair (assuan_context_t ctx, int namespace, int style, int protocol, assuan_fd_t filedes[2]) { #ifdef HAVE_W32_SYSTEM (void)ctx; (void)namespace; (void)style; (void)protocol; (void)filedes; gpg_err_set_errno (ENOSYS); return -1; #else /* FIXME: Debug output missing. */ return __assuan_socketpair (ctx, namespace, style, protocol, filedes); #endif } static int my_socket (assuan_context_t ctx, int namespace, int style, int protocol) { (void)ctx; return _gpgme_io_socket (namespace, style, protocol); } static int my_connect (assuan_context_t ctx, int sock, struct sockaddr *addr, socklen_t length) { (void)ctx; return _gpgme_io_connect (sock, addr, length); } /* Note for Windows: Ignore the incompatible pointer type warning for my_read and my_write. Mingw has been changed to use int for ssize_t on 32 bit systems while we use long. For 64 bit we use int64_t while mingw uses __int64_t. It doe not matter at all because under Windows long and int are both 32 bit even on 64 bit. */ struct assuan_system_hooks _gpgme_assuan_system_hooks = { ASSUAN_SYSTEM_HOOKS_VERSION, my_usleep, my_pipe, my_close, my_read, my_write, my_recvmsg, my_sendmsg, my_spawn, my_waitpid, my_socketpair, my_socket, my_connect }; diff --git a/src/debug.c b/src/debug.c index a2f0e694..29f7dc6d 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,485 +1,433 @@ /* debug.c - helpful output in desperate situations * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #ifndef HAVE_DOSISH_SYSTEM # ifdef HAVE_SYS_TYPES_H # include # endif # ifdef HAVE_SYS_STAT_H # include # endif # include #endif #include #include "util.h" #include "ath.h" #include "sema.h" #include "sys-util.h" #include "debug.h" /* Lock to serialize initialization of the debug output subsystem and output of actual debug messages. */ DEFINE_STATIC_LOCK (debug_lock); /* The amount of detail requested by the user, per environment variable GPGME_DEBUG. */ static int debug_level; /* The output stream for the debug messages. */ static FILE *errfp; /* If not NULL, this malloced string is used instead of the GPGME_DEBUG envvar. It must have been set before the debug subsystem has been initialized. Using it later may or may not have any effect. */ static char *envvar_override; #ifdef HAVE_TLS #define FRAME_NR static __thread int frame_nr = 0; #endif void _gpgme_debug_frame_begin (void) { #ifdef FRAME_NR frame_nr++; #endif } int _gpgme_debug_frame_end (void) { #ifdef FRAME_NR frame_nr--; #endif return 0; } /* Remove leading and trailing white spaces. */ static char * trim_spaces (char *str) { char *string, *p, *mark; string = str; /* Find first non space character. */ for (p = string; *p && isspace (*(unsigned char *) p); p++) ; /* Move characters. */ for (mark = NULL; (*string = *p); string++, p++) if (isspace (*(unsigned char *) p)) { if (!mark) mark = string; } else mark = NULL; if (mark) *mark = '\0'; /* Remove trailing spaces. */ return str; } /* This is an internal function to set debug info. The caller must assure that this function is called only by one thread at a time. The function may have no effect if called after the debug system has been initialized. Returns 0 on success. */ int _gpgme_debug_set_debug_envvar (const char *value) { free (envvar_override); envvar_override = strdup (value); return !envvar_override; } static void debug_init (void) { static int initialized; LOCK (debug_lock); if (!initialized) { gpgme_error_t err; char *e; const char *s1, *s2;; if (envvar_override) { e = strdup (envvar_override); free (envvar_override); envvar_override = NULL; } else { err = _gpgme_getenv ("GPGME_DEBUG", &e); if (err) { UNLOCK (debug_lock); return; } } initialized = 1; errfp = stderr; if (e) { debug_level = atoi (e); s1 = strchr (e, PATHSEP_C); if (s1) { #ifndef HAVE_DOSISH_SYSTEM if (getuid () == geteuid () #if defined(HAVE_GETGID) && defined(HAVE_GETEGID) && getgid () == getegid () #endif ) { #endif char *p; FILE *fp; s1++; if (!(s2 = strchr (s1, PATHSEP_C))) s2 = s1 + strlen (s1); p = malloc (s2 - s1 + 1); if (p) { memcpy (p, s1, s2 - s1); p[s2-s1] = 0; trim_spaces (p); fp = fopen (p,"a"); if (fp) { setvbuf (fp, NULL, _IOLBF, 0); errfp = fp; } free (p); } #ifndef HAVE_DOSISH_SYSTEM } #endif } free (e); } } UNLOCK (debug_lock); if (debug_level > 0) { - _gpgme_debug (DEBUG_INIT, "gpgme_debug: level=%d\n", debug_level); + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, + "gpgme_debug: level=%d\n", debug_level); #ifdef HAVE_W32_SYSTEM { const char *name = _gpgme_get_inst_dir (); - _gpgme_debug (DEBUG_INIT, "gpgme_debug: gpgme='%s'\n", - name? name: "?"); + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, + "gpgme_debug: gpgme='%s'\n", name? name: "?"); } #endif } } /* This should be called as soon as the locks are initialized. It is required so that the assuan logging gets conncted to the gpgme log stream as early as possible. */ void _gpgme_debug_subsystem_init (void) { debug_init (); } -/* Log the formatted string FORMAT at debug level LEVEL or higher. - * - * Returns: 0 - * - * Note that we always return 0 because the old TRACE macro evaluated - * to 0 which issues a warning with newer gcc version about an unused - * values. By using a return value of this function this can be - * avoided. Fixme: It might be useful to check whether the return - * value from the TRACE macros are actually used somewhere. - */ -int -_gpgme_debug (int level, const char *format, ...) -{ - va_list arg_ptr; - int saved_errno; - - saved_errno = errno; - if (debug_level < level) - return 0; - - va_start (arg_ptr, format); - LOCK (debug_lock); - { - struct tm *tp; - time_t atime = time (NULL); - - tp = localtime (&atime); - fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx> ", - 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, - tp->tm_hour, tp->tm_min, tp->tm_sec, - (unsigned long long) ath_self ()); - } -#ifdef FRAME_NR - { - int indent; - - indent = frame_nr > 0? (2 * (frame_nr - 1)):0; - fprintf (errfp, "%*s", indent < 40? indent : 40, ""); - } -#endif - - vfprintf (errfp, format, arg_ptr); - va_end (arg_ptr); - if(format && *format && format[strlen (format) - 1] != '\n') - putc ('\n', errfp); - UNLOCK (debug_lock); - fflush (errfp); - - gpg_err_set_errno (saved_errno); - return 0; -} - /* Log the formatted string FORMAT prefixed with additional info * depending on MODE: * * -1 = Do not print any additional args. * 0 = standalone (used by macro TRACE) * 1 = enter a function (used by macro TRACE_BEG) * 2 = debug a function (used by macro TRACE_LOG) * 3 = leave a function (used by macro TRACE_SUC) * * Returns: 0 * * Note that we always return 0 because the old TRACE macro evaluated * to 0 which issues a warning with newer gcc version about an unused * values. By using a return value of this function this can be * avoided. Fixme: It might be useful to check whether the return * value from the TRACE macros are actually used somewhere. */ int -_gpgme_debugf (int level, int mode, const char *func, const char *tagname, - const char *tagvalue, const char *format, ...) +_gpgme_debug (int level, int mode, const char *func, const char *tagname, + const char *tagvalue, const char *format, ...) { va_list arg_ptr; int saved_errno; saved_errno = errno; if (debug_level < level) return 0; va_start (arg_ptr, format); LOCK (debug_lock); { struct tm *tp; time_t atime = time (NULL); tp = localtime (&atime); fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx> ", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, (unsigned long long) ath_self ()); } #ifdef FRAME_NR { int indent; indent = frame_nr > 0? (2 * (frame_nr - 1)):0; fprintf (errfp, "%*s", indent < 40? indent : 40, ""); } #endif switch (mode) { case -1: /* Do nothing. */ break; case 0: fprintf (errfp, "%s: call: %s=%p ", func, tagname, tagvalue); break; case 1: fprintf (errfp, "%s: enter: %s=%p ", func, tagname, tagvalue); break; case 2: fprintf (errfp, "%s: check: %s=%p ", func, tagname, tagvalue); break; case 3: if (tagname) fprintf (errfp, "%s: leave: %s=%p ", func, tagname, tagvalue); else fprintf (errfp, "%s: leave: ", func); break; default: fprintf (errfp, "%s: m=%d: %s=%p ", func, mode, tagname, tagvalue); break; } vfprintf (errfp, format, arg_ptr); va_end (arg_ptr); if(format && *format && format[strlen (format) - 1] != '\n') putc ('\n', errfp); UNLOCK (debug_lock); fflush (errfp); gpg_err_set_errno (saved_errno); return 0; } - /* Start a new debug line in *LINE, logged at level LEVEL or higher, and starting with the formatted string FORMAT. */ void _gpgme_debug_begin (void **line, int level, const char *format, ...) { va_list arg_ptr; int res; if (debug_level < level) { /* Disable logging of this line. */ *line = NULL; return; } va_start (arg_ptr, format); res = gpgrt_vasprintf ((char **) line, format, arg_ptr); va_end (arg_ptr); if (res < 0) *line = NULL; } /* Add the formatted string FORMAT to the debug line *LINE. */ void _gpgme_debug_add (void **line, const char *format, ...) { va_list arg_ptr; char *toadd; char *result; int res; if (!*line) return; va_start (arg_ptr, format); res = gpgrt_vasprintf (&toadd, format, arg_ptr); va_end (arg_ptr); if (res < 0) { gpgrt_free (*line); *line = NULL; } res = gpgrt_asprintf (&result, "%s%s", *(char **) line, toadd); gpgrt_free (toadd); gpgrt_free (*line); if (res < 0) *line = NULL; else *line = result; } /* Finish construction of *LINE and send it to the debug output stream. */ void _gpgme_debug_end (void **line) { if (!*line) return; /* The smallest possible level is 1, so force logging here by using that. */ - _gpgme_debug (1, "%s", *line); + _gpgme_debug (1, -1, NULL, NULL, NULL, "%s", *line); gpgrt_free (*line); *line = NULL; } #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a')) void _gpgme_debug_buffer (int lvl, const char *const fmt, const char *const func, const char *const buffer, size_t len) { int idx = 0; int j; if (!_gpgme_debug_trace ()) return; if (!buffer) return; while (idx < len) { char str[51]; char *strp = str; char *strp2 = &str[34]; for (j = 0; j < 16; j++) { unsigned char val; if (idx < len) { val = buffer[idx++]; *(strp++) = TOHEX (val >> 4); *(strp++) = TOHEX (val % 16); *(strp2++) = isprint (val) ? val : '.'; } else { *(strp++) = ' '; *(strp++) = ' '; } if (j == 7) *(strp++) = ' '; } *(strp++) = ' '; *(strp2) = '\0'; - _gpgme_debug (lvl, fmt, func, str); + _gpgme_debug (lvl, -1, NULL, NULL, NULL, fmt, func, str); } } diff --git a/src/debug.h b/src/debug.h index 9f9fd5df..06e6d02c 100644 --- a/src/debug.h +++ b/src/debug.h @@ -1,194 +1,198 @@ /* debug.h - interface to debugging functions Copyright (C) 2002, 2004, 2005, 2007 g10 Code GmbH This file is part of GPGME. GPGME is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. GPGME 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef DEBUG_H #define DEBUG_H #include #ifdef HAVE_STDINT_H #include #endif #include "gpgme.h" /* Required for gpgme_error stuff. */ /* Indirect stringification, requires __STDC__ to work. */ #define STRINGIFY(v) #v #define XSTRINGIFY(v) STRINGIFY(v) /* * The debug levels. * * Note that TRACE_LOGBUFX uses the current debug level + 1. */ #define DEBUG_INIT 1 #define DEBUG_GLOBAL 2 #define DEBUG_CTX 3 #define DEBUG_ENGINE 4 #define DEBUG_DATA 5 #define DEBUG_ASSUAN 6 #define DEBUG_SYSIO 7 /* Remove path components from filenames (i.e. __FILE__) for cleaner logs. */ static inline const char *_gpgme_debug_srcname (const char *file) GPGME_GCC_A_PURE; static inline const char * _gpgme_debug_srcname (const char *file) { const char *s = strrchr (file, '/'); return s? s+1:file; } /* Initialization helper function; see debug.c. */ int _gpgme_debug_set_debug_envvar (const char *value); /* Called early to initialize the logging. */ void _gpgme_debug_subsystem_init (void); /* Log the formatted string FORMAT at debug level LEVEL or higher. */ -int _gpgme_debug (int level, const char *format, ...); -int _gpgme_debugf (int level, int mode, - const char *func, const char *tagname, const char *tagvalue, - const char *format, ...) GPGRT_ATTR_PRINTF(6,7); +int _gpgme_debug (int level, int mode, + const char *func, const char *tagname, const char *tagvalue, + const char *format, ...) GPGRT_ATTR_PRINTF(6,7); /* Start a new debug line in *LINE, logged at level LEVEL or higher, and starting with the formatted string FORMAT. */ void _gpgme_debug_begin (void **helper, int level, const char *format, ...); /* Add the formatted string FORMAT to the debug line *LINE. */ void _gpgme_debug_add (void **helper, const char *format, ...); /* Finish construction of *LINE and send it to the debug output stream. */ void _gpgme_debug_end (void **helper); void _gpgme_debug_buffer (int lvl, const char *const fmt, const char *const func, const char *const buffer, size_t len); void _gpgme_debug_frame_begin (void); int _gpgme_debug_frame_end (void); static inline gpgme_error_t _gpgme_trace_gpgme_error (gpgme_error_t err, const char *file, int line) { - _gpgme_debug (DEBUG_ENGINE, "%s:%d: returning error: %s\n", + _gpgme_debug (DEBUG_ENGINE, -1, NULL, NULL, NULL, + "%s:%d: returning error: %s\n", _gpgme_debug_srcname (file), line, gpgme_strerror (err)); return err; } /* Trace support. */ /* FIXME: For now. */ #define _gpgme_debug_trace() 1 #define _TRACE(lvl, name, tag) \ int _gpgme_trace_level = lvl; \ const char *const _gpgme_trace_func = name; \ const char *const _gpgme_trace_tagname = STRINGIFY (tag); \ void *_gpgme_trace_tag = (void *) (uintptr_t) tag; \ _gpgme_debug_frame_begin () #define TRACE_BEG(lvl, name, tag, ...) \ _TRACE (lvl, name, tag); \ - _gpgme_debugf (_gpgme_trace_level, 1, \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - __VA_ARGS__) + _gpgme_debug (_gpgme_trace_level, 1, \ + _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ + __VA_ARGS__) #define TRACE(lvl, name, tag, ...) \ _gpgme_debug_frame_begin (), \ - _gpgme_debugf (lvl, 0, \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag, \ - __VA_ARGS__), \ + _gpgme_debug (lvl, 0, \ + name, STRINGIFY (tag), (void *) (uintptr_t) tag, \ + __VA_ARGS__), \ _gpgme_debug_frame_end () #define TRACE_ERR(err) \ err == 0 ? (TRACE_SUC ("")) : \ - (_gpgme_debug (_gpgme_trace_level, "%s:%d: error: %s <%s>\n", \ - _gpgme_trace_func, __LINE__, gpgme_strerror (err), \ - gpgme_strsource (err)), _gpgme_debug_frame_end (), (err)) + (_gpgme_debug (_gpgme_trace_level, -1, NULL, NULL, NULL, \ + "%s:%d: error: %s <%s>\n", \ + _gpgme_trace_func, __LINE__, gpgme_strerror (err), \ + gpgme_strsource (err)), _gpgme_debug_frame_end (), (err)) /* The cast to void suppresses GCC warnings. */ #define TRACE_SYSRES(res) \ res >= 0 ? ((void) (TRACE_SUC ("result=%i", res)), (res)) : \ - (_gpgme_debug (_gpgme_trace_level, "%s: error: %s\n", \ - _gpgme_trace_func, strerror (errno)), \ + (_gpgme_debug (_gpgme_trace_level, -1, NULL, NULL, NULL, \ + "%s: error: %s\n", \ + _gpgme_trace_func, strerror (errno)), \ _gpgme_debug_frame_end (), (res)) #define TRACE_SYSERR(res) \ res == 0 ? ((void) (TRACE_SUC ("result=%i", res)), (res)) : \ - (_gpgme_debug (_gpgme_trace_level, "%s: error: %s\n", \ + (_gpgme_debug (_gpgme_trace_level, -1, NULL, NULL, NULL, \ + "%s: error: %s\n", \ _gpgme_trace_func, strerror (res)), \ _gpgme_debug_frame_end (), (res)) #define TRACE_SYSERR_NR(res) \ - do { res == 0 ? ((void) (TRACE_SUC ("result=%i", res)), (res)) : \ - (_gpgme_debug (_gpgme_trace_level, "%s: error: %s\n", \ + do { res == 0 ? ((void) (TRACE_SUC ("result=%i", res)), (res)) : \ + (_gpgme_debug (_gpgme_trace_level, -1, NULL, NULL, NULL, \ + "%s: error: %s\n", \ _gpgme_trace_func, strerror (res)), \ _gpgme_debug_frame_end ()); } while (0) #define TRACE_SUC(...) \ - _gpgme_debugf (_gpgme_trace_level, 3, _gpgme_trace_func, NULL, NULL, \ + _gpgme_debug (_gpgme_trace_level, 3, _gpgme_trace_func, NULL, NULL, \ __VA_ARGS__), _gpgme_debug_frame_end () #define TRACE_LOG(...) \ - _gpgme_debugf (_gpgme_trace_level, 2, \ + _gpgme_debug (_gpgme_trace_level, 2, \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ __VA_ARGS__) #define TRACE_LOGBUF(buf, len) \ _gpgme_debug_buffer (_gpgme_trace_level, "%s: check: %s", \ _gpgme_trace_func, buf, len) #define TRACE_LOGBUFX(buf, len) \ _gpgme_debug_buffer (_gpgme_trace_level+1, "%s: check: %s", \ _gpgme_trace_func, buf, len) #define TRACE_SEQ(hlp,fmt) \ _gpgme_debug_begin (&(hlp), _gpgme_trace_level, \ "%s: check: %s=%p, " fmt, _gpgme_trace_func, \ _gpgme_trace_tagname, _gpgme_trace_tag) #define TRACE_ADD0(hlp,fmt) \ _gpgme_debug_add (&(hlp), fmt) #define TRACE_ADD1(hlp,fmt,a) \ _gpgme_debug_add (&(hlp), fmt, (a)) #define TRACE_ADD2(hlp,fmt,a,b) \ _gpgme_debug_add (&(hlp), fmt, (a), (b)) #define TRACE_ADD3(hlp,fmt,a,b,c) \ _gpgme_debug_add (&(hlp), fmt, (a), (b), (c)) #define TRACE_END(hlp,fmt) \ _gpgme_debug_add (&(hlp), fmt); \ _gpgme_debug_end (&(hlp)) #define TRACE_ENABLED(hlp) (!!(hlp)) /* And finally a simple macro to trace the location of an error code. This macro is independent of the other trace macros and may be used without any preconditions. */ #define trace_gpg_error(e) \ _gpgme_trace_gpgme_error (gpg_error (e), __FILE__, __LINE__) #endif /* DEBUG_H */ diff --git a/src/dirinfo.c b/src/dirinfo.c index 10b7b5ee..d4811990 100644 --- a/src/dirinfo.c +++ b/src/dirinfo.c @@ -1,472 +1,481 @@ /* dirinfo.c - Get directory information * Copyright (C) 2009, 2013 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include "gpgme.h" #include "util.h" #include "priv-io.h" #include "debug.h" #include "sema.h" #include "sys-util.h" DEFINE_STATIC_LOCK (dirinfo_lock); /* Constants used internally to select the data. */ enum { WANT_HOMEDIR, WANT_SYSCONFDIR, WANT_BINDIR, WANT_LIBEXECDIR, WANT_LIBDIR, WANT_DATADIR, WANT_LOCALEDIR, WANT_AGENT_SOCKET, WANT_AGENT_SSH_SOCKET, WANT_DIRMNGR_SOCKET, WANT_UISRV_SOCKET, WANT_GPGCONF_NAME, WANT_GPG_NAME, WANT_GPGSM_NAME, WANT_G13_NAME, WANT_GPG_WKS_CLIENT_NAME, WANT_GPG_ONE_MODE }; /* Values retrieved via gpgconf and cached here. */ static struct { int valid; /* Cached information is valid. */ int disable_gpgconf; char *homedir; char *sysconfdir; char *bindir; char *libexecdir; char *libdir; char *datadir; char *localedir; char *agent_socket; char *agent_ssh_socket; char *dirmngr_socket; char *uisrv_socket; char *gpgconf_name; char *gpg_name; char *gpgsm_name; char *g13_name; char *gpg_wks_client_name; int gpg_one_mode; /* System is in gpg1 mode. */ } dirinfo; /* Helper function to be used only by gpgme_set_global_flag. */ void _gpgme_dirinfo_disable_gpgconf (void) { dirinfo.disable_gpgconf = 1; } /* Return the length of the directory part including the trailing * slash of NAME. */ static size_t dirname_len (const char *name) { return _gpgme_get_basename (name) - name; } /* Parse the output of "gpgconf --list-dirs". This function expects that DIRINFO_LOCK is held by the caller. If COMPONENTS is set, the output of --list-components is expected. */ static void parse_output (char *line, int components) { char *value, *p; size_t n; value = strchr (line, ':'); if (!value) return; *value++ = 0; if (components) { /* Skip the second field. */ value = strchr (value, ':'); if (!value) return; *value++ = 0; } p = strchr (value, ':'); if (p) *p = 0; if (_gpgme_decode_percent_string (value, &value, strlen (value)+1, 0)) return; if (!*value) return; if (components) { if (!strcmp (line, "gpg") && !dirinfo.gpg_name) dirinfo.gpg_name = strdup (value); else if (!strcmp (line, "gpgsm") && !dirinfo.gpgsm_name) dirinfo.gpgsm_name = strdup (value); else if (!strcmp (line, "g13") && !dirinfo.g13_name) dirinfo.g13_name = strdup (value); } else { if (!strcmp (line, "homedir") && !dirinfo.homedir) dirinfo.homedir = strdup (value); else if (!strcmp (line, "sysconfdir") && !dirinfo.sysconfdir) dirinfo.sysconfdir = strdup (value); else if (!strcmp (line, "bindir") && !dirinfo.bindir) dirinfo.bindir = strdup (value); else if (!strcmp (line, "libexecdir") && !dirinfo.libexecdir) dirinfo.libexecdir = strdup (value); else if (!strcmp (line, "libdir") && !dirinfo.libdir) dirinfo.libdir = strdup (value); else if (!strcmp (line, "datadir") && !dirinfo.datadir) dirinfo.datadir = strdup (value); else if (!strcmp (line, "localedir") && !dirinfo.localedir) dirinfo.localedir = strdup (value); else if (!strcmp (line, "agent-socket") && !dirinfo.agent_socket) { const char name[] = "S.uiserver"; char *buffer; dirinfo.agent_socket = strdup (value); if (dirinfo.agent_socket) { n = dirname_len (dirinfo.agent_socket); buffer = malloc (n + strlen (name) + 1); if (buffer) { strncpy (buffer, dirinfo.agent_socket, n); strcpy (buffer + n, name); dirinfo.uisrv_socket = buffer; } } } else if (!strcmp (line, "dirmngr-socket") && !dirinfo.dirmngr_socket) dirinfo.dirmngr_socket = strdup (value); else if (!strcmp (line, "agent-ssh-socket") && !dirinfo.agent_ssh_socket) dirinfo.agent_ssh_socket = strdup (value); } } /* Read the directory information from gpgconf. This function expects that DIRINFO_LOCK is held by the caller. PGNAME is the name of the gpgconf binary. If COMPONENTS is set, not the directories bit the name of the componeNts are read. */ static void read_gpgconf_dirs (const char *pgmname, int components) { char linebuf[1024] = {0}; int linelen = 0; char * argv[3]; int rp[2]; struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0}, {-1, -1} }; int status; int nread; char *mark = NULL; argv[0] = (char *)pgmname; argv[1] = (char*)(components? "--list-components" : "--list-dirs"); argv[2] = NULL; if (_gpgme_io_pipe (rp, 1) < 0) return; cfd[0].fd = rp[1]; status = _gpgme_io_spawn (pgmname, argv, IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL); if (status < 0) { _gpgme_io_close (rp[0]); _gpgme_io_close (rp[1]); return; } do { nread = _gpgme_io_read (rp[0], linebuf + linelen, sizeof linebuf - linelen - 1); if (nread > 0) { char *line; const char *lastmark = NULL; size_t nused; linelen += nread; linebuf[linelen] = '\0'; for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 ) { lastmark = mark; if (mark > line && mark[-1] == '\r') mark[-1] = '\0'; else mark[0] = '\0'; parse_output (line, components); } nused = lastmark? (lastmark + 1 - linebuf) : 0; memmove (linebuf, linebuf + nused, linelen - nused); linelen -= nused; } } while (nread > 0 && linelen < sizeof linebuf - 1); _gpgme_io_close (rp[0]); } static const char * get_gpgconf_item (int what) { const char *result = NULL; LOCK (dirinfo_lock); if (!dirinfo.valid) { char *pgmname; pgmname = dirinfo.disable_gpgconf? NULL : _gpgme_get_gpgconf_path (); if (pgmname && access (pgmname, F_OK)) { - _gpgme_debug (DEBUG_INIT, + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, "gpgme-dinfo: gpgconf='%s' [not installed]\n", pgmname); free (pgmname); pgmname = NULL; /* Not available. */ } else - _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: gpgconf='%s'\n", + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, + "gpgme-dinfo: gpgconf='%s'\n", pgmname? pgmname : "[null]"); if (!pgmname) { /* Probably gpgconf is not installed. Assume we are using GnuPG-1. */ dirinfo.gpg_one_mode = 1; pgmname = _gpgme_get_gpg_path (); if (pgmname) dirinfo.gpg_name = pgmname; } else { dirinfo.gpg_one_mode = 0; read_gpgconf_dirs (pgmname, 0); read_gpgconf_dirs (pgmname, 1); dirinfo.gpgconf_name = pgmname; } /* Even if the reading of the directories failed (e.g. due to an too old version gpgconf or no gpgconf at all), we need to mark the entries as valid so that we won't try over and over to read them. Note further that we are not able to change the read values later because they are practically statically allocated. */ dirinfo.valid = 1; if (dirinfo.gpg_name) - _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: gpg='%s'\n", + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, + "gpgme-dinfo: gpg='%s'\n", dirinfo.gpg_name); if (dirinfo.g13_name) - _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: g13='%s'\n", + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, + "gpgme-dinfo: g13='%s'\n", dirinfo.g13_name); if (dirinfo.gpgsm_name) - _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: gpgsm='%s'\n", + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, + "gpgme-dinfo: gpgsm='%s'\n", dirinfo.gpgsm_name); if (dirinfo.homedir) - _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: homedir='%s'\n", + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, + "gpgme-dinfo: homedir='%s'\n", dirinfo.homedir); if (dirinfo.agent_socket) - _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: agent='%s'\n", + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, + "gpgme-dinfo: agent='%s'\n", dirinfo.agent_socket); if (dirinfo.agent_ssh_socket) - _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: ssh='%s'\n", + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, + "gpgme-dinfo: ssh='%s'\n", dirinfo.agent_ssh_socket); if (dirinfo.dirmngr_socket) - _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: dirmngr='%s'\n", + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, + "gpgme-dinfo: dirmngr='%s'\n", dirinfo.dirmngr_socket); if (dirinfo.uisrv_socket) - _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: uisrv='%s'\n", + _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, + "gpgme-dinfo: uisrv='%s'\n", dirinfo.uisrv_socket); } switch (what) { case WANT_HOMEDIR: result = dirinfo.homedir; break; case WANT_SYSCONFDIR: result = dirinfo.sysconfdir; break; case WANT_BINDIR: result = dirinfo.bindir; break; case WANT_LIBEXECDIR: result = dirinfo.libexecdir; break; case WANT_LIBDIR: result = dirinfo.libdir; break; case WANT_DATADIR: result = dirinfo.datadir; break; case WANT_LOCALEDIR: result = dirinfo.localedir; break; case WANT_AGENT_SOCKET: result = dirinfo.agent_socket; break; case WANT_AGENT_SSH_SOCKET: result = dirinfo.agent_ssh_socket; break; case WANT_DIRMNGR_SOCKET: result = dirinfo.dirmngr_socket; break; case WANT_GPGCONF_NAME: result = dirinfo.gpgconf_name; break; case WANT_GPG_NAME: result = dirinfo.gpg_name; break; case WANT_GPGSM_NAME: result = dirinfo.gpgsm_name; break; case WANT_G13_NAME: result = dirinfo.g13_name; break; case WANT_UISRV_SOCKET: result = dirinfo.uisrv_socket; break; case WANT_GPG_ONE_MODE: result = dirinfo.gpg_one_mode? "1":NULL; break; case WANT_GPG_WKS_CLIENT_NAME: if (!dirinfo.gpg_wks_client_name && dirinfo.libexecdir) dirinfo.gpg_wks_client_name = _gpgme_strconcat (dirinfo.libexecdir, "/", "gpg-wks-client", NULL); result = dirinfo.gpg_wks_client_name; break; } UNLOCK (dirinfo_lock); return result; } /* Return the default home directory. Returns NULL if not known. */ const char * _gpgme_get_default_homedir (void) { return get_gpgconf_item (WANT_HOMEDIR); } /* Return the default gpg-agent socket name. Returns NULL if not known. */ const char * _gpgme_get_default_agent_socket (void) { return get_gpgconf_item (WANT_AGENT_SOCKET); } /* Return the default gpg file name. Returns NULL if not known. */ const char * _gpgme_get_default_gpg_name (void) { return get_gpgconf_item (WANT_GPG_NAME); } /* Return the default gpgsm file name. Returns NULL if not known. */ const char * _gpgme_get_default_gpgsm_name (void) { return get_gpgconf_item (WANT_GPGSM_NAME); } /* Return the default g13 file name. Returns NULL if not known. */ const char * _gpgme_get_default_g13_name (void) { return get_gpgconf_item (WANT_G13_NAME); } /* Return the default gpgconf file name. Returns NULL if not known. */ const char * _gpgme_get_default_gpgconf_name (void) { return get_gpgconf_item (WANT_GPGCONF_NAME); } /* Return the default UI-server socket name. Returns NULL if not known. */ const char * _gpgme_get_default_uisrv_socket (void) { return get_gpgconf_item (WANT_UISRV_SOCKET); } /* Return true if we are in GnuPG-1 mode - ie. no gpgconf and agent being optional. */ int _gpgme_in_gpg_one_mode (void) { return !!get_gpgconf_item (WANT_GPG_ONE_MODE); } /* Helper function to return the basename of the passed filename. */ const char * _gpgme_get_basename (const char *name) { const char *s; if (!name || !*name) return name; for (s = name + strlen (name) -1; s >= name; s--) if (*s == '/' #ifdef HAVE_W32_SYSTEM || *s == '\\' || *s == ':' #endif ) return s+1; return name; } /* Return default values for various directories and file names. */ const char * gpgme_get_dirinfo (const char *what) { if (!what) return NULL; else if (!strcmp (what, "homedir")) return get_gpgconf_item (WANT_HOMEDIR); else if (!strcmp (what, "agent-socket")) return get_gpgconf_item (WANT_AGENT_SOCKET); else if (!strcmp (what, "uiserver-socket")) return get_gpgconf_item (WANT_UISRV_SOCKET); else if (!strcmp (what, "gpgconf-name")) return get_gpgconf_item (WANT_GPGCONF_NAME); else if (!strcmp (what, "gpg-name")) return get_gpgconf_item (WANT_GPG_NAME); else if (!strcmp (what, "gpgsm-name")) return get_gpgconf_item (WANT_GPGSM_NAME); else if (!strcmp (what, "g13-name")) return get_gpgconf_item (WANT_G13_NAME); else if (!strcmp (what, "gpg-wks-client-name")) return get_gpgconf_item (WANT_GPG_WKS_CLIENT_NAME); else if (!strcmp (what, "agent-ssh-socket")) return get_gpgconf_item (WANT_AGENT_SSH_SOCKET); else if (!strcmp (what, "dirmngr-socket")) return get_gpgconf_item (WANT_DIRMNGR_SOCKET); else if (!strcmp (what, "sysconfdir")) return get_gpgconf_item (WANT_SYSCONFDIR); else if (!strcmp (what, "bindir")) return get_gpgconf_item (WANT_BINDIR); else if (!strcmp (what, "libexecdir")) return get_gpgconf_item (WANT_LIBEXECDIR); else if (!strcmp (what, "libdir")) return get_gpgconf_item (WANT_LIBDIR); else if (!strcmp (what, "datadir")) return get_gpgconf_item (WANT_DATADIR); else if (!strcmp (what, "localedir")) return get_gpgconf_item (WANT_LOCALEDIR); else return NULL; } diff --git a/src/posix-util.c b/src/posix-util.c index d060c896..881856ca 100644 --- a/src/posix-util.c +++ b/src/posix-util.c @@ -1,158 +1,159 @@ /* posix-util.c - Utility functions for Posix * Copyright (C) 2001 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "util.h" #include "sys-util.h" #include "debug.h" /* These variables store the malloced name of alternative default binaries. The are set only once by gpgme_set_global_flag. */ static char *default_gpg_name; static char *default_gpgconf_name; /* Set the default name for the gpg binary. This function may only be called by gpgme_set_global_flag. Returns 0 on success. Leading directories are removed from NAME. */ int _gpgme_set_default_gpg_name (const char *name) { const char *s; s = strrchr (name, '/'); if (s) name = s + 1; if (!default_gpg_name) default_gpg_name = strdup (name); return !default_gpg_name; } /* Set the default name for the gpgconf binary. This function may only be called by gpgme_set_global_flag. Returns 0 on success. Leading directories are removed from NAME. */ int _gpgme_set_default_gpgconf_name (const char *name) { const char *s; s = strrchr (name, '/'); if (s) name = s + 1; if (!default_gpgconf_name) default_gpgconf_name = strdup (name); return !default_gpgconf_name; } /* Dummy function - see w32-util.c for the actual code. */ int _gpgme_set_override_inst_dir (const char *dir) { (void)dir; return 0; } /* Find an executable program PGM along the envvar PATH. */ static char * walk_path (const char *pgm) { const char *orig_path, *path, *s; char *fname, *p; #ifdef FIXED_SEARCH_PATH orig_path = FIXED_SEARCH_PATH; #else orig_path = getenv ("PATH"); if (!orig_path) orig_path = "/bin:/usr/bin"; #endif fname = malloc (strlen (orig_path) + 1 + strlen (pgm) + 1); if (!fname) return NULL; path = orig_path; for (;;) { for (s=path, p=fname; *s && *s != ':'; s++, p++) *p = *s; if (p != fname && p[-1] != '/') *p++ = '/'; strcpy (p, pgm); if (!access (fname, X_OK)) return fname; if (!*s) break; path = s + 1; } - _gpgme_debug (DEBUG_ENGINE, "gpgme-walk_path: '%s' not found in '%s'", + _gpgme_debug (DEBUG_ENGINE, -1, NULL, NULL, NULL, + "gpgme-walk_path: '%s' not found in '%s'", pgm, orig_path); free (fname); return NULL; } /* Return the full file name of the GPG binary. This function is used if gpgconf was not found and thus it can be assumed that gpg2 is not installed. This function is only called by get_gpgconf_item and may not be called concurrently. */ char * _gpgme_get_gpg_path (void) { return walk_path (default_gpg_name? default_gpg_name : "gpg"); } /* This function is only called by get_gpgconf_item and may not be called concurrently. */ char * _gpgme_get_gpgconf_path (void) { return walk_path (default_gpgconf_name? default_gpgconf_name : "gpgconf"); } /* See w32-util.c */ int _gpgme_get_conf_int (const char *key, int *value) { (void)key; (void)value; return 0; } void _gpgme_allow_set_foreground_window (pid_t pid) { (void)pid; /* Not needed. */ } diff --git a/src/w32-util.c b/src/w32-util.c index 1f8ef6dc..e37fb430 100644 --- a/src/w32-util.c +++ b/src/w32-util.c @@ -1,795 +1,797 @@ /* w32-util.c - Utility functions for the W32 API * Copyright (C) 1999 Free Software Foundation, Inc * Copyright (C) 2001 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2007, 2013 g10 Code GmbH * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include #include #if __MINGW64_VERSION_MAJOR >= 2 # define _WIN32_IE 0x0501 /* Required by mingw64 toolkit. */ #else # define _WIN32_IE 0x0400 /* Required for SHGetSpecialFolderPathA. */ #endif /* We need to include the windows stuff here prior to shlobj.h so that we get the right winsock version. This is usually done in util.h but that header also redefines some Windows functions which we need to avoid unless having included shlobj.h. */ #include #include #include #include #include "util.h" #include "ath.h" #include "sema.h" #include "debug.h" #include "sys-util.h" #define HAVE_ALLOW_SET_FOREGROUND_WINDOW 1 #ifndef F_OK # define F_OK 0 #endif /* The Registry key used by GNUPG. */ #ifdef _WIN64 # define GNUPG_REGKEY_2 "Software\\Wow6432Node\\GNU\\GnuPG" #else # define GNUPG_REGKEY_2 "Software\\GNU\\GnuPG" #endif #ifdef _WIN64 # define GNUPG_REGKEY_3 "Software\\Wow6432Node\\GnuPG" #else # define GNUPG_REGKEY_3 "Software\\GnuPG" #endif DEFINE_STATIC_LOCK (get_path_lock); /* The module handle of this DLL. If we are linked statically, dllmain does not exists and thus the value of my_hmodule will be NULL. The effect is that a GetModuleFileName always returns the file name of the DLL or executable which contains the gpgme code. */ static HMODULE my_hmodule; /* These variables store the malloced name of alternative default binaries. The are set only once by gpgme_set_global_flag. */ static char *default_gpg_name; static char *default_gpgconf_name; /* If this variable is not NULL the value is assumed to be the installation directory. The variable may only be set once by gpgme_set_global_flag and accessed by _gpgme_get_inst_dir. */ static char *override_inst_dir; #define RTLD_LAZY 0 static GPG_ERR_INLINE void * dlopen (const char * name, int flag) { void * hd = LoadLibrary (name); (void)flag; return hd; } static GPG_ERR_INLINE void * dlsym (void * hd, const char * sym) { if (hd && sym) { void * fnc = GetProcAddress (hd, sym); if (!fnc) return NULL; return fnc; } return NULL; } static GPG_ERR_INLINE int dlclose (void * hd) { if (hd) { FreeLibrary (hd); return 0; } return -1; } /* Return a malloced string encoded in UTF-8 from the wide char input string STRING. Caller must free this value. Returns NULL and sets ERRNO on failure. Calling this function with STRING set to NULL is not defined. */ static char * wchar_to_utf8 (const wchar_t *string) { int n; char *result; n = WideCharToMultiByte (CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL); if (n < 0) { gpg_err_set_errno (EINVAL); return NULL; } result = malloc (n+1); if (!result) return NULL; n = WideCharToMultiByte (CP_UTF8, 0, string, -1, result, n, NULL, NULL); if (n < 0) { free (result); gpg_err_set_errno (EINVAL); result = NULL; } return result; } /* Replace all forward slashes by backslashes. */ static void replace_slashes (char *string) { for (; *string; string++) if (*string == '/') *string = '\\'; } /* Get the base name of NAME. Returns a pointer into NAME right after the last slash or backslash or to NAME if no slash or backslash exists. */ static const char * get_basename (const char *name) { const char *mark, *s; for (mark=NULL, s=name; *s; s++) if (*s == '/' || *s == '\\') mark = s; return mark? mark+1 : name; } void _gpgme_allow_set_foreground_window (pid_t pid) { #ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW static int initialized; static BOOL (WINAPI * func)(DWORD); void *handle; if (!initialized) { /* Available since W2000; thus we dynload it. */ initialized = 1; handle = dlopen ("user32.dll", RTLD_LAZY); if (handle) { func = dlsym (handle, "AllowSetForegroundWindow"); if (!func) { dlclose (handle); handle = NULL; } } } if (!pid || pid == (pid_t)(-1)) { TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, "no action for pid %d", (int)pid); } else if (func) { int rc = func (pid); TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, "called for pid %d; result=%d", (int)pid, rc); } else { TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, "function not available"); } #endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */ } /* Wrapper around CancelSynchronousIo which is only available since * Vista. */ void _gpgme_w32_cancel_synchronous_io (HANDLE thread) { static int initialized; static BOOL (WINAPI * func)(DWORD); void *handle; if (!initialized) { /* Available since Vista; thus we dynload it. */ initialized = 1; handle = dlopen ("kernel32.dll", RTLD_LAZY); if (handle) { func = dlsym (handle, "CancelSynchronousIo"); if (!func) { dlclose (handle); handle = NULL; } } } if (func) { if (!func (thread) && GetLastError() != ERROR_NOT_FOUND) { TRACE (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", 0, "called for thread %p: ec=%d", thread, GetLastError ()); } } else { TRACE (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", 0, "function not available"); } } /* Return a string from the W32 Registry or NULL in case of error. Caller must release the return value. A NULL for root is an alias for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */ static char * read_w32_registry_string (const char *root, const char *dir, const char *name) { HKEY root_key, key_handle; DWORD n1, nbytes, type; char *result = NULL; if (!root) root_key = HKEY_CURRENT_USER; else if (!strcmp( root, "HKEY_CLASSES_ROOT")) root_key = HKEY_CLASSES_ROOT; else if (!strcmp( root, "HKEY_CURRENT_USER")) root_key = HKEY_CURRENT_USER; else if (!strcmp( root, "HKEY_LOCAL_MACHINE")) root_key = HKEY_LOCAL_MACHINE; else if (!strcmp( root, "HKEY_USERS")) root_key = HKEY_USERS; else if (!strcmp( root, "HKEY_PERFORMANCE_DATA")) root_key = HKEY_PERFORMANCE_DATA; else if (!strcmp( root, "HKEY_CURRENT_CONFIG")) root_key = HKEY_CURRENT_CONFIG; else return NULL; if (RegOpenKeyExA (root_key, dir, 0, KEY_READ, &key_handle)) { if (root) return NULL; /* no need for a RegClose, so return direct */ /* It seems to be common practise to fall back to HKLM. */ if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) return NULL; /* still no need for a RegClose, so return direct */ } nbytes = 1; if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes)) { if (root) goto leave; /* Try to fallback to HKLM also vor a missing value. */ RegCloseKey (key_handle); if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) return NULL; /* Nope. */ if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes)) goto leave; } n1 = nbytes + 1; result = malloc (n1); if (!result) goto leave; if (RegQueryValueExA (key_handle, name, 0, &type, (LPBYTE) result, &n1)) { free (result); result = NULL; goto leave; } result[nbytes] = 0; /* Make sure it is really a string. */ leave: RegCloseKey (key_handle); return result; } /* Return the name of the directory with the gpgme DLL or the EXE (if statically linked). May return NULL on severe errors. */ const char * _gpgme_get_inst_dir (void) { static char *inst_dir; if (override_inst_dir) return override_inst_dir; LOCK (get_path_lock); if (!inst_dir) { wchar_t *moddir; moddir = malloc ((MAX_PATH+5) * sizeof *moddir); if (moddir) { if (!GetModuleFileNameW (my_hmodule, moddir, MAX_PATH)) *moddir = 0; if (!*moddir) gpg_err_set_errno (ENOENT); else { inst_dir = wchar_to_utf8 (moddir); if (inst_dir) { char *p = strrchr (inst_dir, '\\'); if (p) *p = 0; } } free (moddir); } } UNLOCK (get_path_lock); return inst_dir; } static char * find_program_in_dir (const char *dir, const char *name) { char *result; result = _gpgme_strconcat (dir, "\\", name, NULL); if (!result) return NULL; if (access (result, F_OK)) { free (result); return NULL; } return result; } static char * find_program_at_standard_place (const char *name) { char path[MAX_PATH]; char *result = NULL; /* See https://wiki.tcl-lang.org/page/Getting+Windows+%22special+folders%22+with+Ffidl for details on compatibility. We First try the generic place and then fallback to the x86 (i.e. 32 bit) place. This will prefer a 64 bit of the program over a 32 bit version on 64 bit Windows if installed. */ if (SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILES, 0)) { result = _gpgme_strconcat (path, "\\", name, NULL); if (result && access (result, F_OK)) { free (result); result = NULL; } } if (!result && SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILESX86, 0)) { result = _gpgme_strconcat (path, "\\", name, NULL); if (result && access (result, F_OK)) { free (result); result = NULL; } } return result; } /* Set the default name for the gpg binary. This function may only be called by gpgme_set_global_flag. Returns 0 on success. */ int _gpgme_set_default_gpg_name (const char *name) { if (!default_gpg_name) { default_gpg_name = _gpgme_strconcat (name, ".exe", NULL); if (default_gpg_name) replace_slashes (default_gpg_name); } return !default_gpg_name; } /* Set the default name for the gpgconf binary. This function may only be called by gpgme_set_global_flag. Returns 0 on success. */ int _gpgme_set_default_gpgconf_name (const char *name) { if (!default_gpgconf_name) { default_gpgconf_name = _gpgme_strconcat (name, ".exe", NULL); if (default_gpgconf_name) replace_slashes (default_gpgconf_name); } return !default_gpgconf_name; } /* Set the override installation directory. This function may only be called by gpgme_set_global_flag. Returns 0 on success. */ int _gpgme_set_override_inst_dir (const char *dir) { if (!override_inst_dir) { override_inst_dir = strdup (dir); if (override_inst_dir) { replace_slashes (override_inst_dir); /* Remove a trailing slash. */ if (*override_inst_dir && override_inst_dir[strlen (override_inst_dir)-1] == '\\') override_inst_dir[strlen (override_inst_dir)-1] = 0; } } return !override_inst_dir; } /* Return the full file name of the GPG binary. This function is used iff gpgconf was not found and thus it can be assumed that gpg2 is not installed. This function is only called by get_gpgconf_item and may not be called concurrently. */ char * _gpgme_get_gpg_path (void) { char *gpg = NULL; const char *name, *inst_dir; name = default_gpg_name? get_basename (default_gpg_name) : "gpg.exe"; /* 1. Try to find gpg.exe in the installation directory of gpgme. */ inst_dir = _gpgme_get_inst_dir (); if (inst_dir) { gpg = find_program_in_dir (inst_dir, name); } /* 2. Try to find gpg.exe using that ancient registry key. */ if (!gpg) { char *dir; dir = read_w32_registry_string ("HKEY_LOCAL_MACHINE", GNUPG_REGKEY_2, "Install Directory"); if (dir) { gpg = find_program_in_dir (dir, name); free (dir); } } /* 3. Try to find gpg.exe below CSIDL_PROGRAM_FILES. */ if (!gpg) { name = default_gpg_name? default_gpg_name : "GNU\\GnuPG\\gpg.exe"; gpg = find_program_at_standard_place (name); } /* 4. Print a debug message if not found. */ if (!gpg) - _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpg_path: '%s' not found", name); + _gpgme_debug (DEBUG_ENGINE, -1, NULL, NULL, NULL, + "_gpgme_get_gpg_path: '%s' not found", name); return gpg; } /* This function is only called by get_gpgconf_item and may not be called concurrently. */ char * _gpgme_get_gpgconf_path (void) { char *gpgconf = NULL; const char *inst_dir, *name; name = default_gpgconf_name? get_basename(default_gpgconf_name):"gpgconf.exe"; /* 1. Try to find gpgconf.exe in the installation directory of gpgme. */ inst_dir = _gpgme_get_inst_dir (); if (inst_dir) { gpgconf = find_program_in_dir (inst_dir, name); } /* 2. Try to find gpgconf.exe from GnuPG >= 2.1 below CSIDL_PROGRAM_FILES. */ if (!gpgconf) { const char *name2 = (default_gpgconf_name ? default_gpgconf_name /**/ : "GnuPG\\bin\\gpgconf.exe"); gpgconf = find_program_at_standard_place (name2); } /* 3. Try to find gpgconf.exe using the Windows registry. */ if (!gpgconf) { char *dir; dir = read_w32_registry_string (NULL, GNUPG_REGKEY_2, "Install Directory"); if (!dir) { char *tmp = read_w32_registry_string (NULL, GNUPG_REGKEY_3, "Install Directory"); if (tmp) { dir = _gpgme_strconcat (tmp, "\\bin", NULL); free (tmp); if (!dir) return NULL; } } if (dir) { gpgconf = find_program_in_dir (dir, name); free (dir); } } /* 4. Try to find gpgconf.exe from Gpg4win below CSIDL_PROGRAM_FILES. */ if (!gpgconf) { gpgconf = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe"); } /* 5. Try to find gpgconf.exe relative to us. */ if (!gpgconf && inst_dir) { char *dir = _gpgme_strconcat (inst_dir, "\\..\\..\\GnuPG\\bin"); gpgconf = find_program_in_dir (dir, name); free (dir); } /* 5. Print a debug message if not found. */ if (!gpgconf) - _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpgconf_path: '%s' not found",name); + _gpgme_debug (DEBUG_ENGINE, -1, NULL, NULL, NULL, + "_gpgme_get_gpgconf_path: '%s' not found",name); return gpgconf; } const char * _gpgme_get_w32spawn_path (void) { static char *w32spawn_program; const char *inst_dir; inst_dir = _gpgme_get_inst_dir (); LOCK (get_path_lock); if (!w32spawn_program) w32spawn_program = find_program_in_dir (inst_dir, "gpgme-w32spawn.exe"); UNLOCK (get_path_lock); return w32spawn_program; } /* Return an integer value from gpgme specific configuration entries. VALUE receives that value; function returns true if a value has been configured and false if not. */ int _gpgme_get_conf_int (const char *key, int *value) { char *tmp = read_w32_registry_string (NULL, "Software\\GNU\\gpgme", key); if (!tmp) return 0; *value = atoi (tmp); free (tmp); return 1; } /* mkstemp extracted from libc/sysdeps/posix/tempname.c. Copyright (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. */ static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; /* Generate a temporary file name based on TMPL. TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed does not exist at the time of the call to mkstemp. TMPL is overwritten with the result. */ static int my_mkstemp (char *tmpl) { int len; char *XXXXXX; static uint64_t value; uint64_t random_time_bits; unsigned int count; int fd = -1; int save_errno = errno; /* A lower bound on the number of temporary files to attempt to generate. The maximum total number of temporary file names that can exist for a given template is 62**6. It should never be necessary to try all these combinations. Instead if a reasonable number of names is tried (we define reasonable as 62**3) fail to give the system administrator the chance to remove the problems. */ #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX unsigned int attempts = TMP_MAX; #else unsigned int attempts = ATTEMPTS_MIN; #endif len = strlen (tmpl); if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) { gpg_err_set_errno (EINVAL); return -1; } /* This is where the Xs start. */ XXXXXX = &tmpl[len - 6]; /* Get some more or less random data. */ { FILETIME ft; GetSystemTimeAsFileTime (&ft); random_time_bits = (((uint64_t)ft.dwHighDateTime << 32) | (uint64_t)ft.dwLowDateTime); } value += random_time_bits ^ ath_self (); for (count = 0; count < attempts; value += 7777, ++count) { uint64_t v = value; /* Fill in the random bits. */ XXXXXX[0] = letters[v % 62]; v /= 62; XXXXXX[1] = letters[v % 62]; v /= 62; XXXXXX[2] = letters[v % 62]; v /= 62; XXXXXX[3] = letters[v % 62]; v /= 62; XXXXXX[4] = letters[v % 62]; v /= 62; XXXXXX[5] = letters[v % 62]; fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd >= 0) { gpg_err_set_errno (save_errno); return fd; } else if (errno != EEXIST) return -1; } /* We got out of the loop because we ran out of combinations to try. */ gpg_err_set_errno (EEXIST); return -1; } int _gpgme_mkstemp (int *fd, char **name) { char tmp[MAX_PATH + 2]; char *tmpname; int err; *fd = -1; *name = NULL; err = GetTempPathA (MAX_PATH + 1, tmp); if (err == 0 || err > MAX_PATH + 1) strcpy (tmp,"c:\\windows\\temp"); else { int len = strlen(tmp); /* GetTempPath may return with \ on the end */ while(len > 0 && tmp[len - 1] == '\\') { tmp[len-1] = '\0'; len--; } } tmpname = _gpgme_strconcat (tmp, "\\gpgme-XXXXXX", NULL); if (!tmpname) return -1; *fd = my_mkstemp (tmpname); if (*fd < 0) { free (tmpname); return -1; } *name = tmpname; return 0; } /* Entry point called by the DLL loader. */ #ifdef DLL_EXPORT int WINAPI DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved) { (void)reserved; if (reason == DLL_PROCESS_ATTACH) my_hmodule = hinst; return TRUE; } #endif /*DLL_EXPORT*/