diff options
-rw-r--r-- | .gitignore | 22 | ||||
-rw-r--r-- | COPYING | 20 | ||||
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | README.md | 14 | ||||
-rw-r--r-- | anteraja.c | 45 | ||||
-rw-r--r-- | configure.ac | 13 | ||||
-rw-r--r-- | handler.c | 13 | ||||
-rw-r--r-- | handler.h | 67 | ||||
-rw-r--r-- | pikul.c | 102 | ||||
-rw-r--r-- | pikul.h | 35 | ||||
-rw-r--r-- | shipping.c | 12 | ||||
-rw-r--r-- | shipping.h | 10 |
12 files changed, 359 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc14edf --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +.deps +.libs +*.la +*.lo +*.o +*.swp +aclocal.m4 +autoscan-2.69.log +ar-lib +autom4te.cache +build* +compile +config.* +configure +depcomp +install-sh +libtool +ltmain.sh +Makefile +Makefile.in +missing +stamp-h1 @@ -0,0 +1,20 @@ +Copyright (c) 2021 Erik Prabowo Kamal + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..5d8f4d7 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,6 @@ +lib_LTLIBRARIES = libpikul.la +libpikul_la_SOURCES = pikul.c shipping.c handler.c \ + anteraja.c +libpikul_la_CPPFLAGS = $(DEPS_CFLAGS) +libpikul_la_LDFLAGS = $(DEPS_LIBS) +include_HEADERS = pikul.h diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae5336b --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# Pikul + +# Building + +```sh +$ git clone git://darapsa.org/libpikul.git +$ cd libpikul +$ autoreconf -is +$ mkdir build +$ cd build +$ ../configure +$ make # -jN +$ make install +``` diff --git a/anteraja.c b/anteraja.c new file mode 100644 index 0000000..cf9d685 --- /dev/null +++ b/anteraja.c @@ -0,0 +1,45 @@ +#include "shipping.h" +#include "handler.h" + +extern CURL *curl; + +void anteraja_init(char *provisions[], struct shipping *shipping) +{ + shipping->base = malloc(strlen(*provisions) + 1); + strcpy(shipping->base, *provisions); + headers(shipping, (const char *[]){ "access-key-id", "secret-access-key", NULL }, ++provisions); + shipping->headers = curl_slist_append(shipping->headers, "Content-Type:application/json"); +} + +void anteraja_services_request(const char *origin, const char *destination, double weight, + struct shipping *shipping, char **url, char **post) +{ + static const char *path = "serviceRates"; + *url = malloc(strlen(shipping->base) + strlen(path) + 1); + sprintf(*url, "%s%s", shipping->base, path); + static const char *format = "{" + "\"origin\": \"xx.xx.xx\"," + "\"destination\": \"xx.xx.xx\"," + "\"weight\": xxxxx" + "}"; + *post = malloc(strlen(format) + 1); + sprintf(*post, "{\"origin\": \"%s\",\"destination\": \"%s\",\"weight\": %d}", + origin, destination, (int)weight); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, *post); +} + +size_t anteraja_services_handle(const char *contents, size_t size, size_t nmemb, + struct pikul_services **services) +{ + size_t realsize = size * nmemb; + handle(contents, realsize, &(struct container){ services, (const char *[]){ + "product_code", + "product_name", + "etd", + "rates", + "content", + "services", + NULL + }}); + return realsize; +} diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..c6d63c1 --- /dev/null +++ b/configure.ac @@ -0,0 +1,13 @@ +AC_INIT([pikul], [0.0], [pt@darapsa.co.id]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AC_PROG_CC +AM_PROG_AR +LT_INIT +PKG_CHECK_MODULES([DEPS], [libcurl json-c]) +AC_TYPE_SIZE_T +AC_C_INLINE +AC_FUNC_MALLOC +AC_CHECK_FUNCS([memset]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/handler.c b/handler.c new file mode 100644 index 0000000..581001e --- /dev/null +++ b/handler.c @@ -0,0 +1,13 @@ +#include "handler.h" + +extern inline void handle(const char *, size_t, struct container *); + +void recurse(struct json_object *outer, const char *keys[], struct json_object **services) +{ + struct json_object *inner = NULL; + json_object_object_get_ex(outer, *keys, &inner); + if (*++keys) + recurse(inner, keys, services); + else + *services = inner; +} diff --git a/handler.h b/handler.h new file mode 100644 index 0000000..9c80ab6 --- /dev/null +++ b/handler.h @@ -0,0 +1,67 @@ +#include <string.h> +#include <json.h> +#include "pikul.h" + +enum { + KEY_CODE, + KEY_NAME, + KEY_ETD, + KEY_COST, + KEY_OBJECTS +}; + +struct container { + struct pikul_services **services; + const char **keys[7]; +}; + +extern json_tokener *tokener; +void recurse(struct json_object *, const char *[], struct json_object **); + +inline void handle(const char *contents, size_t num_bytes, struct container *container) +{ + json_object *response = json_tokener_parse_ex(tokener, contents, num_bytes); + enum json_tokener_error error = json_tokener_get_error(tokener); + if (!response) { + if (error == json_tokener_continue) + return; + else { + json_tokener_reset(tokener); + return; + } + } else if (!json_object_is_type(response, json_type_object) || error != json_tokener_success) + return; + struct json_object *services = NULL; + recurse(response, &(*container->keys)[KEY_OBJECTS], &services); + size_t length = json_object_array_length(services); + *(container->services) = malloc(sizeof(struct pikul_services) + + sizeof(struct pikul_service *[length])); + (*(container->services))->length = length; + for (size_t i = 0; i < length; i++) { + (*(container->services))->list[i] = malloc(sizeof(struct pikul_service)); + struct pikul_service *service = (*(container->services))->list[i]; + json_object *object = json_object_array_get_idx(services, i); + struct json_object_iterator iterator = json_object_iter_begin(object); + struct json_object_iterator iterator_end = json_object_iter_end(object); + while (!json_object_iter_equal(&iterator, &iterator_end)) { + const char *key = json_object_iter_peek_name(&iterator); + json_object *val = json_object_iter_peek_value(&iterator); + if (!strcmp(key, (*container->keys)[KEY_COST])) + service->cost = json_object_get_double(val); + else { + int len = json_object_get_string_len(val); + if (len) { + char *value = malloc(len + 1); + strcpy(value, json_object_get_string(val)); + if (!strcmp(key, (*container->keys)[KEY_CODE])) + service->code = value; + else if (!strcmp(key, (*container->keys)[KEY_NAME])) + service->name = value; + else if (!strcmp(key, (*container->keys)[KEY_ETD])) + service->etd = value; + } + } + json_object_iter_next(&iterator); + } + } +} @@ -0,0 +1,102 @@ +#include <string.h> +#include <json.h> +#include "shipping.h" + +CURL *curl; +json_tokener *tokener; +static struct shipping shipping; + +extern void anteraja_init(char *[], struct shipping *); +extern void anteraja_services_request(const char *, const char *, double, + struct shipping *, char **, char **); +extern size_t anteraja_services_handle(const char *, size_t, size_t, struct pikul_services **); + +void pikul_init(enum pikul_company company, char *provisions[]) +{ + curl_global_init(CURL_GLOBAL_SSL); + curl = curl_easy_init(); +#ifdef DEBUG + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); +#endif + shipping.company = company; + switch (company) { + case PIKUL_COMPANY_ANTERAJA: + anteraja_init(provisions, &shipping); + break; + default: + break; + } + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, shipping.headers); + tokener = json_tokener_new(); +} + +struct pikul_services *pikul_services(const char *origin, const char *destination, double weight) +{ + struct pikul_services *services = NULL; + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &services); + char *url; + char *post = NULL; + size_t (*handler)(const char *, size_t, size_t, struct pikul_services **); + switch (shipping.company) { + case PIKUL_COMPANY_ANTERAJA: + anteraja_services_request(origin, destination, weight, &shipping, &url, &post); + handler = anteraja_services_handle; + break; + default: + break; + } + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handler); + curl_easy_perform(curl); + if (post) + free(post); + free(url); + return services; +} + +static inline void free_service(struct pikul_service *service) +{ + if (service->etd) + free(service->etd); + if (service->name) + free(service->name); + free(service->code); + free(service); +} + +void pikul_free_services(struct pikul_services *services) +{ + for (size_t i = 0; i < services->length; i++) + free_service(services->list[i]); + free(services); +} + +static int servicecmp(const void *service1, const void *service2) +{ + return strcmp((*(struct pikul_service * const *)service1)->code, + (*(struct pikul_service * const *)service2)->code); +} + +double pikul_cost(const char *origin, const char *destination, double weight, const char *code) +{ + struct pikul_services *services = pikul_services(origin, destination, weight); + qsort(services->list, services->length, sizeof(struct pikul_service *), servicecmp); + struct pikul_service *key_service = malloc(sizeof(struct pikul_service)); + memset(key_service, '\0', sizeof(struct pikul_service)); + key_service->code = malloc(strlen(code) + 1); + strcpy(key_service->code, code); + double cost = (*(struct pikul_service **)bsearch(&key_service, services->list, + services->length, sizeof(struct pikul_service *), servicecmp))->cost; + free_service(key_service); + pikul_free_services(services); + return cost; +} + +void pikul_cleanup() +{ + free(shipping.base); + json_tokener_free(tokener); + curl_slist_free_all(shipping.headers); + curl_easy_cleanup(curl); + curl_global_cleanup(); +} @@ -0,0 +1,35 @@ +#ifndef PIKUL_H +#define PIKUL_H + +enum pikul_company { + PIKUL_COMPANY_ANTERAJA = 0, + PIKUL_COMPANY_SICEPAT +}; + +struct pikul_service { + char *code; + char *name; + char *etd; + double cost; +}; + +struct pikul_services { + size_t length; + struct pikul_service *list[]; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +void pikul_init(enum pikul_company company, char *provisions[]); +struct pikul_services *pikul_services(const char *origin, const char *destination, double weight); +void pikul_free_services(struct pikul_services *services); +double pikul_cost(const char *origin, const char *destination, double weight, const char *service); +void pikul_cleanup(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/shipping.c b/shipping.c new file mode 100644 index 0000000..f2e263c --- /dev/null +++ b/shipping.c @@ -0,0 +1,12 @@ +#include <string.h> +#include "shipping.h" + +void headers(struct shipping *shipping, const char *fields[], char *provisions[]) +{ + shipping->headers = NULL; + while (*fields) { + char header[strlen(*fields) + strlen(*provisions) + 2]; + sprintf(header, "%s:%s", *fields++, *provisions++); + shipping->headers = curl_slist_append(shipping->headers, header); + } +} diff --git a/shipping.h b/shipping.h new file mode 100644 index 0000000..345eaef --- /dev/null +++ b/shipping.h @@ -0,0 +1,10 @@ +#include <curl/curl.h> +#include "pikul.h" + +struct shipping { + enum pikul_company company; + char *base; + struct curl_slist *headers; +}; + +void headers(struct shipping *, const char *[], char *[]); |