Add finger support to dirmngr.
[gnupg.git] / dirmngr / ks-action.c
1 /* ks-action.c - OpenPGP keyserver actions
2  * Copyright (C) 2011 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26
27 #include "dirmngr.h"
28 #include "misc.h"
29 #include "ks-engine.h"
30 #include "ks-action.h"
31
32
33 /* Copy all data from IN to OUT.  */
34 static gpg_error_t
35 copy_stream (estream_t in, estream_t out)
36 {
37   char buffer[512];
38   size_t nread;
39
40   while (!es_read (in, buffer, sizeof buffer, &nread))
41     {
42       if (!nread)
43         return 0; /* EOF */
44       if (es_write (out, buffer, nread, NULL))
45         break;
46
47     }
48   return gpg_error_from_syserror ();
49 }
50
51
52
53 /* Search all configured keyservers for keys matching PATTERNS and
54    write the result to the provided output stream.  */
55 gpg_error_t
56 ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
57 {
58   gpg_error_t err = 0;
59   int any = 0;
60   uri_item_t uri;
61   estream_t infp;
62
63   if (!patterns)
64     return gpg_error (GPG_ERR_NO_USER_ID);
65
66   /* FIXME: We only take care of the first pattern.  To fully support
67      multiple patterns we might either want to run several queries in
68      parallel and merge them.  We also need to decide what to do with
69      errors - it might not be the best idea to ignore an error from
70      one server and silently continue with another server.  For now we
71      stop at the first error. */
72   for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
73     {
74       if (uri->parsed_uri->is_http)
75         {
76           any = 1;
77           err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d, &infp);
78           if (!err)
79             {
80               err = copy_stream (infp, outfp);
81               es_fclose (infp);
82               break;
83             }
84         }
85     }
86
87   if (!any)
88     err = gpg_error (GPG_ERR_NO_KEYSERVER);
89   return err;
90 }
91
92
93 /* Get the requested keys (matching PATTERNS) using all configured
94    keyservers and write the result to the provided output stream.  */
95 gpg_error_t
96 ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
97 {
98   gpg_error_t err = 0;
99   gpg_error_t first_err = 0;
100   int any = 0;
101   strlist_t sl;
102   uri_item_t uri;
103   estream_t infp;
104
105   if (!patterns)
106     return gpg_error (GPG_ERR_NO_USER_ID);
107
108   /* FIXME: We only take care of the first keyserver.  To fully
109      support multiple keyservers we need to track the result for each
110      pattern and use the next keyserver if one key was not found.  The
111      keyservers might not all be fully synced thus it is not clear
112      whether the first keyserver has the freshest copy of the key.
113      Need to think about a better strategy.  */
114   for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
115     {
116       if (uri->parsed_uri->is_http)
117         {
118           any = 1;
119           for (sl = patterns; !err && sl; sl = sl->next)
120             {
121               err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
122               if (err)
123                 {
124                   /* It is possible that a server does not carry a
125                      key, thus we only save the error and continue
126                      with the next pattern.  FIXME: It is an open
127                      question how to return such an error condition to
128                      the caller.  */
129                   first_err = err;
130                   err = 0;
131                 }
132               else
133                 {
134                   err = copy_stream (infp, outfp);
135                   /* Reading from the keyserver should never fail, thus
136                      return this error.  */
137                   es_fclose (infp);
138                   infp = NULL;
139                 }
140             }
141         }
142     }
143
144   if (!any)
145     err = gpg_error (GPG_ERR_NO_KEYSERVER);
146   else if (!err && first_err)
147     err = first_err; /* fixme: Do we really want to do that?  */
148   return err;
149 }
150
151
152 /* Retrive keys from URL and write the result to the provided output
153    stream OUTFP.  */
154 gpg_error_t
155 ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
156 {
157   gpg_error_t err = 0;
158   estream_t infp;
159   parsed_uri_t parsed_uri;  /* The broken down URI.  */
160
161   if (!url)
162     return gpg_error (GPG_ERR_INV_URI);
163
164   err = http_parse_uri (&parsed_uri, url, 1);
165   if (err)
166     return err;
167
168   if (parsed_uri->is_http)
169     {
170       err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
171     }
172   else if (!parsed_uri->opaque)
173     {
174       err = gpg_error (GPG_ERR_INV_URI);
175     }
176   else if (!strcmp (parsed_uri->scheme, "finger"))
177     {
178       err = ks_finger_get (ctrl, parsed_uri, &infp);
179       if (!err)
180         {
181           err = copy_stream (infp, outfp);
182           /* Reading from the finger serrver should not fail, thus
183              return this error.  */
184           es_fclose (infp);
185         }
186     }
187   else
188     err = gpg_error (GPG_ERR_INV_URI);
189
190   http_release_parsed_uri (parsed_uri);
191   return err;
192 }
193
194
195
196 /* Send an OpenPGP key to all keyservers.  The key in {DATA,DATALEN}
197    is expected in OpenPGP binary transport format.  */
198 gpg_error_t
199 ks_action_put (ctrl_t ctrl, const void *data, size_t datalen)
200 {
201   gpg_error_t err = 0;
202   gpg_error_t first_err = 0;
203   int any = 0;
204   uri_item_t uri;
205
206   for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
207     {
208       if (uri->parsed_uri->is_http)
209         {
210           any = 1;
211           err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
212           if (err)
213             {
214               first_err = err;
215               err = 0;
216             }
217         }
218     }
219
220   if (!any)
221     err = gpg_error (GPG_ERR_NO_KEYSERVER);
222   else if (!err && first_err)
223     err = first_err; /* fixme: Do we really want to do that?  */
224   return err;
225 }