$ git clone http://tcclient.ion.nu/tc_client.git
commit e3341ec8dac53a1f40ee23ace50e615c069db9c2
Author: Alicia <...>
Date: Thu Jun 1 06:10:40 2017 +0000
Added some basic support for tinychat beta (-s/--site tinychat_beta)
diff --git a/ChangeLog b/ChangeLog
index 770d904..b2774a5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -15,6 +15,7 @@ Added a /quit command.
Workaround for the new captcha mechanism.
Bugfix: use //IGNORE to skip characters instead of freezing if the locale can't represent them.
Use curl's curl_easy_unescape() for from_owner notices instead of doing it ourselves (contributed by Aida)
+Added some basic support for tinychat beta (-s/--site tinychat_beta)
modbot: use https instead of http and use the tcclient subdomain since some DNSes have trouble with underscores.
modbot: added an option (--no-unapproved) to not add any unapproved videos to queue (videos still get approved by mods requesting or playing them manually)
tc_client-gtk: fixed a race-condition in the builtin video player.
diff --git a/Makefile b/Makefile
index 006b83c..3f84126 100644
--- a/Makefile
+++ b/Makefile
@@ -113,6 +113,14 @@ ifdef READLINE_LIBS
INSTALLDEPS+=cursedchat
endif
endif
+ifdef JSONC_LIBS
+ifdef WEBSOCKET_LIBS
+ CFLAGS+=-DHAVE_WEBSOCKET=1 $(WEBSOCKET_CFLAGS) $(JSONC_CFLAGS)
+ CONFINFO+=|Will enable support for tinychat beta
+ LIBS+=$(WEBSOCKET_LIBS) $(JSONC_LIBS)
+ OBJ+=tinychat_beta.o
+endif
+endif
ifeq ($(AR),)
AR=ar
endif
@@ -160,7 +168,7 @@ libcamera.a: $(LIBCAMERA_OBJ)
clean:
rm -f $(OBJ) $(IRCHACK_OBJ) $(MODBOT_OBJ) $(CAMVIEWER_OBJ) $(CURSEDCHAT_OBJ) $(TC_CLIENT_GTK_OBJ) $(LIBCAMERA_OBJ) tc_client irchack modbot camviewer cursedchat tc_client-gtk camplaceholder.gif
-SOURCES=Makefile client.c amfparser.c rtmp.c numlist.c amfwriter.c idlist.c colors.c endianutils.c media.c amfparser.h rtmp.h numlist.h amfwriter.h idlist.h colors.h endianutils.h media.h LICENSE README ChangeLog crossbuild.sh testbuilds.sh configure
+SOURCES=Makefile client.c amfparser.c rtmp.c numlist.c amfwriter.c idlist.c colors.c endianutils.c media.c client.h amfparser.h rtmp.h numlist.h amfwriter.h idlist.h colors.h endianutils.h media.h LICENSE README ChangeLog crossbuild.sh testbuilds.sh configure tinychat.c tinychat_beta.c kageshi.c
SOURCES+=utilities/irchack/irchack.c
SOURCES+=utilities/modbot/modbot.c utilities/modbot/queue.c utilities/modbot/queue.h utilities/modbot/commands.html
SOURCES+=utilities/camviewer/camviewer.c
diff --git a/client.c b/client.c
index d3830b1..1935a47 100644
--- a/client.c
+++ b/client.c
@@ -149,6 +149,9 @@ extern int rtmplog;
#endif
extern int init_tinychat(const char* chanpass, const char* username, const char* userpass, struct site* site);
+#ifdef HAVE_WEBSOCKET
+extern int init_tinychat_beta(const char* chanpass, const char* username, const char* userpass, struct site* site);
+#endif
extern int init_kageshi(const char* chanpass, const char* username, const char* userpass, struct site* site);
extern int init_kageshicam(struct site* site);
int main(int argc, char** argv)
@@ -204,11 +207,15 @@ int main(int argc, char** argv)
if(!channel||!nickname){usage(argv[0]); return 1;}
if(sitestr &&
strcmp(sitestr, "tinychat") &&
+#ifdef HAVE_WEBSOCKET
+ strcmp(sitestr, "tinychat_beta") &&
+#endif
strcmp(sitestr, "kageshi") &&
strcmp(sitestr, "kageshicam"))
{
printf("Unknown site '%s'. Currently supported sites:\n"
"tinychat\n"
+ "tinychat_beta (if built with libwebsocket and json-c)\n"
"kageshi\n"
"kageshicam (server/camname/numkey as channel)\n", sitestr);
return 1;
@@ -240,6 +247,13 @@ int main(int argc, char** argv)
{
domain=&domain[3];
if(!strncmp(domain, "www.", 4)){domain=&domain[4];}
+#ifdef HAVE_WEBSOCKET
+ if(!strncmp(domain, "tinychat.com/room/", 18))
+ {
+ sitestr="tinychat_beta";
+ channel=&domain[18];
+ }else
+#endif
if(!strncmp(domain, "tinychat.com/", 13))
{
sitestr="tinychat";
@@ -253,12 +267,18 @@ int main(int argc, char** argv)
char* slash=strchr(channel, '/');
if(slash){slash[0]=0;}
}
- struct site site;
+ struct site site={0};
int sock;
if(!sitestr || !strcmp(sitestr, "tinychat"))
{
sock=init_tinychat(password, account_user, account_pass, &site);
}
+#ifdef HAVE_WEBSOCKET
+ else if(!sitestr || !strcmp(sitestr, "tinychat_beta"))
+ {
+ sock=init_tinychat_beta(password, account_user, account_pass, &site);
+ }
+#endif
else if(!strcmp(sitestr, "kageshi"))
{
sock=init_kageshi(password, account_user, account_pass, &site);
@@ -283,6 +303,11 @@ int main(int argc, char** argv)
pfd[1].events=POLLIN;
pfd[1].revents=0;
struct rtmp rtmp={0,0,0,0,0};
+ conn c;
+#ifdef HAVE_WEBSOCKET
+ if(site.websocket){c.ws=site.websocket;}else
+#endif
+ {c.fd=sock;}
while(1)
{
// Poll for input, very crude chat UI
@@ -362,7 +387,7 @@ int main(int argc, char** argv)
}
else if(!strncmp(buf, "/nick ", 6))
{
- site.nick(sock, &buf[6]);
+ site.nick(c, &buf[6]);
continue;
}
else if(!strncmp(buf, "/msg ", 5))
@@ -371,38 +396,38 @@ int main(int argc, char** argv)
if(msg)
{
msg[0]=0;
- site.sendpm(sock, &msg[1], &buf[5]);
+ site.sendpm(c, &msg[1], &buf[5]);
continue;
}
}
else if(!strncmp(buf, "/opencam ", 9))
{
- site.opencam(sock, &buf[9]);
+ site.opencam(c, &buf[9]);
continue;
}
else if(!strncmp(buf, "/closecam ", 10))
{
- site.closecam(sock, &buf[10]);
+ site.closecam(c, &buf[10]);
continue;
}
else if(!strncmp(buf, "/close ", 7)) // Stop someone's cam/mic broadcast
{
- site.mod_close(sock, &buf[7]);
+ site.mod_close(c, &buf[7]);
continue;
}
else if(!strncmp(buf, "/ban ", 5)) // Ban someone
{
- site.mod_ban(sock, &buf[5]);
+ site.mod_ban(c, &buf[5]);
continue;
}
else if(!strcmp(buf, "/banlist"))
{
- site.mod_banlist(sock);
+ site.mod_banlist(c);
continue;
}
else if(!strncmp(buf, "/forgive ", 9))
{
- site.mod_unban(sock, &buf[9]);
+ site.mod_unban(c, &buf[9]);
continue;
}
else if(!strcmp(buf, "/names"))
@@ -418,22 +443,22 @@ int main(int argc, char** argv)
}
else if(!strcmp(buf, "/mute"))
{
- site.mod_mute(sock);
+ site.mod_mute(c);
continue;
}
else if(!strcmp(buf, "/push2talk"))
{
- site.mod_push2talk(sock);
+ site.mod_push2talk(c);
continue;
}
else if(!strcmp(buf, "/camup"))
{
- site.camup(sock);
+ site.camup(c);
continue;
}
else if(!strcmp(buf, "/camdown"))
{
- site.camdown(sock);
+ site.camdown(c);
continue;
}
else if(!strncmp(buf, "/video ", 7)) // Send video data
@@ -454,7 +479,7 @@ int main(int argc, char** argv)
}
else if(!strncmp(buf, "/topic ", 7))
{
- site.mod_topic(sock, &buf[7]);
+ site.mod_topic(c, &buf[7]);
continue;
}
else if(!strncmp(buf, "/whois ", 7)) // Get account username
@@ -476,7 +501,7 @@ int main(int argc, char** argv)
}
else if(!strncmp(buf, "/allow ", 7))
{
- site.mod_allowbroadcast(sock, &buf[7]);
+ site.mod_allowbroadcast(c, &buf[7]);
continue;
}
else if(!strncmp(buf, "/getnick ", 9))
@@ -488,29 +513,44 @@ int main(int argc, char** argv)
}
else if(!strcmp(buf, "/quit")){break;}
}
- site.sendmessage(sock, buf);
- continue;
+ site.sendmessage(c, buf);
}
- // Got data from server
- pfd[1].revents=0;
- // Read the RTMP stream and handle AMF0 packets
- char rtmpres=rtmp_get(sock, &rtmp);
- if(!rtmpres){printf("Server disconnected\n"); break;}
- if(rtmpres==2){continue;} // Not disconnected, but we didn't get a complete chunk yet either
- if(rtmp.type==RTMP_VIDEO || rtmp.type==RTMP_AUDIO){stream_handledata(&rtmp); continue;}
- if(rtmp.type!=RTMP_AMF0){printf("Got RTMP type 0x%x\n", rtmp.type); fflush(stdout); continue;}
- struct amf* amfin=amf_parse(rtmp.buf, rtmp.length);
- for(i=0; i<commandcount; ++i)
+ if(pfd[1].revents)
{
- if(amfin->itemcount>=commands[i].minargs && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, commands[i].command))
+ // Got data from server
+ pfd[1].revents=0;
+#ifdef HAVE_WEBSOCKET
+ if(site.websocket)
{
- commands[i].callback(amfin, sock);
+ struct websock_head head;
+ if(!websock_readhead(c.ws, &head)){printf("Server disconnected\n"); break;}
+ char buf[head.length];
+ websock_readcontent(c.ws, buf, &head);
+ site.handlewspacket(c.ws, buf, &head);
fflush(stdout);
- break;
+ }else
+#endif
+ {
+ // Read the RTMP stream and handle AMF0 packets
+ char rtmpres=rtmp_get(sock, &rtmp);
+ if(!rtmpres){printf("Server disconnected\n"); break;}
+ if(rtmpres==2){continue;} // Not disconnected, but we didn't get a complete chunk yet either
+ if(rtmp.type==RTMP_VIDEO || rtmp.type==RTMP_AUDIO){stream_handledata(&rtmp); continue;}
+ if(rtmp.type!=RTMP_AMF0){printf("Got RTMP type 0x%x\n", rtmp.type); fflush(stdout); continue;}
+ struct amf* amfin=amf_parse(rtmp.buf, rtmp.length);
+ for(i=0; i<commandcount; ++i)
+ {
+ if(amfin->itemcount>=commands[i].minargs && amfin->items[0].type==AMF_STRING && amf_comparestrings_c(&amfin->items[0].string, commands[i].command))
+ {
+ commands[i].callback(amfin, sock);
+ fflush(stdout);
+ break;
+ }
+ }
+ // if(i==commandcount){printf("Unknown command...\n"); printamf(amfin);} // (Debugging)
+ amf_free(amfin);
}
}
- // if(i==commandcount){printf("Unknown command...\n"); printamf(amfin);} // (Debugging)
- amf_free(amfin);
}
free(rtmp.buf);
curl_easy_cleanup(curl);
diff --git a/client.h b/client.h
index 1c3f312..1139e46 100644
--- a/client.h
+++ b/client.h
@@ -1,21 +1,35 @@
+#ifdef HAVE_WEBSOCKET
+#include <libwebsocket/websock.h>
+#endif
#include "amfparser.h"
+typedef union
+{
+ int fd;
+#ifdef HAVE_WEBSOCKET
+ websock_conn* ws;
+#endif
+} conn;
struct site
{
- void(*sendmessage)(int sock, const char* buf);
- void(*sendpm)(int sock, const char* buf, const char* recipient);
- void(*nick)(int sock, const char* newnick);
- void(*mod_close)(int sock, const char* nick);
- void(*mod_ban)(int sock, const char* nick);
- void(*mod_banlist)(int sock);
- void(*mod_unban)(int sock, const char* nick);
- void(*mod_mute)(int sock);
- void(*mod_push2talk)(int sock);
- void(*mod_topic)(int sock, const char* newtopic);
- void(*mod_allowbroadcast)(int sock, const char* nick);
- void(*opencam)(int sock, const char* arg);
- void(*closecam)(int sock, const char* arg);
- void(*camup)(int sock);
- void(*camdown)(int sock);
+ void(*sendmessage)(conn c, const char* buf);
+ void(*sendpm)(conn c, const char* buf, const char* recipient);
+ void(*nick)(conn c, const char* newnick);
+ void(*mod_close)(conn c, const char* nick);
+ void(*mod_ban)(conn c, const char* nick);
+ void(*mod_banlist)(conn c);
+ void(*mod_unban)(conn c, const char* nick);
+ void(*mod_mute)(conn c);
+ void(*mod_push2talk)(conn c);
+ void(*mod_topic)(conn c, const char* newtopic);
+ void(*mod_allowbroadcast)(conn c, const char* nick);
+ void(*opencam)(conn c, const char* arg);
+ void(*closecam)(conn c, const char* arg);
+ void(*camup)(conn c);
+ void(*camdown)(conn c);
+#ifdef HAVE_WEBSOCKET
+ websock_conn* websocket;
+ void(*handlewspacket)(websock_conn*, void*, struct websock_head*);
+#endif
};
extern char greenroom;
diff --git a/configure b/configure
index c3cf343..167a9a9 100755
--- a/configure
+++ b/configure
@@ -255,6 +255,9 @@ fi
testpkgconfig webkit2gtk-4.0 WEBKITGTK || \
testpkgconfig webkit2gtk-3.0 WEBKITGTK
+testpkgconfig libwebsocket WEBSOCKET
+testpkgconfig json-c JSONC
+
printf 'Checking if we have a working poll()... '
echo '#include <poll.h>' > polltest.c
echo '#include <sys/socket.h>' >> polltest.c
diff --git a/idlist.c b/idlist.c
index aeec5c9..d5f6a00 100644
--- a/idlist.c
+++ b/idlist.c
@@ -1,6 +1,6 @@
/*
tc_client, a simple non-flash client for tinychat(.com)
- Copyright (C) 2014-2016 alicia@ion.nu
+ Copyright (C) 2014-2017 alicia@ion.nu
Copyright (C) 2014-2015 Jade Lea
This program is free software: you can redistribute it and/or modify
@@ -48,6 +48,21 @@ void idlist_remove(const char* name)
}
}
+void idlist_removeid(int id)
+{
+ int i;
+ for(i=0; i<idlistlen; ++i)
+ {
+ if(idlist[i].id==id)
+ {
+ free((void*)idlist[i].name);
+ --idlistlen;
+ memmove(&idlist[i], &idlist[i+1], sizeof(struct idmap)*(idlistlen-i));
+ return;
+ }
+ }
+}
+
void idlist_rename(const char* oldname, const char* newname)
{
int i;
@@ -62,6 +77,20 @@ void idlist_rename(const char* oldname, const char* newname)
}
}
+void idlist_renameid(int id, const char* newname)
+{
+ int i;
+ for(i=0; i<idlistlen; ++i)
+ {
+ if(idlist[i].id==id)
+ {
+ free((void*)idlist[i].name);
+ idlist[i].name=strdup(newname);
+ return;
+ }
+ }
+}
+
int idlist_get(const char* name)
{
int len;
diff --git a/idlist.h b/idlist.h
index bbaae31..108ab40 100644
--- a/idlist.h
+++ b/idlist.h
@@ -1,6 +1,6 @@
/*
tc_client, a simple non-flash client for tinychat(.com)
- Copyright (C) 2014-2016 alicia@ion.nu
+ Copyright (C) 2014-2017 alicia@ion.nu
Copyright (C) 2014-2015 Jade Lea
This program is free software: you can redistribute it and/or modify
@@ -28,7 +28,9 @@ extern int idlistlen;
extern void idlist_add(int id, const char* name, const char* account, char op);
extern void idlist_remove(const char* name);
+extern void idlist_removeid(int id);
extern void idlist_rename(const char* oldname, const char* newname);
+extern void idlist_renameid(int id, const char* newname);
extern int idlist_get(const char* name);
extern const char* idlist_getaccount(const char* name);
extern char idlist_is_op(const char* name);
diff --git a/kageshi.c b/kageshi.c
index 37f1f6f..6da9238 100644
--- a/kageshi.c
+++ b/kageshi.c
@@ -162,7 +162,7 @@ static void result2(struct amf* amfin, int sock)
}
}
-static void sendmessage(int sock, const char* buf)
+static void sendmessage(conn c, const char* buf)
{
char color[8];
memcpy(color, colors[currentcolor%COLORCOUNT], 7);
@@ -184,11 +184,11 @@ static void sendmessage(int sock, const char* buf)
amfstring(&amf, color);
amfstring(&amf, "1");
amfnum(&amf, 1);
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
}
/* Not used yet
-static void sendpmtyping(int sock, const char* nick)
+static void sendpmtyping(conn c, const char* nick)
{
struct rtmp amf;
amfinit(&amf, 3);
@@ -200,11 +200,11 @@ static void sendpmtyping(int sock, const char* nick)
amfstring(&amf, nickname); // Spoofable?
amfstring(&amf, nick);
amfstring(&amf, "1"); // 0 for not typing anymore? (or 2? from observations)
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
}
*/
-static void sendpm(int sock, const char* buf, const char* nick)
+static void sendpm(conn c, const char* buf, const char* nick)
{
char color[8];
memcpy(color, colors[currentcolor%COLORCOUNT], 7);
@@ -226,7 +226,7 @@ static void sendpm(int sock, const char* buf, const char* nick)
amfstring(&amf, color);
amfstring(&amf, "1");
amfnum(&amf, 1);
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
}
static void notimplemented()
@@ -370,14 +370,14 @@ static void result(struct amf* amfin, int sock)
}
}
-static void opencam(int sock, const char* camname)
+static void opencam(conn c, const char* camname)
{
- stream_start(camname, camname, sock);
+ stream_start(camname, camname, c.fd);
}
-static void closecam(int sock, const char* camname)
+static void closecam(conn c, const char* camname)
{
- stream_stopvideo(sock, camname);
+ stream_stopvideo(c.fd, camname);
}
static void notimplemented2()
diff --git a/tinychat.c b/tinychat.c
index 884ae0e..a239a20 100644
--- a/tinychat.c
+++ b/tinychat.c
@@ -204,7 +204,7 @@ static char checknick(const char* nick) // Returns zero if the nick is valid, ot
return 0;
}
-static void sendmessage_priv(int sock, const char* buf, const char* priv)
+static void sendmessage_priv(conn c, const char* buf, const char* priv)
{
int id=priv?idlist_get(priv):0;
char privfield[snprintf(0, 0, "n%i-%s", id, priv?priv:"")+1];
@@ -232,13 +232,13 @@ static void sendmessage_priv(int sock, const char* buf, const char* priv)
amfstring(&bamf, msg);
amfstring(&bamf, colors[currentcolor%COLORCOUNT]);
amfstring(&bamf, privfield);
- amfsend(&bamf, sock);
+ amfsend(&bamf, c.fd);
}
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
free(msg);
}
-static void sendmessage(int sock, const char* buf)
+static void sendmessage(conn c, const char* buf)
{
if(!strncmp(buf, "/priv ", 6))
{
@@ -248,21 +248,21 @@ static void sendmessage(int sock, const char* buf)
char priv[msg-&buf[6]+1];
memcpy(priv, &buf[6], msg-&buf[6]);
priv[msg-&buf[6]]=0;
- sendmessage_priv(sock, &msg[1], priv);
+ sendmessage_priv(c, &msg[1], priv);
return;
}
}
- sendmessage_priv(sock, buf, 0);
+ sendmessage_priv(c, buf, 0);
}
-static void sendpm(int sock, const char* buf, const char* recipient)
+static void sendpm(conn c, const char* buf, const char* recipient)
{
char msg[strlen("/msg 0")+strlen(recipient)+strlen(buf)];
sprintf(msg, "/msg %s %s", recipient, buf);
- sendmessage_priv(sock, msg, recipient);
+ sendmessage_priv(c, msg, recipient);
}
-static void changenick(int sock, const char* newnick)
+static void changenick(conn c, const char* newnick)
{
char badchar;
if((badchar=checknick(newnick)))
@@ -276,7 +276,7 @@ static void changenick(int sock, const char* newnick)
amfnum(&amf, 0);
amfnull(&amf);
amfstring(&amf, newnick);
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
}
static void joinreg(struct amf* amfin, int sock)
@@ -328,7 +328,7 @@ static void joinreg(struct amf* amfin, int sock)
amfsend(&amf, sock);
free(key);
// Set the given nickname
- changenick(sock, nickname);
+ changenick((conn)sock, nickname);
// Keep what the server gave us, just in case
free(nickname);
nickname=strdup(nick);
@@ -399,7 +399,7 @@ static void privmsg(struct amf* amfin, int sock)
}
else if(!strcmp(msg, "/version"))
{
- sendmessage_priv(sock, "/version tc_client-" VERSION, amfin->items[5].string.string);
+ sendmessage_priv((conn)sock, "/version tc_client-" VERSION, amfin->items[5].string.string);
}
free(msg);
}
@@ -442,12 +442,12 @@ static void banned(struct amf* amfin, int sock)
exit(1); // Getting banned is a failure, right?
}
-static void closecam(int sock, const char* nick)
+static void closecam(conn c, const char* nick)
{
unsigned int userid=idlist_get(nick);
char camid[snprintf(0,0,"%u", userid)+1];
sprintf(camid, "%u", userid);
- stream_stopvideo(sock, camid);
+ stream_stopvideo(c.fd, camid);
}
static void fromowner(struct amf* amfin, int sock)
@@ -473,7 +473,7 @@ static void fromowner(struct amf* amfin, int sock)
else if(!strncmp("_close", amfin->items[2].string.string, 6) && !strcmp(&amfin->items[2].string.string[6], nickname))
{
printf("Outgoing media stream was closed\n");
- closecam(sock, nickname);
+ closecam((conn)sock, nickname);
}
}
@@ -547,7 +547,7 @@ static void onstatus(struct amf* amfin, int sock)
stream_handlestatus(amfin, sock);
}
-static void mod_close(int sock, const char* nick)
+static void mod_close(conn c, const char* nick)
{
struct rtmp amf;
amfinit(&amf, 2);
@@ -557,12 +557,12 @@ static void mod_close(int sock, const char* nick)
char buf[strlen("closed: 0")+strlen(nick)];
sprintf(buf, "_close%s", nick);
amfstring(&amf, buf);
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
sprintf(buf, "closed: %s", nick);
- sendmessage(sock, buf);
+ sendmessage(c, buf);
}
-static void mod_ban(int sock, const char* nick)
+static void mod_ban(conn c, const char* nick)
{
struct rtmp amf;
amfinit(&amf, 3);
@@ -574,7 +574,7 @@ static void mod_ban(int sock, const char* nick)
char buf[strlen("notice%20was%20banned%20by%200")+strlen(nick)+strlen(nickname)];
sprintf(buf, "notice%s%%20was%%20banned%%20by%%20%s", nick, nickname);
amfstring(&amf, buf);
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
// printf("%s %s was banned by %s (%s)\n", timestamp(), nick, nickname, account_user);
printf("%s %s was banned by %s\n", timestamp(), nick, nickname);
// kick (this does the actual banning)
@@ -585,27 +585,27 @@ static void mod_ban(int sock, const char* nick)
amfstring(&amf, nick);
sprintf(buf, "%i", idlist_get(nick));
amfstring(&amf, buf);
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
}
-static void mod_banlist(int sock)
+static void mod_banlist(conn c)
{
struct rtmp amf;
amfinit(&amf, 3);
amfstring(&amf, "banlist");
amfnum(&amf, 0);
amfnull(&amf);
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
}
-static void mod_unban(int sock, const char* nick)
+static void mod_unban(conn c, const char* nick)
{
free(unban);
unban=strdup(nick);
- mod_banlist(sock);
+ mod_banlist(c);
}
-static void mod_mute(int sock)
+static void mod_mute(conn c)
{
struct rtmp amf;
amfinit(&amf, 3);
@@ -613,10 +613,10 @@ static void mod_mute(int sock)
amfnum(&amf, 0);
amfnull(&amf);
amfstring(&amf, "mute");
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
}
-static void mod_push2talk(int sock)
+static void mod_push2talk(conn c)
{
struct rtmp amf;
amfinit(&amf, 3);
@@ -624,10 +624,10 @@ static void mod_push2talk(int sock)
amfnum(&amf, 0);
amfnull(&amf);
amfstring(&amf, "push2talk");
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
}
-static void mod_topic(int sock, const char* newtopic)
+static void mod_topic(conn c, const char* newtopic)
{
struct rtmp amf;
amfinit(&amf, 3);
@@ -636,18 +636,18 @@ static void mod_topic(int sock, const char* newtopic)
amfnull(&amf);
amfstring(&amf, newtopic);
amfstring(&amf, "");
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
}
-static void mod_allowbroadcast(int sock, const char* nick)
+static void mod_allowbroadcast(conn c, const char* nick)
{
if(!bpassword){return;}
char buf[strlen("/allowbroadcast 0")+strlen(bpassword)];
sprintf(buf, "/allowbroadcast %s", bpassword);
- sendmessage_priv(sock, buf, nick);
+ sendmessage_priv(c, buf, nick);
}
-static void camup(int sock)
+static void camup(conn c)
{
// Retrieve and send the key for broadcasting access
char* key=getbroadcastkey(channel, nickname, bpassword);
@@ -658,29 +658,29 @@ static void camup(int sock)
amfnum(&amf, 0);
amfnull(&amf);
amfstring(&amf, key);
- amfsend(&amf, sock);
+ amfsend(&amf, c.fd);
free(key);
// Initiate stream
unsigned int userid=idlist_get(nickname);
char camid[snprintf(0,0,"%u", userid)+1];
sprintf(camid, "%u", userid);
- streamout_start(camid, sock);
+ streamout_start(camid, c.fd);
}
-static void camdown(int sock)
+static void camdown(conn c)
{
unsigned int userid=idlist_get(nickname);
char camid[snprintf(0,0,"%u", userid)+1];
sprintf(camid, "%u", userid);
- stream_stopvideo(sock, camid);
+ stream_stopvideo(c.fd, camid);
}
-static void opencam(int sock, const char* nick)
+static void opencam(conn c, const char* nick)
{
unsigned int userid=idlist_get(nick);
char camid[snprintf(0,0,"%u", userid)+1];
sprintf(camid, "%u", userid);
- stream_start(nick, camid, sock);
+ stream_start(nick, camid, c.fd);
}
int init_tinychat(const char* chanpass, const char* username, const char* userpass, struct site* site)
diff --git a/tinychat_beta.c b/tinychat_beta.c
new file mode 100644
index 0000000..c9ecd4e
--- /dev/null
+++ b/tinychat_beta.c
@@ -0,0 +1,207 @@
+/*
+ tc_client, a simple non-flash client for tinychat(.com)
+ Copyright (C) 2017 alicia@ion.nu
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, version 3 of the License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <json.h>
+#include "client.h"
+#include "idlist.h"
+
+static const char* user_join(struct json_object* user)
+{
+ struct json_object* value;
+ if(!json_object_object_get_ex(user, "nick", &value)){return 0;}
+ const char* nick=json_object_get_string(value);
+ if(!json_object_object_get_ex(user, "username", &value)){return 0;}
+ const char* account=json_object_get_string(value);
+ if(!json_object_object_get_ex(user, "handle", &value)){return 0;}
+ int64_t id=json_object_get_int64(value);
+ if(!json_object_object_get_ex(user, "mod", &value)){return 0;}
+ char mod=json_object_get_boolean(value);
+ idlist_add(id, nick, account, mod);
+ return nick;
+}
+
+static const char* chanpass=0;
+static void handlepacket(websock_conn* conn, void* data, struct websock_head* head)
+{
+ // Parse JSON and handle data
+ struct json_tokener* tok=json_tokener_new();
+ struct json_object* obj=json_tokener_parse_ex(tok, data, head->length);
+ json_tokener_free(tok);
+ if(!obj){return;}
+ struct json_object* cmdobj;
+ if(!json_object_object_get_ex(obj, "tc", &cmdobj)){json_object_put(obj); return;}
+ const char* cmd=json_object_get_string(cmdobj);
+ if(!strcmp(cmd, "ping")){websock_write(conn, "{\"tc\":\"pong\"}", 13, WEBSOCK_TEXT);}
+ else if(!strcmp(cmd, "password"))
+ {
+ if(!chanpass){printf("Password required\n"); exit(-1);}
+ char buf[strlen("{\"tc\":\"password\",\"password\":\"\"}0")+strlen(chanpass)];
+ sprintf(buf, "{\"tc\":\"password\",\"password\":\"%s\"}", chanpass);
+ websock_write(conn, buf, strlen(buf), WEBSOCK_TEXT);
+ }
+ else if(!strcmp(cmd, "userlist"))
+ {
+ struct json_object* users;
+ if(!json_object_object_get_ex(obj, "users", &users)){json_object_put(obj); return;}
+ if(!json_object_is_type(users, json_type_array)){json_object_put(obj); return;}
+ printf("Currently online: ");
+ unsigned int i;
+ for(i=0; i<json_object_array_length(users); ++i)
+ {
+ struct json_object* user=json_object_array_get_idx(users, i);
+ if(!user){continue;}
+ const char* nick=user_join(user);
+ if(!nick){continue;}
+ printf(i?", %s":"%s", nick);
+ }
+ printf("\n");
+ // List everyone who is a mod
+ for(i=0; i<idlistlen; ++i)
+ {
+ if(idlist[i].op){printf("%s is a moderator.\n", idlist[i].name);}
+ }
+ }
+ else if(!strcmp(cmd, "msg"))
+ {
+ if(!json_object_object_get_ex(obj, "text", &cmdobj)){json_object_put(obj); return;}
+ const char* msg=json_object_get_string(cmdobj);
+ if(!json_object_object_get_ex(obj, "handle", &cmdobj)){json_object_put(obj); return;}
+ int64_t handle=json_object_get_int64(cmdobj);
+ const char* nick=idlist_getnick(handle);
+ if(!strcmp(nick, nickname)){json_object_put(obj); return;} // Ignore messages from ourselves (server sends it back along with sending it to everyone else)
+ printf("%s %s: %s\n", timestamp(), nick, msg);
+ }
+ else if(!strcmp(cmd, "joined"))
+ {
+ struct json_object* channel;
+ if(!json_object_object_get_ex(obj, "room", &channel)){json_object_put(obj); return;}
+ if(!json_object_object_get_ex(channel, "topic", &cmdobj)){json_object_put(obj); return;}
+ const char* topic=json_object_get_string(cmdobj);
+ printf("Room topic: %s\n", topic);
+ if(!json_object_object_get_ex(obj, "self", &cmdobj)){json_object_put(obj); return;}
+ const char* nick=user_join(cmdobj);
+ printf("Connection ID: %i\n", idlist[0].id);
+ if(nick)
+ {
+ free(nickname);
+ nickname=strdup(nick);
+ }
+ }
+ else if(!strcmp(cmd, "nick"))
+ {
+ if(!json_object_object_get_ex(obj, "success", &cmdobj)){json_object_put(obj); return;}
+ if(!json_object_get_boolean(cmdobj)){json_object_put(obj); return;} // Ignore failed nick changes
+ if(!json_object_object_get_ex(obj, "nick", &cmdobj)){json_object_put(obj); return;}
+ const char* nick=json_object_get_string(cmdobj);
+ if(!json_object_object_get_ex(obj, "handle", &cmdobj)){json_object_put(obj); return;}
+ int64_t handle=json_object_get_int64(cmdobj);
+ const char* oldnick=idlist_getnick(handle);
+ printf("%s %s changed nickname to %s\n", timestamp(), oldnick, nick);
+ if(!strcmp(oldnick, nickname)){free(nickname); nickname=strdup(nick);}
+ idlist_renameid(handle, nick);
+ }
+ else if(!strcmp(cmd, "join"))
+ {
+ const char* nick=user_join(obj);
+ if(!nick){json_object_put(obj); return;}
+ printf("%s %s entered the channel\n", timestamp(), nick);
+ if(!json_object_object_get_ex(obj, "mod", &cmdobj)){json_object_put(obj); return;}
+ if(json_object_get_boolean(cmdobj))
+ {
+ printf("%s is a moderator.\n", nick);
+ }
+ }
+ else if(!strcmp(cmd, "quit"))
+ {
+ if(!json_object_object_get_ex(obj, "handle", &cmdobj)){json_object_put(obj); return;}
+ int64_t handle=json_object_get_int64(cmdobj);
+ const char* nick=idlist_getnick(handle);
+ printf("%s %s left the channel\n", timestamp(), nick);
+ idlist_removeid(handle);
+ }else{
+ write(1, "Received: ", 10);
+ write(1, data, head->length);
+ write(1, "\n", 1);
+ printf("Command: %s\n", cmd);
+ }
+ json_object_put(obj);
+}
+
+static void sendmessage(conn c, const char* msg)
+{
+ struct json_object* obj=json_object_new_object();
+ json_object_object_add(obj, "tc", json_object_new_string("msg"));
+ json_object_object_add(obj, "text", json_object_new_string(msg));
+ const char* json=json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN);
+ websock_write(c.ws, json, strlen(json), WEBSOCK_TEXT);
+ json_object_put(obj);
+}
+
+static void setnick(conn c, const char* nick)
+{
+ struct json_object* obj=json_object_new_object();
+ json_object_object_add(obj, "tc", json_object_new_string("nick"));
+ json_object_object_add(obj, "nick", json_object_new_string(nick));
+ const char* json=json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN);
+ websock_write(c.ws, json, strlen(json), WEBSOCK_TEXT);
+ json_object_put(obj);
+}
+
+static void notimplemented()
+{
+ printf("The requested feature is either not supported by tinychat beta or not yet implemented in tc_client's tinychat beta support\n");
+}
+
+int init_tinychat_beta(const char* chanpass_, const char* username, const char* userpass, struct site* site)
+{
+ site->handlewspacket=handlepacket;
+ site->sendmessage=sendmessage;
+ site->sendpm=notimplemented;
+ site->nick=setnick;
+ site->mod_close=notimplemented;
+ site->mod_ban=notimplemented;
+ site->mod_banlist=notimplemented;
+ site->mod_unban=notimplemented;
+ site->mod_mute=notimplemented;
+ site->mod_push2talk=notimplemented;
+ site->mod_topic=notimplemented;
+ site->mod_allowbroadcast=notimplemented;
+ site->opencam=notimplemented;
+ site->closecam=notimplemented;
+ site->camup=notimplemented;
+ site->camdown=notimplemented;
+ chanpass=chanpass_;
+ int sock=connectto("wss.tinychat.com", "443");
+ site->websocket=websock_new(sock, 1, 0, 0);
+ if(!websock_handshake_client(site->websocket, "/", "wss.tinychat.com", "tc", "https://tinychat.com", 0)){printf("Websocket handshake failed\n"); return -1;}
+ char url[strlen("https://tinychat.com/api/v1.0/room/token/0")+strlen(channel)];
+ sprintf(url, "https://tinychat.com/api/v1.0/room/token/%s", channel);
+ char* tokenfile=http_get(url, 0);
+ if(strlen(tokenfile)<11){return -1;}
+ char* token=&tokenfile[11];
+ char* end;
+ if((end=strchr(token, '"'))){end[0]=0;}
+ const char* fmt="{\"tc\":\"join\",\"useragent\":\"tc_client\",\"token\":\"%s\",\"room\":\"%s\",\"nick\":\"%s\"}";
+ char buf[snprintf(0,0, fmt, token, channel, nickname)+1];
+ sprintf(buf, fmt, token, channel, nickname);
+ free(tokenfile);
+ websock_write(site->websocket, buf, strlen(buf), WEBSOCK_TEXT);
+ return sock;
+}