module util; import asdf; __gshared Config cfg; struct Config { string token, homeserver; string database_path; bool accept_invitations; } string intToStr(T)(T num) { if (num == 0) return "0"; char[] buf; bool minus = num < 0; if (minus) num /= -1; for(short i; num > 0; ++i) { buf = (num % 10 + '0')~buf; num /= 10; } return cast(string)(minus ? '-'~buf : buf); } int strToInt(T)(T str) @nogc { int num; bool minus; for (short i; str.length > i; ++i) { if (str[i] == '-') minus = true; else if (str[i] >= '0' || str[i] <= '9') num = num * 10 + (str[i] - 48); else break; } return minus ? num / -1 : num; } struct JsonObject { import mir.conv: to; string raw; SerdeException deserializeFromAsdf(Asdf data) { this.raw = data.to!string; return null; } // void serialize(S)(ref S serializer) { // try serializer.sink.putSmallEscaped(data.parseJson.to!string); // catch (Exception) serializer.sink.putSmallEscaped("{}"); // } } struct RqRsp { short status; char[] body; char[] headers; string getHeader(string header) @nogc { int prev, splt; for (int i; i < headers.length; ++i) { if (headers[i] == '\0') splt = i; else if (headers[i] == '\1') { if (header == headers[prev..splt]) return cast(string)headers[splt+2..i]; prev = i + 1; } } return null; } } size_t wrt(ubyte[]* outptt, size_t size, size_t nmemb, ubyte* inptt) { auto len = size * nmemb; *outptt ~= inptt[0..len]; return len; } // struct MM { // char* rsp; // size_t len; // } // size_t wrt(MM* outptt, size_t size, size_t nmemb, char* inptt) @nogc { // import core.stdc.stdlib; // import core.stdc.string; // size_t rs = size * nmemb; // outptt.rsp = cast(char*)realloc(outptt.rsp, outptt.len+rs+1); // memcpy(&(outptt.rsp[outptt.len]), inptt, rs); // outptt.len += rs; // outptt.rsp[outptt.len] = '\0'; // return rs; // } size_t hddrz(ubyte[]* outptt, size_t size, size_t nmemb, ubyte* inptt) { auto len = size * nmemb; auto headers = inptt[0..len]; int prev; for (int i; i < len; ++i) { if (headers[i] == '\r' && headers[i + 1] == '\n') { auto line = headers[prev..i]; prev = i + 1; for (int x; x < line.length; ++x) if (line[x] == ':') { *outptt ~= line[0..x] ~ '\0' ~ line[x + 1..$] ~ '\1'; break; } } } return len; } import bindings.curl; const char* UA = "Neptune/1.0 (https://code.lost-skunk.cc/neptune)"; // FIXME: memory corruption RqRsp mkrqst(string url, string method = "GET", string content = null, bool authorized = false) { RqRsp rsp; auto hndl = curl_easy_init(); curl_easy_setopt(hndl, CURLOPT_URL, cast(char*)url); curl_easy_setopt(hndl, CURLOPT_WRITEFUNCTION, &wrt); curl_easy_setopt(hndl, CURLOPT_WRITEDATA, &rsp.body); curl_easy_setopt(hndl, CURLOPT_USERAGENT, UA); curl_easy_setopt(hndl, CURLOPT_HEADERFUNCTION, &hddrz); curl_easy_setopt(hndl, CURLOPT_HEADERDATA, &rsp.headers); if (method) curl_easy_setopt(hndl, CURLOPT_CUSTOMREQUEST, cast(char*)method); if (content) { curl_easy_setopt(hndl, CURLOPT_POST, 1); curl_easy_setopt(hndl, CURLOPT_POSTFIELDS, cast(void*)content); curl_easy_setopt(hndl, CURLOPT_POSTFIELDSIZE, content.length); } curl_slist* authhdr; if (authorized) { authhdr = curl_slist_append(null, cast(char*)cfg.token); curl_easy_setopt(hndl, CURLOPT_HTTPHEADER, authhdr); } int rs = curl_easy_perform(hndl); if (rs != 0) { static foreach (err; __traits(allMembers, CURLcode)) { if (rs == __traits(getMember, CURLcode, err)) throw new Exception(err); } } curl_easy_getinfo(hndl, CURLINFO_RESPONSE_CODE, &rsp.status); if (authorized) curl_slist_free_all(authhdr); curl_easy_cleanup(hndl); return rsp; } RqRsp mkhsrq(string uri, string method = "GET", string content = null) { return mkrqst(cfg.homeserver ~ uri, method, content, true); }