209 lines
6.4 KiB
C
209 lines
6.4 KiB
C
/*
|
|
* Copyright 2015 Artem Savkov <artem.savkov@gmail.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <libwebsockets.h>
|
|
|
|
#include "discord-websockets.h"
|
|
#include "discord-handlers.h"
|
|
|
|
static int discord_ws_send_payload(struct libwebsocket *wsi, const char *pload,
|
|
size_t psize)
|
|
{
|
|
int ret = 0;
|
|
unsigned char *buf = g_malloc0(LWS_SEND_BUFFER_PRE_PADDING + \
|
|
psize + LWS_SEND_BUFFER_POST_PADDING);
|
|
strncpy((char*)&buf[LWS_SEND_BUFFER_PRE_PADDING], pload, psize);
|
|
ret = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], psize,
|
|
LWS_WRITE_TEXT);
|
|
g_free(buf);
|
|
return ret;
|
|
}
|
|
|
|
gboolean discord_ws_keepalive_loop(gpointer data, gint fd,
|
|
b_input_condition cond)
|
|
{
|
|
struct im_connection *ic = data;
|
|
discord_data *dd = ic->proto_data;
|
|
|
|
if (dd->state == WS_READY) {
|
|
libwebsocket_callback_on_writable(dd->lwsctx, dd->lws);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean discord_ws_service_loop(gpointer data, gint fd,
|
|
b_input_condition cond)
|
|
{
|
|
struct im_connection *ic = data;
|
|
|
|
discord_data *dd = ic->proto_data;
|
|
|
|
libwebsocket_service(dd->lwsctx, 0);
|
|
|
|
if (dd->state == WS_CLOSING) {
|
|
imc_logout(ic, TRUE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
discord_ws_callback(struct libwebsocket_context *this,
|
|
struct libwebsocket *wsi,
|
|
enum libwebsocket_callback_reasons reason,
|
|
void *user, void *in, size_t len)
|
|
{
|
|
struct im_connection *ic = libwebsocket_context_user(this);
|
|
|
|
discord_data *dd = ic->proto_data;
|
|
switch(reason) {
|
|
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
|
dd->state = WS_CONNECTED;
|
|
libwebsocket_callback_on_writable(this, wsi);
|
|
break;
|
|
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
|
imcb_error(ic, "Websocket connection error");
|
|
if (in != NULL) {
|
|
imcb_error(ic, in);
|
|
}
|
|
b_event_remove(dd->keepalive_loop_id);
|
|
dd->state = WS_CLOSING;
|
|
break;
|
|
case LWS_CALLBACK_CLIENT_WRITEABLE:
|
|
if (dd->state == WS_CONNECTED) {
|
|
GString *buf = g_string_new("");
|
|
g_string_printf(buf, "{\"d\":{\"v\":3,\"token\":\"%s\",\"properties\":{\"$referring_domain\":\"\",\"$browser\":\"bitlbee-discord\",\"$device\":\"bitlbee\",\"$referrer\":\"\",\"$os\":\"linux\"}},\"op\":2}", dd->token);
|
|
discord_ws_send_payload(wsi, buf->str, buf->len);
|
|
g_string_free(buf, TRUE);
|
|
} else if (dd->state == WS_READY) {
|
|
GString *buf = g_string_new("");
|
|
|
|
g_string_printf(buf, "{\"op\":1,\"d\":%tu}", time(NULL));
|
|
discord_ws_send_payload(dd->lws, buf->str, buf->len);
|
|
g_string_free(buf, TRUE);
|
|
} else {
|
|
g_print("%s: Unhandled writable callback\n", __func__);
|
|
}
|
|
break;
|
|
case LWS_CALLBACK_CLIENT_RECEIVE:
|
|
{
|
|
size_t rpload = libwebsockets_remaining_packet_payload(wsi);
|
|
if (dd->ws_buf == NULL) {
|
|
dd->ws_buf = g_string_new("");
|
|
}
|
|
dd->ws_buf = g_string_append(dd->ws_buf, in);
|
|
if (rpload == 0) {
|
|
discord_parse_message(ic);
|
|
g_string_free(dd->ws_buf, TRUE);
|
|
dd->ws_buf = NULL;
|
|
}
|
|
break;
|
|
}
|
|
case LWS_CALLBACK_CLOSED:
|
|
b_event_remove(dd->keepalive_loop_id);
|
|
dd->state = WS_CLOSING;
|
|
libwebsocket_cancel_service(dd->lwsctx);
|
|
break;
|
|
case LWS_CALLBACK_ADD_POLL_FD:
|
|
{
|
|
struct lws_pollargs *pargs = in;
|
|
dd->main_loop_id = b_input_add(pargs->fd, B_EV_IO_READ,
|
|
discord_ws_service_loop, ic);
|
|
break;
|
|
}
|
|
case LWS_CALLBACK_DEL_POLL_FD:
|
|
b_event_remove(dd->main_loop_id);
|
|
break;
|
|
case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
|
|
{
|
|
struct lws_pollargs *pargs = in;
|
|
int flags = 0;
|
|
b_event_remove(dd->main_loop_id);
|
|
if (pargs->events & POLLIN) {
|
|
flags |= B_EV_IO_READ;
|
|
}
|
|
if (pargs->events & POLLOUT) {
|
|
flags |= B_EV_IO_WRITE;
|
|
}
|
|
dd->main_loop_id = b_input_add(pargs->fd, flags,
|
|
discord_ws_service_loop, ic);
|
|
break;
|
|
}
|
|
case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
|
|
case LWS_CALLBACK_GET_THREAD_ID:
|
|
case LWS_CALLBACK_LOCK_POLL:
|
|
case LWS_CALLBACK_UNLOCK_POLL:
|
|
case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
|
|
case LWS_CALLBACK_PROTOCOL_INIT:
|
|
case LWS_CALLBACK_PROTOCOL_DESTROY:
|
|
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
|
|
case LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH:
|
|
case LWS_CALLBACK_WSI_CREATE:
|
|
case LWS_CALLBACK_WSI_DESTROY:
|
|
// Ignoring these, this block should be removed when defult is set to
|
|
// stay silent.
|
|
break;
|
|
default:
|
|
g_print("%s: unknown rsn=%d\n", __func__, reason);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct libwebsocket_protocols protocols[] =
|
|
{
|
|
{ "http-only,chat", discord_ws_callback, 0, 0 },
|
|
{ NULL, NULL, 0, 0 } /* end */
|
|
};
|
|
|
|
int discord_ws_init(struct im_connection *ic, discord_data *dd)
|
|
{
|
|
struct lws_context_creation_info info;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
info.port = CONTEXT_PORT_NO_LISTEN;
|
|
info.protocols = protocols;
|
|
#ifndef LWS_NO_EXTENSIONS
|
|
info.extensions = libwebsocket_get_internal_extensions();
|
|
#else
|
|
info.extensions = NULL;
|
|
#endif
|
|
info.gid = -1;
|
|
info.uid = -1;
|
|
info.user = ic;
|
|
|
|
lws_set_log_level(0, NULL);
|
|
|
|
dd->lwsctx = libwebsocket_create_context(&info);
|
|
if (dd->lwsctx == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
dd->lws = libwebsocket_client_connect(dd->lwsctx, dd->gateway,
|
|
443, 1, "/", dd->gateway,
|
|
"discordapp.com",
|
|
protocols[0].name, -1);
|
|
return 0;
|
|
}
|
|
|
|
void discord_ws_cleanup(discord_data *dd)
|
|
{
|
|
if (dd->lwsctx != NULL) {
|
|
libwebsocket_context_destroy(dd->lwsctx);
|
|
}
|
|
}
|