Source

six / six.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <getopt.h>
#include <unistd.h>

#include <readline/readline.h>

#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include <pwd.h>
 
#ifndef   NI_MAXHOST
#define   NI_MAXHOST 1025
#endif

static char channel[256];
static char *host;

static int auth_nickserv = 0;
static char *script = NULL;
static char *password = NULL;
static char *autojoin = NULL;

static char last_nickname[256] = "";

static void irc_display(const char *fmt, ...);

#include "config.h"

static char bufin[BUFSIZ], bufout[BUFSIZ];
static size_t sizein = 0, sizeout = 0;

static char *whoami() {
	struct passwd *passwd;

	passwd = getpwuid(getuid());
	if (passwd == NULL) {
		return "johndoe";
	} else {
		return passwd->pw_name;
	}
}

/* Print final message and exit */
static void die(const char *fmt, ...) {
	va_list ap;

	va_start(ap, fmt);
	(void) vfprintf(stderr, fmt, ap);
	va_end(ap);
	if (fmt[strlen(fmt) - 1] == ':')
		fprintf(stderr," errno=%d (%s)", errno, strerror(errno));
	fprintf(stderr, "\n");

	exit(EXIT_FAILURE);
}

/* Establish the connection to host:port */
static int irc_connect(const char *host, const char *port) {
	int r;
	int fd = -1;
	struct addrinfo *ai, *info;

	r = getaddrinfo(host, port, NULL, &ai);
	if (r != 0) {
		return -1;
	}

	for (info = ai; info; info = info->ai_next) {
		fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
		if (fd == -1)
			continue; /* try another host */

		r = connect(fd, info->ai_addr, info->ai_addrlen);
		if (r == 0)
			break; /* successfully connected */

		(void) close(fd);
	}

	free(ai);
	return fd;
}

/* Display the message on the screen */
static void irc_display(const char *fmt, ...) {
	va_list ap;
	char* saved_line;
	int saved_point;

	saved_point = rl_point;
	saved_line = rl_copy_text(0, rl_end);
	(void) rl_set_prompt("");
	rl_replace_line("", 0);
	rl_redisplay();

	va_start(ap, fmt);
	(void) vfprintf(stdout, fmt, ap);
	va_end(ap);
	fprintf(stdout, "\n");

	(void) rl_set_prompt(prompt());
	rl_replace_line(saved_line, 0);
	rl_point = saved_point;
	rl_redisplay();
	free(saved_line);
}

/* Send a formatted string to the IRC server */
int irc_send(const char *fmt, ...) {
	int r;
	va_list ap;
	
	va_start(ap, fmt);
	r = vsnprintf(bufout + sizeout, sizeof(bufout) - sizeout, fmt, ap);
	sizeout += r;
	strncat(bufout + sizeout, "\r\n", sizeof(bufout) - 2);
	sizeout += 2;
	va_end(ap);
}

/* Parse the response from the IRC server and process it */
int irc_response(char *resp) {
	char *cmd = resp;
	char *usr = "";
	char *arg;
	char *msg;

	if (resp[0] == ':') {
		char *c;
		resp++;
		usr = strsep(&resp, " ");
		c = strchr(usr, '!');
		if (c) *c = '\0';
	}

	cmd = strsep(&resp, " ");
	arg = strsep(&resp, " :");
	msg = resp;
	if (strlen(arg) > 0 && *resp == ':') msg++;

	if (strcmp("PONG", cmd) == 0) return;
	if (strcmp("PRIVMSG", cmd) == 0) {
		user_message(arg, usr, msg);
		strncpy(last_nickname, usr, sizeof(last_nickname) - 1);
	} else if (strcmp("PING", cmd) == 0) {
		irc_send("PONG %s", msg);
	} else {
		char *endptr;
		int id;
		id = strtol(cmd, &endptr, 10);
		if (id == 1) { 
			/* first response from server: try to authenicate
			 * and join channel */
			if (auth_nickserv != 0 && password != NULL) {
				irc_send("PRIVMSG NickServ :IDENTIFY %s", password);
			}
			if (script != NULL) {
				irc_send("%s", script);
			}
			if (autojoin != NULL) {
				irc_send("JOIN %s", autojoin);
				strncpy(channel, autojoin, sizeof(channel));
			}
		}
		server_message(usr, cmd, arg, msg);
	}
	return 0;
}

/* TODO: Parse user input and process it */
int irc_request(char *req) {
	char *c;
	char *s;
	if (req[0] == ':') {
		c = &req[1];
		if (*c != '\0' && (*(c+1) == '\0' ||isspace(*(c+1)))) {
			char cmd = *c;
			while (*c && isspace(*++c)); s = c;
			switch (cmd) {
				case 'j':
					irc_send("JOIN %s", s);
					if (strlen(channel) == 0) /* set channel if no other channel set */
						strncpy(channel, s, sizeof(channel));
					break;
				case 'l':
					while (*c != '\0' && !isspace(*++c)); *c++ = '\0';
					if (*s == '\0')
						s = channel; /* leave the default channel */
					if (*c == '\0')
						c = "six - simple IRC kept simple";
					irc_send("PART %s :%s", s, c);
					break;
				case 'm':
					while (*c && !isspace(*++c)); *c++ = '\0';
					your_message(channel, s, c);
					irc_send("PRIVMSG %s :%s", s, c);
					break;
				case 's':
					strncpy(channel, s, sizeof(channel));
					break;
			}
		} else {
			irc_send(c);
		}
	} else {
		your_message(channel, channel, req);
		irc_send("PRIVMSG %s :%s", channel, req);
	}
	return 0;
}

static void rl_handler(char* c) {}

/* Callback: Return is pressed down */
static int rl_newline_handler(int count, int key) {
	char *input;
	input = rl_copy_text(0, rl_end);
	if (strlen(input) > 0) {
		irc_request(input);
	}
	rl_set_prompt(prompt());
	rl_replace_line("", 0);
	rl_redisplay();
	free(input);
	return 0;
}

static char **reply_complete(const char *text, int start, int end) {
	char **matches;

	if (strlen(text) == 0 && start == 0 && strlen(last_nickname) > 0) {
		matches = malloc(2 * sizeof(char *));
		matches[0] = malloc(strlen(last_nickname) + 4);
		strcpy(matches[0], ":m ");
		strcat(matches[0], last_nickname);
		matches[1] = NULL;
		return matches;
	} else {
		return NULL;
	}
}

static void usage() {
	printf("Usage:\n"
			"  six [-p port] [-n nick] [-j channel] [-N] [ -s script ] [-k password] host\n\n");
	exit(EXIT_SUCCESS);
}

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

	memset(channel, 0, sizeof(channel));

	while ((r = getopt(argc, argv, "hp:n:j:k:Ns:")) != -1) {
		switch (r) {
			case 'p':
				port = optarg;
				break;
			case 'n':
				nick = optarg;
				break;
			case 'j':
				autojoin = optarg;
				break;
			case 'k':
				password = optarg;
				break;
			case 'N':
				auth_nickserv = 1;
				break;
			case 's':
				script = optarg;
				break;
			case 'h':
			default:
				usage();
		}
	}

	if (optind == argc - 1) {
		host = argv[optind];
	} else {
		usage();
	}

	/* connect to the server */
	fd = irc_connect(host, port);
	if (fd < 0) {
		die("failed to connect to %s:%d:", host, port);
	}

	/* prepare readline: set handler for CRLF, enable async mode */
	rl_callback_handler_install(prompt(), rl_handler);
	rl_attempted_completion_function = reply_complete;
	rl_bind_key((int) '\n', rl_newline_handler);
	rl_bind_key((int) '\r', rl_newline_handler);
	rl_bind_key((int) '\t', rl_complete);
	rl_erase_empty_line = 1;

	if (nick == NULL) {
		nick = whoami();
	}

	if(auth_nickserv == 0 && password != 0) {
		irc_send("PASS %s", password);
	}
	irc_send("NICK %s", nick);
	irc_send("USER %s localhost %s :%s", nick, host, nick);

	for (;;) {
		fd_set rfds, wfds;
		struct timeval tv;

		FD_ZERO(&rfds);
		FD_ZERO(&wfds);
		FD_SET(STDIN_FILENO, &rfds);
		FD_SET(fd, &rfds);
		if (sizeout > 0) {
			FD_SET(fd, &wfds);
		}

		tv.tv_sec = 120;
		tv.tv_usec = 0;

		r = select(fd + 1, &rfds, &wfds, NULL, &tv);
		if (r < 0) {
			if (errno == EINTR)
				continue;
			die("select() failed:");
		} else if (r == 0) {
			irc_send("PING %s", host);
		} else {
			if (FD_ISSET(fd, &rfds)) {
				char *nl;
				r = read(fd, bufin + sizein, sizeof(bufin) - sizein - 1);
				if (r <= 0) break;

				sizein += r;

				while ((nl = memchr(bufin, '\n', sizein)) != NULL) {
					size_t len;
					*nl = '\0';
					if (*(nl - 1) == '\r') *(nl - 1) = '\0';
					len = strlen(bufin) + 2;
					irc_response(bufin);
					memmove(bufin, bufin + len, sizein - len);
					sizein -= len;
				}
			}
			if (FD_ISSET(fd, &wfds)) {
				r = write(fd, bufout, sizeout);
				if (r < 0)
					die("write failed:");
				memmove(bufout, bufout + r, sizeout - r);
				sizeout -= r;
			} 
			if (FD_ISSET(STDIN_FILENO, &rfds)) {
				rl_callback_read_char();
			}
		}
	}

	close(fd);
	return 0;
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.