* curl-shim.h, curl-shim.c (curl_easy_setopt, curl_easy_perform): Add
[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_USERPWD:
108       curl->auth=va_arg(ap,char *);
109       break;
110     case CURLOPT_WRITEFUNCTION:
111       curl->writer=va_arg(ap,write_func);
112       break;
113     case CURLOPT_FILE:
114       curl->file=va_arg(ap,void *);
115       break;
116     case CURLOPT_ERRORBUFFER:
117       curl->errorbuffer=va_arg(ap,char *);
118       break;
119     case CURLOPT_PROXY:
120       curl->proxy=va_arg(ap,char *);
121       break;
122     case CURLOPT_POST:
123       curl->flags.post=va_arg(ap,unsigned int);
124       break;
125     case CURLOPT_POSTFIELDS:
126       curl->postfields=va_arg(ap,char *);
127       break;
128     case CURLOPT_FAILONERROR:
129       curl->flags.failonerror=va_arg(ap,unsigned int);
130       break;
131     default:
132       /* We ignore the huge majority of curl options */
133       break;
134     }
135
136   return handle_error(curl,CURLE_OK,NULL);
137 }
138
139 CURLcode
140 curl_easy_perform(CURL *curl)
141 {
142   int rc;
143   CURLcode err=CURLE_OK;
144   const char *errstr=NULL;
145
146   if(curl->flags.post)
147     {
148       rc=http_open(&curl->hd,HTTP_REQ_POST,curl->url,curl->auth,0,curl->proxy);
149       if(rc==0)
150         {
151           char content_len[50];
152           unsigned int post_len=strlen(curl->postfields);
153
154           iobuf_writestr(curl->hd.fp_write,
155                          "Content-Type: application/x-www-form-urlencoded\r\n");
156           sprintf(content_len,"Content-Length: %u\r\n",post_len);
157
158           iobuf_writestr(curl->hd.fp_write,content_len);
159
160           http_start_data(&curl->hd);
161           iobuf_write(curl->hd.fp_write,curl->postfields,post_len);
162           rc=http_wait_response(&curl->hd,&curl->status);
163           if(rc==0 && curl->flags.failonerror && curl->status>=300)
164             err=CURLE_HTTP_RETURNED_ERROR;
165         }
166     }
167   else
168     {
169       rc=http_open(&curl->hd,HTTP_REQ_GET,curl->url,curl->auth,0,curl->proxy);
170       if(rc==0)
171         {
172           rc=http_wait_response(&curl->hd,&curl->status);
173           if(rc==0)
174             {
175               if(curl->flags.failonerror && curl->status>=300)
176                 err=CURLE_HTTP_RETURNED_ERROR;
177               else
178                 {
179                   unsigned int maxlen=1024,buflen,len;
180                   byte *line=NULL;
181
182                   while((len=iobuf_read_line(curl->hd.fp_read,
183                                              &line,&buflen,&maxlen)))
184                     {
185                       maxlen=1024;
186                       size_t ret;
187
188                       ret=(curl->writer)(line,len,1,curl->file);
189                       if(ret!=len)
190                         {
191                           err=CURLE_WRITE_ERROR;
192                           break;
193                         }
194                     }
195
196                   m_free(line);
197                   http_close(&curl->hd);
198                 }
199             }
200           else
201             http_close(&curl->hd);
202         }
203     }
204
205   if(rc!=0)
206     {
207       if(rc==G10ERR_NETWORK)
208         errstr=strerror(errno);
209       else
210         errstr=g10_errstr(rc);
211
212       err=CURLE_COULDNT_CONNECT;
213     }
214
215   return handle_error(curl,err,errstr);
216 }
217
218 /* This is not the same exact set that is allowed according to
219    RFC-2396, but it is what the real curl uses. */
220 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
221                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
222                         "0123456789"
223
224 char *
225 curl_escape(char *str,int length)
226 {
227   int len,max,idx,enc_idx=0;
228   char *enc;
229
230   if(length)
231     len=length;
232   else
233     len=strlen(str);
234
235   enc=malloc(len+1);
236   if(!enc)
237     return enc;
238
239   max=len;
240
241   for(idx=0;idx<len;idx++)
242     {
243       if(enc_idx+3>max)
244         {
245           char *tmp;
246
247           max+=100;
248
249           tmp=realloc(enc,max+1);
250           if(!tmp)
251             {
252               free(enc);
253               return NULL;
254             }
255
256           enc=tmp;
257         }
258
259       if(strchr(VALID_URI_CHARS,str[idx]))
260         enc[enc_idx++]=str[idx];
261       else
262         {
263           char numbuf[5];
264           sprintf(numbuf,"%%%02X",str[idx]);
265           strcpy(&enc[enc_idx],numbuf);
266           enc_idx+=3;
267         }
268     }
269
270   enc[enc_idx]='\0';
271
272   return enc;
273 }
274
275 void
276 curl_free(char *ptr)
277 {
278   free(ptr);
279 }