Merge branch 'master' into ECC-INTEGRATION-2-1
[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 nver 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
153 /* Send an OpenPGP key to all keyservers.  The key in {DATA,DATALEN}
154    is expected in OpenPGP binary transport format.  */
155 gpg_error_t
156 ks_action_put (ctrl_t ctrl, const void *data, size_t datalen)
157 {
158   gpg_error_t err = 0;
159   gpg_error_t first_err = 0;
160   int any = 0;
161   uri_item_t uri;
162
163   for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
164     {
165       if (uri->parsed_uri->is_http)
166         {
167           any = 1;
168           err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
169           if (err)
170             {
171               first_err = err;
172               err = 0;
173             }
174         }
175     }
176   
177   if (!any)
178     err = gpg_error (GPG_ERR_NO_KEYSERVER);
179   else if (!err && first_err)
180     err = first_err; /* fixme: Do we really want to do that?  */
181   return err;
182 }
183