All pastes #613938 Raw Edit

Miscellany

public text v1 · immutable
·published 2007-07-11 07:45 UTC
rendered paste body
/*
 *  ntalks.cpp
 *
 *  This file is part of ntalk chat server distribution
 *  Copyright (C) 2001 by Krzysztof Czuba (kab@interia.pl)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 */

#include <iostream>
#include <string>  	// C++ string
#include <list>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#ifdef SOLARIS
#include <sys/filio.h>
#endif
#include <signal.h>

#define VERSION "0.6.1"

#define MESSAGE 	"01"
#define JOIN 		"02"
#define LEAVE 		"03"
#define DISCONNECT 	"04"
#define SHUTDOWN 	"05"
#define ONLINE 		"06"
#define AWAY 		"07"
#define AWAYOFF 	"08"
#define SERVER 		"09"
#define WHOIS 		"10"
#define SRVMESSAGE 	"11"


struct user {
    string nick;
    string addr;
    string since;
    int fd;
};

list<user> user_list;

int server_sockfd, client_sockfd;
int server_len, client_len;
int fd_count;
int result, x, online_count, already_sigint;
char msg_r[512], msg_s[512];

fd_set readfds, testfds;

sockaddr_in server_address, client_address;


void add_fd(int fd)
{
    user u;
    u.nick = "";
    u.fd = fd;

    user_list.push_back(u);
    fd_count++;
}

void del_fd(int fd)
{
    list<user>::iterator i;
    for(i = user_list.begin(); i != user_list.end(); ++i)
    {
	user u = *i;

	if(u.fd == fd)
	{
	    user_list.erase(i);
	    fd_count--;
	    return;
	}
    }
}

void add_nick(int fd, string nick, string addr, string since)
{
    list<user>::iterator i;
    for(i = user_list.begin(); i != user_list.end(); ++i)
    {
	if(i->fd == fd)
	{
	    i->nick = nick;
	    i->addr = addr;
	    i->since = since;
	    return;
	}
    }
}

bool check_nick(string nick)
{
    list<user>::const_iterator i;
    for(i = user_list.begin(); i != user_list.end(); ++i)
    {
	if(i->nick == nick) return 0;
    }

    return 1;
}

void send_all(string messg)
{
    strcpy(msg_s, messg.c_str());

    list<user>::const_iterator i;
    for(i = user_list.begin(); i != user_list.end(); ++i)
    {
	write(i->fd, &msg_s, sizeof(msg_s));
    }
}

void send_fd(const string messg, int fd)
{
    strcpy(msg_s, messg.c_str());
    write(fd, &msg_s, sizeof(msg_s));
}

user find_user(string nick)
{
    list<user>::const_iterator i;
    for(i = user_list.begin(); i != user_list.end(); ++i)
    {
	user u = *i;
	if(u.nick == nick) return u;
    }

    user us;
    us.fd = -1;
    return us;
}

string time()
{
    tm *tm;
    time_t now;
    char tt[9];
    string strtt;

    time(&now);
    tm = localtime(&now);
    strftime(tt, 9, "%H:%M:%S", tm);
    strtt = tt;

    return strtt;
}

string str_online()
{
    string str = "server@server:06:fd:";

    list<user>::iterator i;
    for(i = user_list.begin(); i != user_list.end(); ++i)
    {
	str.append(i->nick);
	str.append(";");
    }

    str.append("\n");

    return str;
}

string create_message_string(string com,
			     string data,
			     string sender = "server@server",
			     string to = "all")
{
    string m = sender;
    m.append(":" + com + ":");
    m.append(to + ":");
    m.append(data);
    m.append("\n");

    return m;
}

void parse_command(const string messg, int fd)
{
    string msg = messg;
    string nick, addr, com, to, data;

    // sender@host:01:all:message

    nick = msg.substr(0, msg.find('@'));
    msg.erase(0, msg.find('@') + 1);
    addr = msg.substr(0, msg.find(':'));
    msg.erase(0, msg.find(':') + 1);
    com = msg.substr(0, msg.find(':'));
    msg.erase(0, msg.find(':') + 1);
    to  = msg.substr(0, msg.find(':'));
    msg.erase(0, msg.find(':') + 1);
    data = msg.substr(0, msg.find('\n'));

    if(com.compare(MESSAGE) == 0)
    {
	send_all(messg);
    }
    else if(com.compare(JOIN) == 0)
    {
	if(!check_nick(nick))
	{
	    string str = create_message_string(DISCONNECT, "Nick already in use");
	    send_fd(str, fd);
	    return;
	}

	add_nick(fd, nick, addr, time());
	send_all(messg);
	send_all(str_online());
    }
    else if(com.compare(LEAVE) == 0)
    {
	send_all(messg);
    }
    else if(com.compare(SERVER) == 0)
    {
	string str = create_message_string(SERVER, "ntalk server " VERSION);
	send_fd(str, fd);
    }
    else if(com.compare(ONLINE) == 0)
    {
	string str = str_online();
	send_fd(str, fd);
    }
    else if(com.compare(AWAY) == 0)
    {
	send_all(messg);
    }
    else if(com.compare(AWAYOFF) == 0)
    {
	send_all(messg);
    }
    else if(com.compare(WHOIS) == 0)
    {
	user u = find_user(data);
	if(u.fd == -1)
	{
	    string tmpstr = data + ": No such user";
	    string str = create_message_string(WHOIS, tmpstr);
	    send_fd(str, fd);
	}
	else
	{
	    string tmpstr = u.nick + "@" + u.addr;
	    string str = create_message_string(WHOIS, u.since, tmpstr);
	    send_fd(str, fd);
	}
    }
}

static void signal_interrupt(int sig)
{
    string msg = create_message_string(SHUTDOWN, "Server shutdown");

    if (!already_sigint)
    {
	already_sigint = 1;
	send_all(msg);
	cout << "Server shut down" << endl;
	exit(0);
    }
}

int main(int argc, char *argv[])
{
    int opt;

    while((opt = getopt(argc, argv, "v")) != -1)
    {
	if(opt == 'v')
	{
	    cout << "version " VERSION "\n";
	    exit(0);
	}

	else exit(0);
    }

    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(9734);
    server_len = sizeof(server_address);

    bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
    listen(server_sockfd, 5);

    online_count = 0;
    fd_count = 0;

    cout << "Running ntalk server " VERSION "\n";

    already_sigint = 0;
    signal(SIGINT, signal_interrupt);

    FD_ZERO(&readfds);
    FD_SET(server_sockfd, &readfds);
    while(1)
    {
	int fd;
	int nread;

	testfds = readfds;

	result = select(FD_SETSIZE, &testfds, (fd_set *)0,
			(fd_set *)0, (struct timeval *) 0);

	if(result < 1)
	{
	    cerr << "server: select";
	    exit(1);
	}

	for(fd = 0; fd < FD_SETSIZE; fd++)
	{
	    if(FD_ISSET(fd, &testfds))
	    {
		if(fd == server_sockfd)
		{
		    client_sockfd = accept(server_sockfd, (sockaddr *)&client_address, (socklen_t *)&client_len);
		    FD_SET(client_sockfd, &readfds);
		    online_count++;
		    add_fd(client_sockfd);
		}
		else
		{
		    ioctl(fd, FIONREAD, &nread);
		    if(nread == 0)
		    {
			close(fd);
			FD_CLR(fd, &readfds);
			online_count--;
			del_fd(fd);
			send_all(str_online());
		    }

		    else
		    {
			read(fd, &msg_r, sizeof(msg_r));
			string msgg = msg_r;
			parse_command(msgg, fd);
		    }
		}
	    }
	}
    }
}