4 * Copyright 2016 Michael Rasmussen <mir@datanom.net>
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.
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.
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,
28 #include <curl/curl.h>
29 #include <curl/easy.h>
36 static gboolean curl_not_initialized
= TRUE
;
37 static struct Data config
;
45 char trace_ascii
; /* 1 or 0 */
49 * This function is burrowed from the libcurl documentation
56 static void dump(const char* text
, FILE* stream
, char* ptr
, size_t size
, char nohex
) {
60 unsigned int width
=0x10;
63 /* without the hex output, we can fit more on screen */
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
);
69 /* hex not disabled, show it */
70 for(c
= 0; c
< width
; c
++) {
72 fprintf(stream
, "%02x ", ptr
[i
+c
]);
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) {
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) {
90 fputc('\n', stream
); /* newline */
96 * This function is burrowed from the libcurl documentation
104 static int my_trace(CURL
* handle
, curl_infotype type
, char* data
, size_t size
, void* userp
) {
105 struct Data
* config
= (struct Data
*)userp
;
107 (void)handle
; /* prevent compiler warning */
111 fprintf(stderr
, "== Info: %s", data
);
112 default: /* in case a new one is introduced to shock us */
114 case CURLINFO_HEADER_OUT
:
115 text
= "=> Send header";
117 case CURLINFO_DATA_OUT
:
118 text
= "=> Send data";
120 case CURLINFO_SSL_DATA_OUT
:
121 text
= "=> Send SSL data";
123 case CURLINFO_HEADER_IN
:
124 text
= "<= Recv header";
126 case CURLINFO_DATA_IN
:
127 text
= "<= Recv data";
129 case CURLINFO_SSL_DATA_IN
:
130 text
= "<= Recv SSL data";
133 dump(text
, stderr
, data
, size
, config
->trace_ascii
);
138 * This function is burrowed from the libcurl documentation
141 * @return void* to memory region
143 static 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
148 return realloc(ptr
, size
);
154 * This function is burrowed from the libcurl documentation
159 * @return number of written bytes
161 static 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);
167 memcpy(&(mem
->memory
[mem
->size
]), ptr
, realsize
);
168 mem
->size
+= realsize
;
169 mem
->memory
[mem
->size
] = 0;
174 static struct curl_slist
* gslist_to_slist(GSList
* list
) {
175 struct curl_slist
* s
= NULL
;
177 for (GSList
* elem
= list
; elem
; elem
= g_slist_next(elem
)) {
178 s
= curl_slist_append(s
, elem
->data
);
184 static void curl_set_request(CURL
* curl
, RequestType 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;
197 curl_easy_setopt(curl
, CURLOPT_CUSTOMREQUEST
, req
);
201 static CURL
* get_curl(Request
* request
, gboolean debug
) {
205 curl
= curl_easy_init();
207 if (request
->password
) {
208 userpwd
= g_strconcat(request
->username
, ":", request
->password
, NULL
);
209 curl_easy_setopt(curl
, CURLOPT_USERPWD
, userpwd
);
211 userpwd
= g_strconcat(request
->username
, NULL
);
212 curl_easy_setopt(curl
, CURLOPT_USERPWD
, 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
);
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);
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);
240 static GSList
* memory_struct_to_gslist(struct MemoryStruct buffer
) {
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
));
256 Request
* request_new() {
257 Request
* r
= g_new0(Request
, 1);
263 void request_free(Request
* request
) {
265 if (request
->headers
) {
266 slist_free_gchar(request
->headers
);
267 request
->headers
= NULL
;
270 g_free(request
->data
);
271 request
->data
= NULL
;
273 if (request
->username
) {
274 g_free(request
->username
);
275 request
->username
= NULL
;
277 if (request
->password
) {
278 g_free(request
->password
);
279 request
->password
= NULL
;
282 g_free(request
->url
);
290 Response
* request_send(Request
* request
, gboolean debug
) {
291 g_return_val_if_fail(request
!= NULL
, NULL
);
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
;
302 headers
.memory
= NULL
;
305 if (curl_not_initialized
) {
306 curl_global_init(CURL_GLOBAL_ALL
);
307 curl_not_initialized
= FALSE
;
310 CURL
* curl
= get_curl(request
, debug
);
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
);
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:");
329 if (g_strstr_len((gchar
*) response
->data
, -1, "423")) {
333 slist_free_gchar(response
);
339 free(headers
.memory
);
340 curl_slist_free_all(http_headers
);
341 curl_easy_cleanup(curl
);
347 void response_free(Response
* response
) {
349 if (response
->headers
) {
350 slist_free_gchar(response
->headers
);
351 response
->headers
= NULL
;
353 if (response
->data
) {
354 g_free(response
->data
);
355 response
->data
= NULL
;