10b7b5eeda7bd230492c73aa39d69b48a2dd5e93
[gpgme.git] / src / dirinfo.c
1 /* dirinfo.c - Get directory information
2  * Copyright (C) 2009, 2013 g10 Code GmbH
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <https://gnu.org/licenses/>.
18  * SPDX-License-Identifier: LGPL-2.1-or-later
19  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "gpgme.h"
29 #include "util.h"
30 #include "priv-io.h"
31 #include "debug.h"
32 #include "sema.h"
33 #include "sys-util.h"
34
35 DEFINE_STATIC_LOCK (dirinfo_lock);
36
37 /* Constants used internally to select the data.  */
38 enum
39   {
40     WANT_HOMEDIR,
41     WANT_SYSCONFDIR,
42     WANT_BINDIR,
43     WANT_LIBEXECDIR,
44     WANT_LIBDIR,
45     WANT_DATADIR,
46     WANT_LOCALEDIR,
47     WANT_AGENT_SOCKET,
48     WANT_AGENT_SSH_SOCKET,
49     WANT_DIRMNGR_SOCKET,
50     WANT_UISRV_SOCKET,
51     WANT_GPGCONF_NAME,
52     WANT_GPG_NAME,
53     WANT_GPGSM_NAME,
54     WANT_G13_NAME,
55     WANT_GPG_WKS_CLIENT_NAME,
56     WANT_GPG_ONE_MODE
57   };
58
59 /* Values retrieved via gpgconf and cached here.  */
60 static struct {
61   int  valid;         /* Cached information is valid.  */
62   int  disable_gpgconf;
63   char *homedir;
64   char *sysconfdir;
65   char *bindir;
66   char *libexecdir;
67   char *libdir;
68   char *datadir;
69   char *localedir;
70   char *agent_socket;
71   char *agent_ssh_socket;
72   char *dirmngr_socket;
73   char *uisrv_socket;
74   char *gpgconf_name;
75   char *gpg_name;
76   char *gpgsm_name;
77   char *g13_name;
78   char *gpg_wks_client_name;
79   int  gpg_one_mode;  /* System is in gpg1 mode.  */
80 } dirinfo;
81
82
83 \f
84 /* Helper function to be used only by gpgme_set_global_flag.  */
85 void
86 _gpgme_dirinfo_disable_gpgconf (void)
87 {
88   dirinfo.disable_gpgconf = 1;
89 }
90
91
92 /* Return the length of the directory part including the trailing
93  * slash of NAME.  */
94 static size_t
95 dirname_len (const char *name)
96 {
97   return _gpgme_get_basename (name) - name;
98 }
99
100
101 /* Parse the output of "gpgconf --list-dirs".  This function expects
102    that DIRINFO_LOCK is held by the caller.  If COMPONENTS is set, the
103    output of --list-components is expected. */
104 static void
105 parse_output (char *line, int components)
106 {
107   char *value, *p;
108   size_t n;
109
110   value = strchr (line, ':');
111   if (!value)
112     return;
113   *value++ = 0;
114   if (components)
115     {
116       /* Skip the second field.  */
117       value = strchr (value, ':');
118       if (!value)
119         return;
120       *value++ = 0;
121     }
122   p = strchr (value, ':');
123   if (p)
124     *p = 0;
125   if (_gpgme_decode_percent_string (value, &value, strlen (value)+1, 0))
126     return;
127   if (!*value)
128     return;
129
130   if (components)
131     {
132       if (!strcmp (line, "gpg") && !dirinfo.gpg_name)
133         dirinfo.gpg_name = strdup (value);
134       else if (!strcmp (line, "gpgsm") && !dirinfo.gpgsm_name)
135         dirinfo.gpgsm_name = strdup (value);
136       else if (!strcmp (line, "g13") && !dirinfo.g13_name)
137         dirinfo.g13_name = strdup (value);
138     }
139   else
140     {
141       if (!strcmp (line, "homedir") && !dirinfo.homedir)
142         dirinfo.homedir = strdup (value);
143       else if (!strcmp (line, "sysconfdir") && !dirinfo.sysconfdir)
144         dirinfo.sysconfdir = strdup (value);
145       else if (!strcmp (line, "bindir") && !dirinfo.bindir)
146         dirinfo.bindir = strdup (value);
147       else if (!strcmp (line, "libexecdir") && !dirinfo.libexecdir)
148         dirinfo.libexecdir = strdup (value);
149       else if (!strcmp (line, "libdir") && !dirinfo.libdir)
150         dirinfo.libdir = strdup (value);
151       else if (!strcmp (line, "datadir") && !dirinfo.datadir)
152         dirinfo.datadir = strdup (value);
153       else if (!strcmp (line, "localedir") && !dirinfo.localedir)
154         dirinfo.localedir = strdup (value);
155       else if (!strcmp (line, "agent-socket") && !dirinfo.agent_socket)
156         {
157           const char name[] = "S.uiserver";
158           char *buffer;
159
160           dirinfo.agent_socket = strdup (value);
161           if (dirinfo.agent_socket)
162             {
163               n = dirname_len (dirinfo.agent_socket);
164               buffer = malloc (n + strlen (name) + 1);
165               if (buffer)
166                 {
167                   strncpy (buffer, dirinfo.agent_socket, n);
168                   strcpy (buffer + n, name);
169                   dirinfo.uisrv_socket = buffer;
170                 }
171             }
172         }
173       else if (!strcmp (line, "dirmngr-socket") && !dirinfo.dirmngr_socket)
174         dirinfo.dirmngr_socket = strdup (value);
175       else if (!strcmp (line, "agent-ssh-socket") && !dirinfo.agent_ssh_socket)
176         dirinfo.agent_ssh_socket = strdup (value);
177     }
178 }
179
180
181 /* Read the directory information from gpgconf.  This function expects
182    that DIRINFO_LOCK is held by the caller.  PGNAME is the name of the
183    gpgconf binary. If COMPONENTS is set, not the directories bit the
184    name of the componeNts are read. */
185 static void
186 read_gpgconf_dirs (const char *pgmname, int components)
187 {
188   char linebuf[1024] = {0};
189   int linelen = 0;
190   char * argv[3];
191   int rp[2];
192   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
193                                    {-1, -1} };
194   int status;
195   int nread;
196   char *mark = NULL;
197
198   argv[0] = (char *)pgmname;
199   argv[1] = (char*)(components? "--list-components" : "--list-dirs");
200   argv[2] = NULL;
201
202   if (_gpgme_io_pipe (rp, 1) < 0)
203     return;
204
205   cfd[0].fd = rp[1];
206
207   status = _gpgme_io_spawn (pgmname, argv, IOSPAWN_FLAG_DETACHED,
208                             cfd, NULL, NULL, NULL);
209   if (status < 0)
210     {
211       _gpgme_io_close (rp[0]);
212       _gpgme_io_close (rp[1]);
213       return;
214     }
215
216   do
217     {
218       nread = _gpgme_io_read (rp[0],
219                               linebuf + linelen,
220                               sizeof linebuf - linelen - 1);
221       if (nread > 0)
222         {
223           char *line;
224           const char *lastmark = NULL;
225           size_t nused;
226
227           linelen += nread;
228           linebuf[linelen] = '\0';
229
230           for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
231             {
232               lastmark = mark;
233               if (mark > line && mark[-1] == '\r')
234                 mark[-1] = '\0';
235               else
236                 mark[0] = '\0';
237
238               parse_output (line, components);
239             }
240
241           nused = lastmark? (lastmark + 1 - linebuf) : 0;
242           memmove (linebuf, linebuf + nused, linelen - nused);
243           linelen -= nused;
244         }
245     }
246   while (nread > 0 && linelen < sizeof linebuf - 1);
247
248   _gpgme_io_close (rp[0]);
249 }
250
251
252 static const char *
253 get_gpgconf_item (int what)
254 {
255   const char *result = NULL;
256
257   LOCK (dirinfo_lock);
258   if (!dirinfo.valid)
259     {
260       char *pgmname;
261
262       pgmname = dirinfo.disable_gpgconf? NULL : _gpgme_get_gpgconf_path ();
263       if (pgmname && access (pgmname, F_OK))
264         {
265           _gpgme_debug (DEBUG_INIT,
266                         "gpgme-dinfo: gpgconf='%s' [not installed]\n", pgmname);
267           free (pgmname);
268           pgmname = NULL; /* Not available.  */
269         }
270       else
271         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: gpgconf='%s'\n",
272                       pgmname? pgmname : "[null]");
273       if (!pgmname)
274         {
275           /* Probably gpgconf is not installed.  Assume we are using
276              GnuPG-1.  */
277           dirinfo.gpg_one_mode = 1;
278           pgmname = _gpgme_get_gpg_path ();
279           if (pgmname)
280             dirinfo.gpg_name = pgmname;
281         }
282       else
283         {
284           dirinfo.gpg_one_mode = 0;
285           read_gpgconf_dirs (pgmname, 0);
286           read_gpgconf_dirs (pgmname, 1);
287           dirinfo.gpgconf_name = pgmname;
288         }
289       /* Even if the reading of the directories failed (e.g. due to an
290          too old version gpgconf or no gpgconf at all), we need to
291          mark the entries as valid so that we won't try over and over
292          to read them.  Note further that we are not able to change
293          the read values later because they are practically statically
294          allocated.  */
295       dirinfo.valid = 1;
296       if (dirinfo.gpg_name)
297         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo:     gpg='%s'\n",
298                       dirinfo.gpg_name);
299       if (dirinfo.g13_name)
300         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo:     g13='%s'\n",
301                       dirinfo.g13_name);
302       if (dirinfo.gpgsm_name)
303         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo:   gpgsm='%s'\n",
304                       dirinfo.gpgsm_name);
305       if (dirinfo.homedir)
306         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: homedir='%s'\n",
307                       dirinfo.homedir);
308       if (dirinfo.agent_socket)
309         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo:   agent='%s'\n",
310                       dirinfo.agent_socket);
311       if (dirinfo.agent_ssh_socket)
312         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo:     ssh='%s'\n",
313                       dirinfo.agent_ssh_socket);
314       if (dirinfo.dirmngr_socket)
315         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: dirmngr='%s'\n",
316                       dirinfo.dirmngr_socket);
317       if (dirinfo.uisrv_socket)
318         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo:   uisrv='%s'\n",
319                       dirinfo.uisrv_socket);
320     }
321   switch (what)
322     {
323     case WANT_HOMEDIR:    result = dirinfo.homedir; break;
324     case WANT_SYSCONFDIR: result = dirinfo.sysconfdir; break;
325     case WANT_BINDIR:     result = dirinfo.bindir; break;
326     case WANT_LIBEXECDIR: result = dirinfo.libexecdir; break;
327     case WANT_LIBDIR:     result = dirinfo.libdir; break;
328     case WANT_DATADIR:    result = dirinfo.datadir; break;
329     case WANT_LOCALEDIR:  result = dirinfo.localedir; break;
330     case WANT_AGENT_SOCKET: result = dirinfo.agent_socket; break;
331     case WANT_AGENT_SSH_SOCKET: result = dirinfo.agent_ssh_socket; break;
332     case WANT_DIRMNGR_SOCKET: result = dirinfo.dirmngr_socket; break;
333     case WANT_GPGCONF_NAME: result = dirinfo.gpgconf_name; break;
334     case WANT_GPG_NAME:   result = dirinfo.gpg_name; break;
335     case WANT_GPGSM_NAME: result = dirinfo.gpgsm_name; break;
336     case WANT_G13_NAME:   result = dirinfo.g13_name; break;
337     case WANT_UISRV_SOCKET:  result = dirinfo.uisrv_socket; break;
338     case WANT_GPG_ONE_MODE: result = dirinfo.gpg_one_mode? "1":NULL; break;
339     case WANT_GPG_WKS_CLIENT_NAME:
340       if (!dirinfo.gpg_wks_client_name && dirinfo.libexecdir)
341         dirinfo.gpg_wks_client_name = _gpgme_strconcat (dirinfo.libexecdir,
342                                                         "/",
343                                                         "gpg-wks-client",
344                                                         NULL);
345       result = dirinfo.gpg_wks_client_name;
346       break;
347     }
348   UNLOCK (dirinfo_lock);
349   return result;
350 }
351
352
353 /* Return the default home directory.   Returns NULL if not known.  */
354 const char *
355 _gpgme_get_default_homedir (void)
356 {
357   return get_gpgconf_item (WANT_HOMEDIR);
358 }
359
360 /* Return the default gpg-agent socket name.  Returns NULL if not known.  */
361 const char *
362 _gpgme_get_default_agent_socket (void)
363 {
364   return get_gpgconf_item (WANT_AGENT_SOCKET);
365 }
366
367 /* Return the default gpg file name.  Returns NULL if not known.  */
368 const char *
369 _gpgme_get_default_gpg_name (void)
370 {
371   return get_gpgconf_item (WANT_GPG_NAME);
372 }
373
374 /* Return the default gpgsm file name.  Returns NULL if not known.  */
375 const char *
376 _gpgme_get_default_gpgsm_name (void)
377 {
378   return get_gpgconf_item (WANT_GPGSM_NAME);
379 }
380
381 /* Return the default g13 file name.  Returns NULL if not known.  */
382 const char *
383 _gpgme_get_default_g13_name (void)
384 {
385   return get_gpgconf_item (WANT_G13_NAME);
386 }
387
388 /* Return the default gpgconf file name.  Returns NULL if not known.  */
389 const char *
390 _gpgme_get_default_gpgconf_name (void)
391 {
392   return get_gpgconf_item (WANT_GPGCONF_NAME);
393 }
394
395 /* Return the default UI-server socket name.  Returns NULL if not
396    known.  */
397 const char *
398 _gpgme_get_default_uisrv_socket (void)
399 {
400   return get_gpgconf_item (WANT_UISRV_SOCKET);
401 }
402
403 /* Return true if we are in GnuPG-1 mode - ie. no gpgconf and agent
404    being optional.  */
405 int
406 _gpgme_in_gpg_one_mode (void)
407 {
408   return !!get_gpgconf_item (WANT_GPG_ONE_MODE);
409 }
410
411
412
413 /* Helper function to return the basename of the passed filename.  */
414 const char *
415 _gpgme_get_basename (const char *name)
416 {
417   const char *s;
418
419   if (!name || !*name)
420     return name;
421   for (s = name + strlen (name) -1; s >= name; s--)
422     if (*s == '/'
423 #ifdef HAVE_W32_SYSTEM
424         || *s == '\\' || *s == ':'
425 #endif
426         )
427       return s+1;
428   return name;
429 }
430
431
432 /* Return default values for various directories and file names.  */
433 const char *
434 gpgme_get_dirinfo (const char *what)
435 {
436   if (!what)
437     return NULL;
438   else if (!strcmp (what, "homedir"))
439     return get_gpgconf_item (WANT_HOMEDIR);
440   else if (!strcmp (what, "agent-socket"))
441     return get_gpgconf_item (WANT_AGENT_SOCKET);
442   else if (!strcmp (what, "uiserver-socket"))
443     return get_gpgconf_item (WANT_UISRV_SOCKET);
444   else if (!strcmp (what, "gpgconf-name"))
445     return get_gpgconf_item (WANT_GPGCONF_NAME);
446   else if (!strcmp (what, "gpg-name"))
447     return get_gpgconf_item (WANT_GPG_NAME);
448   else if (!strcmp (what, "gpgsm-name"))
449     return get_gpgconf_item (WANT_GPGSM_NAME);
450   else if (!strcmp (what, "g13-name"))
451     return get_gpgconf_item (WANT_G13_NAME);
452   else if (!strcmp (what, "gpg-wks-client-name"))
453     return get_gpgconf_item (WANT_GPG_WKS_CLIENT_NAME);
454   else if (!strcmp (what, "agent-ssh-socket"))
455     return get_gpgconf_item (WANT_AGENT_SSH_SOCKET);
456   else if (!strcmp (what, "dirmngr-socket"))
457     return get_gpgconf_item (WANT_DIRMNGR_SOCKET);
458   else if (!strcmp (what, "sysconfdir"))
459     return get_gpgconf_item (WANT_SYSCONFDIR);
460   else if (!strcmp (what, "bindir"))
461     return get_gpgconf_item (WANT_BINDIR);
462   else if (!strcmp (what, "libexecdir"))
463     return get_gpgconf_item (WANT_LIBEXECDIR);
464   else if (!strcmp (what, "libdir"))
465     return get_gpgconf_item (WANT_LIBDIR);
466   else if (!strcmp (what, "datadir"))
467     return get_gpgconf_item (WANT_DATADIR);
468   else if (!strcmp (what, "localedir"))
469     return get_gpgconf_item (WANT_LOCALEDIR);
470   else
471     return NULL;
472 }