/*
webchat, an HTML5/websocket chat platform
Copyright (C) 2015
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 .
*/
#include
#include
#include
#include
#include "../libwebsocket/websock.h"
#include "users.h"
#include "db.h"
#include "channels.h"
struct channel** channels=0;
unsigned int channelcount=0;
struct channel* getchannel(const char* name)
{
unsigned int i;
for(i=0; iname)){return channels[i];}
}
++channelcount;
channels=realloc(channels, sizeof(struct channel*)*channelcount);
struct channel* channel=malloc(sizeof(struct channel));
channels[channelcount-1]=channel;
channel->name=strdup(name);
channel->usercount=0;
channel->users=0;
channel->password=0;
channel->id=-1;
channel->bancount=0;
channel->bans=0; // TODO: Load/save bans in the database too?
printf("Loading channel data for '%s'\n", name);
db_findchannel(name, &channel->id, &channel->password);
printf("ID: %i, password: %s\n", channel->id, channel->password);
return channel;
}
void joinchannel(struct user* user, struct channel* channel)
{
user->channel=channel;
++user->channel->usercount;
user->channel->users=realloc(user->channel->users, sizeof(struct user*)*user->channel->usercount);
user->channel->users[user->channel->usercount-1]=user;
if(channel->id>-1)
{
websock_write(user->socket, "channelstatus:registered", 24, WEBSOCK_TEXT, use_tls);
}else{
websock_write(user->socket, "channelstatus:unregistered", 26, WEBSOCK_TEXT, use_tls);
}
// TODO: move more join-related stuff here, like sending "join:" messages and "names:"
}
struct user* channel_finduser(struct channel* chan, const unsigned char* nickname)
{
unsigned int i;
for(i=0; iusercount; ++i)
{
if(!strcmp(nickname, chan->users[i]->nickname)){return chan->users[i];}
}
return 0;
}
void channel_write(struct channel* chan, unsigned char* msg, unsigned int msglen, unsigned char opcode, struct user* skip)
{
unsigned int i;
for(i=0; iusercount; ++i)
{
if(chan->users[i]==skip || !chan->users[i]->handshake){continue;}
websock_write(chan->users[i]->socket, msg, msglen, WEBSOCK_TEXT, 1);
}
}
char channel_register(struct channel* channel, struct user* owner)
{
int userid;
if(!db_finduser(owner->account, &userid, 0, 0)){return 0;}
if(!db_createchannel(channel->name)){return 0;}
if(!db_findchannel(channel->name, &channel->id, 0)){return 0;}
if(db_addmod(channel->id, userid, PRIV_ALL))
{
owner->modprivileges=PRIV_ALL;
channel_write(channel, "channelstatus:registered", 24, WEBSOCK_TEXT, 0);
// Also notify about the new mod
user_sendmodmsg(owner);
return 1;
}
db_removechannel(channel->name); // If something went wrong, try to prevent damage in the form of orphaned channels
return 0;
}
void channel_ban(struct channel* channel, struct user* user)
{
unsigned int banid=0;
if(channel->bancount>0){banid=channel->bans[channel->bancount-1].id+1;} // ID of last+1 should be unique
++channel->bancount;
channel->bans=realloc(channel->bans, sizeof(struct ban)*channel->bancount);
struct ban* ban=&channel->bans[channel->bancount-1];
ban->addr_family=user->sockaddr.sa_family;
char* addr;
switch(user->sockaddr.sa_family)
{
case AF_INET:
ban->addr_size=4;
addr=malloc(ban->addr_size);
memcpy(addr, &((struct sockaddr_in*)&user->sockaddr)->sin_addr.s_addr, ban->addr_size);
break;
case AF_INET6:
ban->addr_size=16;
addr=malloc(ban->addr_size);
memcpy(addr, ((struct sockaddr_in6*)&user->sockaddr)->sin6_addr.__in6_u.__u6_addr8, ban->addr_size);
break;
default: // This shouldn't happen, I don't think we're even listening on anything but IPv4 yet, but in case it does happen...
printf("Unknown address family %u, banning all of them as a workaround\n", user->sockaddr.sa_family);
ban->addr_size=0;
addr=0;
}
ban->addr=addr;
ban->id=banid;
ban->nickname=strdup(user->nickname);
}
char channel_unban(struct channel* channel, unsigned int id)
{
unsigned int i;
for(i=0; ibancount; ++i)
{
if(channel->bans[i].id==id)
{
free((void*)channel->bans[i].addr);
free((void*)channel->bans[i].nickname);
--channel->bancount;
memmove(&channel->bans[i], &channel->bans[i+1], sizeof(struct ban)*(channel->bancount-i));
return 1;
}
}
return 0;
}
char channel_checkban(struct channel* channel, struct sockaddr* useraddr)
{
unsigned int i;
for(i=0; ibancount; ++i)
{
if(channel->bans[i].addr_family!=useraddr->sa_family){continue;}
void* addr;
switch(useraddr->sa_family)
{
case AF_INET:
addr=&((struct sockaddr_in*)useraddr)->sin_addr.s_addr;
break;
case AF_INET6:
addr=((struct sockaddr_in6*)useraddr)->sin6_addr.__in6_u.__u6_addr8;
break;
}
if(!memcmp(addr, channel->bans[i].addr, channel->bans[i].addr_size)){return 1;}
}
return 0;
}
void channel_free(struct channel* channel)
{
unsigned int i;
for(i=0; ibancount; ++i)
{
free((void*)channel->bans[i].addr);
free((void*)channel->bans[i].nickname);
}
free((void*)channel->name);
free(channel->users);
free(channel->password);
free(channel->bans);
for(i=0; iname)){return 0;} // If something went wrong, at least keep the channel and its mods as is so it can be attempted again
unsigned int i;
// Clear bans
for(i=0; ibancount; ++i)
{
free((void*)channel->bans[i].addr);
free((void*)channel->bans[i].nickname);
}
channel->bans=0;
channel->bancount=0;
// Unset password (if there was one)
free(channel->password);
channel->password=0;
// Clear mod privileges for users
for(i=0; iusercount; ++i)
{
if(channel->users[i]->modprivileges)
{
channel->users[i]->modprivileges=0;
user_sendmodmsg(channel->users[i]);
}
}
// Notify that the channel is once again free to claim
channel->id=-1;
channel_write(channel, "channelstatus:unregistered", 26, WEBSOCK_TEXT, 0);
return 1;
}