]> git.datanom.net - caldav.git/blobdiff - src/curllib.c
First complete version
[caldav.git] / src / curllib.c
diff --git a/src/curllib.c b/src/curllib.c
new file mode 100644 (file)
index 0000000..985bf53
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * curllib.c
+ *
+ * Copyright 2016 Michael Rasmussen <mir@datanom.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <curl/curl.h>
+#include <curl/easy.h>
+#include <glib.h>
+#include "config.h"
+#include "curllib.h"
+#include "caldav.h"
+#include "xml.h"
+
+static gboolean curl_not_initialized = TRUE;
+static struct Data config;
+
+struct MemoryStruct {
+       char * memory;
+       size_t size;
+};
+
+struct Data {
+       char trace_ascii; /* 1 or 0 */
+};
+
+/**
+ * This function is burrowed from the libcurl documentation
+ * @param text
+ * @param stream
+ * @param ptr
+ * @param size
+ * @param nohex
+ */
+static void dump(const char* text, FILE* stream, char* ptr, size_t size, char nohex) {
+       size_t i;
+       size_t c;
+
+       unsigned int width=0x10;
+
+       if(nohex)
+               /* without the hex output, we can fit more on screen */
+               width = 0x40;
+       fprintf(stream, "%s, %zd bytes (0x%zx)\n", text, size, size);
+       for(i=0; i<size; i+= width) {
+               fprintf(stream, "%04zx: ", i);
+               if(!nohex) {
+                       /* hex not disabled, show it */
+                       for(c = 0; c < width; c++) {
+                               if(i+c < size)
+                                       fprintf(stream, "%02x ", ptr[i+c]);
+                               else
+                                       fputs("   ", stream);
+                       }
+               }
+               for(c = 0; (c < width) && (i+c < size); c++) {
+               /* check for 0D0A; if found, skip past and start a new line of output */
+                       if (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
+                               i+=(c+2-width);
+                               break;
+                       }
+                       fprintf(stream, "%c",(ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.');
+                       /* check again for 0D0A, to avoid an extra \n if it's at width */
+                       if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
+                               i+=(c+3-width);
+                               break;
+                       }
+               }
+               fputc('\n', stream); /* newline */
+       }
+       fflush(stream);
+}
+
+/**
+ * This function is burrowed from the libcurl documentation
+ * @param handle
+ * @param type
+ * @param data
+ * @param size
+ * @param userp
+ * @return
+ */
+static int my_trace(CURL* handle, curl_infotype type, char* data, size_t size, void* userp) {
+       struct Data* config = (struct Data *)userp;
+       const char* text;
+       (void)handle; /* prevent compiler warning */
+
+       switch (type) {
+               case CURLINFO_TEXT:
+                       fprintf(stderr, "== Info: %s", data);
+               default: /* in case a new one is introduced to shock us */
+                       return 0;
+               case CURLINFO_HEADER_OUT:
+                       text = "=> Send header";
+                       break;
+               case CURLINFO_DATA_OUT:
+                       text = "=> Send data";
+                       break;
+               case CURLINFO_SSL_DATA_OUT:
+                       text = "=> Send SSL data";
+                       break;
+               case CURLINFO_HEADER_IN:
+                       text = "<= Recv header";
+                       break;
+               case CURLINFO_DATA_IN:
+                       text = "<= Recv data";
+                       break;
+               case CURLINFO_SSL_DATA_IN:
+                       text = "<= Recv SSL data";
+                       break;
+       }
+       dump(text, stderr, data, size, config->trace_ascii);
+       return 0;
+}
+
+/**
+ * This function is burrowed from the libcurl documentation
+ * @param ptr
+ * @param size
+ * @return void* to memory region
+ */
+static void* myrealloc(void* ptr, size_t size) {
+/* There might be a realloc() out there that doesn't like reallocing
+ * NULL pointers, so we take care of it here
+ * */
+       if(ptr)
+               return realloc(ptr, size);
+       else
+               return malloc(size);
+}
+
+/**
+ * This function is burrowed from the libcurl documentation
+ * @param ptr
+ * @param size
+ * @param nmemb
+ * @param data
+ * @return number of written bytes
+ */
+static size_t WriteMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data) {
+       size_t realsize = size * nmemb;
+       struct MemoryStruct* mem = (struct MemoryStruct *)data;
+       mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1);
+
+       if (mem->memory) {
+               memcpy(&(mem->memory[mem->size]), ptr, realsize);
+               mem->size += realsize;
+               mem->memory[mem->size] = 0;
+       }
+       return realsize;
+}
+
+static struct curl_slist* gslist_to_slist(GSList* list) {
+       struct curl_slist* s = NULL;
+
+       for (GSList* elem = list; elem; elem = g_slist_next(elem)) {
+               s = curl_slist_append(s, elem->data);
+       }
+
+       return s;
+}
+
+static void curl_set_request(CURL* curl, RequestType type) {
+       gchar* req = NULL;
+
+       switch (type) {
+               case DELETE: req = g_strdup("DELETE"); break;
+               case PROPFIND: req = g_strdup("PROPFIND"); break;
+               case PUT: req = g_strdup("PUT"); break;
+               case REPORT: req = g_strdup("REPORT"); break;
+               case GET: req = g_strdup("GET"); break;
+               case OPTIONS: req = g_strdup("OPTIONS"); break;
+               case LOCK: req = g_strdup("LOCK"); break;
+               case UNLOCK: req = g_strdup("UNLOCK"); break;
+       }
+       curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, req);
+       g_free(req);
+}
+
+static CURL* get_curl(Request* request, gboolean debug) {
+       CURL* curl;
+       gchar* userpwd;
+
+       curl = curl_easy_init();
+       if (curl) {
+               if (request->password) {
+                       userpwd = g_strconcat(request->username, ":", request->password, NULL);
+                       curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd);
+               } else {
+                       userpwd = g_strconcat(request->username, NULL);
+                       curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd);
+               }
+               g_free(userpwd);
+               curl_set_request(curl, request->request);
+               curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+               curl_easy_setopt(curl, CURLOPT_USERAGENT, PACKAGE_STRING);
+               curl_easy_setopt(curl, CURLOPT_URL, request->url);
+               curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+               curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteMemoryCallback);
+               if (debug) {
+                       config.trace_ascii = 1;
+                       curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);
+                       curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &config);
+                       curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+               }
+               curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+               curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1);
+               curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
+               if (request->request != DELETE && request->request != GET && request->request != OPTIONS) {
+                       curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request->data);
+                       curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE,
+                               (request->data) ? strlen(request->data) : 0);
+               }
+       }
+
+       return curl;
+}
+
+static GSList* memory_struct_to_gslist(struct MemoryStruct buffer) {
+       GSList* list = NULL;
+       gchar **elems, **e;
+
+       if (buffer.size > 0) {
+               elems = g_strsplit(buffer.memory, "\n", 0);
+               for (e = elems; *e != NULL; ++e) {
+                       if (g_strcmp0(*e, "\r") != 0 || g_strcmp0(*e, "") != 0)
+                               list = g_slist_prepend(list, g_strdup(*e));
+               }
+               g_strfreev(elems);
+       }
+
+       return list;
+}
+
+Request* request_new() {
+       Request* r = g_new0(Request, 1);
+
+       return r;
+}
+
+
+void request_free(Request* request) {
+       if (request) {
+               if (request->headers) {
+                       slist_free_gchar(request->headers);
+                       request->headers = NULL;
+               }
+               if (request->data) {
+                       g_free(request->data);
+                       request->data = NULL;
+               }
+               if (request->username) {
+                       g_free(request->username);
+                       request->username = NULL;
+               }
+               if (request->password) {
+                       g_free(request->password);
+                       request->password = NULL;
+               }
+               if (request->url) {
+                       g_free(request->url);
+                       request->url = NULL;
+               }
+               g_free(request);
+               request = NULL;
+       }
+}
+
+Response* request_send(Request* request, gboolean debug) {
+       g_return_val_if_fail(request != NULL, NULL);
+
+       Response* r = g_new0(Response, 1);
+       CURLcode curlres = 0;
+       struct MemoryStruct data;
+       struct MemoryStruct headers;
+       char error_buf[CURL_ERROR_SIZE];
+       struct curl_slist* http_headers;
+
+       data.memory = NULL;
+       data.size = 0;
+       headers.memory = NULL;
+       headers.size = 0;
+
+       if (curl_not_initialized) {
+               curl_global_init(CURL_GLOBAL_ALL);
+               curl_not_initialized = FALSE;
+       }
+
+       CURL* curl = get_curl(request, debug);
+       if (curl) {
+               http_headers = gslist_to_slist(request->headers);
+               curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_headers);
+               curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&data);
+               curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&headers);
+               curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *) &error_buf);
+               curlres = curl_easy_perform(curl);
+               if (curlres != CURLE_OK) {
+                       request->data = g_strdup(error_buf);
+               }
+               else {
+                       curlres = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &r->status);
+                       r->headers = memory_struct_to_gslist(headers);
+                       r->data = g_strndup(data.memory, data.size);
+                       if (r->status == 207 && (request->request == DELETE || request->request == PUT)) {
+                               /* Error code is returnded inside XML response */
+                               GSList* response = find_element("//d:status", r->data, "d", "DAV:");
+                               if (response) {
+                                       if (g_strstr_len((gchar *) response->data, -1, "423")) {
+                                               r->status = 423;
+                                       }
+                               }
+                               slist_free_gchar(response);
+                       }
+               }
+               if (data.memory)
+                       free(data.memory);
+               if (headers.memory)
+                       free(headers.memory);
+               curl_slist_free_all(http_headers);
+               curl_easy_cleanup(curl);
+       }
+
+       return r;
+}
+
+void response_free(Response* response) {
+       if (response) {
+               if (response->headers) {
+                       slist_free_gchar(response->headers);
+                       response->headers = NULL;
+               }
+               if (response->data) {
+                       g_free(response->data);
+                       response->data = NULL;
+               }
+               g_free(response);
+               response = NULL;
+       }
+}
This page took 0.041895 seconds and 5 git commands to generate.