Add global flags disable-gpgconf, gpgconf-name, and gpg-name.
[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 <http://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_AGENT_SOCKET,
41     WANT_GPGCONF_NAME,
42     WANT_GPG_NAME,
43     WANT_GPGSM_NAME,
44     WANT_G13_NAME,
45     WANT_UISRV_SOCKET
46   };
47
48 /* Values retrieved via gpgconf and cached here.  */
49 static struct {
50   int  valid;         /* Cached information is valid.  */
51   int  disable_gpgconf;
52   char *homedir;
53   char *agent_socket;
54   char *gpgconf_name;
55   char *gpg_name;
56   char *gpgsm_name;
57   char *g13_name;
58   char *uisrv_socket;
59 } dirinfo;
60
61
62 \f
63 /* Helper function to be used only by gpgme_set_global_flag.  */
64 void
65 _gpgme_dirinfo_disable_gpgconf (void)
66 {
67   dirinfo.disable_gpgconf = 1;
68 }
69
70
71 /* Parse the output of "gpgconf --list-dirs".  This function expects
72    that DIRINFO_LOCK is held by the caller.  If COMPONENTS is set, the
73    output of --list-components is expected. */
74 static void
75 parse_output (char *line, int components)
76 {
77   char *value, *p;
78
79   value = strchr (line, ':');
80   if (!value)
81     return;
82   *value++ = 0;
83   if (components)
84     {
85       /* Skip the second field.  */
86       value = strchr (value, ':');
87       if (!value)
88         return;
89       *value++ = 0;
90     }
91   p = strchr (value, ':');
92   if (p)
93     *p = 0;
94   if (_gpgme_decode_percent_string (value, &value, strlen (value)+1, 0))
95     return;
96   if (!*value)
97     return;
98
99   if (components)
100     {
101       if (!strcmp (line, "gpg") && !dirinfo.gpg_name)
102         dirinfo.gpg_name = strdup (value);
103       else if (!strcmp (line, "gpgsm") && !dirinfo.gpgsm_name)
104         dirinfo.gpgsm_name = strdup (value);
105       else if (!strcmp (line, "g13") && !dirinfo.g13_name)
106         dirinfo.g13_name = strdup (value);
107     }
108   else
109     {
110       if (!strcmp (line, "homedir") && !dirinfo.homedir)
111         {
112           const char name[] = "S.uiserver";
113
114           dirinfo.homedir = strdup (value);
115           if (dirinfo.homedir)
116             {
117               dirinfo.uisrv_socket = malloc (strlen (dirinfo
118                                                      .homedir)
119                                              + 1 + strlen (name) + 1);
120               if (dirinfo.uisrv_socket)
121                 strcpy (stpcpy (stpcpy (dirinfo.uisrv_socket, dirinfo.homedir),
122                                 DIRSEP_S), name);
123             }
124         }
125       else if (!strcmp (line, "agent-socket") && !dirinfo.agent_socket)
126         dirinfo.agent_socket = strdup (value);
127     }
128 }
129
130
131 /* Read the directory information from gpgconf.  This function expects
132    that DIRINFO_LOCK is held by the caller.  PGNAME is the name of the
133    gpgconf binary. If COMPONENTS is set, not the directories bit the
134    name of the componeNts are read. */
135 static void
136 read_gpgconf_dirs (const char *pgmname, int components)
137 {
138   char linebuf[1024] = {0};
139   int linelen = 0;
140   char * argv[3];
141   int rp[2];
142   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
143                                    {-1, -1} };
144   int status;
145   int nread;
146   char *mark = NULL;
147
148   argv[0] = (char *)pgmname;
149   argv[1] = components? "--list-components" : "--list-dirs";
150   argv[2] = NULL;
151
152   if (_gpgme_io_pipe (rp, 1) < 0)
153     return;
154
155   cfd[0].fd = rp[1];
156
157   status = _gpgme_io_spawn (pgmname, argv, 0, cfd, NULL, NULL, NULL);
158   if (status < 0)
159     {
160       _gpgme_io_close (rp[0]);
161       _gpgme_io_close (rp[1]);
162       return;
163     }
164
165   do
166     {
167       nread = _gpgme_io_read (rp[0],
168                               linebuf + linelen,
169                               sizeof linebuf - linelen - 1);
170       if (nread > 0)
171         {
172           char *line;
173           const char *lastmark = NULL;
174           size_t nused;
175
176           linelen += nread;
177           linebuf[linelen] = '\0';
178
179           for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
180             {
181               lastmark = mark;
182               if (mark > line && mark[-1] == '\r')
183                 mark[-1] = '\0';
184               else
185                 mark[0] = '\0';
186
187               parse_output (line, components);
188             }
189
190           nused = lastmark? (lastmark + 1 - linebuf) : 0;
191           memmove (linebuf, linebuf + nused, linelen - nused);
192           linelen -= nused;
193         }
194     }
195   while (nread > 0 && linelen < sizeof linebuf - 1);
196
197   _gpgme_io_close (rp[0]);
198 }
199
200
201 static const char *
202 get_gpgconf_item (int what)
203 {
204   const char *result = NULL;
205
206   LOCK (dirinfo_lock);
207   if (!dirinfo.valid)
208     {
209       char *pgmname;
210
211       pgmname = dirinfo.disable_gpgconf? NULL : _gpgme_get_gpgconf_path ();
212       if (pgmname && access (pgmname, F_OK))
213         {
214           _gpgme_debug (DEBUG_INIT,
215                         "gpgme-dinfo: gpgconf='%s' [not installed]\n", pgmname);
216           free (pgmname);
217           pgmname = NULL; /* Not available.  */
218         }
219       else
220         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: gpgconf='%s'\n",
221                       pgmname? pgmname : "[null]");
222       if (!pgmname)
223         {
224           /* Probably gpgconf is not installed.  Assume we are using
225              GnuPG-1.  */
226           pgmname = _gpgme_get_gpg_path ();
227           if (pgmname)
228             dirinfo.gpg_name = pgmname;
229         }
230       else
231         {
232           read_gpgconf_dirs (pgmname, 0);
233           read_gpgconf_dirs (pgmname, 1);
234           dirinfo.gpgconf_name = pgmname;
235         }
236       /* Even if the reading of the directories failed (e.g. due to an
237          too old version gpgconf or no gpgconf at all), we need to
238          mark the entries as valid so that we won't try over and over
239          to read them.  Note further that we are not able to change
240          the read values later because they are practically statically
241          allocated.  */
242       dirinfo.valid = 1;
243       if (dirinfo.gpg_name)
244         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo:     gpg='%s'\n",
245                       dirinfo.gpg_name);
246       if (dirinfo.g13_name)
247         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo:     g13='%s'\n",
248                       dirinfo.g13_name);
249       if (dirinfo.gpgsm_name)
250         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo:   gpgsm='%s'\n",
251                       dirinfo.gpgsm_name);
252       if (dirinfo.homedir)
253         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: homedir='%s'\n",
254                       dirinfo.homedir);
255       if (dirinfo.agent_socket)
256         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo:   agent='%s'\n",
257                       dirinfo.agent_socket);
258       if (dirinfo.uisrv_socket)
259         _gpgme_debug (DEBUG_INIT, "gpgme-dinfo:   uisrv='%s'\n",
260                       dirinfo.uisrv_socket);
261     }
262   switch (what)
263     {
264     case WANT_HOMEDIR: result = dirinfo.homedir; break;
265     case WANT_AGENT_SOCKET: result = dirinfo.agent_socket; break;
266     case WANT_GPGCONF_NAME: result = dirinfo.gpgconf_name; break;
267     case WANT_GPG_NAME:   result = dirinfo.gpg_name; break;
268     case WANT_GPGSM_NAME: result = dirinfo.gpgsm_name; break;
269     case WANT_G13_NAME:   result = dirinfo.g13_name; break;
270     case WANT_UISRV_SOCKET:  result = dirinfo.uisrv_socket; break;
271     }
272   UNLOCK (dirinfo_lock);
273   return result;
274 }
275
276
277 /* Return the default home directory.   Returns NULL if not known.  */
278 const char *
279 _gpgme_get_default_homedir (void)
280 {
281   return get_gpgconf_item (WANT_HOMEDIR);
282 }
283
284 /* Return the default gpg-agent socket name.  Returns NULL if not known.  */
285 const char *
286 _gpgme_get_default_agent_socket (void)
287 {
288   return get_gpgconf_item (WANT_AGENT_SOCKET);
289 }
290
291 /* Return the default gpg file name.  Returns NULL if not known.  */
292 const char *
293 _gpgme_get_default_gpg_name (void)
294 {
295   return get_gpgconf_item (WANT_GPG_NAME);
296 }
297
298 /* Return the default gpgsm file name.  Returns NULL if not known.  */
299 const char *
300 _gpgme_get_default_gpgsm_name (void)
301 {
302   return get_gpgconf_item (WANT_GPGSM_NAME);
303 }
304
305 /* Return the default g13 file name.  Returns NULL if not known.  */
306 const char *
307 _gpgme_get_default_g13_name (void)
308 {
309   return get_gpgconf_item (WANT_G13_NAME);
310 }
311
312 /* Return the default gpgconf file name.  Returns NULL if not known.  */
313 const char *
314 _gpgme_get_default_gpgconf_name (void)
315 {
316   return get_gpgconf_item (WANT_GPGCONF_NAME);
317 }
318
319 /* Return the default UI-server socket name.  Returns NULL if not
320    known.  */
321 const char *
322 _gpgme_get_default_uisrv_socket (void)
323 {
324   return get_gpgconf_item (WANT_UISRV_SOCKET);
325 }