diff options
Diffstat (limited to '')
-rw-r--r-- | src/api.c | 82 |
1 files changed, 70 insertions, 12 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: |