diff options
| author | 2023-03-23 14:35:40 -0400 | |
|---|---|---|
| committer | 2023-03-23 14:35:40 -0400 | |
| commit | 5e528479eb4841ae9d75dddcc4b8e64258115557 (patch) | |
| tree | cdf1edc1e3b9d45861f9703623e0b4a304cbc18b | |
| parent | jurl: move to module directory (project) (diff) | |
native: implement curl_mime*
| -rw-r--r-- | jurl.h | 20 | ||||
| -rw-r--r-- | jurl/mime.janet | 49 | ||||
| -rw-r--r-- | jurl_mime.c | 163 | ||||
| -rw-r--r-- | main.c | 64 |
4 files changed, 296 insertions, 0 deletions
@@ -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); +} @@ -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), }; |
