First take on the low-level assuan interface.
[gpgme.git] / src / dirinfo.c
1 /* dirinfo.c - Get directory information
2  * Copyright (C) 2009 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
33 DEFINE_STATIC_LOCK (dirinfo_lock);
34
35 /* Constants used internally to select the data.  */
36 enum 
37   {
38     WANT_HOMEDIR,
39     WANT_AGENT_SOCKET
40   };
41
42 /* Values retrieved via gpgconf and cached here.  */
43 static struct {
44   int  valid;         /* Cached information is valid.  */
45   char *homedir;
46   char *agent_socket;
47 } dirinfo;
48
49
50 /* Parse the output of "gpgconf --list-dirs".  This function expects
51    that DIRINFO_LOCK is held by the caller.  */
52 static void
53 parse_output (char *line)
54 {
55   char *value, *p;
56
57   value = strchr (line, ':');
58   if (!value)
59     return;
60   *value++ = 0;
61   p = strchr (value, ':');
62   if (p)
63     *p = 0;
64   if (_gpgme_decode_percent_string (value, &value, strlen (value)+1, 0))
65     return;
66   if (!*value)
67     return;
68   
69   if (!strcmp (line, "homedir") && !dirinfo.homedir)
70     dirinfo.homedir = strdup (value);
71   else if (!strcmp (line, "agent-socket") && !dirinfo.agent_socket)
72     dirinfo.agent_socket = strdup (value);
73 }
74
75
76 /* Read the directory information from gpgconf.  This function expects
77    that DIRINFO_LOCK is held by the caller.  */
78 static void
79 read_gpgconf_dirs (void) 
80 {
81   const char *pgmname;
82   char linebuf[1024] = {0};
83   int linelen = 0;
84   char * argv[3];
85   int rp[2];
86   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
87                                    {-1, -1} };
88   int status;
89   int nread;
90   char *mark = NULL;
91
92   pgmname = _gpgme_get_gpgconf_path ();
93   if (!pgmname)
94     return;  /* No way.  */
95
96   argv[0] = (char *)pgmname;
97   argv[1] = "--list-dirs";
98   argv[2] = NULL;
99
100   if (_gpgme_io_pipe (rp, 1) < 0)
101     return;
102
103   cfd[0].fd = rp[1];
104
105   status = _gpgme_io_spawn (pgmname, argv, cfd, NULL);
106   if (status < 0)
107     {
108       _gpgme_io_close (rp[0]);
109       _gpgme_io_close (rp[1]);
110       return;
111     }
112
113   do
114     {
115       nread = _gpgme_io_read (rp[0], 
116                               linebuf + linelen, 
117                               sizeof linebuf - linelen - 1);
118       if (nread > 0)
119         {
120           char *line;
121           const char *lastmark = NULL;
122           size_t nused;
123
124           linelen += nread;
125           linebuf[linelen] = '\0';
126
127           for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
128             {
129               lastmark = mark;
130               if (mark > line && mark[-1] == '\r')
131                 mark[-1] = '\0';
132               else
133                 mark[0] = '\0';
134
135               parse_output (line);
136             }
137
138           nused = lastmark? (lastmark + 1 - linebuf) : 0;
139           memmove (linebuf, linebuf + nused, linelen - nused);
140           linelen -= nused;
141         }
142     }
143   while (nread > 0 && linelen < sizeof linebuf - 1);
144   
145   _gpgme_io_close (rp[0]);
146 }
147
148
149 static const char *
150 get_gpgconf_dir (int what)
151 {
152   const char *result = NULL;
153
154   LOCK (dirinfo_lock);
155   if (!dirinfo.valid)
156     {
157       read_gpgconf_dirs ();
158       /* Even if the reading of the directories failed (e.g. due to an
159          too old version gpgconf or no gpgconf at all), we need to
160          mark the entries as valid so that we won't try over and over
161          to read them.  Note further that we are not able to change
162          the read values later because they are practically statically
163          allocated.  */
164       dirinfo.valid = 1;
165     }
166   switch (what)
167     {
168     case WANT_HOMEDIR: result = dirinfo.homedir; break;
169     case WANT_AGENT_SOCKET: result = dirinfo.agent_socket; break;
170     }
171   UNLOCK (dirinfo_lock);
172   return result;
173 }
174
175
176 /* Return the default home directory.   Returns NULL if not known.  */
177 const char *
178 _gpgme_get_default_homedir (void)
179 {
180   return get_gpgconf_dir (WANT_HOMEDIR);
181 }
182
183 /* Return the default gpg-agent socket name.  Returns NULL if not known.  */
184 const char *
185 _gpgme_get_default_agent_socket (void)
186 {
187   return get_gpgconf_dir (WANT_AGENT_SOCKET);
188 }
189