From 12cde42c929b63a1ef1b2ad7f3482336419980b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=A6=8C=EA=A6=AB=EA=A6=B6=EA=A6=8F=EA=A7=80=EA=A6=A6?= =?UTF-8?q?=EA=A6=BF=EA=A6=A7=EA=A6=AE=EA=A6=91=EA=A6=A9=EA=A6=AD=EA=A7=80?= Date: Wed, 2 Feb 2022 09:29:10 +0800 Subject: Asynchronous connection Important updates: 1. Emscripten port. 2. HTTP request code copied from libicclient & slightly fixed. 3. Cookies, for maintaining authorisation between different async handles. --- Makefile.am | 14 ++-- client.c | 87 ++++++++++--------- post.c | 3 - post.h | 43 ---------- request.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++- request.h | 113 +++---------------------- rtclient.h | 11 ++- rtclient/search.h | 12 +-- rtclient/ticket.h | 14 +--- rtclient/typedefs.h | 18 ++++ rtclient/user.h | 8 +- search.c | 58 +++++-------- ticket.c | 235 ++++++++++++++++++++++++++++++---------------------- user.c | 82 +++++++++--------- 14 files changed, 522 insertions(+), 403 deletions(-) delete mode 100644 post.c delete mode 100644 post.h create mode 100644 rtclient/typedefs.h diff --git a/Makefile.am b/Makefile.am index 43a977c..71876b7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,13 +1,10 @@ lib_LTLIBRARIES = librtclient.la librtclient_la_SOURCES = \ - request.h \ - request.c \ - post.h \ - post.c \ - user.c \ - ticket.c \ - search.c \ - client.c + client.c \ + request.c \ + user.c \ + ticket.c \ + search.c librtclient_la_CPPFLAGS = -I${prefix}/include librtclient_la_LDFLAGS = -L${prefix}/lib if EMSCRIPTEN @@ -18,6 +15,7 @@ librtclient_la_LDFLAGS += $(CURL_LIBS) endif include_HEADERS = rtclient.h pkginclude_HEADERS = \ + rtclient/typedefs.h \ rtclient/user.h \ rtclient/ticket.h \ rtclient/search.h diff --git a/client.c b/client.c index f452b06..85bc1e7 100644 --- a/client.c +++ b/client.c @@ -1,57 +1,68 @@ -#include #include +#include #include "request.h" #include "rtclient.h" -CURL *curl = NULL; +#ifdef __EMSCRIPTEN__ +emscripten_fetch_attr_t attr; +#else char *server_url = NULL; +char *cookies_path = NULL; +char *cainfo = NULL; +#endif -bool rtclient_init(const char *url, const char *certificate) +void rtclient_init(const char *url, const char *cookies, + const char *certificate) { +#ifdef __EMSCRIPTEN__ + emscripten_fetch_attr_init(&attr); + attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; +#else + size_t length = strlen(url); + size_t append = url[length - 1] != '/'; + server_url = malloc(length + append + 1); + strcpy(server_url, url); + if (append) + strcat(server_url, "/"); + cookies_path = malloc(strlen(cookies) + 1); + strcpy(cookies_path, cookies); curl_global_init(CURL_GLOBAL_SSL); - curl = curl_easy_init(); - if (curl) { - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); - curl_easy_setopt(curl, CURLOPT_REFERER, url); - if (certificate) - curl_easy_setopt(curl, CURLOPT_CAINFO, certificate); -#ifdef DEBUG - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); -#endif - size_t length = strlen(url); - bool append = !(bool)(url[length - 1] == '/'); - server_url = malloc(length + (size_t)append + 1); - strcpy(server_url, url); - if (append) - strcat(server_url, "/"); + if (certificate) { + cainfo = malloc(strlen(certificate) + 1); + strcpy(cainfo, certificate); } +#endif +} - return (bool)curl; +void rtclient_login(const char *name, const char *password, + void (*handler)(rtclient_response *)) +{ + request(handler, NULL, &(struct body){ 2, { + { "user", name }, + { "pass", password } + }}, ""); } -void rtclient_login(const char *name, const char *password) +void rtclient_free_response(rtclient_response *response) { - struct curl_httppost *post, *last = NULL; - curl_formadd(&post, &last - , CURLFORM_COPYNAME, "user" - , CURLFORM_PTRCONTENTS, name - , CURLFORM_END); - curl_formadd(&post, &last - , CURLFORM_COPYNAME, "pass" - , CURLFORM_PTRCONTENTS, password - , CURLFORM_END); - last = NULL; - request(NULL, NULL, post, ""); - curl_formfree(post); - post = NULL; +#ifdef __EMSCRIPTEN__ + if (response->userData) + free(response->userData); + emscripten_fetch_close(response); +#else + free(response->data); + curl_easy_cleanup(response->curl); + free(response); +#endif } void rtclient_cleanup() { - if (curl) { - free(server_url); - curl_easy_cleanup(curl); - } +#ifndef __EMSCRIPTEN__ + if (cainfo) + free(cainfo); + free(cookies_path); + free(server_url); curl_global_cleanup(); +#endif } diff --git a/post.c b/post.c deleted file mode 100644 index 8dca62f..0000000 --- a/post.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "post.h" - -extern inline void post(const char *, const char *[], size_t); diff --git a/post.h b/post.h deleted file mode 100644 index 8420d73..0000000 --- a/post.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef RTCLIENT_POST_H -#define RTCLIENT_POST_H - -#include "request.h" - -inline void post(const char *path, const char *pairs[], size_t n) -{ - size_t length = 0; - for (size_t i = 0; i < n; i += 2) { - const char *pair = pairs[i]; - if (pair && strcmp(pair, "")) - length += strlen(pair) + strlen(pairs[i + 1]) + 3; - } - - char content[length + 1]; - memset(content, 0, strlen(content)); - for (size_t i = 0; i < n; i += 2) { - const char *pair = pairs[i]; - if (pair && strcmp(pair, "")) - sprintf(content, "%s%s: %s\n", content, pairs[i + 1] - , pair); - } -#ifdef DEBUG -#ifdef ANDROID - __android_log_print(ANDROID_LOG_DEBUG, "librtclient", "%s\nContent:\n%s" - , __func__, content); -#else - fprintf(stderr, "%s\nContent:\n%s", __func__, content); -#endif // ANDROID -#endif // DEBUG - - struct curl_httppost *post, *last = NULL; - curl_formadd(&post, &last - , CURLFORM_COPYNAME, "content" - , CURLFORM_PTRCONTENTS, content - , CURLFORM_END); - last = NULL; - request(NULL, NULL, post, "%s", path); - curl_formfree(post); - post = NULL; -} - -#endif // RTCLIENT_POST_H diff --git a/request.c b/request.c index 652ec71..299bf2c 100644 --- a/request.c +++ b/request.c @@ -1,4 +1,227 @@ +#if defined __ANDROID__ && defined DEBUG +#include +#endif +#include +#include +#include +#include #include "request.h" -extern inline void request(size_t (*writefunction)(void *, size_t, size_t, void *) - , void *writedata, struct curl_httppost *post, char *fmt, ...); +#ifdef __EMSCRIPTEN__ + +extern emscripten_fetch_attr_t attr; + +#else + +#ifdef HAVE_THREADS_H +#include +#else +#include +typedef pthread_t thrd_t; +#endif +#include + +extern char *server_url; +extern char *cookies_path; +extern char *cainfo; + +struct container { + struct curl_httppost *post; + void (*handler)(rtclient_response *); + rtclient_response *response; +}; + +static size_t append(char *data, size_t size, size_t nmemb, rtclient_response *response) +{ + size_t realsize = size * nmemb; + response->data = realloc(response->data, response->numBytes + realsize + 1); + memcpy(&(response->data[response->numBytes]), data, realsize); + response->numBytes += realsize; + response->data[response->numBytes] = '\0'; + return realsize; +} + +static +#ifdef HAVE_THREADS_H +int +#else +void * +#endif +async(void *arg) +{ +#ifdef HAVE_THREADS_H + int ret = thrd_success; +#endif + struct container *container = (struct container *)arg; + CURLcode res = curl_easy_perform(container->response->curl); + if (container->post) + curl_formfree(container->post); + if (res == CURLE_OK && container->handler) + container->handler(container->response); + else { +#ifdef HAVE_THREADS_H + ret = thrd_error; +#endif +#ifdef DEBUG + const char *error = curl_easy_strerror(res); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, "librtclient.so", "%s", error); +#else + fprintf(stderr, "%s\n", error); +#endif +#endif + } + free(container); +#ifdef HAVE_THREADS_H + return ret; +#else + return NULL; +#endif +} +#endif + +void request(void (*handler)(rtclient_response *), void (*callback)(void *), struct body *body, char *fmt, ...) +{ + va_list ap; + char *p, *sval; + unsigned int uval; + size_t length = +#ifndef __EMSCRIPTEN__ + strlen(server_url) + +#endif + strlen(fmt); + + va_start(ap, fmt); + for (p = fmt; *p; p++) { + if (*p != '%') + continue; + switch(*++p) { + case 's': + sval = va_arg(ap, char *); + length += strlen(sval) - 2; + break; + case 'u': + uval = va_arg(ap, unsigned int); + do { + length++; + } while ((uval /= 10)); + length -= 2; + break; + case 'c': + length++; + break; + default: + break; + } + } + va_end(ap); + + char url[length + 1]; +#ifdef __EMSCRIPTEN__ + memset(url, '\0', length + 1); +#else + length = 0; + strcpy(url, server_url); +#endif + + va_start(ap, fmt); + for (p = fmt; *p; p++) { + if (*p != '%') + continue; + switch(*++p) { + case 's': + sval = va_arg(ap, char *); + strcat(url, sval); + length = strlen(url); + break; + case 'u': + uval = va_arg(ap, unsigned int); + sprintf(url, "%s%u", url, uval); + length = strlen(url); + break; + case 'c': + url[length++] = (char)va_arg(ap, int); + url[length] = '\0'; + break; + default: + break; + } + } + va_end(ap); + +#ifdef __EMSCRIPTEN__ + if (handler) + attr.onsuccess = handler; + if (body) { + size_t length = 0; + char *post = malloc(1); + memset(post, '\0', 1); + for (size_t i = 0; i < body->num_pairs; i++) { + struct pair pair = body->pairs[i]; + if (!pair.value) + continue; + length += strlen(pair.key) + strlen(pair.value) + (i ? 1 : 0) + 1; + post = realloc(post, length + 1); + if (i) + strcat(post, "&"); + sprintf(post, "%s%s=%s", post, pair.key, pair.value); + } + strcpy(attr.requestMethod, "POST"); + const char *headers[] = { "Content-Type", "application/x-www-form-urlencoded", NULL }; + attr.requestHeaders = headers; + attr.requestData = post; + attr.requestDataSize = length + 1; + attr.userData = post; + } else { + strcpy(attr.requestMethod, "GET"); + attr.userData = callback; + } + emscripten_fetch(&attr, url); +#else + CURL *curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookies_path); + FILE *cookies_file = fopen(cookies_path, "r"); + if (cookies_file) + fclose(cookies_file); + else + curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookies_path); + curl_easy_setopt(curl, CURLOPT_REFERER, url); + if (cainfo) + curl_easy_setopt(curl, CURLOPT_CAINFO, cainfo); +#ifdef DEBUG + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); +#endif + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, append); + rtclient_response *response = malloc(sizeof(rtclient_response)); + response->data = malloc(1); + response->numBytes = 0; + response->userData = callback; + curl_easy_setopt(curl, CURLOPT_WRITEDATA, response); + struct curl_httppost *post = NULL; + struct curl_httppost *last = NULL; + if (body) { + for (size_t i = 0; i < body->num_pairs; i++) { + struct pair pair = body->pairs[i]; + if (!pair.value) + continue; + curl_formadd(&post, &last, CURLFORM_COPYNAME, pair.key, CURLFORM_PTRCONTENTS, pair.value, CURLFORM_END); + } + last = NULL; + curl_easy_setopt(curl, CURLOPT_HTTPPOST, post); + } else + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + struct container *container = malloc(sizeof(struct container)); + response->curl = curl; + container->post = post; + container->handler = handler; + container->response = response; + thrd_t thread; +#ifdef HAVE_THREADS_H + thrd_create(&thread, async, container); +#else + pthread_create(&thread, NULL, async, container); +#endif +#endif +} diff --git a/request.h b/request.h index 932f75e..4c559fb 100644 --- a/request.h +++ b/request.h @@ -1,105 +1,16 @@ -#ifndef RTCLIENT_REQUEST_H -#define RTCLIENT_REQUEST_H +#ifndef REQUEST_H +#define REQUEST_H -#if defined(ANDROID) && defined(DEBUG) -#include -#endif -#include -#include -#include -#include - -extern CURL *curl; -extern char *server_url; - -inline void request(size_t (*writefunction)(void *, size_t, size_t, void *) - , void *writedata, struct curl_httppost *post, char *fmt, ...) -{ - va_list ap; - char *p, *sval; - unsigned int uval; - size_t length = strlen(server_url) + strlen(fmt); - - va_start(ap, fmt); - for (p = fmt; *p; p++) { - if (*p != '%') - continue; - switch(*++p) { - case 's': - sval = va_arg(ap, char *); - length += strlen(sval) - 2; - break; - case 'u': - uval = va_arg(ap, unsigned int); - do { - length++; - } while ((uval /= 10)); - length -= 2; - break; - case 'c': - length++; - break; - default: - break; - } - } - va_end(ap); +#include "rtclient/typedefs.h" - char url[length + 1]; - length = 0; - strcpy(url, server_url); +struct body { + size_t num_pairs; + struct pair { + const char *key; + const char *value; + } pairs[20]; +}; - va_start(ap, fmt); - for (p = fmt; *p; p++) { - if (*p != '%') - continue; - switch(*++p) { - case 's': - sval = va_arg(ap, char *); - strcat(url, sval); - length = strlen(url); - break; - case 'u': - uval = va_arg(ap, unsigned int); - sprintf(url, "%s%u", url, uval); - length = strlen(url); - break; - case 'c': - url[length++] = (char)va_arg(ap, int); - url[length] = '\0'; - break; - default: - break; - } - } - va_end(ap); +void request(void (*)(rtclient_response *), void (*)(void *), struct body *, char *, ...); - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunction); - if (writedata) - curl_easy_setopt(curl, CURLOPT_WRITEDATA, writedata); - else - curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout); - if (post) - curl_easy_setopt(curl, CURLOPT_HTTPPOST, post); - else - curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); - -#ifdef DEBUG - CURLcode res = -#endif // DEBUG - curl_easy_perform(curl); -#ifdef DEBUG - if (res != CURLE_OK) { - const char *error = curl_easy_strerror(res); -#ifdef ANDROID - __android_log_print(ANDROID_LOG_ERROR, "librtclient", "%s: %s" - , __func__, error); -#else - fprintf(stderr, "%s: %s\n", __func__, error); -#endif // ANDROID - } -#endif // DEBUG -} - -#endif // RTCLIENT_REQUEST_H +#endif diff --git a/rtclient.h b/rtclient.h index dac2198..49541e3 100644 --- a/rtclient.h +++ b/rtclient.h @@ -1,13 +1,18 @@ #ifndef RTCLIENT_H #define RTCLIENT_H +#include "rtclient/typedefs.h" + #ifdef __cplusplus extern "C" { #endif - bool rtclient_init(const char *url, const char *certificate); - void rtclient_login(const char *name, const char *password); - void rtclient_cleanup(); +void rtclient_init(const char *url, const char *cookies, + const char *certificate); +void rtclient_login(const char *name, const char *password, + void (*handler)(rtclient_response *)); +void rtclient_free_response(rtclient_response *response); +void rtclient_cleanup(); #ifdef __cplusplus } diff --git a/rtclient/search.h b/rtclient/search.h index e0e386a..25b7258 100644 --- a/rtclient/search.h +++ b/rtclient/search.h @@ -1,21 +1,15 @@ #ifndef RTCLIENT_SEARCH_H #define RTCLIENT_SEARCH_H -struct rtclient_search_ticket_list { - size_t length; - struct rtclient_ticket *tickets[]; -}; - #ifdef __cplusplus extern "C" { #endif - void rtclient_search_ticket(struct rtclient_search_ticket_list **listptr - , const char *query); - void rtclient_search_ticket_free(struct rtclient_search_ticket_list *list); +void rtclient_search_ticket(const char *query, + void (*callback)(struct rtclient_ticket **)); #ifdef __cplusplus } #endif -#endif // RTCLIENT_SEARCH_H +#endif diff --git a/rtclient/ticket.h b/rtclient/ticket.h index 9fdd9a0..0600c34 100644 --- a/rtclient/ticket.h +++ b/rtclient/ticket.h @@ -56,11 +56,6 @@ struct rtclient_ticket_history { struct rtclient_ticket_history_attachment_list *attachments; }; -struct rtclient_ticket_history_list { - size_t length; - struct rtclient_ticket_history *histories[]; -}; - #ifdef __cplusplus extern "C" { #endif @@ -79,12 +74,9 @@ void rtclient_ticket_new(const char *queue, const char *starts, const char *due, const char *text); - void rtclient_ticket_history(struct rtclient_ticket_history_list **listptr - , unsigned int id, bool long_format); - void rtclient_ticket_history_free - (struct rtclient_ticket_history *history); - void rtclient_ticket_history_list_free - (struct rtclient_ticket_history_list *list); +void rtclient_ticket_history_list(unsigned int id, bool long_format, + void (*callback)(struct rtclient_ticket_history **)); +void rtclient_ticket_history_free(struct rtclient_ticket_history *history); #ifdef __cplusplus } diff --git a/rtclient/typedefs.h b/rtclient/typedefs.h new file mode 100644 index 0000000..e15b1df --- /dev/null +++ b/rtclient/typedefs.h @@ -0,0 +1,18 @@ +#ifndef RTCLIENT_TYPEDEFS_H +#define RTCLIENT_TYPEDEFS_H + +#include +#ifdef __EMSCRIPTEN__ +#include +typedef emscripten_fetch_t rtclient_response; +#else +#include +typedef struct { + void *userData; + char *data; + size_t numBytes; + CURL *curl; +} rtclient_response; +#endif + +#endif diff --git a/rtclient/user.h b/rtclient/user.h index 5f62d17..02b2daa 100644 --- a/rtclient/user.h +++ b/rtclient/user.h @@ -1,6 +1,8 @@ #ifndef RTCLIENT_USER_H #define RTCLIENT_USER_H +#include "typedefs.h" + enum rtclient_user_lang { RTCLIENT_USER_LANG_NONE = 0, RTCLIENT_USER_LANG_AR, @@ -464,9 +466,9 @@ void rtclient_user_new(const char *name, enum rtclient_user_timezone timezone, bool disabled, bool privileged); - void rtclient_user_showid(struct rtclient_user **userptr, unsigned int id); - void rtclient_user_showname(struct rtclient_user **userptr, const char *name); - void rtclient_user_free(struct rtclient_user *user); +void rtclient_user_showid(unsigned int id, void (*)(struct rtclient_user *)); +void rtclient_user_showname(const char *name, void (*)(struct rtclient_user *)); +void rtclient_user_free(struct rtclient_user *user); #ifdef __cplusplus } diff --git a/search.c b/search.c index 03ab593..b1a36a0 100644 --- a/search.c +++ b/search.c @@ -6,46 +6,38 @@ #endif #endif #include +#include #include "request.h" +#include "rtclient.h" #include "rtclient/ticket.h" #include "rtclient/search.h" -typedef struct rtclient_ticket rtclient_ticket; -typedef struct rtclient_search_ticket_list rtclient_search_ticket_list; - -static size_t ticket_handler(void *contents, size_t size, size_t nmemb - , void *writedata) +static void handle_search(rtclient_response *response) { - size_t realsize = size * nmemb; - char response[realsize + 1]; - memcpy(response, contents, realsize); - response[realsize] = '\0'; - char lines[strlen(response) + 1]; - strcpy(lines, response); + char data[response->numBytes]; + strcpy(data, response->data); + char lines[response->numBytes]; + strcpy(lines, response->data); - char *line = strtok(response, "\n"); + char *line = strtok(data, "\n"); if (strstr(line, "200 Ok")) { line = strtok(NULL, "\n"); size_t length = 0; do { length++; if (!strcmp(line, "No matching results.")) - return realsize; + return; } while ((line = strtok(NULL, "\n"))); - rtclient_search_ticket_list **listptr - = (rtclient_search_ticket_list **)writedata; - *listptr = malloc(sizeof(rtclient_search_ticket_list) - + length * sizeof(rtclient_ticket)); - rtclient_search_ticket_list *list = *listptr; - list->length = length; + struct rtclient_ticket **list + = malloc(sizeof(struct rtclient_ticket) * (length + 1)); char *linesaveptr = NULL; line = strtok_r(lines, "\n", &linesaveptr); line = strtok_r(NULL, "\n", &linesaveptr); for (size_t i = 0; i < length; i++) { - list->tickets[i] = malloc(sizeof(rtclient_ticket)); - rtclient_ticket *ticket = list->tickets[i]; + list[i] = malloc(sizeof(struct rtclient_ticket)); + struct rtclient_ticket *ticket = list[i]; char *tokensaveptr = NULL; char *token = strtok_r(line, ":", &tokensaveptr); @@ -57,6 +49,8 @@ static size_t ticket_handler(void *contents, size_t size, size_t nmemb line = strtok_r(NULL, "\n", &linesaveptr); } + list[length] = NULL; + ((void (*)(struct rtclient_ticket **))response->userData)(list); } else { #ifdef DEBUG #ifdef __ANDROID__ @@ -67,24 +61,12 @@ static size_t ticket_handler(void *contents, size_t size, size_t nmemb #endif #endif } - - return realsize; -} - -void rtclient_search_ticket(rtclient_search_ticket_list **listptr - , const char *query) -{ - request(ticket_handler, (void *)listptr, NULL, "%s%s" - , "REST/1.0/search/ticket?query=", query); + rtclient_free_response(response); } -void rtclient_search_ticket_free(rtclient_search_ticket_list *list) +void rtclient_search_ticket(const char *query, + void (*callback)(struct rtclient_ticket **)) { - for (size_t i = 0; i < list->length; i++) { - rtclient_ticket *ticket = list->tickets[i]; - free(ticket->subject); - free(ticket); - } - free(list); - list = NULL; + request(handle_search, (void (*)(void *))callback, NULL, + "%s%s", "REST/1.0/search/ticket?query=", query); } diff --git a/ticket.c b/ticket.c index 9053d71..b3310be 100644 --- a/ticket.c +++ b/ticket.c @@ -1,13 +1,15 @@ #if defined __ANDROID__ && defined DEBUG #include #endif +#include #include -#include "post.h" +#include +#include +#include "request.h" +#include "rtclient.h" #include "rtclient/ticket.h" typedef struct rtclient_ticket rtclient_ticket; -typedef struct rtclient_ticket_history ticket_history; -typedef struct rtclient_ticket_history_list history_list; typedef struct rtclient_ticket_history_attachment history_attachment; typedef struct rtclient_ticket_history_attachment_list attachment_list; @@ -26,50 +28,45 @@ void rtclient_ticket_new(const char *queue, const char *due, const char *text) { - post("REST/1.0/ticket/new", (const char *[]){ - "ticket/new", "id" - , queue, "Queue" - , requestor, "Requestor" - , subject, "Subject" - , cc, "Cc" - , admin_cc, "AdminCc" - , owner, "Owner" - , status, "Status" - , priority, "Priority" - , initial_priority, "InitialPriority" - , final_priority, "FinalPriority" - , time_estimated, "TimeEstimated" - , starts, "Starts" - , due, "Due" - , text, "Text" - }, 28); + request(NULL, NULL, &(struct body){ 15, { + { "id", "ticket/new" }, + { "Queue", queue }, + { "Requestor", requestor }, + { "Subject", subject }, + { "Cc", cc }, + { "AdminCc", admin_cc }, + { "Owner", owner }, + { "Status", status }, + { "Priority", priority }, + { "InitialPriority", initial_priority }, + { "FinalPriority", final_priority }, + { "TimeEstimated", time_estimated }, + { "Starts", starts }, + { "Due", due }, + { "Text", text } + }}, "%s", "REST/1.0/ticket/new"); } -static size_t history_handler(void *contents, size_t size, size_t nmemb - , void *writedata) +static void handle_history(rtclient_response *response) { - size_t realsize = size * nmemb; - char response[realsize + 1]; - memcpy(response, contents, realsize); - response[realsize] = '\0'; + char data[response->numBytes]; + strcpy(data, response->data); char *linesaveptr = NULL; - char *line = strtok_r(response, "\n", &linesaveptr); + char *line = strtok_r(data, "\n", &linesaveptr); if (strstr(line, "200 Ok")) { line = strtok_r(NULL, "\n", &linesaveptr); char *lengthstr = strtok(line, "/"); size_t length = atoi(&lengthstr[2]); - history_list **listptr = (history_list **)writedata; - *listptr = malloc(sizeof(history_list) - + length * sizeof(ticket_history)); - history_list *list = *listptr; - list->length = length; + struct rtclient_ticket_history **list + = malloc(sizeof(struct rtclient_ticket_history *) + * (length + 1)); for (size_t i = 0; i < length; i++) { line = strtok_r(NULL, "\n", &linesaveptr); - list->histories[i] = malloc(sizeof(ticket_history)); - ticket_history *ticket_history = list->histories[i]; + list[i] = malloc(sizeof(struct rtclient_ticket_history)); + struct rtclient_ticket_history *ticket_history = list[i]; char *tokensaveptr = NULL; char *token = strtok_r(line, ":", &tokensaveptr); @@ -94,6 +91,8 @@ static size_t history_handler(void *contents, size_t size, size_t nmemb = malloc(sizeof(attachment_list)); ticket_history->attachments->length = 0; } + list[length] = NULL; + ((void (*)(struct rtclient_ticket_history **))response->userData)(list); } else { #ifdef DEBUG #ifdef __ANDROID__ @@ -104,33 +103,40 @@ static size_t history_handler(void *contents, size_t size, size_t nmemb #endif #endif } - - return realsize; + rtclient_free_response(response); } -static size_t history_l_handler(void *contents, size_t size, size_t nmemb - , void *writedata) +static void handle_history_l(rtclient_response *response) { - size_t realsize = size * nmemb; - char response[realsize + 1]; - memcpy(response, contents, realsize); - response[realsize] = '\0'; + char data[response->numBytes]; + strcpy(data, response->data); char *linesaveptr = NULL; - char *line = strtok_r(response, "\n", &linesaveptr); + char *line = strtok_r(data, "\n", &linesaveptr); if (strstr(line, "200 Ok")) { - history_list **listptr = (history_list **)writedata; - history_list *list = *listptr; - for (size_t i = 0; i < list->length; i++) { - ticket_history *ticket_history = list->histories[i]; - strtok_r(NULL, "\n", &linesaveptr); - line = strtok_r(NULL, "\n", &linesaveptr); + line = strtok_r(NULL, "\n", &linesaveptr); + char *tokensaveptr = NULL; + strtok_r(line, "/", &tokensaveptr); + size_t length = atoi(strtok_r(NULL, "(", &tokensaveptr)); + struct rtclient_ticket_history **list + = malloc(sizeof(struct rtclient_ticket_history *) + * (length + 1)); + + for (size_t i = 0; i < length; i++) { line = strtok_r(NULL, "\n", &linesaveptr); + if (i) + line = strtok_r(NULL, "\n", &linesaveptr); + list[i] = malloc(sizeof(struct rtclient_ticket_history)); + struct rtclient_ticket_history *ticket_history = list[i]; - char *tokensaveptr = NULL; strtok_r(line, ":", &tokensaveptr); char *token = strtok_r(NULL, ":", &tokensaveptr); + ticket_history->id = atoi(++token); + line = strtok_r(NULL, "\n", &linesaveptr); + + strtok_r(line, ":", &tokensaveptr); + token = strtok_r(NULL, ":", &tokensaveptr); ticket_history->ticket = atoi(++token); line = strtok_r(NULL, "\n", &linesaveptr); @@ -141,42 +147,60 @@ static size_t history_l_handler(void *contents, size_t size, size_t nmemb strtok_r(line, ":", &tokensaveptr); token = strtok_r(NULL, ":", &tokensaveptr); - enum rtclient_ticket_history_type type - = ticket_history->type; if (!strcmp(++token, "AddLink")) - type = RTCLIENT_TICKET_HISTORY_TYPE_ADD_LINK; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_ADD_LINK; else if (!strcmp(token, "AddMember")) - type = RTCLIENT_TICKET_HISTORY_TYPE_ADD_MEMBER; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_ADD_MEMBER; else if (!strcmp(token, "AddMembership")) - type = RTCLIENT_TICKET_HISTORY_TYPE_ADD_MEMBERSHIP; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_ADD_MEMBERSHIP; else if (!strcmp(token, "AddReminder")) - type = RTCLIENT_TICKET_HISTORY_TYPE_ADD_REMINDER; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_ADD_REMINDER; else if (!strcmp(token, "AddWatcher")) - type = RTCLIENT_TICKET_HISTORY_TYPE_ADD_WATCHER; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_ADD_WATCHER; else if (!strcmp(token, "Comment")) - type = RTCLIENT_TICKET_HISTORY_TYPE_COMMENT; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_COMMENT; else if (!strcmp(token, "CommentEmailRecord")) - type = RTCLIENT_TICKET_HISTORY_TYPE_COMMENT_EMAIL_RECORD; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_COMMENT_EMAIL_RECORD; else if (!strcmp(token, "Correspond")) - type = RTCLIENT_TICKET_HISTORY_TYPE_CORRESPOND; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_CORRESPOND; else if (!strcmp(token, "Create")) - type = RTCLIENT_TICKET_HISTORY_TYPE_CREATE; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_CREATE; else if (!strcmp(token, "DelWatcher")) - type = RTCLIENT_TICKET_HISTORY_TYPE_DEL_WATCHER; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_DEL_WATCHER; else if (!strcmp(token, "Disabled")) - type = RTCLIENT_TICKET_HISTORY_TYPE_DISABLED; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_DISABLED; else if (!strcmp(token, "EmailRecord")) - type = RTCLIENT_TICKET_HISTORY_TYPE_EMAIL_RECORD; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_EMAIL_RECORD; else if (!strcmp(token, "ResolveReminder")) - type = RTCLIENT_TICKET_HISTORY_TYPE_RESOLVE_REMINDER; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_RESOLVE_REMINDER; else if (!strcmp(token, "Set")) - type = RTCLIENT_TICKET_HISTORY_TYPE_SET; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_SET; else if (!strcmp(token, "SetWatcher")) - type = RTCLIENT_TICKET_HISTORY_TYPE_SET_WATCHER; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_SET_WATCHER; else if (!strcmp(token, "Status")) - type = RTCLIENT_TICKET_HISTORY_TYPE_STATUS; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_STATUS; else if (!strcmp(token, "SystemError")) - type = RTCLIENT_TICKET_HISTORY_TYPE_SYSTEM_ERROR; + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_SYSTEM_ERROR; + else + ticket_history->type + = RTCLIENT_TICKET_HISTORY_TYPE_NONE; line = strtok_r(NULL, "\n", &linesaveptr); strtok_r(line, ":", &tokensaveptr); @@ -184,23 +208,28 @@ static size_t history_l_handler(void *contents, size_t size, size_t nmemb if (token && strcmp(token, "")) { ticket_history->field = malloc(strlen(token)); strcpy(ticket_history->field, ++token); - } + } else + ticket_history->field = NULL; line = strtok_r(NULL, "\n", &linesaveptr); strtok_r(line, ":", &tokensaveptr); token = strtok_r(NULL, ":", &tokensaveptr); if (token && strcmp(token, "")) { - ticket_history->old_value = malloc(strlen(token)); + ticket_history->old_value + = malloc(strlen(token)); strcpy(ticket_history->old_value, ++token); - } + } else + ticket_history->old_value = NULL; line = strtok_r(NULL, "\n", &linesaveptr); strtok_r(line, ":", &tokensaveptr); token = strtok_r(NULL, ":", &tokensaveptr); if (token && strcmp(token, "")) { - ticket_history->new_value = malloc(strlen(token)); + ticket_history->new_value + = malloc(strlen(token)); strcpy(ticket_history->new_value, ++token); - } + } else + ticket_history->new_value = NULL; line = strtok_r(NULL, "\n", &linesaveptr); strtok_r(line, ":", &tokensaveptr); @@ -208,9 +237,18 @@ static size_t history_l_handler(void *contents, size_t size, size_t nmemb if (token && strcmp(token, "")) { ticket_history->data = malloc(strlen(token)); strcpy(ticket_history->data, ++token); - } + } else + ticket_history->data = NULL; line = strtok_r(NULL, "\n", &linesaveptr); + strtok_r(line, ":", &tokensaveptr); + token = strtok_r(NULL, ":", &tokensaveptr); + if (token && strcmp(token, "")) { + ticket_history->description + = malloc(strlen(token)); + strcpy(ticket_history->description, ++token); + } else + ticket_history->description = NULL; line = strtok_r(NULL, "\n", &linesaveptr); strtok_r(line, ":", &tokensaveptr); @@ -220,9 +258,9 @@ static size_t history_l_handler(void *contents, size_t size, size_t nmemb strcpy(content, ++token); line = strtok_r(NULL, "\n", &linesaveptr); while (strncmp(line, "Creator", 7)) { - ticket_history->content = realloc(content - , strlen(content) - + strlen(line) + 2); + ticket_history->content = realloc(content, + strlen(content) + strlen(line) + + 2); content = ticket_history->content; sprintf(content, "%s\n%s", content, line); line = strtok_r(NULL, "\n", &linesaveptr); @@ -259,16 +297,19 @@ static size_t history_l_handler(void *contents, size_t size, size_t nmemb if (!token) break; + ticket_history->attachments + = malloc(sizeof(attachment_list)); + ticket_history->attachments->length = 0; size_t i = 0; while (strcmp(line, "--")) { ticket_history->attachments - = realloc(ticket_history->attachments - , sizeof - (attachment_list) + = realloc(ticket_history->attachments, + sizeof(attachment_list) + (i + 1) * sizeof (history_attachment)); - attachment_list *list = ticket_history->attachments; + attachment_list *list + = ticket_history->attachments; list->length++; list->attachments[i] = malloc(sizeof(history_attachment)); @@ -296,6 +337,9 @@ static size_t history_l_handler(void *contents, size_t size, size_t nmemb token = strtok_r(line, ": ()", &tokensaveptr); } } + list[length] = NULL; + ((void (*)(struct rtclient_ticket_history **)) + response->userData)(list); } else { #ifdef DEBUG #ifdef __ANDROID__ @@ -306,21 +350,22 @@ static size_t history_l_handler(void *contents, size_t size, size_t nmemb #endif #endif } - - return realsize; + rtclient_free_response(response); } -void rtclient_ticket_history(history_list **listptr - , unsigned int id, bool long_format) +void rtclient_ticket_history_list(unsigned int id, bool long_format, + void (*callback)(struct rtclient_ticket_history **)) { - request(history_handler, (void *)listptr, NULL, "%s%u%s" - , "REST/1.0/ticket/", id, "/history"); if (long_format) - request(history_l_handler, (void *)listptr, NULL, "%s%u%s" - , "REST/1.0/ticket/", id, "/history?format=l"); + request(handle_history_l, (void (*)(void *))callback, NULL, + "%s%u%s", "REST/1.0/ticket/", id, + "/history?format=l"); + else + request(handle_history, (void (*)(void *))callback, NULL, + "%s%u%s", "REST/1.0/ticket/", id, "/history"); } -void rtclient_ticket_history_free(ticket_history *history) +void rtclient_ticket_history_free(struct rtclient_ticket_history *history) { attachment_list *list = history->attachments; for (size_t i = 0; i < list->length; i++) { @@ -345,11 +390,3 @@ void rtclient_ticket_history_free(ticket_history *history) free(history); history = NULL; } - -void rtclient_ticket_history_list_free(history_list *list) -{ - for (size_t i = 0; i < list->length; i++) - rtclient_ticket_history_free(list->histories[i]); - free(list); - list = NULL; -} diff --git a/user.c b/user.c index c40c2e9..d4347c6 100644 --- a/user.c +++ b/user.c @@ -6,12 +6,12 @@ #endif #endif #include +#include #include -#include "post.h" +#include "request.h" +#include "rtclient.h" #include "rtclient/user.h" -typedef struct rtclient_user rtclient_user; - void rtclient_user_new(const char *name, const char *password, const char *email_address, @@ -37,46 +37,40 @@ void rtclient_user_new(const char *name, bool disabled, bool privileged) { - post("REST/1.0/user/new", (const char *[]){ - name, "Name" - , password, "Password" - , email_address, "EmailAddress" - , real_name, "RealName" - , nick_name, "NickName" - , organization, "Organization" - , address1, "Address1" - , address2, "Address2" - , city, "City" - , state, "State" - , zip, "Zip" - , country, "Country" - , home_phone, "HomePhone" - , work_phone, "WorkPhone" - , mobile_phone, "MobilePhone" - , pager_phone, "PagerPhone" - , contact_info, "ContactInfo" - , comments, "Comments" - , signature, "Signature" - , gecos, "Gecos" - }, 40); + request(NULL, NULL, &(struct body){ 20, { + { "Name", name }, + { "Password", password }, + { "EmailAddress", email_address }, + { "RealName", real_name }, + { "NickName", nick_name }, + { "Organization", organization }, + { "Address1", address1 }, + { "Address2", address2 }, + { "City", city }, + { "State", state }, + { "Zip", zip }, + { "Country", country }, + { "HomePhone", home_phone }, + { "WorkPhone", work_phone }, + { "MobilePhone", mobile_phone }, + { "PagerPhone", pager_phone }, + { "ContactInfo", contact_info }, + { "Comments", comments }, + { "Signature", signature }, + { "Gecos", gecos } + }}, "%s", "REST/1.0/user/new"); } -static size_t show_handler(void *contents, size_t size, size_t nmemb - , void *writedata) +static void handle_show(rtclient_response *response) { - size_t realsize = size * nmemb; - char response[realsize + 1]; - memcpy(response, contents, realsize); - response[realsize] = '\0'; + char data[response->numBytes]; + strcpy(data, response->data); char *linesaveptr = NULL; - char *line = strtok_r(response, "\n", &linesaveptr); + char *line = strtok_r(data, "\n", &linesaveptr); if (strstr(line, "200 Ok")) { line = strtok_r(NULL, "\n", &linesaveptr); - - rtclient_user **userptr = (rtclient_user **)writedata; - *userptr = malloc(sizeof(rtclient_user)); - rtclient_user *user = *userptr; + struct rtclient_user *user = malloc(sizeof(struct rtclient_user)); user->password = NULL; user->email_address = NULL; user->real_name = NULL; @@ -100,7 +94,6 @@ static size_t show_handler(void *contents, size_t size, size_t nmemb user->timezone = RTCLIENT_USER_TIMEZONE_NONE; user->privileged = false; user->disabled = true; - do { char *tokensaveptr = NULL; char *token = strtok_r(line, ":", &tokensaveptr); @@ -192,6 +185,7 @@ static size_t show_handler(void *contents, size_t size, size_t nmemb user->disabled = (bool)atoi(++token); } } while ((line = strtok_r(NULL, "\n", &linesaveptr))); + ((void (*)(struct rtclient_user *))response->userData)(user); } else { #ifdef DEBUG #ifdef __ANDROID__ @@ -202,22 +196,20 @@ static size_t show_handler(void *contents, size_t size, size_t nmemb #endif #endif } - - return realsize; + rtclient_free_response(response); } -void rtclient_user_showid(rtclient_user **userptr, unsigned int id) +void rtclient_user_showid(unsigned int id, void (*callback)(struct rtclient_user *)) { - request(show_handler, (void *)userptr, NULL, "%s%u", "REST/1.0/user/", id); + request(handle_show, (void (*)(void *))callback, NULL, "%s%u", "REST/1.0/user/", id); } -void rtclient_user_showname(rtclient_user **userptr, const char *name) +void rtclient_user_showname(const char *name, void (*callback)(struct rtclient_user *)) { - request(show_handler, (void *)userptr, NULL, "%s%s", "REST/1.0/user/" - , name); + request(handle_show, (void (*)(void *))callback, NULL, "%s%s", "REST/1.0/user/", name); } -void rtclient_user_free(rtclient_user *user) +void rtclient_user_free(struct rtclient_user *user) { if (user->gecos) free(user->gecos); -- cgit v1.2.3