summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore22
-rw-r--r--COPYING20
-rw-r--r--Makefile.am6
-rw-r--r--README.md14
-rw-r--r--anteraja.c45
-rw-r--r--configure.ac13
-rw-r--r--handler.c13
-rw-r--r--handler.h67
-rw-r--r--pikul.c102
-rw-r--r--pikul.h35
-rw-r--r--shipping.c12
-rw-r--r--shipping.h10
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
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..9f27616
--- /dev/null
+++ b/COPYING
@@ -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);
+ }
+ }
+}
diff --git a/pikul.c b/pikul.c
new file mode 100644
index 0000000..b13d64a
--- /dev/null
+++ b/pikul.c
@@ -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();
+}
diff --git a/pikul.h b/pikul.h
new file mode 100644
index 0000000..8046d09
--- /dev/null
+++ b/pikul.h
@@ -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 *[]);