* gpgkeys_hkp.c: New hkp handler that uses curl or curl-shim.
[gnupg.git] / keyserver / curl-shim.c
1 /* curl-shim.c - Implement a small subset of the curl API in terms of
2  * the iobuf HTTP API
3  *
4  * Copyright (C) 2005 Free Software Foundation, Inc.
5  *
6  * This file is part of GnuPG.
7  *
8  * GnuPG is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * GnuPG is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21  */
22
23 #include <config.h>
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include "http.h"
30 #include "util.h"
31 #include "curl-shim.h"
32
33 static CURLcode
34 handle_error(CURL *curl,CURLcode err,const char *str)
35 {
36   if(curl->errorbuffer)
37     {
38       /* Make sure you never exceed CURL_ERROR_SIZE, currently set to
39          256 in curl-shim.h */
40       switch(err)
41         {
42         case CURLE_OK:
43           strcpy(curl->errorbuffer,"okay");
44           break;
45
46         case CURLE_COULDNT_CONNECT:
47           strcpy(curl->errorbuffer,"couldn't connect");
48           break;
49
50         case CURLE_WRITE_ERROR:
51           strcpy(curl->errorbuffer,"write error");
52           break;
53
54         case CURLE_HTTP_RETURNED_ERROR:
55           sprintf(curl->errorbuffer,"url returned error %u",curl->status);
56           break;
57
58         default:
59           strcpy(curl->errorbuffer,"generic error");
60           break;
61         }
62
63       if(str && (strlen(curl->errorbuffer)+2+strlen(str)+1)<=CURL_ERROR_SIZE)
64         {
65           strcat(curl->errorbuffer,": ");
66           strcat(curl->errorbuffer,str);
67         }
68     }
69
70   return err;
71 }
72
73 CURLcode
74 curl_global_init(long flags)
75 {
76   return CURLE_OK;
77 }
78
79 void
80 curl_global_cleanup(void) {}
81
82 CURL *
83 curl_easy_init(void)
84 {
85   return calloc(1,sizeof(CURL));
86 }
87
88 void
89 curl_easy_cleanup(CURL *curl)
90 {
91   free(curl);
92 }
93
94 CURLcode
95 curl_easy_setopt(CURL *curl,CURLoption option,...)
96 {
97   va_list ap;
98
99   va_start(ap,option);
100
101   switch(option)
102     {
103     case CURLOPT_URL:
104       curl->url=va_arg(ap,char *);
105       break;
106     case CURLOPT_WRITEFUNCTION:
107       curl->writer=va_arg(ap,write_func);
108       break;
109     case CURLOPT_FILE:
110       curl->file=va_arg(ap,void *);
111       break;
112     case CURLOPT_ERRORBUFFER:
113       curl->errorbuffer=va_arg(ap,char *);
114       break;
115     case CURLOPT_PROXY:
116       curl->proxy=va_arg(ap,char *);
117       break;
118     case CURLOPT_POST:
119       curl->flags.post=va_arg(ap,unsigned int);
120       break;
121     case CURLOPT_POSTFIELDS:
122       curl->postfields=va_arg(ap,char *);
123       break;
124     case CURLOPT_FAILONERROR:
125       curl->flags.failonerror=va_arg(ap,unsigned int);
126       break;
127     default:
128       /* We ignore the huge majority of curl options */
129       break;
130     }
131
132   return handle_error(curl,CURLE_OK,NULL);
133 }
134
135 CURLcode
136 curl_easy_perform(CURL *curl)
137 {
138   int rc;
139   CURLcode err=CURLE_OK;
140   const char *errstr=NULL;
141
142   if(curl->flags.post)
143     {
144       rc=http_open(&curl->hd,HTTP_REQ_POST,curl->url,0,curl->proxy);
145       if(rc==0)
146         {
147           char content_len[50];
148           unsigned int post_len=strlen(curl->postfields);
149
150           iobuf_writestr(curl->hd.fp_write,
151                          "Content-Type: application/x-www-form-urlencoded\r\n");
152           sprintf(content_len,"Content-Length: %u\r\n",post_len);
153
154           iobuf_writestr(curl->hd.fp_write,content_len);
155
156           http_start_data(&curl->hd);
157           iobuf_write(curl->hd.fp_write,curl->postfields,post_len);
158           rc=http_wait_response(&curl->hd,&curl->status);
159           if(rc==0 && curl->flags.failonerror && curl->status>=300)
160             err=CURLE_HTTP_RETURNED_ERROR;
161         }
162     }
163   else
164     {
165       rc=http_open(&curl->hd,HTTP_REQ_GET,curl->url,0,curl->proxy);
166       if(rc==0)
167         {
168           rc=http_wait_response(&curl->hd,&curl->status);
169           if(rc==0)
170             {
171               if(curl->flags.failonerror && curl->status>=300)
172                 err=CURLE_HTTP_RETURNED_ERROR;
173               else
174                 {
175                   unsigned int maxlen=1024,buflen,len;
176                   byte *line=NULL;
177
178                   while((len=iobuf_read_line(curl->hd.fp_read,
179                                              &line,&buflen,&maxlen)))
180                     {
181                       maxlen=1024;
182                       size_t ret;
183
184                       ret=(curl->writer)(line,len,1,curl->file);
185                       if(ret!=len)
186                         {
187                           err=CURLE_WRITE_ERROR;
188                           break;
189                         }
190                     }
191
192                   m_free(line);
193                   http_close(&curl->hd);
194                 }
195             }
196           else
197             http_close(&curl->hd);
198         }
199     }
200
201   if(rc!=0)
202     {
203       if(rc==G10ERR_NETWORK)
204         errstr=strerror(errno);
205       else
206         errstr=g10_errstr(rc);
207
208       err=CURLE_COULDNT_CONNECT;
209     }
210
211   return handle_error(curl,err,errstr);
212 }
213
214 /* This is not the same exact set that is allowed according to
215    RFC-2396, but it is what the real curl uses. */
216 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
217                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
218                         "0123456789"
219
220 char *
221 curl_escape(char *str,int length)
222 {
223   int len,max,idx,enc_idx=0;
224   char *enc;
225
226   if(length)
227     len=length;
228   else
229     len=strlen(str);
230
231   enc=malloc(len+1);
232   if(!enc)
233     return enc;
234
235   max=len;
236
237   for(idx=0;idx<len;idx++)
238     {
239       if(enc_idx+3>max)
240         {
241           char *tmp;
242
243           max+=100;
244
245           tmp=realloc(enc,max+1);
246           if(!tmp)
247             {
248               free(enc);
249               return NULL;
250             }
251
252           enc=tmp;
253         }
254
255       if(strchr(VALID_URI_CHARS,str[idx]))
256         enc[enc_idx++]=str[idx];
257       else
258         {
259           char numbuf[5];
260           sprintf(numbuf,"%%%02X",str[idx]);
261           strcpy(&enc[enc_idx],numbuf);
262           enc_idx+=3;
263         }
264     }
265
266   enc[enc_idx]='\0';
267
268   return enc;
269 }
270
271 void
272 curl_free(char *ptr)
273 {
274   free(ptr);
275 }