/*
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
#include
#include "../libwebsocket/websock.h"
#include "channels.h"
#include "db.h"
#include "users.h"
unsigned int usercount=0;
struct user** users=0;
gnutls_certificate_credentials_t cert=0;
gnutls_priority_t priority=0;
struct user* adduser(int socket, struct sockaddr* addr, socklen_t addrlen)
{
++usercount;
users=realloc(users, sizeof(struct user*)*usercount);
users[usercount-1]=malloc(sizeof(struct user));
#ifdef use_tls
if(!cert)
{
gnutls_global_init();
gnutls_certificate_allocate_credentials(&cert);
gnutls_certificate_set_x509_key_file(cert, "cert.pem", "key.pem", GNUTLS_X509_FMT_PEM);
gnutls_priority_init(&priority, "PERFORMANCE:%SERVER_PRECEDENCE", 0);
}
gnutls_init(&users[usercount-1]->socket, GNUTLS_SERVER);
gnutls_priority_set(users[usercount-1]->socket, priority);
gnutls_credentials_set(users[usercount-1]->socket, GNUTLS_CRD_CERTIFICATE, cert);
gnutls_certificate_server_set_request(users[usercount-1]->socket, GNUTLS_CERT_IGNORE);
gnutls_transport_set_int(users[usercount-1]->socket, socket);
int ret;
do{
ret=gnutls_handshake(users[usercount-1]->socket);
}
while(ret<0 && !gnutls_error_is_fatal(ret));
users[usercount-1]->rawsocket=socket;
#else
users[usercount-1]->rawsocket=socket;
users[usercount-1]->socket=&users[usercount-1]->rawsocket;
#endif
users[usercount-1]->handshake=0;
users[usercount-1]->channel=0;
users[usercount-1]->bincmd=bincmd_none;
users[usercount-1]->relations=0;
users[usercount-1]->relationcount=0;
users[usercount-1]->broadcasting=0;
users[usercount-1]->firstmedia=0;
users[usercount-1]->firstmedialength=0;
users[usercount-1]->httpcam=0;
users[usercount-1]->account=0;
users[usercount-1]->modprivileges=0;
users[usercount-1]->packet.data=0;
unsigned int i=0;
unsigned int j=0;
while(jnickname, "Guest-", 6) && atoi(&users[j]->nickname[6])==i){++i; j=0; continue;}
++j;
}
// TODO: option to set nickname during handshake if it's not already taken, avoid guest joins
users[usercount-1]->nickname=malloc(snprintf(0,0,"Guest-%u", i)+1);
sprintf(users[usercount-1]->nickname, "Guest-%u", i);
strcpy(users[usercount-1]->color, "000000");
memcpy(&users[usercount-1]->sockaddr, addr, addrlen);
return users[usercount-1];
}
void freeuser(struct user* user)
{
unsigned int i;
if(user->channel)
{
for(i=0; ichannel->usercount; ++i)
{
if(user->channel->users[i]==user)
{
--user->channel->usercount;
memmove(&user->channel->users[i], &user->channel->users[i+1], sizeof(struct user*)*(user->channel->usercount-i));
if(user->channel->usercount==0 && user->channel->id==-1) // Last one to leave frees the channel, unless it's a registered channel which might have a banlist and/or topic to keep
{
channel_free(user->channel);
}
}
}
}
for(i=0; irelationcount; ++i)
{
unsigned int j;
for(j=0; jrelations[i].user->relationcount; ++j)
{
if(user->relations[i].user->relations[j].user==user)
{
--user->relations[i].user->relationcount;
memmove(&user->relations[i].user->relations[j], &user->relations[i].user->relations[j+1], sizeof(struct relation)*(user->relations[i].user->relationcount-j));
--j; // (moved everything one step down)
}
}
}
free(user->relations);
free(user->nickname);
#ifdef use_tls
gnutls_deinit(user->socket);
#endif
close(user->rawsocket);
free(user->account);
free(user->packet.data);
free(user);
}
void user_addrelation(struct user* a, struct user* b, enum relationtype type)
{
++a->relationcount;
a->relations=realloc(a->relations, sizeof(struct relation)*a->relationcount);
a->relations[a->relationcount-1].user=b;
a->relations[a->relationcount-1].type=type;
type=(type%2?type-1:type+1); // Opposite type
++b->relationcount;
b->relations=realloc(b->relations, sizeof(struct relation)*b->relationcount);
b->relations[b->relationcount-1].user=a;
b->relations[b->relationcount-1].type=type;
}
void user_removerelation(struct user* a, struct user* b, enum relationtype type)
{
unsigned int i;
for(i=0; irelationcount; ++i)
{
if(a->relations[i].user==b && a->relations[i].type==type)
{
--a->relationcount;
memmove(&a->relations[i], &a->relations[i+1], sizeof(struct relation)*(a->relationcount-i));
--i;
}
}
type=(type%2?type-1:type+1); // Opposite type
for(i=0; irelationcount; ++i)
{
if(b->relations[i].user==a && b->relations[i].type==type)
{
--b->relationcount;
memmove(&b->relations[i], &b->relations[i+1], sizeof(struct relation)*(b->relationcount-i));
--i;
}
}
}
void user_removerelations(struct user* user, enum relationtype type)
{
unsigned int i;
for(i=0; irelationcount; ++i)
{
if(user->relations[i].type==type)
{
user_removerelation(user, user->relations[i].user, type);
--i;
}
}
}
char user_login(struct user* user, const char* username, const char* password)
{
char* passhash;
char* salt;
int userid;
if(!db_finduser(username, &userid, &passhash, &salt)){return 0;}
// Hash the given password+salt
char hash[129];
db_mkhash(password, salt, hash);
if(!strcmp(hash, passhash))
{
free(passhash);
free(salt);
// Mark user as logged in, check for mod privileges
free(user->account); // Avoid unlikely but potential memory leak (logging in again without logging out)
user->account=strdup(username);
if(user->channel->id>-1)
{
user->modprivileges=db_getmod(user->channel->id, userid);
}else{
user->modprivileges=0;
}
return 1;
}
free(passhash);
free(salt);
return 0;
}
void user_sendmodmsg(struct user* user)
{
char modmsg[snprintf(0,0, "mod:%s:%i", user->nickname, user->modprivileges)+1];
sprintf(modmsg, "mod:%s:%i", user->nickname, user->modprivileges);
channel_write(user->channel, modmsg, strlen(modmsg), WEBSOCK_TEXT, 0);
}