Line data Source code
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 :
83 : /* Helper function to be used only by gpgme_set_global_flag. */
84 : void
85 0 : _gpgme_dirinfo_disable_gpgconf (void)
86 : {
87 0 : dirinfo.disable_gpgconf = 1;
88 0 : }
89 :
90 :
91 : /* Return the length of the directory part including the trailing
92 : * slash of NAME. */
93 : static size_t
94 98 : dirname_len (const char *name)
95 : {
96 98 : 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 1862 : parse_output (char *line, int components)
105 : {
106 : char *value, *p;
107 : size_t n;
108 :
109 1862 : value = strchr (line, ':');
110 1862 : if (!value)
111 0 : return;
112 1862 : *value++ = 0;
113 1862 : if (components)
114 : {
115 : /* Skip the second field. */
116 588 : value = strchr (value, ':');
117 588 : if (!value)
118 0 : return;
119 588 : *value++ = 0;
120 : }
121 1862 : p = strchr (value, ':');
122 1862 : if (p)
123 0 : *p = 0;
124 1862 : if (_gpgme_decode_percent_string (value, &value, strlen (value)+1, 0))
125 0 : return;
126 1862 : if (!*value)
127 0 : return;
128 :
129 1862 : if (components)
130 : {
131 588 : if (!strcmp (line, "gpg") && !dirinfo.gpg_name)
132 98 : dirinfo.gpg_name = strdup (value);
133 490 : else if (!strcmp (line, "gpgsm") && !dirinfo.gpgsm_name)
134 98 : dirinfo.gpgsm_name = strdup (value);
135 392 : else if (!strcmp (line, "g13") && !dirinfo.g13_name)
136 0 : dirinfo.g13_name = strdup (value);
137 : }
138 : else
139 : {
140 1274 : if (!strcmp (line, "homedir") && !dirinfo.homedir)
141 98 : dirinfo.homedir = strdup (value);
142 1176 : else if (!strcmp (line, "sysconfdir") && !dirinfo.sysconfdir)
143 98 : dirinfo.sysconfdir = strdup (value);
144 1078 : else if (!strcmp (line, "bindir") && !dirinfo.bindir)
145 98 : dirinfo.bindir = strdup (value);
146 980 : else if (!strcmp (line, "libexecdir") && !dirinfo.libexecdir)
147 98 : dirinfo.libexecdir = strdup (value);
148 882 : else if (!strcmp (line, "libdir") && !dirinfo.libdir)
149 98 : dirinfo.libdir = strdup (value);
150 784 : else if (!strcmp (line, "datadir") && !dirinfo.datadir)
151 98 : dirinfo.datadir = strdup (value);
152 686 : else if (!strcmp (line, "localedir") && !dirinfo.localedir)
153 98 : dirinfo.localedir = strdup (value);
154 588 : else if (!strcmp (line, "agent-socket") && !dirinfo.agent_socket)
155 98 : {
156 98 : const char name[] = "S.uiserver";
157 : char *buffer;
158 :
159 98 : dirinfo.agent_socket = strdup (value);
160 98 : if (dirinfo.agent_socket)
161 : {
162 98 : n = dirname_len (dirinfo.agent_socket);
163 98 : buffer = malloc (n + strlen (name) + 1);
164 98 : if (buffer)
165 : {
166 98 : strncpy (buffer, dirinfo.agent_socket, n);
167 98 : strcpy (buffer + n, name);
168 98 : dirinfo.uisrv_socket = buffer;
169 : }
170 : }
171 : }
172 490 : else if (!strcmp (line, "dirmngr-socket") && !dirinfo.dirmngr_socket)
173 98 : dirinfo.dirmngr_socket = strdup (value);
174 392 : else if (!strcmp (line, "agent-ssh-socket") && !dirinfo.agent_ssh_socket)
175 98 : 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 196 : read_gpgconf_dirs (const char *pgmname, int components)
186 : {
187 196 : char linebuf[1024] = {0};
188 196 : int linelen = 0;
189 : char * argv[3];
190 : int rp[2];
191 196 : struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
192 : {-1, -1} };
193 : int status;
194 : int nread;
195 196 : char *mark = NULL;
196 :
197 196 : argv[0] = (char *)pgmname;
198 196 : argv[1] = (char*)(components? "--list-components" : "--list-dirs");
199 196 : argv[2] = NULL;
200 :
201 196 : if (_gpgme_io_pipe (rp, 1) < 0)
202 0 : return;
203 :
204 196 : cfd[0].fd = rp[1];
205 :
206 196 : status = _gpgme_io_spawn (pgmname, argv, IOSPAWN_FLAG_DETACHED,
207 : cfd, NULL, NULL, NULL);
208 196 : if (status < 0)
209 : {
210 0 : _gpgme_io_close (rp[0]);
211 0 : _gpgme_io_close (rp[1]);
212 0 : return;
213 : }
214 :
215 : do
216 : {
217 392 : nread = _gpgme_io_read (rp[0],
218 : linebuf + linelen,
219 : sizeof linebuf - linelen - 1);
220 392 : if (nread > 0)
221 : {
222 : char *line;
223 196 : const char *lastmark = NULL;
224 : size_t nused;
225 :
226 196 : linelen += nread;
227 196 : linebuf[linelen] = '\0';
228 :
229 2058 : for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
230 : {
231 1862 : lastmark = mark;
232 1862 : if (mark > line && mark[-1] == '\r')
233 0 : mark[-1] = '\0';
234 : else
235 1862 : mark[0] = '\0';
236 :
237 1862 : parse_output (line, components);
238 : }
239 :
240 196 : nused = lastmark? (lastmark + 1 - linebuf) : 0;
241 196 : memmove (linebuf, linebuf + nused, linelen - nused);
242 196 : linelen -= nused;
243 : }
244 : }
245 392 : while (nread > 0 && linelen < sizeof linebuf - 1);
246 :
247 196 : _gpgme_io_close (rp[0]);
248 : }
249 :
250 :
251 : static const char *
252 1891 : get_gpgconf_item (int what)
253 : {
254 1891 : const char *result = NULL;
255 :
256 1891 : LOCK (dirinfo_lock);
257 1891 : if (!dirinfo.valid)
258 : {
259 : char *pgmname;
260 :
261 98 : pgmname = dirinfo.disable_gpgconf? NULL : _gpgme_get_gpgconf_path ();
262 98 : if (pgmname && access (pgmname, F_OK))
263 : {
264 0 : _gpgme_debug (DEBUG_INIT,
265 : "gpgme-dinfo: gpgconf='%s' [not installed]\n", pgmname);
266 0 : free (pgmname);
267 0 : pgmname = NULL; /* Not available. */
268 : }
269 : else
270 98 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: gpgconf='%s'\n",
271 : pgmname? pgmname : "[null]");
272 98 : if (!pgmname)
273 : {
274 : /* Probably gpgconf is not installed. Assume we are using
275 : GnuPG-1. */
276 0 : dirinfo.gpg_one_mode = 1;
277 0 : pgmname = _gpgme_get_gpg_path ();
278 0 : if (pgmname)
279 0 : dirinfo.gpg_name = pgmname;
280 : }
281 : else
282 : {
283 98 : dirinfo.gpg_one_mode = 0;
284 98 : read_gpgconf_dirs (pgmname, 0);
285 98 : read_gpgconf_dirs (pgmname, 1);
286 98 : 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 98 : dirinfo.valid = 1;
295 98 : if (dirinfo.gpg_name)
296 98 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: gpg='%s'\n",
297 : dirinfo.gpg_name);
298 98 : if (dirinfo.g13_name)
299 0 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: g13='%s'\n",
300 : dirinfo.g13_name);
301 98 : if (dirinfo.gpgsm_name)
302 98 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: gpgsm='%s'\n",
303 : dirinfo.gpgsm_name);
304 98 : if (dirinfo.homedir)
305 98 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: homedir='%s'\n",
306 : dirinfo.homedir);
307 98 : if (dirinfo.agent_socket)
308 98 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: agent='%s'\n",
309 : dirinfo.agent_socket);
310 98 : if (dirinfo.agent_ssh_socket)
311 98 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: ssh='%s'\n",
312 : dirinfo.agent_ssh_socket);
313 98 : if (dirinfo.dirmngr_socket)
314 98 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: dirmngr='%s'\n",
315 : dirinfo.dirmngr_socket);
316 98 : if (dirinfo.uisrv_socket)
317 98 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: uisrv='%s'\n",
318 : dirinfo.uisrv_socket);
319 : }
320 1891 : switch (what)
321 : {
322 1 : case WANT_HOMEDIR: result = dirinfo.homedir; break;
323 1 : case WANT_SYSCONFDIR: result = dirinfo.sysconfdir; break;
324 1 : case WANT_BINDIR: result = dirinfo.bindir; break;
325 1 : case WANT_LIBEXECDIR: result = dirinfo.libexecdir; break;
326 1 : case WANT_LIBDIR: result = dirinfo.libdir; break;
327 1 : case WANT_DATADIR: result = dirinfo.datadir; break;
328 1 : case WANT_LOCALEDIR: result = dirinfo.localedir; break;
329 101 : case WANT_AGENT_SOCKET: result = dirinfo.agent_socket; break;
330 1 : case WANT_AGENT_SSH_SOCKET: result = dirinfo.agent_ssh_socket; break;
331 1 : case WANT_DIRMNGR_SOCKET: result = dirinfo.dirmngr_socket; break;
332 199 : case WANT_GPGCONF_NAME: result = dirinfo.gpgconf_name; break;
333 362 : case WANT_GPG_NAME: result = dirinfo.gpg_name; break;
334 197 : case WANT_GPGSM_NAME: result = dirinfo.gpgsm_name; break;
335 197 : case WANT_G13_NAME: result = dirinfo.g13_name; break;
336 99 : case WANT_UISRV_SOCKET: result = dirinfo.uisrv_socket; break;
337 726 : case WANT_GPG_ONE_MODE: result = dirinfo.gpg_one_mode? "1":NULL; break;
338 : case WANT_GPG_WKS_CLIENT_NAME:
339 1 : if (!dirinfo.gpg_wks_client_name && dirinfo.libexecdir)
340 1 : dirinfo.gpg_wks_client_name = _gpgme_strconcat (dirinfo.libexecdir,
341 : "/",
342 : "gpg-wks-client",
343 : NULL);
344 1 : result = dirinfo.gpg_wks_client_name;
345 1 : break;
346 : }
347 1891 : UNLOCK (dirinfo_lock);
348 1891 : return result;
349 : }
350 :
351 :
352 : /* Return the default home directory. Returns NULL if not known. */
353 : const char *
354 0 : _gpgme_get_default_homedir (void)
355 : {
356 0 : 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 100 : _gpgme_get_default_agent_socket (void)
362 : {
363 100 : 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 361 : _gpgme_get_default_gpg_name (void)
369 : {
370 361 : 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 196 : _gpgme_get_default_gpgsm_name (void)
376 : {
377 196 : 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 196 : _gpgme_get_default_g13_name (void)
383 : {
384 196 : 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 198 : _gpgme_get_default_gpgconf_name (void)
390 : {
391 198 : 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 98 : _gpgme_get_default_uisrv_socket (void)
398 : {
399 98 : 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 726 : _gpgme_in_gpg_one_mode (void)
406 : {
407 726 : 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 832 : _gpgme_get_basename (const char *name)
415 : {
416 : const char *s;
417 :
418 832 : if (!name || !*name)
419 0 : return name;
420 4128 : for (s = name + strlen (name) -1; s >= name; s--)
421 4128 : if (*s == '/'
422 : #ifdef HAVE_W32_SYSTEM
423 : || *s == '\\' || *s == ':'
424 : #endif
425 : )
426 832 : return s+1;
427 0 : return name;
428 : }
429 :
430 :
431 : /* Return default values for various directories and file names. */
432 : const char *
433 16 : gpgme_get_dirinfo (const char *what)
434 : {
435 16 : if (!what)
436 0 : return NULL;
437 16 : else if (!strcmp (what, "homedir"))
438 1 : return get_gpgconf_item (WANT_HOMEDIR);
439 15 : else if (!strcmp (what, "agent-socket"))
440 1 : return get_gpgconf_item (WANT_AGENT_SOCKET);
441 14 : else if (!strcmp (what, "uiserver-socket"))
442 1 : return get_gpgconf_item (WANT_UISRV_SOCKET);
443 13 : else if (!strcmp (what, "gpgconf-name"))
444 1 : return get_gpgconf_item (WANT_GPGCONF_NAME);
445 12 : else if (!strcmp (what, "gpg-name"))
446 1 : return get_gpgconf_item (WANT_GPG_NAME);
447 11 : else if (!strcmp (what, "gpgsm-name"))
448 1 : return get_gpgconf_item (WANT_GPGSM_NAME);
449 10 : else if (!strcmp (what, "g13-name"))
450 1 : return get_gpgconf_item (WANT_G13_NAME);
451 9 : else if (!strcmp (what, "gpg-wks-client-name"))
452 1 : return get_gpgconf_item (WANT_GPG_WKS_CLIENT_NAME);
453 8 : else if (!strcmp (what, "agent-ssh-socket"))
454 1 : return get_gpgconf_item (WANT_AGENT_SSH_SOCKET);
455 7 : else if (!strcmp (what, "dirmngr-socket"))
456 1 : return get_gpgconf_item (WANT_DIRMNGR_SOCKET);
457 6 : else if (!strcmp (what, "sysconfdir"))
458 1 : return get_gpgconf_item (WANT_SYSCONFDIR);
459 5 : else if (!strcmp (what, "bindir"))
460 1 : return get_gpgconf_item (WANT_BINDIR);
461 4 : else if (!strcmp (what, "libexecdir"))
462 1 : return get_gpgconf_item (WANT_LIBEXECDIR);
463 3 : else if (!strcmp (what, "libdir"))
464 1 : return get_gpgconf_item (WANT_LIBDIR);
465 2 : else if (!strcmp (what, "datadir"))
466 1 : return get_gpgconf_item (WANT_DATADIR);
467 1 : else if (!strcmp (what, "localedir"))
468 1 : return get_gpgconf_item (WANT_LOCALEDIR);
469 : else
470 0 : return NULL;
471 : }
|