diff options
author | Anton Luka Šijanec <anton@sijanec.eu> | 2021-09-13 23:10:12 +0200 |
---|---|---|
committer | Anton Luka Šijanec <anton@sijanec.eu> | 2021-09-13 23:10:12 +0200 |
commit | 32ee8fb67c0b513d253f07759d133340691d6239 (patch) | |
tree | a3e2b9e2d3b098d93100c54e63b4673129bbdd36 /src | |
parent | login now works, yay! (diff) | |
download | discord.c-32ee8fb67c0b513d253f07759d133340691d6239.tar discord.c-32ee8fb67c0b513d253f07759d133340691d6239.tar.gz discord.c-32ee8fb67c0b513d253f07759d133340691d6239.tar.bz2 discord.c-32ee8fb67c0b513d253f07759d133340691d6239.tar.lz discord.c-32ee8fb67c0b513d253f07759d133340691d6239.tar.xz discord.c-32ee8fb67c0b513d253f07759d133340691d6239.tar.zst discord.c-32ee8fb67c0b513d253f07759d133340691d6239.zip |
Diffstat (limited to '')
-rw-r--r-- | src/api.c | 82 | ||||
-rw-r--r-- | src/h.c | 27 | ||||
-rw-r--r-- | src/lib.c | 2 | ||||
-rw-r--r-- | src/main.c | 4 |
4 files changed, 94 insertions, 21 deletions
@@ -4,6 +4,11 @@ #define DC_SERVER_PORT 443 #define DC_SERVER_SSL 1 #define DC_SERVER_ORIGIN "https://discord.com" +#define DC_WS_ADDRESS "gateway.discord.gg" +#define DC_WS_PORT DC_SERVER_PORT +#define DC_WS_SSL DC_SERVER_SSL +#define DC_WS_ORIGIN DC_SERVER_ORIGIN +#define DC_WS_PATH "/" #define DC_API_PREFIX "/api/v9/" #define DC_LWS_ABLE_TO_PARSE_HEADERS 1 /* libwebsockets information: libwebsockets works with event loops and discord.c primary targets debian for building (it must work on debian), but debian does not ship libwebsockets with glib or libevent event loop support, so loops are implemented in the classic poll() style. this reduces performance probably. if you use discord.c API with support for a platform specific event loop, you may rewrite LWS to do things differently. currently calls into API (dc_api_i and dc_api_o) will both do LWS stuff. */ @@ -17,6 +22,7 @@ static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us, struct dc_lws_pass * pass = (struct dc_lws_pass *) us; switch (rs) { case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: /* TODO: handle and report somehow */ + pass->api_io.status = DC_NET_ERROR; lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char *) in : "(null)"); break; case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: /* lws gives us len space after *in */ @@ -110,12 +116,28 @@ static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us, free(pass->body); /* _READ was not called and pointer was static */ free(pass); /* called at the final moment when user pointer and wsi is still */ break; /* accessible - we can free the struct that was passed in as a heap ptr */ + case LWS_CALLBACK_CLIENT_ESTABLISHED: /* websocket established */ + pass->api_io.status = DC_OK; + break; + case LWS_CALLBACK_CLIENT_CLOSED: /* websocket closed */ + pass->api_io.status = DC_NET_ERROR; + break; + case LWS_CALLBACK_CLIENT_RECEIVE: /* websocket receive, pass to json parser ? */ + lwsl_hexdump_notice(in, len); + if (pass->json_reason == LEJPCB_COMPLETE || !(pass->api_io.status & DC_LEJP_CONSTRUCTED)) { /* we regenerate a new context in case we didn't do that yet or in case the previous one finished parsing */ + pass->api_io.status |= DC_LEJP_CONSTRUCTED; + lejp_construct(pass->json_context, dc_json_cb, us /* == (void *) pass */, dc_json_paths, LWS_ARRAY_SIZE(dc_json_paths)); + } + lejp_parse(pass->json_context, (const unsigned char) in, len); + break; default: break; } return lws_callback_http_dummy(wsi, rs, us, in, len); } void dc_api_i (struct dc_api_io i) { /* this function does not call attached functions, only output does that */ + struct lws_client_connect_info info; + struct dc_lws_pass * pass; if (!i.program) return; if (i.program->lws_context && !(i.status & DC_FROM_LWS)) @@ -131,7 +153,6 @@ void dc_api_i (struct dc_api_io i) { /* this function does not call attached fun if (!i.program->clients_length) /* already attempted login */ DC_MR(i.program->clients); i.program->clients[0] = i.client; - struct lws_client_connect_info info; memset(&info, 0, sizeof(info)); info.context = i.program->lws_context; #if DC_SERVER_SSL @@ -143,7 +164,7 @@ void dc_api_i (struct dc_api_io i) { /* this function does not call attached fun info.host = info.address; /* info.origin = DC_SERVER_ORIGIN; */ /* just don't send it */ info.method = "POST"; - struct dc_lws_pass * pass = calloc(1, sizeof(struct dc_lws_pass)); /* cb frees */ + pass = calloc(1, sizeof(struct dc_lws_pass)); /* cb frees */ fprintf(stderr, "allocated pass at %p\n", (void *) pass); i.status |= DC_MUST_FREE; pass->body_length = smprintf(&pass->body, DC_LOGIN_FORMAT, i.client->email, i.client->password); @@ -159,33 +180,70 @@ void dc_api_i (struct dc_api_io i) { /* this function does not call attached fun info.protocol = dc_lws_protocols[0].name; if (i.client->authorization) /* attempt cookie login without user/pass */ strcpy(pass->headers[DC_LWS_AUTHORIZATION], i.client->authorization); + i.status = DC_UNSET /* we clear the status */; lws_client_connect_via_info(&info); break; case DC_API_LOGIN_CB: -#define DC_STACK_RETURN(x) do { i.status = x; dc_api_stack(i); return; } while (0) +#define DC_STACK_RETURN(x) do { i.client->status = x; dc_api_stack(i); return; } while (0) i.type = DC_API_LOGIN; + i.status &= ~(DC_OK); if (!i.pass->body) { - i.status |= DC_REQUEST_FAILED; - i.status &= ~(DC_OK); + i.client->status |= DC_REQUEST_FAILED; dc_api_stack(i); break; } fprintf(stderr, "DEBUG: %s\n", i.pass->body); if (strstr(i.pass->body, "INVALID_LOGIN")) - DC_STACK_ERROR(DC_BAD_LOGIN); + DC_STACK_RETURN(DC_BAD_LOGIN); if (strstr(i.pass->body, "captcha-required")) - DC_STACK_ERROR(DC_CAPTCHA_NEEDED); + DC_STACK_RETURN(DC_CAPTCHA_NEEDED); if (strstr(i.pass->body, "ACCOUNT_LOGIN_VERIFICATION_EMAIL")) - DC_STACK_ERROR(DC_VERIFICATION_NEEDED); - char * cp, c2; - if (!(cp = strstr(i.pass->body, "\"token\": \"")) || !(c2 = strchr(cp+strlen("\"token\": \""), '"'))) - DC_STACK_ERROR(DC_ERROR); + DC_STACK_RETURN(DC_VERIFICATION_NEEDED); + char * cp, * c2; +#define DC_LTS "\"token\": \"" + if (!(cp = strstr(i.pass->body, DC_LTS)+strlen(DC_LTS)) || !(c2 = strchr(cp+strlen(DC_LTS), '"'))) + DC_STACK_RETURN(DC_ERROR); c2[0] = '\0'; /* body is on heap, we can edit it */ free(i.client->authorization); /* in case we set it previously */ i.client->authorization = strdup(cp); fprintf(stderr, "got token %s\n", i.client->authorization); - DC_STACK_RETURN(DC_OK | DC_INCOMPLETE /* we now have to request properties */); + i.client->status |= DC_OK | DC_INCOMPLETE; + dc_api_stack(i); + /* we now have to connect to the websocket */ + goto ws; /* we could call dc_api_i but that just fills stack */ + break; + case DC_API_WS: + ws: + memset(&info, 0, sizeof(info)); + info.context = i.program->lws_context; + if (!info.context) + fprintf(stderr, "[BUG] !!! !info.context\n"); + info.port = DC_WS_PORT; + info.address = DC_WS_ADDRESS; + info.path = DC_WS_PATH; + info.host = info.address; + info.origin = DC_WS_ORIGIN; +#if DC_WS_SSL + info.ssl_connection = LCCSCF_USE_SSL; +#endif + info.protocol = dc_lws_protocols[0].name; + info.local_protocol_name = info.protocol; + pass = calloc(1, sizeof(struct dc_lws_pass)); + i.type = DC_API_WS_CB; + memcpy(&pass->api_io, &i, sizeof(i)); + pass->api_io.pass = pass; + info.userdata = pass; + if (i.client->authorization) /* attempt cookie login without user/pass */ + strcpy(pass->headers[DC_LWS_AUTHORIZATION], i.client->authorization); + fprintf(stderr, "starting websocket session\n"); + i.status = DC_UNSET; /* we clear the status */ + if (!lws_client_connect_via_info(&info)) { + i.client->status = DC_NET_ERROR; + dc_api_stack(i); + } break; + case DC_API_WS_CB: + dc_api_stack(i); /* .status is either NET_ERROR 4 closed or error or OK 4 esta */ case DC_API_REGISTER: break; case DC_API_STATUS: @@ -18,7 +18,7 @@ enum dc_status { /* theese are flags and should be and-checked */ DC_UNSET = 0, /* default value when enum is calloced */ DC_INCOMPLETE = 1 << 0, /* struct SHALL NOT be used by the ui, it is yet to be filled by api */ - DC_OK = 1 << 1, /* success status, api usually sets this after completion/filling of the strct */ + DC_OK = 1 << 1, /* success status and also ws established*/ DC_BAD_LOGIN = 1 << 2, /* login failed because of wrong credentials */ DC_VERIFICATION_NEEDED = 1 << 3, /* login: check email, click link/reg: tough luck ur IP flagd */ DC_CAPTCHA_NEEDED = 1 << 4, /* must solve captcha, tough luck, not impl, use browser login */ @@ -30,8 +30,10 @@ enum dc_status { /* theese are flags and should be and-checked */ DC_FROM_LWS = 1 << 10, /* LWS cb is the caller, so do not attempt to do lws_service (loop) */ DC_MUST_FREE = 1 << 11, /* cb pass: body must be freed when request is done with user_data */ DC_REQUEST_FAILED = 1 << 12, /* http request failed, reported to ui */ - DC_ERROR = 1 << 13 /* unknown error, non implemented non expected response */ -}; + DC_ERROR = 1 << 13, /* unknown error, non implemented non expected response */ + DC_NET_ERROR = 1 << 14, /* network failed or ws closed */ + DC_LEJP_CONSTRUCTED = 1 << 15 /* json parser was constructed */ +}; /* note: when checking status, first check for DC_OK, if it's set then disregard errors! */ enum dc_permissions { /* other permissions exist, but are not implemented/understood */ DC_ALL_PERMISSIONS = 1 << 3, /* this is incredibly retarded, why is this SEPARATE?!? - admins */ DC_CHANNEL_VIEW = 1 << 10, /* all enum fields here have values same as the server values */ @@ -63,7 +65,11 @@ enum dc_api_io_type { DC_API_LOGIN, /* i: pass a dc_client-tr1, to relogin FIX prev retd cl not create new */ /* o: the previously passed dc_client with set status, do not use yet! \/ */ DC_API_LOGIN_CB,/* i: used internally for passing response from http client to api, see source */ - /* o: to tell user that client is now fully filled and ready for use */ + /* o: to tell user that ->client is now fully filled and ready for use */ + DC_API_WS, /* i: internal, LOGIN_CB calls it to start websocket setup */ + /* o: ->status of websocket setup (DC_OK indicates success) */ + DC_API_WS_CB, /* i: internal, for passing response from ws lib to api, see source */ + /* o: N/A */ DC_API_REGISTER,/* i: pass a dc_client, to relogin FIX pr rt cl&cl->user not creat new */ /* o: the previously passed dc_client with set status */ DC_API_STATUS, /* i: N/A */ @@ -74,7 +80,7 @@ enum dc_api_io_type { /* o: prev passed dc_role but filled (or not: ->status may indicate error) */ DC_API_ATTACH /* i: attaches function to handle output types */ /* o: N/A */ -}; +}; /* do not confuse yourself, when for example login response is checked for errors, check client->status and not struct dc_api_io's member named status. that member is mostly only used internally. */ /* enum dc_status (* dc_api_attached_func) (struct dc_api_io, void * data); */ /* i tried simplifying but didn't work */ struct dc_api_io { /* output struct does NOT contain void * data (user pointer) from the input struct! */ DC_STRUCT_PREFIX /* mostly useless here but it's only a couple of bytes so wth */ @@ -108,12 +114,21 @@ char * dc_lws_headers[] = { "Authorization:", "Content-Type:" }; +enum dc_json_paths { /* lws reduces the following char array to uint8_t, so we can match easier */ + DC_JSON_OP, + DC_JSON_PATHS_LENGTH +} +char * dc_json_paths[] = { /* array of paths we are interested in */ + "op", +} struct dc_lws_pass { /* struct that is allocated for in dc_lws_cb unique per connection in void * us */ char * body; /* this contains post body and when _CB is called, it contains response */ size_t body_length; /* body is NULL terminated or NULL in case of failure */ char headers[DC_LWS_HEADERS_LENGTH][DC_LWS_MAX_HEADER_LENGTH]; /* nofree, a static 2d array */ int status; /* HTTP response code /\ headers contain request headers, then resp. */ struct dc_api_io api_io; /* so dc_api_io can decide what shall be passed into _CB */ + struct lejp_ctx json; /* holds a context for lejp */ + enum lejp_callbacks json_reason; /* holds last reason sent to json callback */ }; struct dc_client { DC_STRUCT_PREFIX @@ -300,7 +315,7 @@ void dc_attached_function_free (struct dc_attached_function * s) { } static int dc_lws_cb (struct lws *, enum lws_callback_reasons, void *, void *, size_t); static const struct lws_protocols dc_lws_protocols[] = { - {"dc", dc_lws_cb, sizeof(struct dc_lws_pass), DC_LWS_MAX_RX, 0, NULL, 0}, + {"dc", dc_lws_cb, /* sizeof(struct dc_lws_pass) */ 0 /* lws naj NE ALOCIRA */, DC_LWS_MAX_RX, 0, NULL, 0}, {NULL, NULL, 0, 0, 0, NULL, 0} }; #define DC_ISASQ(shortname) DC_ISA(struct dc_##shortname, shortname##s) /* in struct array of structs quick */ @@ -1,4 +1,4 @@ -int smprintf(char ** str, const char * format, ...) { /* allocates automaticalls (: */ +int smprintf (char ** str, const char * format, ...) { /* allocates automaticalls (: */ va_list ap, aq; va_start(ap, format); va_copy(aq, ap); @@ -9,8 +9,8 @@ int main (int argc, char * argv[]) { struct dc_program * p = dc_program_init(); struct dc_client * client = dc_client_init(); lws_set_log_level(0xFF /* all message types */, NULL /* not change output location - cerr */); - client->email = strdup(argv[1]); - client->password = strdup(argv[2]); + client->email = strdup(getenv("DC_E")); + client->password = strdup(getenv("DC_P")); struct dc_api_io i = { .program = p, .type = DC_API_LOGIN, |