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