a8cca98daac58b283af4c71de60760baf63912bd
[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 * Timeout for a transfer
54 */
55 long timeout;
56 };
57
58 /**
59 * Data to pass to curl callback
60 */
61 typedef struct {
62 fetcher_callback_t cb;
63 void *user;
64 } cb_data_t;
65
66 /**
67 * Curl callback function, invokes fetcher_callback_t function
68 */
69 static size_t curl_cb(void *ptr, size_t size, size_t nmemb, cb_data_t *data)
70 {
71 size_t realsize = size * nmemb;
72
73 if (data->cb(data->user, chunk_create(ptr, realsize)))
74 {
75 return realsize;
76 }
77 return 0;
78 }
79
80 METHOD(fetcher_t, fetch, status_t,
81 private_curl_fetcher_t *this, char *uri, void *userdata)
82 {
83 char error[CURL_ERROR_SIZE];
84 status_t status;
85 cb_data_t data = {
86 .cb = this->cb,
87 .user = userdata,
88 };
89
90 if (this->cb == fetcher_default_callback)
91 {
92 *(chunk_t*)userdata = chunk_empty;
93 }
94
95 if (curl_easy_setopt(this->curl, CURLOPT_URL, uri) != CURLE_OK)
96 { /* URL type not supported by curl */
97 return NOT_SUPPORTED;
98 }
99 curl_easy_setopt(this->curl, CURLOPT_ERRORBUFFER, error);
100 curl_easy_setopt(this->curl, CURLOPT_FAILONERROR, TRUE);
101 curl_easy_setopt(this->curl, CURLOPT_NOSIGNAL, TRUE);
102 if (this->timeout)
103 {
104 curl_easy_setopt(this->curl, CURLOPT_TIMEOUT, this->timeout);
105 }
106 curl_easy_setopt(this->curl, CURLOPT_CONNECTTIMEOUT, CONNECT_TIMEOUT);
107 curl_easy_setopt(this->curl, CURLOPT_WRITEFUNCTION, (void*)curl_cb);
108 curl_easy_setopt(this->curl, CURLOPT_WRITEDATA, &data);
109 if (this->headers)
110 {
111 curl_easy_setopt(this->curl, CURLOPT_HTTPHEADER, this->headers);
112 }
113
114 DBG2(DBG_LIB, " sending http request to '%s'...", uri);
115 switch (curl_easy_perform(this->curl))
116 {
117 case CURLE_UNSUPPORTED_PROTOCOL:
118 status = NOT_SUPPORTED;
119 break;
120 case CURLE_OK:
121 status = SUCCESS;
122 break;
123 default:
124 DBG1(DBG_LIB, "libcurl http request failed: %s", error);
125 status = FAILED;
126 break;
127 }
128 return status;
129 }
130
131 METHOD(fetcher_t, set_option, bool,
132 private_curl_fetcher_t *this, fetcher_option_t option, ...)
133 {
134 bool supported = TRUE;
135 va_list args;
136
137 va_start(args, option);
138 switch (option)
139 {
140 case FETCH_REQUEST_DATA:
141 {
142 chunk_t data = va_arg(args, chunk_t);
143
144 curl_easy_setopt(this->curl, CURLOPT_POSTFIELDS, (char*)data.ptr);
145 curl_easy_setopt(this->curl, CURLOPT_POSTFIELDSIZE, data.len);
146 break;
147 }
148 case FETCH_REQUEST_TYPE:
149 {
150 char header[BUF_LEN];
151 char *request_type = va_arg(args, char*);
152
153 snprintf(header, BUF_LEN, "Content-Type: %s", request_type);
154 this->headers = curl_slist_append(this->headers, header);
155 break;
156 }
157 case FETCH_REQUEST_HEADER:
158 {
159 char *header = va_arg(args, char*);
160
161 this->headers = curl_slist_append(this->headers, header);
162 break;
163 }
164 case FETCH_HTTP_VERSION_1_0:
165 {
166 curl_easy_setopt(this->curl, CURLOPT_HTTP_VERSION,
167 CURL_HTTP_VERSION_1_0);
168 break;
169 }
170 case FETCH_TIMEOUT:
171 {
172 this->timeout = va_arg(args, u_int);
173 break;
174 }
175 case FETCH_CALLBACK:
176 {
177 this->cb = va_arg(args, fetcher_callback_t);
178 break;
179 }
180 case FETCH_SOURCEIP:
181 {
182 char buf[64];
183
184 snprintf(buf, sizeof(buf), "%H", va_arg(args, host_t*));
185 supported = curl_easy_setopt(this->curl, CURLOPT_INTERFACE,
186 buf) == CURLE_OK;
187 break;
188 }
189 default:
190 supported = FALSE;
191 break;
192 }
193 va_end(args);
194 return supported;
195 }
196
197 METHOD(fetcher_t, destroy, void,
198 private_curl_fetcher_t *this)
199 {
200 curl_slist_free_all(this->headers);
201 curl_easy_cleanup(this->curl);
202 free(this);
203 }
204
205 /*
206 * Described in header.
207 */
208 curl_fetcher_t *curl_fetcher_create()
209 {
210 private_curl_fetcher_t *this;
211
212 INIT(this,
213 .public = {
214 .interface = {
215 .fetch = _fetch,
216 .set_option = _set_option,
217 .destroy = _destroy,
218 },
219 },
220 .curl = curl_easy_init(),
221 .cb = fetcher_default_callback,
222 );
223
224 if (!this->curl)
225 {
226 free(this);
227 return NULL;
228 }
229 return &this->public;
230 }