14de4d6c0669517a128bf971ffe163846eb951d4
[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 /* Called by the engine's help functions to print the actual help.  */
53 gpg_error_t
54 ks_print_help (ctrl_t ctrl, const char *text)
55 {
56   return dirmngr_status_help (ctrl, text);
57 }
58
59
60 /* Run the help command for the engine responsible for URI.  */
61 gpg_error_t
62 ks_action_help (ctrl_t ctrl, const char *url)
63 {
64   gpg_error_t err;
65   parsed_uri_t parsed_uri;  /* The broken down URI.  */
66
67   if (!url || !*url)
68     {
69       ks_print_help (ctrl, "Known schemata:\n");
70       parsed_uri = NULL;
71     }
72   else
73     {
74       err = http_parse_uri (&parsed_uri, url, 1);
75       if (err)
76         return err;
77     }
78
79   /* Call all engines to give them a chance to print a help sting.  */
80   err = ks_hkp_help (ctrl, parsed_uri);
81   if (!err)
82     err = ks_http_help (ctrl, parsed_uri);
83   if (!err)
84     err = ks_finger_help (ctrl, parsed_uri);
85   if (!err)
86     err = ks_kdns_help (ctrl, parsed_uri);
87
88   if (!parsed_uri)
89     ks_print_help (ctrl,
90                    "(Use the schema followed by a colon for specific help.)");
91   else
92     http_release_parsed_uri (parsed_uri);
93   return err;
94 }
95
96
97 /* Search all configured keyservers for keys matching PATTERNS and
98    write the result to the provided output stream.  */
99 gpg_error_t
100 ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
101 {
102   gpg_error_t err = 0;
103   int any = 0;
104   uri_item_t uri;
105   estream_t infp;
106
107   if (!patterns)
108     return gpg_error (GPG_ERR_NO_USER_ID);
109
110   /* FIXME: We only take care of the first pattern.  To fully support
111      multiple patterns we might either want to run several queries in
112      parallel and merge them.  We also need to decide what to do with
113      errors - it might not be the best idea to ignore an error from
114      one server and silently continue with another server.  For now we
115      stop at the first error. */
116   for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
117     {
118       if (uri->parsed_uri->is_http)
119         {
120           any = 1;
121           err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d, &infp);
122           if (!err)
123             {
124               err = copy_stream (infp, outfp);
125               es_fclose (infp);
126               break;
127             }
128         }
129     }
130
131   if (!any)
132     err = gpg_error (GPG_ERR_NO_KEYSERVER);
133   return err;
134 }
135
136
137 /* Get the requested keys (matching PATTERNS) using all configured
138    keyservers and write the result to the provided output stream.  */
139 gpg_error_t
140 ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
141 {
142   gpg_error_t err = 0;
143   gpg_error_t first_err = 0;
144   int any = 0;
145   strlist_t sl;
146   uri_item_t uri;
147   estream_t infp;
148
149   if (!patterns)
150     return gpg_error (GPG_ERR_NO_USER_ID);
151
152   /* FIXME: We only take care of the first keyserver.  To fully
153      support multiple keyservers we need to track the result for each
154      pattern and use the next keyserver if one key was not found.  The
155      keyservers might not all be fully synced thus it is not clear
156      whether the first keyserver has the freshest copy of the key.
157      Need to think about a better strategy.  */
158   for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
159     {
160       if (uri->parsed_uri->is_http)
161         {
162           any = 1;
163           for (sl = patterns; !err && sl; sl = sl->next)
164             {
165               err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
166               if (err)
167                 {
168                   /* It is possible that a server does not carry a
169                      key, thus we only save the error and continue
170                      with the next pattern.  FIXME: It is an open
171                      question how to return such an error condition to
172                      the caller.  */
173                   first_err = err;
174                   err = 0;
175                 }
176               else
177                 {
178                   err = copy_stream (infp, outfp);
179                   /* Reading from the keyserver should never fail, thus
180                      return this error.  */
181                   es_fclose (infp);
182                   infp = NULL;
183                 }
184             }
185         }
186     }
187
188   if (!any)
189     err = gpg_error (GPG_ERR_NO_KEYSERVER);
190   else if (!err && first_err)
191     err = first_err; /* fixme: Do we really want to do that?  */
192   return err;
193 }
194
195
196 /* Retrive keys from URL and write the result to the provided output
197    stream OUTFP.  */
198 gpg_error_t
199 ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
200 {
201   gpg_error_t err = 0;
202   estream_t infp;
203   parsed_uri_t parsed_uri;  /* The broken down URI.  */
204
205   if (!url)
206     return gpg_error (GPG_ERR_INV_URI);
207
208   err = http_parse_uri (&parsed_uri, url, 1);
209   if (err)
210     return err;
211
212   if (parsed_uri->is_http)
213     {
214       err = ks_http_fetch (ctrl, url, &infp);
215       if (!err)
216         {
217           err = copy_stream (infp, outfp);
218           es_fclose (infp);
219         }
220     }
221   else if (!parsed_uri->opaque)
222     {
223       err = gpg_error (GPG_ERR_INV_URI);
224     }
225   else if (!strcmp (parsed_uri->scheme, "finger"))
226     {
227       err = ks_finger_fetch (ctrl, parsed_uri, &infp);
228       if (!err)
229         {
230           err = copy_stream (infp, outfp);
231           es_fclose (infp);
232         }
233     }
234   else if (!strcmp (parsed_uri->scheme, "kdns"))
235     {
236       err = ks_kdns_fetch (ctrl, parsed_uri, &infp);
237       if (!err)
238         {
239           err = copy_stream (infp, outfp);
240           es_fclose (infp);
241         }
242     }
243   else
244     err = gpg_error (GPG_ERR_INV_URI);
245
246   http_release_parsed_uri (parsed_uri);
247   return err;
248 }
249
250
251
252 /* Send an OpenPGP key to all keyservers.  The key in {DATA,DATALEN}
253    is expected in OpenPGP binary transport format.  */
254 gpg_error_t
255 ks_action_put (ctrl_t ctrl, const void *data, size_t datalen)
256 {
257   gpg_error_t err = 0;
258   gpg_error_t first_err = 0;
259   int any = 0;
260   uri_item_t uri;
261
262   for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
263     {
264       if (uri->parsed_uri->is_http)
265         {
266           any = 1;
267           err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
268           if (err)
269             {
270               first_err = err;
271               err = 0;
272             }
273         }
274     }
275
276   if (!any)
277     err = gpg_error (GPG_ERR_NO_KEYSERVER);
278   else if (!err && first_err)
279     err = first_err; /* fixme: Do we really want to do that?  */
280   return err;
281 }