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