]> git.datanom.net - caldav.git/blame - src/curllib.c
First complete version
[caldav.git] / src / curllib.c
CommitLineData
e1b22e2b
MR
1/*
2 * curllib.c
3 *
4 * Copyright 2016 Michael Rasmussen <mir@datanom.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22#ifdef HAVE_CONFIG_H
23# include "config.h"
24#endif
25
26#include <string.h>
27#include <stdlib.h>
28#include <curl/curl.h>
29#include <curl/easy.h>
30#include <glib.h>
31#include "config.h"
32#include "curllib.h"
33#include "caldav.h"
34#include "xml.h"
35
36static gboolean curl_not_initialized = TRUE;
37static struct Data config;
38
39struct MemoryStruct {
40 char * memory;
41 size_t size;
42};
43
44struct Data {
45 char trace_ascii; /* 1 or 0 */
46};
47
48/**
49 * This function is burrowed from the libcurl documentation
50 * @param text
51 * @param stream
52 * @param ptr
53 * @param size
54 * @param nohex
55 */
56static void dump(const char* text, FILE* stream, char* ptr, size_t size, char nohex) {
57 size_t i;
58 size_t c;
59
60 unsigned int width=0x10;
61
62 if(nohex)
63 /* without the hex output, we can fit more on screen */
64 width = 0x40;
65 fprintf(stream, "%s, %zd bytes (0x%zx)\n", text, size, size);
66 for(i=0; i<size; i+= width) {
67 fprintf(stream, "%04zx: ", i);
68 if(!nohex) {
69 /* hex not disabled, show it */
70 for(c = 0; c < width; c++) {
71 if(i+c < size)
72 fprintf(stream, "%02x ", ptr[i+c]);
73 else
74 fputs(" ", stream);
75 }
76 }
77 for(c = 0; (c < width) && (i+c < size); c++) {
78 /* check for 0D0A; if found, skip past and start a new line of output */
79 if (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
80 i+=(c+2-width);
81 break;
82 }
83 fprintf(stream, "%c",(ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.');
84 /* check again for 0D0A, to avoid an extra \n if it's at width */
85 if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
86 i+=(c+3-width);
87 break;
88 }
89 }
90 fputc('\n', stream); /* newline */
91 }
92 fflush(stream);
93}
94
95/**
96 * This function is burrowed from the libcurl documentation
97 * @param handle
98 * @param type
99 * @param data
100 * @param size
101 * @param userp
102 * @return
103 */
104static int my_trace(CURL* handle, curl_infotype type, char* data, size_t size, void* userp) {
105 struct Data* config = (struct Data *)userp;
106 const char* text;
107 (void)handle; /* prevent compiler warning */
108
109 switch (type) {
110 case CURLINFO_TEXT:
111 fprintf(stderr, "== Info: %s", data);
112 default: /* in case a new one is introduced to shock us */
113 return 0;
114 case CURLINFO_HEADER_OUT:
115 text = "=> Send header";
116 break;
117 case CURLINFO_DATA_OUT:
118 text = "=> Send data";
119 break;
120 case CURLINFO_SSL_DATA_OUT:
121 text = "=> Send SSL data";
122 break;
123 case CURLINFO_HEADER_IN:
124 text = "<= Recv header";
125 break;
126 case CURLINFO_DATA_IN:
127 text = "<= Recv data";
128 break;
129 case CURLINFO_SSL_DATA_IN:
130 text = "<= Recv SSL data";
131 break;
132 }
133 dump(text, stderr, data, size, config->trace_ascii);
134 return 0;
135}
136
137/**
138 * This function is burrowed from the libcurl documentation
139 * @param ptr
140 * @param size
141 * @return void* to memory region
142 */
143static void* myrealloc(void* ptr, size_t size) {
144/* There might be a realloc() out there that doesn't like reallocing
145 * NULL pointers, so we take care of it here
146 * */
147 if(ptr)
148 return realloc(ptr, size);
149 else
150 return malloc(size);
151}
152
153/**
154 * This function is burrowed from the libcurl documentation
155 * @param ptr
156 * @param size
157 * @param nmemb
158 * @param data
159 * @return number of written bytes
160 */
161static size_t WriteMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data) {
162 size_t realsize = size * nmemb;
163 struct MemoryStruct* mem = (struct MemoryStruct *)data;
164 mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1);
165
166 if (mem->memory) {
167 memcpy(&(mem->memory[mem->size]), ptr, realsize);
168 mem->size += realsize;
169 mem->memory[mem->size] = 0;
170 }
171 return realsize;
172}
173
174static struct curl_slist* gslist_to_slist(GSList* list) {
175 struct curl_slist* s = NULL;
176
177 for (GSList* elem = list; elem; elem = g_slist_next(elem)) {
178 s = curl_slist_append(s, elem->data);
179 }
180
181 return s;
182}
183
184static void curl_set_request(CURL* curl, RequestType type) {
185 gchar* req = NULL;
186
187 switch (type) {
188 case DELETE: req = g_strdup("DELETE"); break;
189 case PROPFIND: req = g_strdup("PROPFIND"); break;
190 case PUT: req = g_strdup("PUT"); break;
191 case REPORT: req = g_strdup("REPORT"); break;
192 case GET: req = g_strdup("GET"); break;
193 case OPTIONS: req = g_strdup("OPTIONS"); break;
194 case LOCK: req = g_strdup("LOCK"); break;
195 case UNLOCK: req = g_strdup("UNLOCK"); break;
196 }
197 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, req);
198 g_free(req);
199}
200
201static CURL* get_curl(Request* request, gboolean debug) {
202 CURL* curl;
203 gchar* userpwd;
204
205 curl = curl_easy_init();
206 if (curl) {
207 if (request->password) {
208 userpwd = g_strconcat(request->username, ":", request->password, NULL);
209 curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd);
210 } else {
211 userpwd = g_strconcat(request->username, NULL);
212 curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd);
213 }
214 g_free(userpwd);
215 curl_set_request(curl, request->request);
216 curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
217 curl_easy_setopt(curl, CURLOPT_USERAGENT, PACKAGE_STRING);
218 curl_easy_setopt(curl, CURLOPT_URL, request->url);
219 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
220 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteMemoryCallback);
221 if (debug) {
222 config.trace_ascii = 1;
223 curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);
224 curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &config);
225 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
226 }
227 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
228 curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1);
229 curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
230 if (request->request != DELETE && request->request != GET && request->request != OPTIONS) {
231 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request->data);
232 curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE,
233 (request->data) ? strlen(request->data) : 0);
234 }
235 }
236
237 return curl;
238}
239
240static GSList* memory_struct_to_gslist(struct MemoryStruct buffer) {
241 GSList* list = NULL;
242 gchar **elems, **e;
243
244 if (buffer.size > 0) {
245 elems = g_strsplit(buffer.memory, "\n", 0);
246 for (e = elems; *e != NULL; ++e) {
247 if (g_strcmp0(*e, "\r") != 0 || g_strcmp0(*e, "") != 0)
248 list = g_slist_prepend(list, g_strdup(*e));
249 }
250 g_strfreev(elems);
251 }
252
253 return list;
254}
255
256Request* request_new() {
257 Request* r = g_new0(Request, 1);
258
259 return r;
260}
261
262
263void request_free(Request* request) {
264 if (request) {
265 if (request->headers) {
266 slist_free_gchar(request->headers);
267 request->headers = NULL;
268 }
269 if (request->data) {
270 g_free(request->data);
271 request->data = NULL;
272 }
273 if (request->username) {
274 g_free(request->username);
275 request->username = NULL;
276 }
277 if (request->password) {
278 g_free(request->password);
279 request->password = NULL;
280 }
281 if (request->url) {
282 g_free(request->url);
283 request->url = NULL;
284 }
285 g_free(request);
286 request = NULL;
287 }
288}
289
290Response* request_send(Request* request, gboolean debug) {
291 g_return_val_if_fail(request != NULL, NULL);
292
293 Response* r = g_new0(Response, 1);
294 CURLcode curlres = 0;
295 struct MemoryStruct data;
296 struct MemoryStruct headers;
297 char error_buf[CURL_ERROR_SIZE];
298 struct curl_slist* http_headers;
299
300 data.memory = NULL;
301 data.size = 0;
302 headers.memory = NULL;
303 headers.size = 0;
304
305 if (curl_not_initialized) {
306 curl_global_init(CURL_GLOBAL_ALL);
307 curl_not_initialized = FALSE;
308 }
309
310 CURL* curl = get_curl(request, debug);
311 if (curl) {
312 http_headers = gslist_to_slist(request->headers);
313 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_headers);
314 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&data);
315 curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers);
316 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf);
317 curlres = curl_easy_perform(curl);
318 if (curlres != CURLE_OK) {
319 request->data = g_strdup(error_buf);
320 }
321 else {
322 curlres = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &r->status);
323 r->headers = memory_struct_to_gslist(headers);
324 r->data = g_strndup(data.memory, data.size);
325 if (r->status == 207 && (request->request == DELETE || request->request == PUT)) {
326 /* Error code is returnded inside XML response */
327 GSList* response = find_element("//d:status", r->data, "d", "DAV:");
328 if (response) {
329 if (g_strstr_len((gchar *) response->data, -1, "423")) {
330 r->status = 423;
331 }
332 }
333 slist_free_gchar(response);
334 }
335 }
336 if (data.memory)
337 free(data.memory);
338 if (headers.memory)
339 free(headers.memory);
340 curl_slist_free_all(http_headers);
341 curl_easy_cleanup(curl);
342 }
343
344 return r;
345}
346
347void response_free(Response* response) {
348 if (response) {
349 if (response->headers) {
350 slist_free_gchar(response->headers);
351 response->headers = NULL;
352 }
353 if (response->data) {
354 g_free(response->data);
355 response->data = NULL;
356 }
357 g_free(response);
358 response = NULL;
359 }
360}
This page took 0.071902 seconds and 5 git commands to generate.