curl: Log error code too
[strongswan.git] / src / libstrongswan / plugins / curl / curl_fetcher.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Copyright (C) 2007 Andreas Steffen
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include <curl/curl.h>
18
19 #include <library.h>
20 #include <utils/debug.h>
21
22 #include "curl_fetcher.h"
23
24 #define CONNECT_TIMEOUT 10
25
26 typedef struct private_curl_fetcher_t private_curl_fetcher_t;
27
28 /**
29 * private data of a curl_fetcher_t object.
30 */
31 struct private_curl_fetcher_t {
32 /**
33 * Public data
34 */
35 curl_fetcher_t public;
36
37 /**
38 * CURL handle
39 */
40 CURL* curl;
41
42 /**
43 * Optional HTTP headers
44 */
45 struct curl_slist *headers;
46
47 /**
48 * Callback function
49 */
50 fetcher_callback_t cb;
51
52 /**
53 * Variable that receives the response code
54 */
55 u_int *result;
56
57 /**
58 * Timeout for a transfer
59 */
60 long timeout;
61 };
62
63 /**
64 * Data to pass to curl callback
65 */
66 typedef struct {
67 fetcher_callback_t cb;
68 void *user;
69 } cb_data_t;
70
71 /**
72 * Curl callback function, invokes fetcher_callback_t function
73 */
74 static size_t curl_cb(void *ptr, size_t size, size_t nmemb, cb_data_t *data)
75 {
76 size_t realsize = size * nmemb;
77
78 if (data->cb(data->user, chunk_create(ptr, realsize)))
79 {
80 return realsize;
81 }
82 return 0;
83 }
84
85 METHOD(fetcher_t, fetch, status_t,
86 private_curl_fetcher_t *this, char *uri, void *userdata)
87 {
88 char error[CURL_ERROR_SIZE], *enc_uri;
89 CURLcode curl_status;
90 status_t status;
91 long result = 0;
92 cb_data_t data = {
93 .cb = this->cb,
94 .user = userdata,
95 };
96
97 if (this->cb == fetcher_default_callback)
98 {
99 *(chunk_t*)userdata = chunk_empty;
100 }
101
102 /* the URI has to be URL-encoded, we only replace spaces as replacing other
103 * characters (e.g. '/' or ':') would render the URI invalid */
104 enc_uri = strreplace(uri, " ", "%20");
105
106 if (curl_easy_setopt(this->curl, CURLOPT_URL, enc_uri) != CURLE_OK)
107 { /* URL type not supported by curl */
108 status = NOT_SUPPORTED;
109 goto out;
110 }
111 curl_easy_setopt(this->curl, CURLOPT_ERRORBUFFER, error);
112 curl_easy_setopt(this->curl, CURLOPT_FAILONERROR, FALSE);
113 curl_easy_setopt(this->curl, CURLOPT_NOSIGNAL, TRUE);
114 if (this->timeout)
115 {
116 curl_easy_setopt(this->curl, CURLOPT_TIMEOUT, this->timeout);
117 }
118 curl_easy_setopt(this->curl, CURLOPT_CONNECTTIMEOUT, CONNECT_TIMEOUT);
119 curl_easy_setopt(this->curl, CURLOPT_WRITEFUNCTION, (void*)curl_cb);
120 curl_easy_setopt(this->curl, CURLOPT_WRITEDATA, &data);
121 if (this->headers)
122 {
123 curl_easy_setopt(this->curl, CURLOPT_HTTPHEADER, this->headers);
124 }
125
126 DBG2(DBG_LIB, " sending http request to '%s'...", uri);
127 curl_status = curl_easy_perform(this->curl);
128 switch (curl_status)
129 {
130 case CURLE_UNSUPPORTED_PROTOCOL:
131 status = NOT_SUPPORTED;
132 break;
133 case CURLE_OK:
134 curl_easy_getinfo(this->curl, CURLINFO_RESPONSE_CODE,
135 &result);
136 if (this->result)
137 {
138 *this->result = result;
139 }
140 status = (result >= 200 && result < 300) ? SUCCESS : FAILED;
141 break;
142 default:
143 DBG1(DBG_LIB, "libcurl http request failed [%d]: %s", curl_status,
144 error);
145 status = FAILED;
146 break;
147 }
148
149 out:
150 if (enc_uri != uri)
151 {
152 free(enc_uri);
153 }
154 return status;
155 }
156
157 METHOD(fetcher_t, set_option, bool,
158 private_curl_fetcher_t *this, fetcher_option_t option, ...)
159 {
160 bool supported = TRUE;
161 va_list args;
162
163 va_start(args, option);
164 switch (option)
165 {
166 case FETCH_REQUEST_DATA:
167 {
168 chunk_t data = va_arg(args, chunk_t);
169
170 curl_easy_setopt(this->curl, CURLOPT_POSTFIELDS, (char*)data.ptr);
171 curl_easy_setopt(this->curl, CURLOPT_POSTFIELDSIZE, data.len);
172 break;
173 }
174 case FETCH_REQUEST_TYPE:
175 {
176 char header[BUF_LEN];
177 char *request_type = va_arg(args, char*);
178
179 snprintf(header, BUF_LEN, "Content-Type: %s", request_type);
180 this->headers = curl_slist_append(this->headers, header);
181 break;
182 }
183 case FETCH_REQUEST_HEADER:
184 {
185 char *header = va_arg(args, char*);
186
187 this->headers = curl_slist_append(this->headers, header);
188 break;
189 }
190 case FETCH_HTTP_VERSION_1_0:
191 {
192 curl_easy_setopt(this->curl, CURLOPT_HTTP_VERSION,
193 CURL_HTTP_VERSION_1_0);
194 break;
195 }
196 case FETCH_TIMEOUT:
197 {
198 this->timeout = va_arg(args, u_int);
199 break;
200 }
201 case FETCH_CALLBACK:
202 {
203 this->cb = va_arg(args, fetcher_callback_t);
204 break;
205 }
206 case FETCH_RESPONSE_CODE:
207 {
208 this->result = va_arg(args, u_int*);
209 break;
210 }
211 case FETCH_SOURCEIP:
212 {
213 char buf[64];
214
215 snprintf(buf, sizeof(buf), "%H", va_arg(args, host_t*));
216 supported = curl_easy_setopt(this->curl, CURLOPT_INTERFACE,
217 buf) == CURLE_OK;
218 break;
219 }
220 default:
221 supported = FALSE;
222 break;
223 }
224 va_end(args);
225 return supported;
226 }
227
228 METHOD(fetcher_t, destroy, void,
229 private_curl_fetcher_t *this)
230 {
231 curl_slist_free_all(this->headers);
232 curl_easy_cleanup(this->curl);
233 free(this);
234 }
235
236 /*
237 * Described in header.
238 */
239 curl_fetcher_t *curl_fetcher_create()
240 {
241 private_curl_fetcher_t *this;
242
243 INIT(this,
244 .public = {
245 .interface = {
246 .fetch = _fetch,
247 .set_option = _set_option,
248 .destroy = _destroy,
249 },
250 },
251 .curl = curl_easy_init(),
252 .cb = fetcher_default_callback,
253 );
254
255 if (!this->curl)
256 {
257 free(this);
258 return NULL;
259 }
260 return &this->public;
261 }