aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChloe Kudryavtsev <code@toast.bunkerlabs.net>2023-03-23 14:35:40 -0400
committerChloe Kudryavtsev <code@toast.bunkerlabs.net>2023-03-23 14:35:40 -0400
commit5e528479eb4841ae9d75dddcc4b8e64258115557 (patch)
treecdf1edc1e3b9d45861f9703623e0b4a304cbc18b
parentjurl: move to module directory (project) (diff)
native: implement curl_mime*
-rw-r--r--jurl.h20
-rw-r--r--jurl/mime.janet49
-rw-r--r--jurl_mime.c163
-rw-r--r--main.c64
4 files changed, 296 insertions, 0 deletions
diff --git a/jurl.h b/jurl.h
index 6a23a81..e6e23df 100644
--- a/jurl.h
+++ b/jurl.h
@@ -44,5 +44,25 @@ JANET_CFUN(jurl_strerror);
// jurl_getinfo.c
JANET_CFUN(jurl_getinfo);
+// jurl_mime.c
+struct jurl_mime {
+ CURL *curl;
+ curl_mime *handle;
+ int clean;
+};
+typedef struct jurl_mime jurl_mime;
+JANET_CFUN(jurl_mime_new);
+JANET_CFUN(jurl_mime_addpart);
+JANET_CFUN(jurl_mime_name);
+JANET_CFUN(jurl_mime_data);
+JANET_CFUN(jurl_mime_data_cb);
+JANET_CFUN(jurl_mime_filedata);
+JANET_CFUN(jurl_mime_filename);
+JANET_CFUN(jurl_mime_type);
+JANET_CFUN(jurl_mime_headers);
+JANET_CFUN(jurl_mime_encoder);
+JANET_CFUN(jurl_mime_subparts);
+jurl_mime *janet_getjurlmime(Janet *argv, int32_t n);
+
// jurl_setopt.c
JANET_CFUN(jurl_setopt);
diff --git a/jurl/mime.janet b/jurl/mime.janet
new file mode 100644
index 0000000..bad43fe
--- /dev/null
+++ b/jurl/mime.janet
@@ -0,0 +1,49 @@
+# wrapper around jurl-native/mime stuff
+(import jurl-native)
+
+(defn- some? [x] (not (nil? x)))
+(defmacro- some-do
+ [handle sym act]
+ ~(when (some? ,sym) (,act ,handle ,sym)))
+
+(defn- mime-part
+ [handle {:name name
+ :attach amime
+ :data data
+ :data-cb data-cb
+ :filedata filedata
+ :filename filename
+ :type mimetype
+ :headers headers
+ :encoder encoder}]
+ (some-do handle name jurl-native/mime-name)
+ (some-do handle data jurl-native/mime-data)
+ (some-do handle data-cb jurl-native/mime-data-cb)
+ (some-do handle filedata jurl-native/mime-filedata)
+ (some-do handle filename jurl-native/mime-filename)
+ (some-do handle mimetype jurl-native/mime-type)
+ (some-do handle encoder jurl-native/mime-encoder)
+ (when (some? amime)
+ (:attach amime handle))
+ (when (some? headers)
+ (jurl-native/mime-headers handle (->> headers
+ (map pairs)
+ (map (fn [[k v]] (string/format "%s: %s" k v)))
+ sort
+ freeze))))
+
+# define a complete mime in one go
+(defn new
+ [& parts]
+ (def out (jurl-native/mime-new))
+ (each part parts
+ (mime-part (:addpart out) part))
+ out)
+
+# example
+(comment (new {:name data
+ :data "a form"}
+ {:name file
+ :data "pretending to be a file"
+ :filename "remote.file"
+ :type "application/json"}))
diff --git a/jurl_mime.c b/jurl_mime.c
new file mode 100644
index 0000000..912c467
--- /dev/null
+++ b/jurl_mime.c
@@ -0,0 +1,163 @@
+// jurl_mime implements curl_mime_*
+#include "jurl.h"
+
+static int jurl_mime_gc(void *p, size_t s) {
+ (void) s;
+ jurl_mime *mime = (jurl_mime*)p;
+ if (mime->clean) curl_mime_free(mime->handle);
+ curl_easy_cleanup(mime->curl);
+ return 0;
+}
+
+static JanetMethod jurl_mime_methods[] = {
+ {"addpart", jurl_mime_addpart},
+ {"attach", jurl_mime_subparts},
+ {NULL, NULL},
+};
+
+static int jurl_mime_get(void *p, Janet key, Janet *out) {
+ (void) p;
+ if (!janet_checktype(key, JANET_KEYWORD)) {
+ return 0;
+ }
+ return janet_getmethod(janet_unwrap_keyword(key), jurl_mime_methods, out);
+}
+
+static const JanetAbstractType jurl_mimetype = {
+ "jurl-mime", // name
+ jurl_mime_gc, // gc
+ NULL, // gcmark
+ jurl_mime_get, //get
+ JANET_ATEND_GET
+};
+
+jurl_mime *janet_getjurlmime(Janet *argv, int32_t n) {
+ return (jurl_mime*)janet_getabstract(argv, n, &jurl_mimetype);
+}
+
+// we generate a separate handle for the generation, it's used a lot
+JANET_CFUN(jurl_mime_new) {
+ janet_fixarity(argc, 0);
+ jurl_mime *mime = (jurl_mime*)janet_abstract(&jurl_mimetype, sizeof(jurl_mime));
+ mime->clean = 1; // clean by default
+ mime->curl = curl_easy_init();
+ mime->handle = curl_mime_init(mime->curl);
+ return janet_wrap_abstract(mime);
+}
+
+JANET_CFUN(jurl_mime_addpart) {
+ janet_fixarity(argc, 1);
+ jurl_mime *mime = janet_getjurlmime(argv, 0);
+ return janet_wrap_pointer(curl_mime_addpart(mime->handle));
+}
+
+JANET_CFUN(jurl_mime_name) {
+ janet_fixarity(argc, 2);
+ curl_mimepart *part = (curl_mimepart*)janet_getpointer(argv, 0);
+ CURLcode ret;
+ if (janet_checktype(argv[1], JANET_NIL)) {
+ ret = curl_mime_name(part, NULL);
+ } else {
+ const char *s = (const char*)janet_getcstring(argv, 1);
+ ret = curl_mime_name(part, s);
+ }
+ return jurl_geterror(ret);
+}
+
+JANET_CFUN(jurl_mime_data) {
+ janet_fixarity(argc, 2);
+ curl_mimepart *part = (curl_mimepart*)janet_getpointer(argv, 0);
+ CURLcode ret;
+ if (janet_checktype(argv[1], JANET_NIL)) {
+ ret = curl_mime_data(part, NULL, 0);
+ } else {
+ JanetByteView bytes = janet_getbytes(argv, 1);
+ ret = curl_mime_data(part, (const char*)bytes.bytes, bytes.len);
+ }
+ return jurl_geterror(ret);
+}
+
+// TODO
+JANET_CFUN(jurl_mime_data_cb) {
+ janet_panic("not implemented");
+ return janet_wrap_nil(); // unreachable
+}
+
+JANET_CFUN(jurl_mime_filedata) {
+ janet_fixarity(argc, 2);
+ curl_mimepart *part = (curl_mimepart*)janet_getpointer(argv, 0);
+ CURLcode ret;
+ if (janet_checktype(argv[1], JANET_NIL)) {
+ ret = curl_mime_filedata(part, NULL);
+ } else {
+ const char *s = (const char*)janet_getcstring(argv, 1);
+ ret = curl_mime_filedata(part, s);
+ }
+ return jurl_geterror(ret);
+}
+
+JANET_CFUN(jurl_mime_filename) {
+ janet_fixarity(argc, 2);
+ curl_mimepart *part = (curl_mimepart*)janet_getpointer(argv, 0);
+ CURLcode ret;
+ if (janet_checktype(argv[1], JANET_NIL)) {
+ ret = curl_mime_filename(part, NULL);
+ } else {
+ const char *s = (const char*)janet_getcstring(argv, 1);
+ ret = curl_mime_filename(part, s);
+ }
+ return jurl_geterror(ret);
+}
+
+JANET_CFUN(jurl_mime_type) {
+ janet_fixarity(argc, 2);
+ curl_mimepart *part = (curl_mimepart*)janet_getpointer(argv, 0);
+ CURLcode ret;
+ if (janet_checktype(argv[1], JANET_NIL)) {
+ ret = curl_mime_type(part, NULL);
+ } else {
+ const char *s = (const char*)janet_getcstring(argv, 1);
+ ret = curl_mime_type(part, s);
+ }
+ return jurl_geterror(ret);
+}
+
+// TODO: really? I have to do cleanup *again*? ;-;
+JANET_CFUN(jurl_mime_headers) {
+ janet_panic("not implemented");
+ return janet_wrap_nil(); // unreachable
+}
+
+JANET_CFUN(jurl_mime_encoder) {
+ janet_fixarity(argc, 2);
+ curl_mimepart *part = (curl_mimepart*)janet_getpointer(argv, 0);
+ CURLcode ret;
+ if (janet_checktype(argv[1], JANET_NIL)) {
+ ret = curl_mime_encoder(part, NULL);
+ } else {
+ if (janet_keyeq(argv[1], "binary")) {
+ ret = curl_mime_encoder(part, "binary");
+ } else if (janet_keyeq(argv[1], "8bit")) {
+ ret = curl_mime_encoder(part, "8bit");
+ } else if (janet_keyeq(argv[1], "7bit")) {
+ ret = curl_mime_encoder(part, "7bit");
+ } else if (janet_keyeq(argv[1], "base64")) {
+ ret = curl_mime_encoder(part, "base64");
+ } else if (janet_keyeq(argv[1], "quoted-printable")) {
+ ret = curl_mime_encoder(part, "quoted-printable");
+ } else {
+ janet_panic("jurl_mime_encoder: invalid encoding type");
+ }
+ }
+ return jurl_geterror(ret);
+}
+
+// this is exposed as (:attach mime part)
+JANET_CFUN(jurl_mime_subparts) {
+ janet_fixarity(argc, 2);
+ jurl_mime *mime = janet_getjurlmime(argv, 0);
+ curl_mimepart *part = (curl_mimepart*)janet_getpointer(argv, 1);
+ CURLcode ret = curl_mime_subparts(part, mime->handle);
+ if (ret == CURLE_OK) mime->clean = 0; // no longer needs cleaning
+ return jurl_geterror(ret);
+}
diff --git a/main.c b/main.c
index c5c94ee..7d12434 100644
--- a/main.c
+++ b/main.c
@@ -61,6 +61,57 @@ JANET_FN(jurl_getinfo,
"(jurl-native/getinfo handle 1234)",
"Get a curl info from handle");
+// jurl_mime.c
+JANET_FN(jurl_mime_new,
+ "(jurl-native/mime-new)",
+ "Create a new mime form");
+
+JANET_FN(jurl_mime_addpart,
+ "(jurl-native/mime-addpart mime)"
+ "\n"
+ "(:addpart mime)",
+ "Add a part to a mime form");
+
+JANET_FN(jurl_mime_name,
+ "(jurl-native/mime-name part string)",
+ "Set a name for the mimepart");
+
+JANET_FN(jurl_mime_data,
+ "(jurl-native/mime-data part string)"
+ "\n"
+ "(jurl-native/mime-data part buffer)",
+ "Set a string or buffer as the data source for the mimepart");
+
+JANET_FN(jurl_mime_data_cb,
+ "(jurl-native/mime-data-cb part (fn ...))",
+ "Set a callback as the data source for the mimepart");
+
+JANET_FN(jurl_mime_filedata,
+ "(jurl-native/mime-filedata part \"local.file\")",
+ "Set a file as the data source for the mimepart");
+
+JANET_FN(jurl_mime_filename,
+ "(jurl-native/mime-filename part \"remote.file\")",
+ "Set a custom remote filename for the upload mimepart");
+
+JANET_FN(jurl_mime_type,
+ "(jurl-native/mime-type part \"image/png\")",
+ "Set a custom mimetype for the mimepart");
+
+JANET_FN(jurl_mime_headers,
+ "(jurl-native/mime-headers part [headers...])",
+ "Set custom headers for the mimepart");
+
+JANET_FN(jurl_mime_encoder,
+ "(jurl-native/mime-encoder part :encoder)",
+ "Set a part to be encoded using :encoder.\n"
+ "Available encoders are:\n"
+ ":binary :8bit :7bit :base64 :quoted-printable");
+
+JANET_FN(jurl_mime_subparts,
+ "(jurl-native/mime-attach mime part)",
+ "Attach mime to part as a subpart mime");
+
// jurl_setopt.c
JANET_FN(jurl_setopt,
"(jurl-native/setopt handle :option value)"
@@ -86,6 +137,19 @@ static const JanetRegExt cfuns[] = {
// jurl_getinfo.c
JANET_REG("getinfo", jurl_getinfo),
+ // jurl_mime.c
+ JANET_REG("mime-new", jurl_mime_new),
+ JANET_REG("mime-addpart", jurl_mime_addpart),
+ JANET_REG("mime-name", jurl_mime_name),
+ JANET_REG("mime-data", jurl_mime_data),
+ JANET_REG("mime-data-cb", jurl_mime_data_cb),
+ JANET_REG("mime-filedata", jurl_mime_filedata),
+ JANET_REG("mime-filename", jurl_mime_filename),
+ JANET_REG("mime-type", jurl_mime_type),
+ JANET_REG("mime-headers", jurl_mime_headers),
+ JANET_REG("mime-encoder", jurl_mime_encoder),
+ JANET_REG("mime-attach", jurl_mime_subparts),
+
// jurl_setopt.c
JANET_REG("setopt", jurl_setopt),
};