initial commit

This commit is contained in:
snow32a
2026-06-17 00:10:06 +03:00
parent 72b86c4153
commit 62187e7d6e
35 changed files with 6170 additions and 0 deletions
Executable
+14
View File
@@ -0,0 +1,14 @@
CompileFlags:
CompilationDatabase: .
Remove:
- "-I/usr/include/pidgin"
- "-I/usr/include/gtk*"
- "-I/usr/include/glib*"
- "-I/usr/include/pango*"
- "-I/usr/include/libpurple"
- "-I/usr/lib/glib*"
- "-pthread"
Add:
- "--target=i686-w64-mingw32"
- "-I/usr/i686-w64-mingw32/include"
- "-I/usr/lib/gcc/i686-w64-mingw32/12/include"
Executable
+1
View File
@@ -0,0 +1 @@
compile_commands.json
Executable
BIN
View File
Binary file not shown.
+426
View File
@@ -0,0 +1,426 @@
#include "chatwnd.h"
#include <stdbool.h>
#include <windows.h>
#include "snoctrl.h"
#include "../discordtypes.h"
#include "../hmap/hashmap.h"
#include <libpng18/png.h>
#include <wingdi.h>
#include "pnghlp.h"
ChatWnd *chatwnds = NULL;
int chatwndcount = 0;
HFONT regfont;
HFONT boldfont;
HFONT h1font;
HFONT h2font;
HFONT h3font;
hmap pfp_map;
ChatWnd *GetChatControlDetails(HWND hwnd) {
for (int i = 0; i < chatwndcount; i++) {
if (chatwnds[i].hWnd == hwnd) {
return &chatwnds[i];
}
}
}
void ChatView_SetUserPfp(char *path, char *user) {
HANDLE f = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (f == INVALID_HANDLE_VALUE) {
MessageBoxA(NULL, "Error accessing the path for the pfp set!",
"Snow's Controls - ChatView", 0);
return;
}
LARGE_INTEGER size;
GetFileSizeEx(f, &size);
size_t file_size = (size_t)size.QuadPart;
uint8_t *buf = malloc(file_size);
DWORD read = 0;
ReadFile(f, buf, file_size, &read, NULL);
png_structp png =
png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
PngBuffer pngbuf = {buf, file_size, 0};
png_set_read_fn(png, &pngbuf, PngReadBufCB);
png_read_info(png, info);
BITMAPINFO bmi = {0};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = png_get_image_width(png, info);
bmi.bmiHeader.biHeight = -png_get_image_height(png, info); // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB; // THIS IS RAW - gordon ramsey
if (setjmp(png_jmpbuf(png))) {
png_destroy_read_struct(&png, &info, NULL);
// err
MessageBoxA(NULL, "zzzzzzzz", "zzzzzzzzz", 0);
}
png_set_expand(png);
png_set_strip_16(png);
png_set_filler(png, 0xFF,
PNG_FILLER_AFTER); // ensures RGBA or RGBX cause winblows
png_read_update_info(png, info);
int width = png_get_image_width(png, info);
int height = png_get_image_height(png, info);
png_bytep *rows = malloc(height * sizeof(png_bytep));
for (int y = 0; y < height; y++)
rows[y] = malloc(png_get_rowbytes(png, info));
// flat BGRA buffer for GDI
uint8_t *fbuf = malloc(width * height * 4);
png_read_image(png, rows);
for (int y = 0; y < height; y++) {
png_bytep src = rows[y];
uint8_t *dst = fbuf + y * width * 4;
for (int x = 0; x < width; x++) {
dst[x * 4 + 0] = src[x * 4 + 2]; // B
dst[x * 4 + 1] = src[x * 4 + 1]; // G
dst[x * 4 + 2] = src[x * 4 + 0]; // R
dst[x * 4 + 3] = src[x * 4 + 3]; // A
}
}
void *gdibuf = NULL;
HBITMAP bmp =
CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &gdibuf, NULL, 0);
// GDI allocates its own dumbahh buffer so we just memcpy to it
memcpy(gdibuf, fbuf, width * height * 4);
free(fbuf);
HDC memdc = CreateCompatibleDC(NULL);
SelectObject(memdc, bmp);
IntPfpTableItem it;
it.key = user;
it.bmp = memdc;
hashmap_set(pfp_map, &it);
CloseHandle(f);
}
void InsertChatMessage(DiscordMessage msg, HWND hwnd) {
ChatWnd *cwnd = GetChatControlDetails(hwnd);
cwnd->uimsgs =
realloc(cwnd->uimsgs, (cwnd->uimsgcnt + 1) * sizeof(GUIMessage));
cwnd->uimsgs[cwnd->uimsgcnt].msg = msg;
cwnd->uimsgs[cwnd->uimsgcnt].vw = hwnd;
cwnd->uimsgcnt++;
InvalidateRect(hwnd, NULL, FALSE);
UpdateWindow(hwnd); // forces WM_PAINT now, contentHeight is fresh
RECT clientRect;
GetClientRect(hwnd, &clientRect);
int viewHeight = clientRect.bottom - clientRect.top;
cwnd->scrollOffset = max(0, cwnd->contentHeight - viewHeight);
SCROLLINFO si = {sizeof(SCROLLINFO)};
si.fMask = SIF_POS;
si.nPos = cwnd->scrollOffset;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
InvalidateRect(hwnd, NULL, FALSE); // repaint with correct offset
}
void ClearChatControl(HWND hwnd) {
ChatWnd *cwnd = GetChatControlDetails(hwnd);
int count = cwnd->uimsgcnt;
cwnd->uimsgcnt = 0;
for (int i = 0; i < count; i++) {
DiscordMessage hmsg = (DiscordMessage)cwnd->uimsgs[i].msg;
free(hmsg.content);
free(hmsg.channelID);
free(hmsg.id);
free(hmsg.author.Username);
free(hmsg.author.DisplayName);
free(hmsg.author.id);
free(hmsg.author.avatar);
}
cwnd->uimsgs = realloc(cwnd->uimsgs, sizeof(GUIMessage));
}
LONG MeasureTotalMessageHeight(HWND hwnd) {}
static uint64_t pfp_hash(const void *item, uint64_t seed0, uint64_t seed1) {
const IntPfpTableItem *e = item;
return hashmap_sip(e->key, strlen(e->key), seed0, seed1);
}
static int pfp_compare(const void *a, const void *b, void *udata) {
return strcmp(((IntPfpTableItem *)a)->key, ((IntPfpTableItem *)b)->key);
}
static void pfp_free(void *pfpitem) {
IntPfpTableItem *item = pfpitem;
free(item->bmp);
free(item->key);
}
#define UsernameFieldHeight 15
#define PfpPadding 6
#define YPadding 4
#define PfpSize 40
#define MinMsgH PfpSize + 10
/*
| X
+------------+--------------------------------------------------------+
| |
| |
-X | |
| |
| |
+------------+
|
*/
BOOL IsStartMsg(ChatWnd *cwnd, int i) {
if (i > 0) {
return (strcmp(cwnd->uimsgs[i].msg.author.id,
cwnd->uimsgs[i - 1].msg.author.id) == 0)
? FALSE
: TRUE;
} else {
return TRUE;
}
}
BOOL ShouldGoUp(ChatWnd *cwnd, int i) {
if (i > 0) {
return (IsStartMsg(cwnd, i - 1) && !IsStartMsg(cwnd, i));
} else {
return FALSE;
}
}
LRESULT CALLBACK ChatWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
switch (uMsg) {
case WM_CREATE: {
chatwnds = realloc(chatwnds, (chatwndcount + 1) * sizeof(ChatWnd));
chatwnds[chatwndcount].hWnd = hwnd;
chatwnds[chatwndcount].uimsgcnt = 0;
chatwnds[chatwndcount].uimsgs = malloc(sizeof(GUIMessage));
chatwnds[chatwndcount].scrollOffset = 0;
chatwnds[chatwndcount].contentHeight = 0;
chatwnds[chatwndcount].pfpmap = pfp_map =
hashmap_new(sizeof(IntPfpTableItem), 256, 0, 0, pfp_hash,
pfp_compare, pfp_free, NULL);
chatwndcount++;
break;
}
case WM_PAINT: {
ChatWnd *cwnd = GetChatControlDetails(hwnd);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT clientRect;
GetClientRect(hwnd, &clientRect);
int viewHeight = clientRect.bottom - clientRect.top;
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
SetStretchBltMode(hdc, HALFTONE);
SetBrushOrgEx(hdc, 0, 0, NULL);
typedef struct {
int TextContentHeight;
int UnpaddedMessageHeight;
int ClampedUnpaddedMessageHeight;
int UnclampedTotalMessageHeight;
int TotalMessageHeight;
int MessageHeightForUse;
BOOL StartMsg;
BOOL PushBack;
} MessageDetails;
cwnd->contentHeight = 0;
MessageDetails msgdet[cwnd->uimsgcnt];
for (int i = 0; i < cwnd->uimsgcnt; i++) {
RECT contr = {0, 0, clientRect.right - clientRect.left - 6 - 6 - 40,
0};
DrawTextA(hdc, cwnd->uimsgs[i].msg.content,
strlen(cwnd->uimsgs[i].msg.content), &contr,
DT_CALCRECT | DT_WORDBREAK);
// printf("%i | %i\n", i, contr.bottom - contr.top);
msgdet[i].TextContentHeight = contr.bottom - contr.top;
msgdet[i].StartMsg = IsStartMsg(cwnd, i);
if (msgdet[i].StartMsg) {
if (i + 1 < cwnd->uimsgcnt) {
if (!IsStartMsg(cwnd, i + 1)) {
msgdet[i].PushBack = TRUE;
} else {
msgdet[i].PushBack = FALSE;
}
} else {
msgdet[i].PushBack = FALSE;
}
if (msgdet[i].PushBack) {
msgdet[i].UnpaddedMessageHeight =
15 + msgdet[i].TextContentHeight;
msgdet[i].TotalMessageHeight =
15 + msgdet[i].TextContentHeight;
} else {
msgdet[i].UnpaddedMessageHeight =
max(40, 15 + msgdet[i].TextContentHeight);
msgdet[i].TotalMessageHeight =
max(40, 15 + msgdet[i].TextContentHeight)+6;
}
} else {
msgdet[i].TotalMessageHeight = msgdet[i].TextContentHeight ;
msgdet[i].PushBack = FALSE;
}
cwnd->contentHeight += msgdet[i].TotalMessageHeight;
}
RECT msgrect;
msgrect = clientRect;
msgrect.top -= cwnd->scrollOffset;
for (int i = 0; i < cwnd->uimsgcnt; i++) {
if (msgrect.top + msgdet[i].TotalMessageHeight < 0) {
msgrect.top += msgdet[i].TotalMessageHeight;
continue;
}
if (msgrect.top > clientRect.bottom) {
break;
}
DiscordMessage msg = cwnd->uimsgs[i].msg;
if (msgdet[i].StartMsg) {
RECT pfprect = msgrect;
pfprect.left += 6;
// pfprect.top += 6;
pfprect.bottom = pfprect.top + 40;
pfprect.right = pfprect.left + 40;
IntPfpTableItem searchfilters;
searchfilters.key = msg.author.id;
IntPfpTableItem *pfplookup =
hashmap_get(pfp_map, &searchfilters);
if (pfplookup) {
HBITMAP hBitmap =
(HBITMAP)GetCurrentObject(hdc, OBJ_BITMAP);
BITMAP bmp;
GetObject(hBitmap, sizeof(BITMAP), &bmp);
int width = bmp.bmWidth;
int height = bmp.bmHeight;
BITMAP bmpInfo;
HBITMAP hSrcBmp =
(HBITMAP)GetCurrentObject(pfplookup->bmp, OBJ_BITMAP);
GetObject(hSrcBmp, sizeof(BITMAP), &bmpInfo);
StretchBlt(hdc, pfprect.left, pfprect.top, 40, 40,
pfplookup->bmp, 0, 0, bmpInfo.bmWidth,
bmpInfo.bmHeight, SRCCOPY);
} else {
FillRect(hdc, &pfprect, (HBRUSH)BLACK_BRUSH);
}
RECT usernamerect = msgrect;
usernamerect.left += (6 + 40 + 6);
// usernamerect.top += 6;
char *rndname = cwnd->uimsgs[i].msg.author.DisplayName
? cwnd->uimsgs[i].msg.author.DisplayName
: cwnd->uimsgs[i].msg.author.Username;
SelectObject(hdc, boldfont);
DrawTextA(hdc, rndname, strlen(rndname), &usernamerect, 0);
RECT contentrect = msgrect;
contentrect.left += (6 + 40 + 6);
contentrect.top += 15;
contentrect.bottom =
contentrect.top + msgdet[i].TextContentHeight;
SelectObject(hdc, regfont);
DrawTextA(hdc, cwnd->uimsgs[i].msg.content,
strlen(cwnd->uimsgs[i].msg.content), &contentrect,
DT_WORDBREAK);
} else {
RECT contentrect = msgrect;
contentrect.left += (6 + 40 + 6);
// contentrect.top += 6;
contentrect.bottom =
contentrect.top + msgdet[i].TextContentHeight;
SelectObject(hdc, regfont);
DrawTextA(hdc, cwnd->uimsgs[i].msg.content,
strlen(cwnd->uimsgs[i].msg.content), &contentrect,
DT_WORDBREAK);
}
msgrect.top += msgdet[i].TotalMessageHeight;
}
/* ── 4. SYNC SCROLL BAR ───────────────────────────────────────────────
*/
SCROLLINFO si = {sizeof(SCROLLINFO)};
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = 0;
si.nMax = cwnd->contentHeight - 1;
si.nPage = viewHeight;
si.nPos = cwnd->scrollOffset;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
EndPaint(hwnd, &ps);
break;
}
case WM_VSCROLL: {
ChatWnd *cwnd = GetChatControlDetails(hwnd);
RECT clientRect;
GetClientRect(hwnd, &clientRect);
int viewHeight = clientRect.bottom - clientRect.top;
int maxScroll = max(0, cwnd->contentHeight - viewHeight);
SCROLLINFO si = {sizeof(SCROLLINFO)};
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, SB_VERT, &si);
switch (LOWORD(wParam)) {
case SB_LINEUP:
cwnd->scrollOffset -= 20;
break;
case SB_LINEDOWN:
cwnd->scrollOffset += 20;
break;
case SB_PAGEUP:
cwnd->scrollOffset -= viewHeight;
break;
case SB_PAGEDOWN:
cwnd->scrollOffset += viewHeight;
break;
case SB_THUMBTRACK:
cwnd->scrollOffset = si.nTrackPos;
break;
case SB_THUMBPOSITION:
cwnd->scrollOffset = si.nPos;
break;
case SB_TOP:
cwnd->scrollOffset = 0;
break;
case SB_BOTTOM:
cwnd->scrollOffset = maxScroll;
break;
}
cwnd->scrollOffset = max(0, min(cwnd->scrollOffset, maxScroll));
si.fMask = SIF_POS;
si.nPos = cwnd->scrollOffset;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
InvalidateRect(hwnd, NULL, FALSE);
break;
}
case WM_DESTROY:
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <windows.h>
#include <wingdi.h>
#include "snoctrl.h"
#include "../discordtypes.h"
#include "../hmap/hashmap.h"
typedef struct {
DiscordMessage msg;
HWND vw;
} GUIMessage;
typedef struct {
GUIMessage* uimsgs;
int uimsgcnt;
HWND hWnd;
int scrollOffset;
int contentHeight;
hmap pfpmap;
} ChatWnd;
typedef struct{
char *key;
HDC bmp;
} IntPfpTableItem;
ChatWnd* GetChatControlDetails(HWND hwnd);
void InsertChatMessage(DiscordMessage msg, HWND hwnd);
void ClearChatControl(HWND hwnd);
LRESULT CALLBACK ChatWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam);
void ChatView_SetUserPfp(char *path, char *user);
+203
View File
@@ -0,0 +1,203 @@
#include "guildwnd.h"
#include <libpng18/png.h>
#include <stdbool.h>
#include "pnghlp.h"
#include <windows.h>
#include <windowsx.h>
#include <wingdi.h>
int guildwndcount = 0;
GuildWnd *guildwnds = NULL;
GuildWnd *GetGuildControlDetails(HWND hwnd) {
for (int i = 0; i < guildwndcount; i++) {
if (guildwnds[i].hWnd == hwnd) {
return &guildwnds[i];
}
}
}
void GuildView_InsertGuild(HWND hwnd, GUIGuild gld) {
GuildWnd *guildwnd = GetGuildControlDetails(hwnd);
guildwnd->uigldcnt++;
guildwnd->uiglds =
realloc(guildwnd->uiglds, guildwnd->uigldcnt * sizeof(GUIGuild));
GUIGuild toins = gld;
toins.title = strdup(toins.title);
toins.id = strdup(toins.id);
guildwnd->uiglds[guildwnd->uigldcnt - 1] = toins;
InvalidateRect(hwnd, NULL, TRUE);
}
void GuildView_SetIcon(HWND hwnd, HDC icon, char *id) {
GuildWnd *guildwnd = GetGuildControlDetails(hwnd);
for (int i = 0; i < guildwnd->uigldcnt; i++) {
if (strcmp(guildwnd->uiglds[i].id, id) == 0) {
guildwnds->uiglds[i].icon = icon;
break;
}
}
}
void GuildView_SetMentionCount(HWND hwnd, char *id, int cnt) {
GuildWnd *guildwnd = GetGuildControlDetails(hwnd);
for (int i = 0; i < guildwnd->uigldcnt; i++) {
if (strcmp(guildwnd->uiglds[i].id, id) == 0) {
guildwnds->uiglds[i].MentionCount = cnt;
break;
}
}
InvalidateRect(hwnd, NULL, FALSE);
}
void GuildView_SetDMsIcon(HWND hwnd, HDC icon) {
GuildWnd *guildwnd = GetGuildControlDetails(hwnd);
guildwnds->dmsicon = icon;
}
LRESULT CALLBACK GuildWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
switch (uMsg) {
case WM_CREATE:
guildwndcount++;
guildwnds = realloc(guildwnds, guildwndcount * sizeof(GuildWnd));
guildwnds[guildwndcount - 1].uigldcnt = 0;
guildwnds[guildwndcount - 1].uiglds = NULL;
guildwnds[guildwndcount - 1].hWnd = hwnd;
guildwnds[guildwndcount - 1].scrollOffset = 0;
return 0;
break;
case WM_PAINT: {
RECT ctlrect;
GetClientRect(hwnd, &ctlrect);
GuildWnd *gwnd = GetGuildControlDetails(hwnd);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ctlrect, (HBRUSH)(COLOR_3DFACE + 1));
SetStretchBltMode(hdc, HALFTONE);
RECT rc = ctlrect;
int ctlw = ctlrect.right - ctlrect.left;
int guildsize = ctlw - 16;
rc.left += 8;
rc.right = rc.left + guildsize;
rc.top += 8;
rc.bottom = rc.top + guildsize;
if (gwnd->dmsicon) {
BITMAP bmpInfo;
HBITMAP hSrcBmp =
(HBITMAP)GetCurrentObject(gwnd->dmsicon, OBJ_BITMAP);
GetObject(hSrcBmp, sizeof(BITMAP), &bmpInfo);
StretchBlt(hdc, rc.left, rc.top, guildsize, guildsize,
gwnd->dmsicon, 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight,
SRCCOPY);
} else {
FillRect(hdc, &rc, (HBRUSH)(COLOR_3DSHADOW + 1));
}
HPEN hPenDark = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
HPEN hPenLight =
CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
HPEN hOldPen;
hOldPen = SelectObject(hdc, hPenDark);
MoveToEx(hdc, rc.left, rc.top + guildsize + 8, NULL);
LineTo(hdc, rc.right, rc.top + guildsize + 8);
SelectObject(hdc, hPenLight);
MoveToEx(hdc, rc.left, rc.top + guildsize + 1 + 8, NULL);
LineTo(hdc, rc.right, rc.top + guildsize + 1 + 8);
SelectObject(hdc, hOldPen);
DeleteObject(hPenDark);
DeleteObject(hPenLight);
for (int i = 0; i < gwnd->uigldcnt; i++) {
RECT rc = ctlrect;
rc.left += 8;
rc.right = rc.left + guildsize;
rc.top += i * (guildsize + 8) + 8 + guildsize + 10 + 8;
rc.bottom = rc.top + guildsize;
if (i == gwnd->selIndex) {
RECT rcs = rc;
rcs.left = ctlrect.left;
rcs.right = ctlrect.right;
rcs.top -= 4;
rcs.bottom += 4;
FillRect(hdc, &rcs, (HBRUSH)(COLOR_HIGHLIGHT + 1));
}
if (gwnd->uiglds[i].icon) {
BITMAP bmpInfo;
HBITMAP hSrcBmp =
(HBITMAP)GetCurrentObject(gwnd->uiglds[i].icon, OBJ_BITMAP);
GetObject(hSrcBmp, sizeof(BITMAP), &bmpInfo);
StretchBlt(hdc, rc.left, rc.top, guildsize, guildsize,
gwnd->uiglds[i].icon, 0, 0, bmpInfo.bmWidth,
bmpInfo.bmHeight, SRCCOPY);
} else {
FillRect(hdc, &rc, (HBRUSH)(COLOR_3DSHADOW + 1));
}
if (gwnd->uiglds[i].MentionCount) {
HBRUSH indicatorbrush = CreateSolidBrush((COLORREF)0x000000FF);
RECT rc = ctlrect;
rc.left += 8;
rc.right = rc.left + guildsize;
rc.top += i * (guildsize + 8) + 8 + guildsize + 10 + 8;
rc.bottom = rc.top + guildsize;
rc.top = i * (guildsize + 8) + 8 + guildsize + 10 + 8 + guildsize - 16;
rc.left = rc.left + guildsize - 16;
FillRect(hdc, &rc, indicatorbrush);
char unreadtxt[16];
wsprintf(unreadtxt,"%i",gwnd->uiglds[i].MentionCount);
DrawText(hdc,unreadtxt,strlen(unreadtxt),&rc,DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
}
EndPaint(hwnd, &ps);
return 0;
}
case WM_LBUTTONUP: {
GuildWnd *gwnd = GetGuildControlDetails(hwnd);
RECT ctlrect;
GetClientRect(hwnd, &ctlrect);
int ctlw = ctlrect.right - ctlrect.left;
int guildsize = ctlw - 16;
if (GET_Y_LPARAM(lParam) < guildsize + 10 + 8) {
if (gwnd->selIndex != 1) {
gwnd->selIndex = -1;
NMGUILDVIEW nm = {0};
nm.hdr.hwndFrom = hwnd;
nm.hdr.idFrom = GetDlgCtrlID(hwnd);
nm.hdr.code = GVN_ITEMCLICK;
nm.index = GUILDVIEW_DMS;
nm.guild = (GUIGuild){NULL};
nm.pt = (POINT){GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
SendMessage(GetParent(hwnd), WM_NOTIFY, (WPARAM)nm.hdr.idFrom,
(LPARAM)&nm);
InvalidateRect(hwnd, NULL, FALSE);
}
} else {
int index = (GET_Y_LPARAM(lParam) - 8 - guildsize - 10 - 8) /
(guildsize + 8);
if (index != gwnd->selIndex) {
NMGUILDVIEW nm = {0};
nm.hdr.hwndFrom = hwnd;
nm.hdr.idFrom = GetDlgCtrlID(hwnd);
nm.hdr.code = GVN_ITEMCLICK;
nm.index = index;
nm.guild = gwnd->uiglds[index];
nm.pt = (POINT){GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
gwnd->selIndex = index;
SendMessage(GetParent(hwnd), WM_NOTIFY, (WPARAM)nm.hdr.idFrom,
(LPARAM)&nm);
InvalidateRect(hwnd, NULL, FALSE);
}
}
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
+37
View File
@@ -0,0 +1,37 @@
#include <windows.h>
#include "../hmap/hashmap.h"
#define GVN_ITEMCLICK (0U - 1800U)
#define GUILDVIEW_MAGIC 0xACC0
#define GUILDVIEW_DMS -1
typedef struct {
char *title;
char *id;
HDC icon;
int MentionCount;
void* data;
} GUIGuild;
typedef struct {
NMHDR hdr;
int index;
GUIGuild guild;
POINT pt;
} NMGUILDVIEW;
typedef NMGUILDVIEW* LPNMGUILDVIEW;
typedef struct {
GUIGuild* uiglds;
int uigldcnt;
HWND hWnd;
int scrollOffset;
int contentHeight;
HDC dmsicon;
int selIndex;
} GuildWnd;
LRESULT CALLBACK GuildWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam);
void GuildView_InsertGuild(HWND hwnd, GUIGuild gld);
void GuildView_SetIcon(HWND hwnd, HDC icon, char *id);
void GuildView_SetDMsIcon(HWND hwnd, HDC icon);
void GuildView_SetMentionCount(HWND hwnd, char *id, int cnt);
+36
View File
@@ -0,0 +1,36 @@
#include <windows.h>
#include "snoctrl.h"
void InitSnowsControls() {
NONCLIENTMETRICS ncm = { sizeof(NONCLIENTMETRICS) };
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
regfont=CreateFontIndirect(&ncm.lfMessageFont);
ncm.lfMessageFont.lfWeight=FW_BOLD;
boldfont=CreateFontIndirect(&ncm.lfMessageFont);
ncm.lfMessageFont.lfHeight=-32;
h1font=CreateFontIndirect(&ncm.lfMessageFont);
ncm.lfMessageFont.lfHeight=-24;
h2font=CreateFontIndirect(&ncm.lfMessageFont);
h3font = boldfont;
const char CLASS_NAME_CHAT[] = "BackcordChat";
WNDCLASS wcc = {};
wcc.lpfnWndProc = ChatWndProc;
wcc.hInstance = GetModuleHandle(NULL);
wcc.lpszClassName = CLASS_NAME_CHAT;
wcc.hCursor = LoadCursorA(NULL, IDC_ARROW);
RegisterClass(&wcc);
const char CLASS_NAME_GUILD[] = "BackcordGuild";
WNDCLASS wcg = {};
wcg.lpfnWndProc = GuildWndProc;
wcg.hInstance = GetModuleHandle(NULL);
wcg.lpszClassName = CLASS_NAME_GUILD;
wcg.hCursor = LoadCursorA(NULL, IDC_ARROW);
RegisterClass(&wcg);
}
+275
View File
@@ -0,0 +1,275 @@
#include "pnghlp.h"
#include <libloaderapi.h>
#include <string.h>
#include <windows.h>
void PngReadBufCB(png_structp png, png_bytep out, png_size_t length) {
PngBuffer *buf = png_get_io_ptr(png);
memcpy(out, buf->data + buf->pos, length);
buf->pos += length;
}
HDC LoadPNGImage(char *path) {
HANDLE f = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (f == INVALID_HANDLE_VALUE) {
MessageBoxA(NULL, "Error accessing the path!", "PNG Helper", 0);
return NULL;
}
LARGE_INTEGER size;
GetFileSizeEx(f, &size);
size_t file_size = (size_t)size.QuadPart;
uint8_t *buf = malloc(file_size);
DWORD read = 0;
ReadFile(f, buf, file_size, &read, NULL);
png_structp png =
png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
PngBuffer pngbuf = {buf, file_size, 0};
png_set_read_fn(png, &pngbuf, PngReadBufCB);
png_read_info(png, info);
BITMAPINFO bmi = {0};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = png_get_image_width(png, info);
bmi.bmiHeader.biHeight = -png_get_image_height(png, info); // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB; // THIS IS RAW - gordon ramsey
if (setjmp(png_jmpbuf(png))) {
png_destroy_read_struct(&png, &info, NULL);
// err
MessageBoxA(NULL, "zzzzzzzz", "zzzzzzzzz", 0);
return NULL;
}
png_set_expand(png);
png_set_strip_16(png);
png_set_filler(png, 0xFF,
PNG_FILLER_AFTER); // ensures RGBA or RGBX cause winblows
png_read_update_info(png, info);
int width = png_get_image_width(png, info);
int height = png_get_image_height(png, info);
png_bytep *rows = malloc(height * sizeof(png_bytep));
for (int y = 0; y < height; y++)
rows[y] = malloc(png_get_rowbytes(png, info));
// flat BGRA buffer for GDI
uint8_t *fbuf = malloc(width * height * 4);
png_read_image(png, rows);
for (int y = 0; y < height; y++) {
png_bytep src = rows[y];
uint8_t *dst = fbuf + y * width * 4;
for (int x = 0; x < width; x++) {
dst[x * 4 + 0] = src[x * 4 + 2]; // B
dst[x * 4 + 1] = src[x * 4 + 1]; // G
dst[x * 4 + 2] = src[x * 4 + 0]; // R
dst[x * 4 + 3] = src[x * 4 + 3]; // A
}
}
void *gdibuf = NULL;
HBITMAP bmp =
CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &gdibuf, NULL, 0);
// GDI allocates its own dumbahh buffer so we just memcpy to it
memcpy(gdibuf, fbuf, width * height * 4);
free(fbuf);
HDC memdc = CreateCompatibleDC(NULL);
SelectObject(memdc, bmp);
CloseHandle(f);
return memdc;
}
HBITMAP LoadPNGBitmap(char *path) {
HANDLE f = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (f == INVALID_HANDLE_VALUE) {
MessageBoxA(NULL, "Error accessing the path!", "PNG Helper", 0);
return NULL;
}
LARGE_INTEGER size;
GetFileSizeEx(f, &size);
size_t file_size = (size_t)size.QuadPart;
uint8_t *buf = malloc(file_size);
DWORD read = 0;
ReadFile(f, buf, file_size, &read, NULL);
png_structp png =
png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
PngBuffer pngbuf = {buf, file_size, 0};
png_set_read_fn(png, &pngbuf, PngReadBufCB);
png_read_info(png, info);
BITMAPINFO bmi = {0};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = png_get_image_width(png, info);
bmi.bmiHeader.biHeight = -png_get_image_height(png, info); // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB; // THIS IS RAW - gordon ramsey
if (setjmp(png_jmpbuf(png))) {
png_destroy_read_struct(&png, &info, NULL);
// err
MessageBoxA(NULL, "zzzzzzzz", "zzzzzzzzz", 0);
return NULL;
}
png_set_expand(png);
png_set_strip_16(png);
png_set_filler(png, 0xFF,
PNG_FILLER_AFTER); // ensures RGBA or RGBX cause winblows
png_read_update_info(png, info);
int width = png_get_image_width(png, info);
int height = png_get_image_height(png, info);
png_bytep *rows = malloc(height * sizeof(png_bytep));
for (int y = 0; y < height; y++)
rows[y] = malloc(png_get_rowbytes(png, info));
// flat BGRA buffer for GDI
uint8_t *fbuf = malloc(width * height * 4);
png_read_image(png, rows);
for (int y = 0; y < height; y++) {
png_bytep src = rows[y];
uint8_t *dst = fbuf + y * width * 4;
for (int x = 0; x < width; x++) {
dst[x * 4 + 0] = src[x * 4 + 2]; // B
dst[x * 4 + 1] = src[x * 4 + 1]; // G
dst[x * 4 + 2] = src[x * 4 + 0]; // R
dst[x * 4 + 3] = src[x * 4 + 3]; // A
}
}
void *gdibuf = NULL;
HBITMAP bmp =
CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &gdibuf, NULL, 0);
// GDI allocates its own dumbahh buffer so we just memcpy to it
memcpy(gdibuf, fbuf, width * height * 4);
free(fbuf);
return bmp;
}
HDC LoadPNGImageFromBytes(char *bytes, int buflen) {
png_structp png =
png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
PngBuffer pngbuf = {bytes, buflen, 0};
png_set_read_fn(png, &pngbuf, PngReadBufCB);
png_read_info(png, info);
BITMAPINFO bmi = {0};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = png_get_image_width(png, info);
bmi.bmiHeader.biHeight = -png_get_image_height(png, info); // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB; // THIS IS RAW - gordon ramsey
if (setjmp(png_jmpbuf(png))) {
png_destroy_read_struct(&png, &info, NULL);
// err
MessageBoxA(NULL, "zzzzzzzz", "zzzzzzzzz", 0);
return NULL;
}
png_set_expand(png);
png_set_strip_16(png);
png_set_filler(png, 0xFF,
PNG_FILLER_AFTER); // ensures RGBA or RGBX cause winblows
png_read_update_info(png, info);
int width = png_get_image_width(png, info);
int height = png_get_image_height(png, info);
png_bytep *rows = malloc(height * sizeof(png_bytep));
for (int y = 0; y < height; y++)
rows[y] = malloc(png_get_rowbytes(png, info));
// flat BGRA buffer for GDI
uint8_t *fbuf = malloc(width * height * 4);
png_read_image(png, rows);
for (int y = 0; y < height; y++) {
png_bytep src = rows[y];
uint8_t *dst = fbuf + y * width * 4;
for (int x = 0; x < width; x++) {
dst[x * 4 + 0] = src[x * 4 + 2]; // B
dst[x * 4 + 1] = src[x * 4 + 1]; // G
dst[x * 4 + 2] = src[x * 4 + 0]; // R
dst[x * 4 + 3] = src[x * 4 + 3]; // A
}
}
void *gdibuf = NULL;
HBITMAP bmp =
CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &gdibuf, NULL, 0);
// GDI allocates its own dumbahh buffer so we just memcpy to it
memcpy(gdibuf, fbuf, width * height * 4);
free(fbuf);
HDC memdc = CreateCompatibleDC(NULL);
SelectObject(memdc, bmp);
return memdc;
}
HDC LoadPNGImageFromResource(HMODULE hModule, int res) {
HRSRC resf = FindResource(hModule, MAKEINTRESOURCE(res), RT_RCDATA);
DWORD len = SizeofResource(hModule, resf);
HGLOBAL resl = LoadResource(hModule, resf);
void* resbuf = LockResource(resl);
png_structp png =
png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
PngBuffer pngbuf = {resbuf, len, 0};
png_set_read_fn(png, &pngbuf, PngReadBufCB);
png_read_info(png, info);
BITMAPINFO bmi = {0};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = png_get_image_width(png, info);
bmi.bmiHeader.biHeight = -png_get_image_height(png, info); // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB; // THIS IS RAW - gordon ramsey
if (setjmp(png_jmpbuf(png))) {
png_destroy_read_struct(&png, &info, NULL);
// err
MessageBoxA(NULL, "zzzzzzzz", "zzzzzzzzz", 0);
return NULL;
}
png_set_expand(png);
png_set_strip_16(png);
png_set_filler(png, 0xFF,
PNG_FILLER_AFTER); // ensures RGBA or RGBX cause winblows
png_read_update_info(png, info);
int width = png_get_image_width(png, info);
int height = png_get_image_height(png, info);
png_bytep *rows = malloc(height * sizeof(png_bytep));
for (int y = 0; y < height; y++)
rows[y] = malloc(png_get_rowbytes(png, info));
// flat BGRA buffer for GDI
uint8_t *fbuf = malloc(width * height * 4);
png_read_image(png, rows);
for (int y = 0; y < height; y++) {
png_bytep src = rows[y];
uint8_t *dst = fbuf + y * width * 4;
for (int x = 0; x < width; x++) {
dst[x * 4 + 0] = src[x * 4 + 2]; // B
dst[x * 4 + 1] = src[x * 4 + 1]; // G
dst[x * 4 + 2] = src[x * 4 + 0]; // R
dst[x * 4 + 3] = src[x * 4 + 3]; // A
}
}
void *gdibuf = NULL;
HBITMAP bmp =
CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &gdibuf, NULL, 0);
// GDI allocates its own dumbahh buffer so we just memcpy to it
memcpy(gdibuf, fbuf, width * height * 4);
free(fbuf);
HDC memdc = CreateCompatibleDC(NULL);
SelectObject(memdc, bmp);
return memdc;
}
+13
View File
@@ -0,0 +1,13 @@
#include <libpng18/png.h>
#include <stdint.h>
#include <windows.h>
typedef struct {
const uint8_t *data;
size_t size;
size_t pos;
} PngBuffer;
void PngReadBufCB(png_structp png, png_bytep out, png_size_t length);
HDC LoadPNGImage(char *path);
HDC LoadPNGImageFromBytes(char *bytes, int buflen);
HDC LoadPNGImageFromResource(HMODULE hModule, int res);
HBITMAP LoadPNGBitmap(char *path);
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "chatwnd.h"
#include "guildwnd.h"
extern HFONT regfont;
extern HFONT boldfont;
extern HFONT h1font;
extern HFONT h2font;
extern HFONT h3font;
void InitSnowsControls();
Executable
+3
View File
@@ -0,0 +1,3 @@
#define user_agent \
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " \
"Chrome/142.0.0.0 Safari/537.36"
Executable
+2
View File
@@ -0,0 +1,2 @@
category: id='1479059798421930076' name='Text Channels'
category: id='1450471843638284413' name='Text Channels'
Executable
+433
View File
@@ -0,0 +1,433 @@
#include "discordtypes.h"
#include "http.h"
#include "config.h"
#include <windows.h>
#include <winnt.h>
#include <winternl.h>
#include <string.h>
#include <cjson/cJSON.h>
#include "ws.h"
#include "globals.h"
#include "hmap/hashmap.h"
hmap GuildsTable = NULL;
hmap ChannelsTable = NULL;
DiscordChannel *PrivateChannels = NULL;
int PrivateChannelCount = 0;
static uint64_t GuildHash(const void *item, uint64_t seed0, uint64_t seed1) {
const DiscordGuild *e = item;
return hashmap_sip(e->id, strlen(e->id), seed0, seed1);
}
static int GuildCompare(const void *a, const void *b, void *udata) {
return strcmp(((DiscordGuild *)a)->id, ((DiscordGuild *)b)->id);
}
static void GuildFree(void *item) {}
static uint64_t ChannelHash(const void *item, uint64_t seed0, uint64_t seed1) {
const DiscordChannel *e = item;
return hashmap_sip(e->id, strlen(e->id), seed0, seed1);
}
static int ChannelCompare(const void *a, const void *b, void *udata) {
return strcmp(((DiscordChannel *)a)->id, ((DiscordChannel *)b)->id);
}
static void ChannelFree(void *item) {}
extern void onDiscordGuildLoad(DiscordGuild *guild, char *id);
extern void onDiscordUpdatedGuildReadState(DiscordGuild gld);
int DiscordSendMessage(const char *channelID, const char *content) {
cJSON *payload = cJSON_CreateObject();
cJSON *name = cJSON_CreateString(content);
cJSON_AddItemToObject(payload, "content", name);
char *endpoint = malloc(28 + strlen(channelID));
endpoint[0] = '\0';
strcat(endpoint, "/api/v9/channels/");
strcat(endpoint, channelID);
strcat(endpoint, "/messages");
char *forNothingGng;
char *payld = cJSON_Print(payload);
long resplen;
char headers[17 + strlen(token) + 1];
wsprintf(headers, "Authorization: %s\r\n", token);
SendShortHTTPReq("discord.com", "POST", endpoint, headers, user_agent,
"application/json", payld, strlen(payld), &forNothingGng,
&resplen);
free(payld);
cJSON_free(payload);
free(forNothingGng);
}
int DiscordListGuildChannels(char *guildID, DiscordChannel **out) {
DiscordGuild *gld;
DiscordGuild search;
search.id = guildID;
gld = hashmap_get(GuildsTable, &search);
DiscordChannel *arr1 = (RtlAllocateHeap(
GetProcessHeap(), 0, sizeof(DiscordChannel) * gld->ChannelCount));
memset(arr1, 0, sizeof(DiscordChannel) * gld->ChannelCount);
*out = arr1;
for (int i = 0; i < gld->ChannelCount; i++) {
(*out)[i] = gld->channels[i];
}
return gld->ChannelCount;
}
char *DiscordFetchTmpPfp(char *userID, char *hash) {
char *endpoint = malloc(80);
wsprintfA(endpoint, "/avatars/%s/%s.png?size=512", userID, hash);
char *HTTPResp;
long resplen;
char headers[17 + strlen(token) + 1];
wsprintf(headers, "Authorization: %s\r\n", token);
SendShortHTTPReq("cdn.discordapp.com", "GET", endpoint, headers, user_agent,
"application/json", NULL, 0, &HTTPResp, &resplen);
free(endpoint);
char tempPath[MAX_PATH];
DWORD len = GetTempPathA(MAX_PATH, tempPath);
char *path = malloc(MAX_PATH + strlen(hash) + 1);
wsprintfA(path, "%s%s.png", tempPath, hash);
HANDLE file = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD written;
if (file == INVALID_HANDLE_VALUE) {
MessageBoxA(NULL,
"oof, failed opening the file to save the tmp pfp file!",
"Backcord - DiscordFetchTmpPfp", 0);
free(path);
return NULL;
}
WriteFile(file, HTTPResp, resplen, &written, NULL);
CloseHandle(file);
free(HTTPResp);
return path;
}
char *DiscordFetchTmpGuildIcon(char *guildID, char *hash) {
char *endpoint = malloc(80);
wsprintfA(endpoint, "/icons/%s/%s.png?size=512", guildID, hash);
char *HTTPResp;
long resplen;
char headers[17 + strlen(token) + 1];
wsprintf(headers, "Authorization: %s\r\n", token);
SendShortHTTPReq("cdn.discordapp.com", "GET", endpoint, headers, user_agent,
"application/json", NULL, 0, &HTTPResp, &resplen);
free(endpoint);
char tempPath[MAX_PATH];
DWORD len = GetTempPathA(MAX_PATH, tempPath);
char *path = malloc(MAX_PATH + strlen(hash) + 1);
wsprintfA(path, "%s%s.png", tempPath, hash);
HANDLE file = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD written;
if (file == INVALID_HANDLE_VALUE) {
MessageBoxA(NULL,
"oof, failed opening the file to save the tmp icon file!",
"Backcord - Guild Icon Retrieval", 0);
free(path);
return NULL;
}
WriteFile(file, HTTPResp, resplen, &written, NULL);
CloseHandle(file);
free(HTTPResp);
return path;
}
int DiscordGetChannelHistory(const char *channelID, unsigned int amount,
DiscordMessage **msgs) {
cJSON *payload = cJSON_CreateObject();
char *endpoint = malloc(38 + strlen(channelID));
memset((endpoint), 0, (27 + strlen(channelID)));
strcat(endpoint, "/api/v9/channels/");
strcat(endpoint, channelID);
strcat(endpoint, "/messages?limit=");
char *amountstr = malloc(3 * sizeof(char));
wsprintf(amountstr, "%i", amount);
strcat(endpoint, amountstr);
free(amountstr);
char *HTTPResp;
long resplen;
char headers[17 + strlen(token) + 1];
wsprintf(headers, "Authorization: %s\r\n", token);
SendShortHTTPReq("discord.com", "GET", endpoint, headers, user_agent,
"application/json", NULL, 0, &HTTPResp, &resplen);
cJSON *resp = cJSON_Parse(HTTPResp);
if (!resp)
return 0;
if (!cJSON_IsArray(resp))
return 0;
*msgs = malloc(sizeof(DiscordMessage) * amount);
for (int i = 0; i < cJSON_GetArraySize(resp); i++) {
cJSON *item = cJSON_GetArrayItem(resp, i);
(*msgs)[i].content = cJSON_GetObjectItem(item, "content")->valuestring;
(*msgs)[i].channelID =
cJSON_GetObjectItem(item, "channel_id")->valuestring;
(*msgs)[i].id = cJSON_GetObjectItem(item, "id")->valuestring;
(*msgs)[i].type = cJSON_GetObjectItem(item, "type")->valueint;
cJSON *author = cJSON_GetObjectItem(item, "author");
cJSON *dispname = cJSON_GetObjectItem(author, "global_name");
(*msgs)[i].author.DisplayName = (dispname && cJSON_IsString(dispname))
? strdup(dispname->valuestring)
: NULL;
cJSON *av = cJSON_GetObjectItem(author, "avatar");
(*msgs)[i].author.avatar =
(av && cJSON_IsString(av)) ? strdup(av->valuestring) : NULL;
cJSON *ct = cJSON_GetObjectItem(item, "content");
(*msgs)[i].content =
(ct && cJSON_IsString(ct)) ? strdup(ct->valuestring) : strdup("");
(*msgs)[i].author.Username =
cJSON_GetObjectItem(author, "username")->valuestring;
(*msgs)[i].author.DisplayName =
cJSON_GetObjectItem(author, "global_name")->valuestring;
(*msgs)[i].author.id = cJSON_GetObjectItem(author, "id")->valuestring;
(*msgs)[i].author.avatar =
cJSON_GetObjectItem(author, "avatar")->valuestring;
}
return cJSON_GetArraySize(resp);
free(HTTPResp);
}
int ConnectGateway(const char *channelID, const char *content) {}
DiscordGuild *GetGuild(char *id) {
DiscordGuild *g;
DiscordGuild search;
search.id = id;
g = (DiscordGuild*)hashmap_get(GuildsTable, &search);
return g;
}
int DiscordListPrivateChannels(DiscordChannel **out) {
*out = PrivateChannels;
return PrivateChannelCount;
}
void HandleREADY(cJSON *json) {
cJSON *data = cJSON_GetObjectItem(json, "d");
if (!cJSON_IsObject(data))
return;
cJSON *guilds = cJSON_GetObjectItem(data, "guilds");
if (!cJSON_IsArray(guilds))
return;
int size = cJSON_GetArraySize(guilds);
GuildsTable = hashmap_new(sizeof(DiscordGuild), 256, 0, 0, GuildHash,
GuildCompare, GuildFree, NULL);
ChannelsTable = hashmap_new(sizeof(DiscordChannel), 256, 0, 0, ChannelHash,
ChannelCompare, ChannelFree, NULL);
cJSON *privchannels = cJSON_GetObjectItem(data, "private_channels");
PrivateChannelCount = cJSON_GetArraySize(privchannels);
PrivateChannels = malloc(sizeof(DiscordChannel) * PrivateChannelCount);
for (int j = 0; j < PrivateChannelCount; j++) {
cJSON *ch = cJSON_GetArrayItem(privchannels, j);
if (!cJSON_IsObject(ch))
continue;
cJSON *chName = cJSON_GetObjectItem(ch, "name");
cJSON *chType = cJSON_GetObjectItem(ch, "type");
cJSON *chId = cJSON_GetObjectItem(ch, "id");
cJSON *chPid = cJSON_GetObjectItem(ch, "parent_id");
cJSON *chRecp = cJSON_GetObjectItem(ch, "recipients");
if (!cJSON_IsString(chId) || !cJSON_IsNumber(chType))
continue;
PrivateChannels[j].GuildID = NULL;
PrivateChannels[j].name = chName ? strdup(chName->valuestring) : NULL;
PrivateChannels[j].id = strdup(chId->valuestring);
PrivateChannels[j].type = chType->valueint;
PrivateChannels[j].parentID = (chPid && cJSON_IsString(chPid))
? strdup(chPid->valuestring)
: NULL;
PrivateChannels[j].receipents =
malloc(cJSON_GetArraySize(chRecp) * sizeof(DiscordUser));
for (int i = 0; i < cJSON_GetArraySize(chRecp); i++) {
cJSON *recep = cJSON_GetArrayItem(chRecp, i);
cJSON *recpAv = cJSON_GetObjectItem(recep, "avatar");
PrivateChannels[j].receipents[i].avatar =
(recpAv && cJSON_IsString(recpAv)) ? strdup(recpAv->valuestring)
: NULL;
cJSON *recpUn = cJSON_GetObjectItem(recep, "username");
PrivateChannels[j].receipents[i].Username =
(recpUn && cJSON_IsString(recpUn)) ? strdup(recpUn->valuestring)
: NULL;
cJSON *recpDn = cJSON_GetObjectItem(recep, "global_name");
PrivateChannels[j].receipents[i].DisplayName =
(recpDn && cJSON_IsString(recpDn)) ? strdup(recpDn->valuestring)
: NULL;
cJSON *recpId = cJSON_GetObjectItem(recep, "id");
PrivateChannels[j].receipents[i].id =
(recpId && cJSON_IsString(recpId)) ? strdup(recpId->valuestring)
: NULL;
}
}
for (int i = size - 1; i >= 0; i--) {
cJSON *guild = cJSON_GetArrayItem(guilds, i);
if (!cJSON_IsObject(guild))
continue;
cJSON *channels = cJSON_GetObjectItem(guild, "channels");
int channelCount = cJSON_GetArraySize(channels);
DiscordChannel *gchannels =
malloc(sizeof(DiscordChannel) * channelCount);
for (int j = 0; j < channelCount; j++) {
cJSON *ch = cJSON_GetArrayItem(channels, j);
if (!cJSON_IsObject(ch))
continue;
cJSON *chName = cJSON_GetObjectItem(ch, "name");
cJSON *chType = cJSON_GetObjectItem(ch, "type");
cJSON *chId = cJSON_GetObjectItem(ch, "id");
cJSON *chPid = cJSON_GetObjectItem(ch, "parent_id");
if (!cJSON_IsString(chName) || !cJSON_IsString(chId) ||
!cJSON_IsNumber(chType))
continue;
gchannels[j].name = strdup(chName->valuestring);
gchannels[j].id = strdup(chId->valuestring);
gchannels[j].type = chType->valueint;
gchannels[j].parentID = (chPid && cJSON_IsString(chPid))
? strdup(chPid->valuestring)
: NULL;
gchannels[j].GuildID =
strdup(cJSON_GetObjectItem(guild, "id")->valuestring);
hashmap_set(ChannelsTable,&gchannels[j]);
}
char *name = cJSON_GetObjectItem(guild, "name")->valuestring;
char *id = cJSON_GetObjectItem(guild, "id")->valuestring;
DiscordGuild *g = malloc(sizeof(DiscordGuild));
g->id = strdup(id);
g->name = strdup(name);
cJSON *icon = cJSON_GetObjectItem(guild, "icon");
g->IconHash =
(icon && cJSON_IsString(icon)) ? strdup(icon->valuestring) : NULL;
g->ObtainedIconPath = NULL;
g->channels = gchannels;
g->ChannelCount = channelCount;
g->MentionCount = 0;
hashmap_set(GuildsTable, g);
onDiscordGuildLoad(g, id);
}
cJSON *readstates = cJSON_GetObjectItem(data, "read_state");
for (int i = 0; i < cJSON_GetArraySize(readstates); i++) {
cJSON *readstate = cJSON_GetArrayItem(readstates, i);
DiscordChannel chnprop;
chnprop.id = cJSON_GetObjectItem(readstate, "id")->valuestring;
DiscordChannel *che =
(DiscordChannel *)hashmap_get(ChannelsTable, &chnprop);
if (!che) {
// bradar WHAT IS THIS
printf("NOT FOUND: Channel %s\n", chnprop.id);
continue;
}
che->ReadState.MentionCount =
cJSON_GetObjectItem(readstate, "mention_count")
? cJSON_GetObjectItem(readstate, "mention_count")->valueint
: 0;
if (cJSON_GetObjectItem(readstate, "mention_count")
? cJSON_GetObjectItem(readstate, "mention_count")->valueint
: 0) {
if (che->GuildID) {
DiscordGuild *gld = GetGuild(che->GuildID);
MessageBoxA(NULL, gld->name, "AAAAAAAAAA", 0);
if (gld) {
gld->MentionCount += che->ReadState.MentionCount;
onDiscordUpdatedGuildReadState(*gld);
}
}
}
}
}
extern void onDiscordReceiveMessage(DiscordMessage msg);
void HandleMessageCreate(cJSON *json) {
cJSON *d = cJSON_GetObjectItem(json, "d");
cJSON *author = cJSON_GetObjectItem(d, "author");
DiscordMessage msg;
msg.author.DisplayName =
strdup(cJSON_GetObjectItem(author, "global_name")->valuestring);
msg.author.id = strdup(cJSON_GetObjectItem(author, "id")->valuestring);
msg.channelID = strdup(cJSON_GetObjectItem(d, "channel_id")->valuestring);
msg.content = strdup(cJSON_GetObjectItem(d, "content")->valuestring);
if (cJSON_GetObjectItem(author, "avatar")) {
msg.author.avatar = cJSON_GetObjectItem(author, "avatar")->valuestring;
}
onDiscordReceiveMessage(msg);
}
void HandleDiscordDispatch(cJSON *json) {
if (!cJSON_IsObject(json))
return;
cJSON *t = cJSON_GetObjectItem(json, "t");
if (!t || !cJSON_IsString(t))
return;
if (strcmp(t->valuestring, "READY") == 0) {
HandleREADY(json);
}
if (strcmp(t->valuestring, "MESSAGE_CREATE") == 0) {
HandleMessageCreate(json);
}
}
void CALLBACK HeartbeatProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent,
DWORD dwTime) {
SendWebSocket(GatewaySSL, "{\"op\": 1,\"d\": null}", 19, 0x81);
}
void WebSocketOnDataArrival(SSL *ssl, char *buffer, size_t length) {
cJSON *json = cJSON_Parse(buffer);
if (json == NULL) {
if (strstr(buffer, "Authentication failed") != 0) {
MessageBoxA(NULL,
"The provided token may be outdated, there was an "
"issue in authentication!",
"Backcord - Gateway Error", MB_ICONERROR);
}
const char *error = cJSON_GetErrorPtr();
if (error) {
printf("Parse error before: %s\n", error);
return;
}
}
int opcode = (cJSON_GetObjectItem(json, "op"))->valueint;
cJSON *data = (cJSON_GetObjectItem(json, "d"));
if (opcode == 10) {
// received hello.. discord api wants us to send a heartbeat rn and
// identify
SendWebSocket(ssl, "{\"op\": 1,\"d\": null}", 19, FIN_LAST | WS_TEXT);
char identifybuf[650 + strlen(token)];
wsprintf(
identifybuf,
"{\"op\":2,\"d\":{\"token\":\"%s\",\"properties\":{\"os\":"
"\"Linux\",\"browser\":\"Chrome\",\"device\":\"\",\"has_client_"
"mods\":false,\"browser_user_agent\":\"Mozilla/5.0 (Linux; Android "
"6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/145.0.0.0 Mobile Safari/537.36\",\"release_channel\": "
"\"stable\"},\"presence\":{},\"compress\":false,"
"\"client_state\":{\"guild_versions\":{}}}}",
token);
SendWebSocket(ssl, identifybuf, strlen(identifybuf), 0x81);;
SetTimer(NULL, 0,
cJSON_GetObjectItem(data, "heartbeat_interval")->valueint * 4 /
5,
HeartbeatProc);
} else if (opcode == 11) {
// heartbeat ack
} else if (opcode == 1) {
// discords heart fluttered :wilted_rose:
SendWebSocket(GatewaySSL, "{\"op\": 1,\"d\": null}", 19, 0x81);
} else if (opcode == 0) {
HandleDiscordDispatch(json);
} else if (opcode == 7) {
MessageBoxA(NULL, "reconnect", "discordapi.c", 0);
}
cJSON_Delete(json);
}
Executable
+13
View File
@@ -0,0 +1,13 @@
#include "globals.h"
#include "components/snoctrl.h"
int DiscordSendMessage(const char *channelID, const char* content);
int DiscordListGuildChannels(char* guildID, DiscordChannel** out);
#define GUILD_TEXT 0
#define CHANNEL_TYPE_DM 1
#define GUILD_VOICE 2
#define GROUPCHAT 3
#define GUILD_CATEGORY 4
int DiscordGetChannelHistory(const char* channelID, unsigned int amount, DiscordMessage** msgs);
char *DiscordFetchTmpPfp(char *userID, char *hash);
char *DiscordFetchTmpGuildIcon(char *guildID, char *hash);
int DiscordListPrivateChannels(DiscordChannel** out);
Executable
+40
View File
@@ -0,0 +1,40 @@
#pragma once
typedef struct{
char* Username;
char* DisplayName;
char* id;
char *avatar;
} DiscordUser;
typedef struct {
char *channelID;
DiscordUser author;
char* content;
char* id;
int type;
} DiscordMessage;
typedef struct {
int MentionCount;
} ReadState;
typedef struct {
char *id;
char *name;
char *parentID; /* category ID this channel belongs to, NULL if none */
char *topic; /* channel topic/description, NULL if unset */
char *lastMessageID; /* snowflake of last message, NULL if none */
int type;
int position; /* sort order within the guild/category */
int flags;
int receipentCount;
DiscordUser* receipents;
ReadState ReadState;
char* GuildID;
} DiscordChannel;
typedef struct {
char *id;
char *name;
char *IconHash;
char *ObtainedIconPath;
DiscordChannel* channels;
int ChannelCount;
int MentionCount;
} DiscordGuild;
Executable
+2290
View File
File diff suppressed because it is too large Load Diff
Executable
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <openssl/ssl.h>
#include <windows.h>
#include <commctrl.h>
#include "discordtypes.h"
extern char* token;
extern SSL *GatewaySSL;
typedef struct{
HTREEITEM itm;
char* id;
} ChannelUIEntry;
Executable
+1154
View File
File diff suppressed because it is too large Load Diff
Executable
+63
View File
@@ -0,0 +1,63 @@
// Copyright 2020 Joshua J Baker. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
#ifndef HASHMAP_H
#define HASHMAP_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#if defined(__cplusplus)
extern "C" {
#endif // __cplusplus
struct hashmap;
typedef struct hashmap* hmap;
struct hashmap *hashmap_new(size_t elsize, size_t cap, uint64_t seed0,
uint64_t seed1,
uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1),
int (*compare)(const void *a, const void *b, void *udata),
void (*elfree)(void *item),
void *udata);
struct hashmap *hashmap_new_with_allocator(void *(*malloc)(size_t),
void *(*realloc)(void *, size_t), void (*free)(void*), size_t elsize,
size_t cap, uint64_t seed0, uint64_t seed1,
uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1),
int (*compare)(const void *a, const void *b, void *udata),
void (*elfree)(void *item),
void *udata);
void hashmap_free(struct hashmap *map);
void hashmap_clear(struct hashmap *map, bool update_cap);
size_t hashmap_count(const struct hashmap *map);
bool hashmap_oom(struct hashmap *map);
const void *hashmap_get(const struct hashmap *map, const void *item);
const void *hashmap_set(struct hashmap *map, const void *item);
const void *hashmap_delete(struct hashmap *map, const void *item);
const void *hashmap_probe(struct hashmap *map, uint64_t position);
bool hashmap_scan(struct hashmap *map, bool (*iter)(const void *item, void *udata), void *udata);
bool hashmap_iter(struct hashmap *map, size_t *i, void **item);
uint64_t hashmap_sip(const void *data, size_t len, uint64_t seed0, uint64_t seed1);
uint64_t hashmap_murmur(const void *data, size_t len, uint64_t seed0, uint64_t seed1);
uint64_t hashmap_xxhash3(const void *data, size_t len, uint64_t seed0, uint64_t seed1);
const void *hashmap_get_with_hash(const struct hashmap *map, const void *key, uint64_t hash);
const void *hashmap_delete_with_hash(struct hashmap *map, const void *key, uint64_t hash);
const void *hashmap_set_with_hash(struct hashmap *map, const void *item, uint64_t hash);
void hashmap_set_grow_by_power(struct hashmap *map, size_t power);
void hashmap_set_load_factor(struct hashmap *map, double load_factor);
// DEPRECATED: use `hashmap_new_with_allocator`
void hashmap_set_allocator(void *(*malloc)(size_t), void (*free)(void*));
#if defined(__cplusplus)
}
#endif // __cplusplus
#endif // HASHMAP_H
Executable
+193
View File
@@ -0,0 +1,193 @@
#include "http.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdio.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
static void AppendHeader(char* buf,
const char* key, const char* value)
{
strcat(buf, key);
strcat(buf, ": ");
strcat(buf, value);
strcat(buf, "\r\n");
}
char* ProcessChunkedTransfer(char* chunked, size_t chunked_len, size_t *out_len) {
char* result = malloc(chunked_len);
size_t written = 0;
char* curpos = chunked;
while(1) {
char* crlf = strstr(curpos, "\r\n");
if(!crlf) break;
int chunklen = (int)strtol(curpos, NULL, 16);
if(chunklen == 0) break;
char* data = crlf + 2;
memcpy(result + written, data, chunklen);
written += chunklen;
curpos = data + chunklen + 2;
}
*out_len = written;
return result;
}
int SendShortHTTPReq(const char* hostname,const char* reqtype, const char* endpoint, const char* extra_headers, const char* user_agent, const char* content_type, void* payload, unsigned long payload_size, char** response, long* responselen)
{
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
MessageBoxA(NULL, "socket failed", "Aa", 0);
return 1;
}
struct sockaddr_in server;
RtlZeroMemory(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(443); // HTTPS brotatochippiee
server.sin_addr = **(struct in_addr**)gethostbyname(hostname)->h_addr_list;
if (connect(sock, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
MessageBoxA(NULL, "connect failed", "Aa", 0);
closesocket(sock);
return 1;
}
// this debugging thing served us well yall MessageBoxA(NULL, "onto ze ctx", "Aa", 0);
SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
if (!ctx) {
MessageBoxA(NULL, "SSL_CTX_new failed", "Aa", 0);
unsigned long err = ERR_get_error();
if (err == 0) {
MessageBoxA(NULL, "unknown OpenSSL error", "title", MB_ICONERROR);
return 1;
}
char buf[256];
ERR_error_string_n(err, buf, sizeof(buf));
MessageBoxA(NULL, buf, "title", MB_ICONERROR);
return 1;
}
SSL* ssl = SSL_new(ctx);
if (!ssl) {
MessageBoxA(NULL, "SSL_new failed qwq", "Aa", 0);
return 1;
}
// Attach socket to SSL
SSL_set_fd(ssl, (int)sock);
// SNI (importante shi)
SSL_set_tlsext_host_name(ssl, hostname);
if (SSL_connect(ssl) <= 0) {
MessageBoxA(NULL, "SSL_connect failed", "Aa", 0);
SSL_free(ssl);
SSL_CTX_free(ctx);
closesocket(sock);
return 1;
}
SSL_write(ssl, reqtype, (int)strlen(reqtype)); // Sends request type
SSL_write(ssl, " ", 1); // stupid fuckass space
SSL_write(ssl, endpoint, (int)strlen(endpoint)); // endpoint
SSL_write(ssl, " HTTP/1.1\r\n", 11); // the protocol thing.. ykw i will be more formal
SSL_write(ssl, "Host: ", 6); // Host beginning
SSL_write(ssl, hostname, (int)(strlen(hostname))); // Host data
SSL_write(ssl, "\r\n", 2); // Host end
SSL_write(ssl, "User-Agent: ", 12); // User Agent beginning
SSL_write(ssl, user_agent, (int)(strlen(user_agent))); // User Agent data
SSL_write(ssl, "\r\n", 2); // User Agent end
if (payload != 0) {
SSL_write(ssl, "Content-Type: ", 14); // Content Type beginning
SSL_write(ssl, content_type, (int)strlen(content_type));
SSL_write(ssl, "\r\n", 2); // Content Type end
SSL_write(ssl, "Content-Length: ", 16); // Content Length beginning
char lenstr[64];
RtlZeroMemory(lenstr,64);
wsprintfA(lenstr,"%d",payload_size);
SSL_write(ssl, lenstr, (int)(strlen(lenstr))); // Content Length data
SSL_write(ssl, "\r\n", 2); // Content Length end
}
SSL_write(ssl, extra_headers, (int)(strlen(extra_headers))); // Extra Header
SSL_write(ssl, "Connection: close\r\n", 19); //Short-term connection
SSL_write(ssl, "\r\n", 2); //Ends the header
if(payload)
SSL_write(ssl, payload, (int)payload_size); //Payload
char* buffer = malloc(4096);
size_t sizeofbuf = 4096;
size_t total_bytes = 0;
int bytes_read;
while ((bytes_read = SSL_read(ssl, buffer + total_bytes, (int)(sizeofbuf - total_bytes - 1))) > 0) {
total_bytes += bytes_read;
if (total_bytes + 1 >= sizeofbuf) {
sizeofbuf += 4096;
char* newbuf = realloc(buffer, sizeofbuf);
if (!newbuf) {
//what the fuck had possibly happened on bros pc :sob:
free(buffer);
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
closesocket(sock);
return -1;
}
buffer = newbuf;
}
}
buffer[total_bytes] = '\0';
char *endofhdr = strstr(buffer, "\r\n\r\n");
if (!endofhdr) {
free(buffer);
SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ctx);
closesocket(sock);
return -1;
}
char *body_start = endofhdr + 4;
size_t body_len = total_bytes - (body_start - buffer);
if (strstr(buffer, "Transfer-Encoding: chunked")) {
size_t outlen;
*response = ProcessChunkedTransfer(body_start,body_len,&outlen);
*responselen = (long)outlen;
free(buffer);
} else {
char *copy = malloc(body_len + 1);
memcpy(copy, body_start, body_len);
copy[body_len] = '\0';
*response = copy;
*responselen = (long)body_len;
free(buffer);
}
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
closesocket(sock);
return 0;
}
int InitHTTP() {
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData); //me when macros
}
int CleanupHTTP() {
WSACleanup();
}
Executable
+11
View File
@@ -0,0 +1,11 @@
void InitSockets();
int SendShortHTTPReq(const char *hostname, const char *reqtype,
const char *endpoint, const char *extra_headers,
const char *user_agent, const char *content_type,
void *payload, unsigned long payload_size, char** response, long* responselen);
static void AppendHeader(char *buf, const char *key, const char *value);
int InitHTTP();
int CleanupHTTP();
+3
View File
File diff suppressed because one or more lines are too long
+116
View File
@@ -0,0 +1,116 @@
#include <libloaderapi.h>
#include <minwindef.h>
#include <windef.h>
#include <windows.h>
#include <wingdi.h>
int ret = 1;
HWND tokenTxtField;
WNDPROC txtWndProc;
HFONT CueFont;
HWND loginbtn;
LRESULT CALLBACK loginwndproc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
switch (uMsg) {
case WM_CREATE:
return 0;
case WM_PAINT: {
PAINTSTRUCT lppaint;
HDC hdc = BeginPaint(hwnd, &lppaint);
RECT clientrect;
GetClientRect(hwnd, &clientrect);
FillRect(hdc, &clientrect, (HBRUSH)(COLOR_3DFACE + 1));
EndPaint(hwnd, &lppaint);
return 0;
};
case WM_COMMAND: {
if (lParam == (LPARAM)loginbtn) {
ret = 0;
}
return 0;
}
case WM_DESTROY:
return 0;
default:
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
}
}
LRESULT CALLBACK TokenTxtWndProc(HWND hWnd, UINT msg, WPARAM wParam,
LPARAM lParam) {
if (msg == WM_PAINT) {
if (GetWindowTextLength(hWnd) == 0) {
PAINTSTRUCT lpPaint;
HDC hDC = BeginPaint(hWnd, &lpPaint);
RECT clRect;
GetClientRect(hWnd, &clRect);
FillRect(hDC, &clRect, (HBRUSH)(COLOR_WINDOW + 1));
SetTextColor(hDC, RGB(128, 128, 128));
SelectObject(hDC, CueFont);
DrawText(hDC, "Token...", 8, &clRect, 0);
EndPaint(hWnd, &lpPaint);
return 0;
} else {
return txtWndProc(hWnd, msg, wParam, lParam);
}
} else {
return txtWndProc(hWnd, msg, wParam, lParam);
}
}
char *PromptToken() {
char *clsname = "BackcordLogin";
WNDCLASS wc = {0};
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
wc.lpszClassName = clsname;
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
wc.lpfnWndProc = loginwndproc;
RegisterClassA(&wc);
HWND hwnd = CreateWindowExA(0, clsname, "Backcord - Login", WS_OVERLAPPED,
CW_USEDEFAULT, CW_USEDEFAULT, 320, 480, NULL,
NULL, GetModuleHandleA(NULL), NULL);
RECT clientrect;
GetClientRect(hwnd, &clientrect);
HWND banner =
CreateWindowExA(0, "STATIC", "", WS_VISIBLE | WS_CHILD | SS_BITMAP, 0,
0, clientrect.right - clientrect.left, 64, hwnd, NULL,
GetModuleHandleA(NULL), NULL);
SendMessage(
banner, STM_SETIMAGE, IMAGE_BITMAP,
(LPARAM)(LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(102))));
tokenTxtField =
CreateWindowExA(WS_EX_STATICEDGE, "EDIT", "", WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL | ES_PASSWORD, 20,
64 + 20, clientrect.right - clientrect.left - 40, 20,
hwnd, NULL, GetModuleHandleA(NULL), NULL);
SendMessage(tokenTxtField, EM_SETLIMITTEXT, 0, 0);
NONCLIENTMETRICS ncm = {sizeof(NONCLIENTMETRICS)};
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
HFONT hmsgfont = CreateFontIndirect(&ncm.lfMessageFont);
SendMessage(tokenTxtField, WM_SETFONT, (WPARAM)hmsgfont, TRUE);
LOGFONTA CueFontLog = ncm.lfMessageFont;
CueFontLog.lfItalic = 1;
CueFont = CreateFontIndirect(&CueFontLog);
txtWndProc = (WNDPROC)GetWindowLong(tokenTxtField, GWL_WNDPROC);
SetWindowLong(tokenTxtField, GWL_WNDPROC, (long)TokenTxtWndProc);
loginbtn = CreateWindowExA(0, "BUTTON", "Login",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
clientrect.right - clientrect.left - 10 - 75,
clientrect.bottom - clientrect.top - 10 - 25, 75,
25, hwnd, NULL, GetModuleHandleA(NULL), NULL);
SendMessage(loginbtn, WM_SETFONT, (WPARAM)hmsgfont, TRUE);
ShowWindow(hwnd, SW_SHOW);
while (ret) {
MSG msg;
if (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
char *tok = malloc(GetWindowTextLength(tokenTxtField) + 1);
GetWindowText(tokenTxtField, tok, GetWindowTextLength(tokenTxtField) + 1);
DestroyWindow(hwnd);
return tok;
}
+1
View File
@@ -0,0 +1 @@
char* PromptToken();
Executable
+507
View File
@@ -0,0 +1,507 @@
#include <windows.h>
#include <wingdi.h>
#include <commctrl.h>
#include "components/chatwnd.h"
#include "discordtypes.h"
#include "http.h"
#include "ws.h"
#include "discordapi.h"
#include <cjson/cJSON.h>
#include <winnt.h>
#include "config.h"
#include "globals.h"
#include "hmap/hashmap.h"
#include "components/snoctrl.h"
#include "components/pnghlp.h"
#include "rc/res.h"
#include "login.h"
#define GUILD_PADDING 4
static DiscordMessage *PendingRndMsg = NULL;
static DiscordMessage *LastRndMsg = NULL; // rnd is render NOT random :sob:
char *curChannel;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static HWND hWnd;
static HWND hMsg;
static HWND hMsgList;
static HWND hSend;
static HWND hChTree;
static HWND hDMsList;
static HWND hGSel;
static HWND hPfpArea;
static HWND hMemList;
static HIMAGELIST hSidePfps;
HINSTANCE hInstance;
SSL *GatewaySSL;
char *token;
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, PSTR pCmdLine,
int nCmdShow) {
#ifdef test_token
token = test_token;
#else
// prompt for token
HKEY hKey;
RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\Backcord", 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKey,
NULL);
DWORD type, size = 0;
LONG qres = RegQueryValueExA(hKey, "Token", NULL, &type, NULL, &size);
if (qres == ERROR_SUCCESS) {
token = malloc(size);
RegQueryValueExA(hKey, "Token", NULL, &type, (BYTE *)token, &size);
} else {
token = PromptToken();
RegSetValueExA(hKey, "Token", 0, REG_SZ, (const BYTE *)token,
(DWORD)(strlen(token) + 1));
}
RegCloseKey(hKey);
#endif
INITCOMMONCONTROLSEX icex;
icex.dwICC = ICC_WIN95_CLASSES | ICC_STANDARD_CLASSES | ICC_BAR_CLASSES |
ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES;
InitCommonControls();
InitSnowsControls();
hSidePfps = ImageList_Create(32, 32, ILC_COLOR32 | ILC_MASK, 12, 1);
hInstance = hinstance;
// register the window claws
const char CLASS_NAME[] = "BackcordMain";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
RegisterClass(&wc);
hWnd = CreateWindowEx(0, // Optional window styles.
CLASS_NAME, // Window class
"Backcord", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hWnd == NULL) {
return 0;
}
ShowWindow(hWnd, nCmdShow);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
BOOL identify = FALSE;
void onDiscordGuildLoad(DiscordGuild *guild, char *id) {
GUIGuild uigld;
uigld.icon = NULL;
uigld.id = id;
uigld.title = guild->name;
uigld.data = guild;
uigld.MentionCount = guild->MentionCount;
GuildView_InsertGuild(hGSel, uigld);
if (guild->IconHash) {
char tempPath[MAX_PATH];
DWORD len = GetTempPathA(MAX_PATH, tempPath);
char *path = malloc(MAX_PATH + strlen(guild->id) + 1);
wsprintfA(path, "%s%s.png", tempPath, guild->IconHash);
if (GetFileAttributesA(path) == INVALID_FILE_ATTRIBUTES) {
char *path = DiscordFetchTmpGuildIcon(guild->id,
guild->IconHash); // testing
}
GuildView_SetIcon(hGSel, LoadPNGImage(path), id);
free(path);
}
}
void onDiscordUpdatedGuildReadState(DiscordGuild guild) {
GuildView_SetMentionCount(hGSel, guild.id, guild.MentionCount);
}
static uint64_t channel_hash(const void *item, uint64_t seed0, uint64_t seed1) {
const ChannelUIEntry *e = item;
return hashmap_sip(e->id, strlen(e->id), seed0, seed1);
}
static int channel_compare(const void *a, const void *b, void *udata) {
return strcmp(((ChannelUIEntry *)a)->id, ((ChannelUIEntry *)b)->id);
}
void onDiscordReceiveMessage(DiscordMessage msg) {
if (curChannel && strcmp(msg.channelID, curChannel) == 0) {
if (msg.author.avatar) {
char tempPath[MAX_PATH];
DWORD len = GetTempPathA(MAX_PATH, tempPath);
char *path = malloc(MAX_PATH + strlen(msg.author.avatar) + 1);
wsprintfA(path, "%s%s.png", tempPath, msg.author.avatar);
if (GetFileAttributesA(path) == INVALID_FILE_ATTRIBUTES) {
char *path = DiscordFetchTmpPfp(msg.author.id,
msg.author.avatar); // testing
}
ChatView_SetUserPfp(path, msg.author.id);
}
InsertChatMessage(msg, hMsgList); // the backend provides strdup
}
}
void *msgoldwndproc;
LRESULT CALLBACK MsgBarSubclassProc(HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam) {
if (msg == WM_KEYDOWN) {
if (wParam == VK_RETURN) {
if (!(GetKeyState(VK_SHIFT) &
0x8000)) // if no shift then do cool stuff
{
char *msgcontent = malloc(GetWindowTextLength(hMsg) + 1);
GetWindowText(hMsg, msgcontent, GetWindowTextLength(hMsg) + 1);
DiscordSendMessage(curChannel, msgcontent);
SetWindowText(hwnd, ""); // clear input
return 0; // preventing uhh newline
}
}
}
return CallWindowProc(msgoldwndproc, hwnd, msg, wParam, lParam);
}
HBITMAP ResizeBitmap(HBITMAP hOrig, int w, int h) {
BITMAP bm;
GetObject(hOrig, sizeof(bm), &bm);
HDC hdcScreen = GetDC(NULL);
HDC hdcSrc = CreateCompatibleDC(hdcScreen);
HDC hdcDst = CreateCompatibleDC(hdcScreen);
HBITMAP hResized = CreateCompatibleBitmap(hdcScreen, w, h);
SelectObject(hdcSrc, hOrig);
SelectObject(hdcDst, hResized);
SetStretchBltMode(hdcDst, HALFTONE);
SetBrushOrgEx(hdcDst, 0, 0, NULL);
StretchBlt(hdcDst, 0, 0, w, h, hdcSrc, 0, 0, bm.bmWidth, bm.bmHeight,
SRCCOPY);
DeleteDC(hdcSrc);
DeleteDC(hdcDst);
ReleaseDC(NULL, hdcScreen);
DeleteObject(hOrig);
return hResized;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
switch (uMsg) {
case WM_CREATE:
InitHTTP();
OpenWebSocket("gateway.discord.gg", "/?encoding=json&v=9", &GatewaySSL);
RECT rc;
GetClientRect(hwnd, &rc);
hGSel = CreateWindowEx(0, // Optional window styles.
"BackcordGuild", // Window class
"", // Window text
WS_CHILD | WS_VISIBLE | LVS_LIST, // Window style
// Size and position
0, 0, 75, rc.bottom - rc.top - 80,
hwnd, // Parent window
(HMENU)102, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
GuildView_SetDMsIcon(
hGSel, LoadPNGImageFromResource(hInstance, IDI_BACKCORDIC));
hChTree = CreateWindowExA(WS_EX_CLIENTEDGE, WC_TREEVIEWA, NULL,
WS_CHILD | WS_VISIBLE | TVS_HASLINES |
TVS_LINESATROOT | TVS_HASBUTTONS,
75, 0, 128, rc.bottom - rc.top - 80, hwnd,
(HMENU)103, hInstance, NULL);
hDMsList = CreateWindowExA(
WS_EX_CLIENTEDGE, WC_LISTVIEWA, NULL, WS_CHILD | WS_VISIBLE, 75, 0,
128, rc.bottom - rc.top - 80, hwnd, (HMENU)103, hInstance, NULL);
ShowWindow(hDMsList, SW_HIDE);
hMsgList = CreateWindowEx(0, // Optional window styles.
"BackcordChat", // Window class
"", // Window text
WS_CHILD | WS_VISIBLE, // Window style
// Size and position
75 + 128, 0, rc.right - 128 - 75,
rc.bottom - rc.top - 25,
hwnd, // Parent window
(HMENU)105, // Menu
hInstance, // Instance handle
NULL); // Additional application data
hPfpArea = CreateWindowEx(
0, // Optional window styles.
"BUTTON", // Window class
"User Profile", // Window text
ES_MULTILINE | WS_CHILD | WS_VISIBLE | BS_GROUPBOX, // Window style
// Size and position
0, rc.bottom - rc.top - 80, 128 + 75, 80,
hwnd, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
SendMessage(hPfpArea, WM_SETFONT, (WPARAM)regfont, 0);
hMsg = CreateWindowEx(WS_EX_CLIENTEDGE, // Optional window styles.
"EDIT", // Window class
"", // Window text
ES_MULTILINE | ES_AUTOVSCROLL | WS_CHILD |
WS_VISIBLE, // Window style
// Size and position
128 + 75, rc.bottom - rc.top - 25,
rc.right - rc.left - 128 - 75 - 25, 25,
hwnd, // Parent window
(HMENU)104, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
SendMessage(hMsg, WM_SETFONT, (WPARAM)regfont, 0);
msgoldwndproc = (void *)SetWindowLongPtr(hMsg, GWLP_WNDPROC,
(long)MsgBarSubclassProc);
hSend = CreateWindowEx(
0, // Optional window styles.
"BUTTON", // Window class
"E", // Window text
BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, // Window style
// Size and position
rc.right - 25, rc.bottom - rc.top - 25, 25, 25,
hwnd, // Parent window
(HMENU)(101), // Menu
hInstance, // Instance handle
NULL // Additional application data
);
SendMessage(hSend, WM_SETFONT, (WPARAM)regfont, 0);
return 0;
break;
case WM_SIZE: {
int width = LOWORD(lParam);
int height = HIWORD(lParam);
SetWindowPos(hPfpArea, NULL, 0, height - 80, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
SetWindowPos(hGSel, NULL, 0, 0, 75, height - 80, SWP_NOZORDER);
SetWindowPos(hMsg, NULL, 128 + 75, height - 25, width - 128 - 75 - 25,
25, SWP_NOZORDER);
SetWindowPos(hChTree, NULL, 75, 0, 128, height - 80, SWP_NOZORDER);
SetWindowPos(hMsgList, NULL, 128 + 75, 0, width - 128 - 75, height - 25,
SWP_NOZORDER);
SetWindowPos(hSend, NULL, width - 25, height - 25, 25, 25,
SWP_NOZORDER | SWP_NOSIZE);
return 0;
break;
}
case WM_COMMAND:
if (lParam == hSend) {
char msgcontent[GetWindowTextLength(hMsg) + 1];
GetWindowText(hMsg, msgcontent, GetWindowTextLength(hMsg) + 1);
DiscordSendMessage(curChannel, msgcontent);
}
break;
case WM_NOTIFY:
NMHDR pNMHDR = *(NMHDR *)lParam;
LPNMLVCUSTOMDRAW lvcd = (LPNMLVCUSTOMDRAW)lParam;
if (pNMHDR.hwndFrom == hGSel) {
LPNMGUILDVIEW pnmv = (LPNMGUILDVIEW)lParam;
if (pnmv->index == GUILDVIEW_DMS) {
DiscordChannel *dms;
int cnt = DiscordListPrivateChannels(&dms);
TreeView_DeleteAllItems(hChTree);
TreeView_SetImageList(hChTree, hSidePfps, TVSIL_NORMAL);
TVINSERTSTRUCT tvInsert = {0};
HTREEITEM hParent = TVI_ROOT;
int pfpidx = 0;
for (int i = 0; i < cnt; i++) {
if (dms[i].type == CHANNEL_TYPE_DM) {
printf("%s\n", dms[i].receipents[0].Username);
if (dms[i].receipents[0].avatar) {
char tempPath[MAX_PATH];
DWORD len = GetTempPathA(MAX_PATH, tempPath);
char path[MAX_PATH +
strlen(dms[i].receipents[0].avatar) + 1];
wsprintfA(path, "%s%s.png", tempPath,
dms[i].receipents[0].avatar);
if (GetFileAttributesA(path) ==
INVALID_FILE_ATTRIBUTES) {
char* path2 = DiscordFetchTmpPfp(
dms[i].receipents[0].id,
dms[i].receipents[0].avatar); // testing
ImageList_Add(
hSidePfps,
ResizeBitmap(LoadPNGBitmap(path2), 32, 32),
NULL);
free(path2);
} else {
ImageList_Add(
hSidePfps,
ResizeBitmap(LoadPNGBitmap(path), 32, 32),
NULL);
}
}
tvInsert.hParent = hParent; // Parent is the root item
tvInsert.hInsertAfter = TVI_LAST;
tvInsert.item.mask = TVIF_TEXT | TVIF_PARAM |
TVIS_EXPANDED | TVIF_STATE |
TVIF_IMAGE;
tvInsert.item.pszText =
dms[i].receipents[0].DisplayName
? dms[i].receipents[0].DisplayName
: dms[i].receipents[0].Username;
tvInsert.item.lParam = (LPARAM) & (dms[i]);
tvInsert.item.state = TVIS_EXPANDED;
tvInsert.item.stateMask = TVIS_EXPANDED;
tvInsert.item.iImage =
dms[i].receipents[0].avatar ? pfpidx : 0;
HTREEITEM lres = (HTREEITEM)SendMessage(
hChTree, TVM_INSERTITEM, 0, (LPARAM)&tvInsert);
if (dms[i].receipents[0].avatar) {
pfpidx++;
}
}
}
} else {
DiscordGuild *guild;
guild = pnmv->guild.data;
DiscordChannel *Channels;
int ChannelCount =
DiscordListGuildChannels(guild->id, &Channels);
TreeView_DeleteAllItems(hChTree);
TreeView_SetImageList(hChTree, NULL, TVSIL_NORMAL);
struct hashmap *ChannelUITable =
hashmap_new(sizeof(ChannelUIEntry), 0, 0, 0, channel_hash,
channel_compare, NULL, NULL);
for (int i = 0; i < ChannelCount; i++) {
char *VisualName;
if (Channels[i].type == GUILD_TEXT) {
VisualName = malloc(strlen(Channels[i].name) + 2);
*VisualName = '#';
strcpy(VisualName + 1, Channels[i].name);
} else {
VisualName = malloc(strlen(Channels[i].name) + 1);
strcpy(VisualName, Channels[i].name);
}
TVINSERTSTRUCT tvInsert = {0};
HTREEITEM hParent = TVI_ROOT;
if (Channels[i].parentID) {
ChannelUIEntry key = {0};
key.id = Channels[i].parentID;
if (hashmap_get(ChannelUITable, &key)) {
ChannelUIEntry *chnl =
(ChannelUIEntry *)(hashmap_get(ChannelUITable,
&key));
hParent = chnl->itm;
}
}
tvInsert.hParent = hParent; // Parent is the root item
tvInsert.hInsertAfter = TVI_LAST;
tvInsert.item.mask =
TVIF_TEXT | TVIF_PARAM | TVIS_EXPANDED | TVIF_STATE;
tvInsert.item.pszText = (VisualName);
tvInsert.item.lParam = (LPARAM) & (Channels[i]);
tvInsert.item.state = TVIS_EXPANDED;
tvInsert.item.stateMask = TVIS_EXPANDED;
HTREEITEM lres = (HTREEITEM)SendMessage(
hChTree, TVM_INSERTITEM, 0, (LPARAM)&tvInsert);
if (Channels[i].type == GUILD_CATEGORY) {
ChannelUIEntry *chnl = malloc(sizeof(ChannelUIEntry));
chnl->itm = lres;
chnl->id = strdup(Channels[i].id);
hashmap_set(ChannelUITable, chnl);
}
free(VisualName);
}
}
} else if (pNMHDR.hwndFrom == hChTree) {
LPNMTREEVIEW pnmv = (LPNMTREEVIEW)lParam;
if (pNMHDR.code == TVN_SELCHANGED &&
pnmv->itemNew.state & TVIS_SELECTED) {
DiscordChannel *chnl = ((DiscordChannel *)pnmv->itemNew.lParam);
if (chnl->type != GUILD_CATEGORY) {
char *title = malloc(14 + strlen(pnmv->itemNew.pszText));
wsprintf(title, "Backcord - %s", pnmv->itemNew.pszText);
SetWindowTextA(hwnd, title);
DiscordMessage *msgs;
int msgcnt = DiscordGetChannelHistory(chnl->id, 50, &msgs);
ClearChatControl(hMsgList);
ListView_DeleteAllItems(hMsgList);
if (curChannel)
free(curChannel);
curChannel = strdup(chnl->id);
if (msgcnt > 0) {
for (int i = msgcnt - 1; i >= 0; i--) {
if (msgs[i].author.avatar) {
char tempPath[MAX_PATH];
DWORD len = GetTempPathA(MAX_PATH, tempPath);
char *path =
malloc(MAX_PATH +
strlen(msgs[i].author.avatar) + 1);
wsprintfA(path, "%s%s.png", tempPath,
msgs[i].author.avatar);
if (GetFileAttributesA(path) ==
INVALID_FILE_ATTRIBUTES) {
char *path = DiscordFetchTmpPfp(
msgs[i].author.id,
msgs[i].author.avatar); // testing
}
ChatView_SetUserPfp(path, msgs[i].author.id);
}
InsertChatMessage((msgs[i]), hMsgList);
}
}
ListView_EnsureVisible(hMsgList, msgcnt - 1, FALSE);
free(title);
free(msgs);
}
}
break;
}
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_BTNFACE + 1));
EndPaint(hwnd, &ps);
return 0;
break;
}
case WM_CLOSE:
DestroyWindow(hwnd);
PostQuitMessage(0);
case WM_DESTROY:
CleanupHTTP();
return TRUE;
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Executable
+16
View File
@@ -0,0 +1,16 @@
CC = i686-w64-mingw32-gcc
WINDRES = i686-w64-mingw32-windres
SRCS = *.c */*.c
RES = rc/res.o
Backcord: $(RES)
$(WINDRES) rc/res.rc -o rc/res.o
$(CC) $(SRCS) $(RES) \
-Wl,-Bstatic -lpng -lz \
-Wl,-Bdynamic -lgdi32 -lssl -lcrypto -lcrypt32 -lws2_32 \
-luser32 -lcomctl32 -lcomdlg32 -lcjson -lntdll \
-o Backcord.exe
clean:
rm -f rc/res.o Backcord.exe
Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

+2
View File
@@ -0,0 +1,2 @@
#define IDI_BACKCORDIC 101
#define IDI_BACKCORDBANNERIC 102
BIN
View File
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
#include "res.h"
IDI_BACKCORDIC RCDATA "backcord_icon.png"
IDI_BACKCORDBANNERIC BITMAP "backcordbanner.bmp"
+11
View File
@@ -0,0 +1,11 @@
#include <windows.h>
BOOL WINAPI IsProcessorFeaturePresent(DWORD ProcessorFeature)
{
return FALSE;
}
FARPROC WINAPI DelayLoadFailureHook(_In_ LPCSTR pszDllName,
_In_ LPCSTR pszProcName) {
return (void*)(IsProcessorFeaturePresent);
}
Executable
+241
View File
@@ -0,0 +1,241 @@
#include "http.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdio.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "ws.h"
DWORD WINAPI WSThreadProc(LPVOID ssl);
int OpenWebSocket(const char *hostname, const char *path, SSL **out_ssl) {
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
MessageBoxA(NULL, "socket failed", "WebSocket Error", 0);
return 1;
}
struct sockaddr_in server;
RtlZeroMemory(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(443);
server.sin_addr = **(struct in_addr **)gethostbyname(hostname)->h_addr_list;
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) ==
SOCKET_ERROR) {
MessageBoxA(NULL, "connect failed", "WebSocket Error", 0);
closesocket(sock);
return 1;
}
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
if (!ctx) {
MessageBoxA(NULL, "SSL_CTX_new failed", "WebSocket Error", 0);
closesocket(sock);
return 1;
}
SSL *ssl = SSL_new(ctx);
if (!ssl) {
MessageBoxA(NULL, "SSL_new failed", "WebSocket Error", 0);
SSL_CTX_free(ctx);
closesocket(sock);
return 1;
}
SSL_set_fd(ssl, (int)sock);
SSL_set_tlsext_host_name(ssl, hostname);
if (SSL_connect(ssl) <= 0) {
MessageBoxA(NULL, "SSL_connect failed", "WebSocket Error", 0);
SSL_free(ssl);
SSL_CTX_free(ctx);
closesocket(sock);
return 1;
}
// anything but a Base64 lib
const char *ws_key = "tLGs4Zw47vMKJ8bT5AXjLw==";
// send WebSocket upgrade request
char request[2048];
wsprintfA(request,
"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: %s\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n",
path, hostname, ws_key);
SSL_write(ssl, request, (int)strlen(request));
// read response into epic stack arr
char buffer[4096];
int bytes = SSL_read(ssl, &buffer, sizeof(buffer) - 1);
if (bytes > 0) {
buffer[bytes] = '\0';
// check for 101 Switching Protocols otherwise it means smth fucked up
if (strstr(buffer, "101") == NULL) {
MessageBoxA(NULL, buffer, "websocket isnt websocketing", 0);
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
closesocket(sock);
return 1;
}
}
CreateThread(0, 8192, WSThreadProc, ssl, 0, 0);
// connection upgraded to WebSocket yay
*out_ssl = ssl;
return 0;
}
DWORD WINAPI WSThreadProc(LPVOID ssl) {
char *buffer;
size_t length;
while (1) {
int result = ReadWebSocket(ssl, &buffer, &length);
if (length != 0 && result != 0) {
WebSocketOnDataArrival(ssl, buffer, length);
// printf("%s\n",buffer);
// LE IMPORTANT: free the buffer after processing... memleak goez
// brr otherewise
free(buffer);
} else {
break;
}
}
return 0;
};
int SendWebSocket(SSL *ssl, const char *data, size_t length,
unsigned char flags) {
unsigned char frame[14];
size_t frame_size = 0;
// Flags are to our goofy ahh param
frame[0] = flags;
frame_size++;
// Mask bit set (client must mask the ahh) (stolen code)
if (length < 126) {
frame[1] = 0x80 | (unsigned char)length;
frame_size++;
} else if (length < 65536) {
frame[1] = 0x80 | 126;
frame[2] = (length >> 8) & 0xFF;
frame[3] = length & 0xFF;
frame_size += 3;
} else {
frame[1] = 0x80 | 127;
frame[2] = (length >> 56) & 0xFF;
frame[3] = (length >> 48) & 0xFF;
frame[4] = (length >> 40) & 0xFF;
frame[5] = (length >> 32) & 0xFF;
frame[6] = (length >> 24) & 0xFF;
frame[7] = (length >> 16) & 0xFF;
frame[8] = (length >> 8) & 0xFF;
frame[9] = length & 0xFF;
frame_size += 9;
}
// whats even the purpose of ts, just fake the masks
unsigned char mask[4];
for (unsigned char i = 0; i < 4; i++) {
mask[i] = (unsigned char)(i & 0xFF);
}
RtlCopyMemory(&frame[frame_size], mask, 4); // me when i rtlcopymemory to look cool
frame_size += 4;
// Send frame header
if (SSL_write(ssl, frame, (int)frame_size) <= 0) {
return 1;
}
// Mask and send payload
unsigned char *masked_data = (unsigned char *)malloc(length);
if (!masked_data) {
return 1;
}
for (size_t i = 0; i < length; i++) {
masked_data[i] = data[i] ^ mask[i % 4];
}
int result = SSL_write(ssl, masked_data, (int)length);
free(masked_data);
return (result <= 0) ? 1 : 0;
}
int ReadWebSocket(SSL *ssl, char **out_buffer, size_t *out_length) {
*out_buffer = malloc(10); // tmp buffer
unsigned char opcode;
BOOL hasSetFirstOpcode = FALSE;
unsigned long long payloadLen = 0;
while (1) {
unsigned long long curPayloadLen = 0;
unsigned char fin;
unsigned char coreHeaders;
if (SSL_read(ssl, &coreHeaders, 1) != 1) {
return 0;
}
fin = coreHeaders >> 7;
if (hasSetFirstOpcode != TRUE) {
opcode = (coreHeaders << 4) >> 4;
hasSetFirstOpcode = TRUE;
}
unsigned char plHeaders;
SSL_read(ssl, &plHeaders, 1);
unsigned char tlen = (plHeaders << 1) >> 1;
if (tlen == 127) {
SSL_read(ssl, &curPayloadLen, 8);
curPayloadLen = ntohll(curPayloadLen);
payloadLen += curPayloadLen;
} else if (tlen == 126) {
unsigned short templen;
SSL_read(ssl, &templen, 2);
curPayloadLen = ntohs(templen);
payloadLen += curPayloadLen;
} else {
curPayloadLen = tlen;
payloadLen += curPayloadLen;
}
if (opcode == 0x01) {
*out_buffer = realloc(*out_buffer, payloadLen + 1);
*out_length = payloadLen + 1;
} else {
*out_buffer = realloc(*out_buffer, payloadLen);
*out_length = payloadLen;
}
unsigned long long totalRead = 0;
while (totalRead < curPayloadLen) {
int bytesRead = SSL_read(
ssl, *out_buffer + (payloadLen - curPayloadLen) + totalRead,
curPayloadLen - totalRead);
if (bytesRead <= 0) {
free(*out_buffer);
return 0;
}
totalRead += bytesRead;
}
if (fin == 1) {
break;
}
}
if (opcode == 0x01) {
(*out_buffer)[payloadLen] = '\0';
}
return 1;
}
Executable
+10
View File
@@ -0,0 +1,10 @@
#include <openssl/ssl.h>
int OpenWebSocket(const char *hostname, const char *path, SSL **out_ssl);
int ReadWebSocket(SSL* ssl, char** out_buffer, size_t* out_length);
int SendWebSocket(SSL *ssl, const char *data, size_t length, unsigned char flags);
void WebSocketOnDataArrival(SSL *ssl, char *buffer, size_t length);
#define FIN_LAST 0x80
#define FIN_MORE 0x00
#define WS_TEXT 0x01