/*
 * query.c: This program analize the idle of an IRC user and try to guess
 *          for possible query with other user. The query prediction isn't
 *          always true, anyway try it and have fun :)
 *
 *
 * Copyright (c) 2003, eazy <eazy@ondaquadra.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * *  Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>

#define PORT 6667
#define USER "USER prolipol 0 * :none\n"
#define NICK "NICK prolipol\n"
#define TIMEOUT 5
#define IDLE_THRESHOLD 120
#define TRUST 12
#define N 10
#define IDLE 1
#define XLEN 80
#define SECOND 300

typedef struct nick {
	char           *nickname;
	long            idle;
	int             trust;
	struct nick    *next;
}               nick;

typedef struct chan {
	char           *channel;
	nick           *lnick;
	struct chan    *next;
}               chan;

int             sig = 0, update = 0, count = 0, verbose = 0;

void
err_quit(void)
{
	perror("error");
	exit(-1);
}

void
usage(char *prog)
{
	fprintf(stderr, "Usage: %s [ -v ] -h hostname -n nickname\n"
		"\t-v\tprint graph of the statistic\n"
		"\t-h\tspecify the hostname of the irc server\n"
		"\t-n\tspecify the nickname of the user you want to monitor\n", prog);
	exit(-1);
}

void
writen(int fd, const void *buf, size_t count)
{
	if (write(fd, buf, count) < 0)
		err_quit();
}

/*
 * The signal handler function is called every time the timer
 * set by alert(), default 30 seconds, reach 0
 * The sig_handler() function call other function that probe
 * and sample idle value, update nick list, calculate statistics
 * and check for possible query
 */

static void
sigalrm(int signo)
{
	if (count == N) {
		update = 1;
		count = 0;
	}
	sig = 1;
	count++;
}

/*
void
print_nick_list(chan * list)
{
	chan           *index = list;
	nick           *index_nick;

	if (!list) {
		fprintf(stderr, "print_nick_list() function called with a NULL argument\n");
		return;
	}
	while (index != NULL) {
		index_nick = index->lnick;
		while (index_nick != NULL) {
			printf("%s\n", index_nick->nickname);
			index_nick = index_nick->next;
		}
		index = index->next;
	}
}
*/

void
names(int sock, const char *channel)
{
	char            buff[100];

	if (!channel) {
		fprintf(stderr, "names() function called with a NULL argument\n");
		return;
	}
	memset(buff, 0, sizeof(buff));
	snprintf(buff, sizeof(buff) - 1, "NAMES %s\n", channel);
	writen(sock, buff, strlen(buff));
}

void
join_channel(int sock, const char *channel)
{
	char            buff[100];

	if (!channel) {
		fprintf(stderr, "join_channel() function called with a NULL argument\n");
		return;
	}
	memset(buff, 0, sizeof(buff));
	snprintf(buff, sizeof(buff) - 1, "JOIN %s\n", channel);
	writen(sock, buff, strlen(buff));
}

void
whois(int sock, const char *nickname, int idle)
{
	char            buff[100];

	if (!nickname) {
		fprintf(stderr, "whois() function called with a NULL argument\n");
		return;
	}
	memset(buff, 0, sizeof(buff));
	if (idle)
		snprintf(buff, sizeof(buff) - 1, "WHOIS %s %s\n", nickname, nickname);
	else
		snprintf(buff, sizeof(buff) - 1, "WHOIS %s\n", nickname);
	writen(sock, buff, strlen(buff));
}

void
ident(int sock)
{
	char            buff[100];

	fprintf(stderr, "Identify to server");
	memset(buff, 0, sizeof(buff));
	strncpy(buff, USER, sizeof(buff) - 1);
	writen(sock, buff, strlen(buff));
	sleep(1);
	memset(buff, 0, sizeof(buff));
	strncpy(buff, NICK, sizeof(buff) - 1);
	writen(sock, buff, strlen(buff));
	sleep(10);
	fprintf(stderr, "\t\t\t[OK]\n");
}

/*
 * Send a PONG message as reply to server PING
 *
 * From Request for Comments: 2812
 *
 * The PING command is used to test the presence of an active client or
 * server at the other end of the connection.  Servers send a PING
 * message at regular intervals if no other activity detected coming
 * from a connection.  If a connection fails to respond to a PING
 * message within a set amount of time, that connection is closed.  A
 * PING message MAY be sent even if the connection is active.
 *
 * When a PING message is received, the appropriate PONG message MUST be
 * sent as reply to <server1> (server which sent the PING message out)
 * as soon as possible.  If the <server2> parameter is specified, it
 * represents the target of the ping, and the message gets forwarded
 * there.
 */

int
ping_pong(int sock, const char *buff)
{
	char            reply[100], *begin, *end;

	if (!buff) {
		fprintf(stderr, "ping_pong() function called with a NULL argument\n");
		return (0);
	}
	if ((begin = strstr(buff, "PING")) != NULL) {
		memset(reply, 0, sizeof(reply));
		snprintf(reply, sizeof(reply) - 1, "PONG %s", begin + 5);
		if ((end = strchr(reply, '\n')) != NULL)
			*(end + 1) = 0;
		writen(sock, reply, strlen(reply));
		sleep(1);
		return (1);
	}
	return (0);
}

/*
 * Buid a list of the nick in the channel from the
 * RPL_NAMREPLY message received from the server
 *
 * From Request for Comments: 2812
 *
 * 353    RPL_NAMREPLY
 *        "( "=" / "*" / "@" ) <channel>
 *         :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
 *
 * - "@" is used for secret channels, "*" for private
 *   channels, and "=" for others (public channels).
 *
 * - To reply to a NAMES message, a reply pair consisting
 *   of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the
 *   server back to the client.  If there is no channel
 *   found as in the query, then only RPL_ENDOFNAMES is
 *   returned.  The exception to this is when a NAMES
 *   message is sent with no parameters and all visible
 *   channels and contents are sent back in a series of
 *   RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark
 *   the end.
 */

int
nick_list(char const * buff, chan * list)
{
	char           *temp, *token, *channel, *end = NULL;
	chan           *index = list;
	nick          **index_nick, *index_nick_old, *ptr;

	if (!buff || !list) {
		fprintf(stderr, "nick_list() function called with a NULL argument\n");
		return (0);
	}
	if ((temp = calloc(strlen(buff) + 1, sizeof(char))) == NULL)
		err_quit();
	strcpy(temp, buff);

	/*
         * Parse the input string to verify if it is a RPL_NAMREPLY
         */
	if ((token = strstr(temp, "353")) != NULL) {

		/*
	         * If the parsed string is a RPL_NAMREPLY, search the '#' character in it
	         */
		if ((token = strchr(token, '#')) != NULL) {

			/*
		         * Save the channel name from the parsed string and search for it
		         * in the channel list already build by channel_sampler()
		         */
			if ((channel = strtok(token, " ")) == NULL)
				goto fail;

			while (index != NULL && strcmp(channel, index->channel) != 0)
				index = index->next;
			if (index == NULL)
				goto fail;

			index_nick = &index->lnick;
			index_nick_old = index->lnick;
			/*
		         * Build a list of nick in the channel. The while loop end when
		         * strtok() return NULL or at the first token that contain a
		         * line feed or carriage return
		         */
			while ((token = strtok(NULL, " ")) != NULL && end == NULL) {

				if ((end = strchr(token, '\n')) != NULL)
					*end = 0;
				if ((end = strchr(token, '\r')) != NULL)
					*end = 0;
				if (!strlen(token))
					break;

				/*
                                 * Remove op and voice symbols from the nickname
                                 */
				if (token[0] == ':')
					token++;
				if (token[0] == '@' || token[0] == '+')
					token++;

				if ((*index_nick = malloc(sizeof(nick))) == NULL)
					err_quit();

				if (((*index_nick)->nickname = calloc(strlen(token) + 1, sizeof(char))) == NULL)
					err_quit();
				strcpy((*index_nick)->nickname, token);
				(*index_nick)->idle = 0;
				(*index_nick)->trust = 0;
				(*index_nick)->next = NULL;
				index_nick = &(*index_nick)->next;
			}
			if (index->lnick != index_nick_old) {

				/*
			         * Copy in the new nick list the values of idle and trust from
			         * the old one
			         */
				while (index_nick_old != NULL) {
					index_nick = &index->lnick;
					while (*index_nick != NULL) {
						if (strcmp(index_nick_old->nickname, (*index_nick)->nickname) == 0) {
							(*index_nick)->idle = index_nick_old->idle;
							(*index_nick)->trust = index_nick_old->trust;
							break;
						}
						index_nick = &(*index_nick)->next;
					}

					/*
					 * Free one element of the old list for every iteration
					 * of the loop
					 */
					ptr = index_nick_old;
					index_nick_old = index_nick_old->next;
					free(ptr);
				}
				free(temp);
				return (1);
			}
		}
	}
fail:
	free(temp);
	return (0);
}

/*
 * Build a list of the channel that a specific nickname has joined.
 * The list is derived from the RPL_WHOISCHANNELS message received
 * from the server in response to a WHOIS message for that nickname
 *
 * From Request for Comments: 2812
 *
 * 319    RPL_WHOISCHANNELS
 *        "<nick> :*( ( "@" / "+" ) <channel> " " )"
 *
 * - Replies 311 - 313, 317 - 319 are all replies
 *   generated in response to a WHOIS message.  Given that
 *   there are enough parameters present, the answering
 *   server MUST either formulate a reply out of the above
 *   numerics (if the query nick is found) or return an
 *   error reply.  The '*' in RPL_WHOISUSER is there as
 *   the literal character and not as a wild card.  For
 *   each reply set, only RPL_WHOISCHANNELS may appear
 *   more than once (for long lists of channel names).
 *   The '@' and '+' characters next to the channel name
 *   indicate whether a client is a channel operator or
 *   has been granted permission to speak on a moderated
 *   channel.  The RPL_ENDOFWHOIS reply is used to mark
 *   the end of processing a WHOIS message.
 */

int
channel_sampler(const char *buff, chan ** list, const char *nick)
{
	char           *temp, *token, *nickname, *end = NULL;
	chan          **index = list;

	if (!buff || !list || !nick) {
		fprintf(stderr, "channel_sampler() function called with a NULL argument\n");
		return (0);
	}
	if ((temp = calloc(strlen(buff) + 1, sizeof(char))) == NULL)
		err_quit();
	strcpy(temp, buff);

	/*
         * Parse the input string and verify if it is a valid
         * RPL_WHOISCHANNELS message
         */
	if ((token = strstr(temp, "319")) != NULL) {
		token += 4;

		/*
	         * If the parsed string is a RPL_WHOISCHANNELS message, remove the
	         * useless token and save the nickname which the reply refer to.
	         * After that, compare the nickname saved from the string with the
	         * one supplied as argv of the main()
	         */
		if (strtok(token, " ") != NULL) {
			if ((nickname = strtok(NULL, " ")) == NULL)
				goto fail;

			if (strcmp(nickname, nick) == 0) {

				/*
			         * Build a list of the channel that the supplied nickname has
			         * joined. If the strcmp() in the previous statment return 0,
			         * the while loop parse the RPL_WHOISCHANNELS message until
			         * strtok() return NULL or return a token that contain a
			         * line feed or carriage return
			         */
				while ((token = strtok(NULL, " ")) != NULL && end == NULL) {

					if ((end = strchr(token, '\n')) != NULL)
						*end = 0;
					if ((end = strchr(token, '\r')) != NULL)
						*end = 0;
					if (!strlen(token))
						break;

					/*
                                         * Remove op and voice symbols from the nickname
                                         */
					if (token[0] == ':')
						token++;
					if (token[0] == '@' || token[0] == '+')
						token++;

					if ((*index = malloc(sizeof(chan))) == NULL)
						err_quit();

					if (((*index)->channel = calloc(strlen(token) + 1, sizeof(char))) == NULL)
						err_quit();
					strcpy((*index)->channel, token);
					(*index)->lnick = NULL;
					(*index)->next = NULL;
					index = &(*index)->next;
				}
				if (*list != NULL) {
					free(temp);
					return (1);
				}
			}
		}
	}
fail:
	free(temp);
	return (0);
}

/*
 * Reset trust value of nickname that speak in chennel
 */

void
reset_trust(const char *buff, chan * list, const char *nickname)
{
	int             i = 0;
	char           *temp, *token;
	chan           *index = list;
	nick           *index_nick;

	if (!buff || !list || !nickname) {
		fprintf(stderr, "reset_trust() function called with a NULL argument\n");
		return;
	}
	if ((temp = calloc(strlen(buff) + 1, sizeof(char))) == NULL)
		err_quit();
	strcpy(temp, buff);

	/*
         * Parse and save the nickname that speak in channel
         */
	if ((token = strtok(temp, " ")) != NULL && token[0] == ':') {
		token++;
		while (token[i] != '!' && token[i] != 0)
			i++;
		if (token[i] == '!') {
			token[i] = 0;

			/*
		         * The trust value of the saved nickname will be reset
		         */
			if (strcmp(nickname, token) != 0) {
				while (index != NULL) {
					index_nick = index->lnick;
					while (index_nick != NULL && strcmp(token, index_nick->nickname) != 0)
						index_nick = index_nick->next;

					/*
			                 * To reset the nickname we assign a trust value of -3
			                 * because in the next idle probe its value will be
			                 * incremented and will reach the 0
			                 */
					if (index_nick != NULL)
						index_nick->trust = -3;
					index = index->next;
				}
			}
			/*
		         * If the saved nickname is the same of the nick supplied from
		         * the command line, the trust value of all nick in the channel
		         * will be reset
		         */
			else {
				while (index != NULL) {
					index_nick = index->lnick;
					while (index_nick != NULL) {
						index_nick->trust = -3;
						index_nick = index_nick->next;
					}
					index = index->next;
				}
			}
		}
	}
	free(temp);
}

/*
 * Parse and sample the idle contained in the RPL_WHOISIDLE
 * message received in responce of the WHOIS issued by
 * idle_prober() function
 *
 * From Request for Comments: 2812
 *
 * 317    RPL_WHOISIDLE
 *        "<nick> <integer> :seconds idle"
 *
 * - Replies 311 - 313, 317 - 319 are all replies
 *   generated in response to a WHOIS message.
 */

int
idle_sampler(const char *buff, nick * list)
{
	char           *temp, *token, *nickname, *idle;
	nick           *index = list;

	if (!buff || !list) {
		fprintf(stderr, "idle_sampler() function called with a NULL argument\n");
		return (0);
	}
	if ((temp = calloc(strlen(buff) + 1, sizeof(char))) == NULL)
		err_quit();
	strcpy(temp, buff);

	/*
         * Parse the input string to verify if it is a valid
         * RPL_WHOISIDLE message
         */
	if ((token = strstr(temp, "317")) != NULL) {
		token += 4;

		/*
	         * If the parsed string is a RPL_WHOISIDLE message, remove
	         * useless token from it
	         */
		if (strtok(token, " ") != NULL) {

			/*
		         * Save the nickname from the parsed string and search for it
		         * in the nick list already build by nick_list()
		         */
			if ((nickname = strtok(NULL, " ")) == NULL)
				goto fail;

			while ((index != NULL) && (strcmp(nickname, index->nickname) != 0))
				index = index->next;
			if (index == NULL)
				goto fail;
			else {

				/*
			         * If the nickname contained in the RPL_WHOISIDLE is found in
			         * the nick list, the value of the idle is stored in the nick
			         * struct
			         */
				if ((idle = strtok(NULL, " ")) == NULL)
					goto fail;
				index->idle = atol(idle);
			}
			free(temp);
			return (1);
		}
	}
fail:
	free(temp);
	return (0);
}

/*
 * For every nick in all channel issue a WHOIS that elicit a
 * RPL_WHOISIDLE message in response, the value of the idle
 * contained in this message will be parsed and stored in the
 * nick struct by the idle_sampler() function
 */

void
idle_prober(int sock, chan * list, const char *nickname)
{
	int             r;
	chan           *index = list;
	nick           *index_nick;
	char            buff[1000];
	fd_set          readset;
	struct timeval  time;

	if (!list || !nickname) {
		fprintf(stderr, "idle_prober() function called with a NULL argument\n");
		return;
	}
	fprintf(stderr, "Probing users idle");
	while (index != NULL) {
		index_nick = index->lnick;
		while (index_nick != NULL) {
			FD_ZERO(&readset);
			FD_SET(sock, &readset);
			time.tv_sec = TIMEOUT;
			time.tv_usec = 0;

			whois(sock, index_nick->nickname, IDLE);

			/*
		         * Set a timeout on the I/O operation performed by read()
		         * It's possible to redefine TIMEOUT to modify the amount of
		         * time that select() must wait a response before return
		         */
			if (select(sock + 1, &readset, NULL, NULL, &time) > 0) {
				if ((r = read(sock, buff, sizeof(buff) - 1)) < 0)
					err_quit();
				buff[r] = 0;
			} else {
				buff[0] = 0;
				fprintf(stderr, "Read timeout\n");
			}

			/*
		         * Parse and sample the idle contained in the RPL_WHOISIDLE
		         * message received in responce of the WHOIS issued above
		         */
			if (idle_sampler(buff, index->lnick));
			else
				ping_pong(sock, buff);
			reset_trust(buff, list, nickname);
			index_nick = index_nick->next;
		}
		index = index->next;
	}
	fprintf(stderr, "\t\t\t[OK]\n");
}

void
print_graph(chan * list)
{
	int             i;
	chan           *index = list;
	nick           *index_nick;

	while (index != NULL) {
		index_nick = index->lnick;
		printf("\t|\n");
		while (index_nick != NULL) {
			printf("\t|");
			for (i = 0; i < index_nick->trust && i < XLEN; i++)
				printf("x");
			for (; i < XLEN; i += 8)
				printf("\t");
			printf("\t%s\n", index_nick->nickname);
			index_nick = index_nick->next;
		}
		printf("\t|");
		for (i = 0; i < XLEN; i++)
			printf("_");
		printf("\n\t\ttrust value for channel %s\n\n\n\n", index->channel);

		index_nick = index->lnick;
		printf("\t|\n");
		while (index_nick != NULL) {
			printf("\t|");
			for (i = 0; i < index_nick->idle * XLEN / SECOND && i < XLEN; i++)
				printf("x");
			for (; i < XLEN; i += 8)
				printf("\t");
			printf("\t%s\n", index_nick->nickname);
			index_nick = index_nick->next;
		}
		printf("\t|");
		for (i = 0; i < XLEN; i++)
			printf("_");
		printf("\n\t\tidle value for channel %s\n\n\n\n", index->channel);

		index = index->next;
	}
}

/*
 * Calculate and update channel statistics based on the values
 * sampled by idle_sampler()
 */

void
idle_stat(chan * list, const char *nickname)
{
	chan           *index = list;
	nick           *index_nick;
	long            nickname_idle, add_idle;

	if (!list || !nickname) {
		fprintf(stderr, "idle_stat() function called with a NULL argument\n");
		return;
	}
	while (index != NULL) {
		index_nick = index->lnick;

		/*
	         * Search in the nick list for the nickname supplied as argv
	         * of the main()
	         */
		while (index_nick != NULL && strcmp(nickname, index_nick->nickname) != 0)
			index_nick = index_nick->next;

		/*
	         * If the nickname supplied as argument in the command line is
	         * not found in the channel, set the idle for that nick to a
	         * dummy value. This cause the trust value of all the nick in
	         * the channel to be set to 0
	         */
		if (index_nick == NULL)
			nickname_idle = (IDLE_THRESHOLD * 2) + 1;
		else
			/*
		         * Save the the idle of the nickname supplied as argument in
		         * the command line
		         */
			nickname_idle = index_nick->idle;

		index_nick = index->lnick;
		while (index_nick != NULL) {
			add_idle = nickname_idle + index_nick->idle;

			/*
		         * If the sum of the idle of the nickname specified from the
		         * command line and the one retrived from the nick list is bigger
		         * than IDLE_THRESHOLD * 2, then the trust value of the retrived
		         * nick will be set to 0
		         */
			if (add_idle > IDLE_THRESHOLD * 2) {
				index_nick->trust = 0;
				index_nick = index_nick->next;
				continue;
			}
			/*
		         * The trust for the nick in the channel will be increased
		         * proportionally to the value of the sum calcolated as described
		         * above
		         */
			else if (add_idle <= IDLE_THRESHOLD * 2 / 3)
				index_nick->trust += 3;
			else if (add_idle <= IDLE_THRESHOLD * 4 / 3)
				index_nick->trust += 2;
			else
				index_nick->trust++;
			index_nick = index_nick->next;
		}
		index = index->next;
	}
}

/*
 * Check channel statistics and prompt for possible query
 */

void
idle_check(chan * list, const char *nickname)
{
	chan           *index = list;
	nick           *index_nick;

	if (!list || !nickname) {
		fprintf(stderr, "idle_check() function called with a NULL argument\n");
		return;
	}
	if (verbose)
		print_graph(list);
	while (index != NULL) {
		index_nick = index->lnick;
		while (index_nick != NULL) {

			/*
		         * If the trust value of a nickname in the channel has reached
		         * the value specified by the TRUST constant, prompt the user
		         * for possible query between this user and the one specified
		         * as argument of the command line
		         */
			if (index_nick->trust >= TRUST && strcmp(nickname, index_nick->nickname) != 0)
				printf("Possible query with user: %s\n", index_nick->nickname);
			index_nick = index_nick->next;
		}
		index = index->next;
	}
	printf("\n");
}

int
main(int argc, char **argv)
{
	struct sockaddr_in servaddr;
	int             opt, sock, r, first = 1;
	char            buff[1000], *hostname = NULL, *nick = NULL;
	chan           *list = NULL, *join = NULL, *index = NULL;
	struct hostent *host;
	struct sigaction act;

	while ((opt = getopt(argc, argv, "vh:n:")) != -1)
		switch (opt) {
		case 'v':
			verbose = 1;
			break;
		case 'h':
			hostname = optarg;
			break;
		case 'n':
			nick = optarg;
			break;
		case '?':
		default:
			usage(argv[0]);
		}

	if (optind < argc)
		usage(argv[0]);

	if (!hostname | !nick)
		usage(argv[0]);

	act.sa_handler = sigalrm;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	act.sa_flags |= SA_RESTART;

	if (sigaction(SIGALRM, &act, NULL) < 0)
		err_quit();

	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		err_quit();

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	if ((host = gethostbyname(hostname)) == NULL) {
		fprintf(stderr, "Can't resolve hostname %s\n", hostname);
		exit(0);
	}
	memcpy(&servaddr.sin_addr, host->h_addr, sizeof(struct in_addr));
	servaddr.sin_port = htons(PORT);

	fprintf(stderr, "Connecting to server");
	if (connect(sock, (struct sockaddr *) & servaddr, sizeof(servaddr)) < 0)
		err_quit();

	fprintf(stderr, "\t\t\t[OK]\n");
	ident(sock);

	/*
         * Build a list of channels that the nickname supplied
         * from the command line has joined
         */
	fprintf(stderr, "Building channel list");
	while (!channel_sampler(buff, &list, nick)) {
		whois(sock, nick, 0);
		sleep(2);
		ping_pong(sock, buff);
		if ((r = read(sock, buff, sizeof(buff) - 1)) < 0)
			err_quit();
		buff[r] = 0;
	}
	fprintf(stderr, "\t\t\t[OK]\n");
	join = list;

	while (1) {

		/*
	         * Join all the channel in the list already build by
	         * channel_sampler() function
	         */
		if (join != NULL) {
			sleep(3);
			join_channel(sock, join->channel);
			join = join->next;
		}
		/*
	         * Query the IRC server for the list of nick in all channels
	         * The response will be handled by nick_list() that update
	         * the nick list
	         */
		if (update) {
			index = list;
			update = 0;
		}
		if (index != NULL) {
			sleep(3);
			names(sock, index->channel);
			index = index->next;
		}
		if (sig) {
			fprintf(stderr, "\t\t\t[OK]\n");
			idle_prober(sock, list, nick);
			idle_stat(list, nick);
			idle_check(list, nick);
			if (alarm(30) != 0)
				fprintf(stderr, "Alarm was already set\n");
			fprintf(stderr, "Waiting alarm signal");
			sig = 0;
		}
		if ((r = read(sock, buff, sizeof(buff) - 1)) < 0)
			err_quit();
		buff[r] = 0;

		/*
	         * Buid or update the list of the nick in the channel from
	         * the RPL_NAMREPLY message received from the server
	         */
		if (nick_list(buff, list)) {
			if (first) {

				/*
			         * Set the first alarm() that elicit a SIGALRM signal
			         * handled by sig_handler()
			         */
				if (alarm(30) != 0)
					fprintf(stderr, "Alarm was already set\n");
				fprintf(stderr, "Waiting alarm signal");
				first = 0;
			} else
				ping_pong(sock, buff);
		}
		reset_trust(buff, list, nick);
	}
}
