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