* curl-shim.h, curl-shim.c (curl_escape, curl_free): Emulate
[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 handle_error(CURL *curl,CURLcode err,const char *str)
34 {
35   if(curl->errorbuffer)
36     {
37       switch(err)
38         {
39         case CURLE_OK:
40           strcpy(curl->errorbuffer,"okay");
41           break;
42
43         case CURLE_COULDNT_CONNECT:
44           strcpy(curl->errorbuffer,"couldn't connect");
45           break;
46
47         case CURLE_WRITE_ERROR:
48           strcpy(curl->errorbuffer,"write error");
49           break;
50
51         default:
52           strcpy(curl->errorbuffer,"generic error");
53           break;
54         }
55
56       if(str && (strlen(curl->errorbuffer)+2+strlen(str)+1)<=CURL_ERROR_SIZE)
57         {
58           strcat(curl->errorbuffer,": ");
59           strcat(curl->errorbuffer,str);
60         }
61     }
62
63   return err;
64 }
65
66 CURLcode curl_global_init(long flags)
67 {
68   return CURLE_OK;
69 }
70
71 void curl_global_cleanup(void) {}
72
73 CURL *curl_easy_init(void)
74 {
75   return calloc(1,sizeof(CURL));
76 }
77
78 void curl_easy_cleanup(CURL *curl)
79 {
80   free(curl);
81 }
82
83 CURLcode curl_easy_setopt(CURL *curl,CURLoption option,...)
84 {
85   va_list ap;
86
87   va_start(ap,option);
88
89   switch(option)
90     {
91     case CURLOPT_URL:
92       curl->url=va_arg(ap,char *);
93       break;
94     case CURLOPT_WRITEFUNCTION:
95       curl->writer=va_arg(ap,write_func);
96       break;
97     case CURLOPT_FILE:
98       curl->file=va_arg(ap,void *);
99       break;
100     case CURLOPT_ERRORBUFFER:
101       curl->errorbuffer=va_arg(ap,char *);
102       break;
103     case CURLOPT_PROXY:
104       curl->proxy=va_arg(ap,char *);
105       break;
106     default:
107       /* We ignore the huge majority of curl options */
108       break;
109     }
110
111   return handle_error(curl,CURLE_OK,NULL);
112 }
113
114 CURLcode curl_easy_perform(CURL *curl)
115 {
116   int rc;
117   CURLcode err=CURLE_OK;
118   const char *errstr=NULL;
119
120   rc=http_open_document(&curl->hd,curl->url,0,curl->proxy);
121   if(rc!=0)
122     {
123       if(rc==G10ERR_NETWORK)
124         errstr=strerror(errno);
125       else
126         errstr=g10_errstr(rc);
127
128       err=CURLE_COULDNT_CONNECT;
129     }
130   else
131     {
132       unsigned int maxlen=1024,buflen,len;
133       byte *line=NULL;
134
135       while((len=iobuf_read_line(curl->hd.fp_read,&line,&buflen,&maxlen)))
136         {
137           maxlen=1024;
138           size_t ret;
139
140           ret=(curl->writer)(line,len,1,curl->file);
141           if(ret!=len)
142             {
143               err=CURLE_WRITE_ERROR;
144               break;
145             }
146         }
147
148       m_free(line);
149       http_close(&curl->hd);
150     }
151
152   return handle_error(curl,err,errstr);
153 }
154
155 /* This is not the same exact set that is allowed according to
156    RFC-2396, but it is what the real curl uses. */
157 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
158                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
159                         "0123456789"
160
161 char *curl_escape(char *str,int length)
162 {
163   int len,max,idx,enc_idx=0;
164   char *enc;
165
166   if(length)
167     len=length;
168   else
169     len=strlen(str);
170
171   enc=malloc(len+1);
172   if(!enc)
173     return enc;
174
175   max=len;
176
177   for(idx=0;idx<len;idx++)
178     {
179       if(enc_idx+3>max)
180         {
181           char *tmp;
182
183           max+=100;
184
185           tmp=realloc(enc,max+1);
186           if(!tmp)
187             {
188               free(enc);
189               return NULL;
190             }
191
192           enc=tmp;
193         }
194
195       if(strchr(VALID_URI_CHARS,str[idx]))
196         enc[enc_idx++]=str[idx];
197       else
198         {
199           char numbuf[5];
200           sprintf(numbuf,"%%%02X",str[idx]);
201           strcpy(&enc[enc_idx],numbuf);
202           enc_idx+=3;
203         }
204     }
205
206   enc[enc_idx]='\0';
207
208   return enc;
209 }
210
211 void curl_free(char *ptr)
212 {
213   free(ptr);
214 }