sic.c
author Anselm R. Garbe <arg@suckless.org>
Fri Apr 13 11:50:51 2007 +0200 (2 years ago)
changeset 89 a6692d249139
parent 85 96eb1bfede5b
child 91 ebbeaf18284b
permissions -rw-r--r--
updating copyright notice in sic as well
     1 /* © 2005-2007 Anselm R. Garbe <garbeam at gmail dot com>
     2  * © 2005 Nico Golde <nico at ngolde dot de>
     3  * See LICENSE file for license details. */
     4 #include <errno.h>
     5 #include <netdb.h>
     6 #include <netinet/in.h>
     7 #include <stdarg.h>
     8 #include <stdio.h>
     9 #include <stdlib.h>
    10 #include <string.h>
    11 #include <time.h>
    12 #include <unistd.h>
    13 #include <sys/socket.h>
    14 #include <sys/time.h>
    15 
    16 #define PINGTIMEOUT 300
    17 #define MAXMSG 4096
    18 
    19 static char *host = "irc.oftc.net";
    20 static unsigned short port = 6667;
    21 static char *password = NULL;
    22 static char nick[32];
    23 
    24 static char bufin[MAXMSG], bufout[MAXMSG];
    25 static char channel[256];
    26 static int srv;
    27 static time_t trespond;
    28 
    29 static void
    30 eprint(const char *errstr, ...) {
    31 	va_list ap;
    32 
    33 	va_start(ap, errstr);
    34 	vfprintf(stderr, errstr, ap);
    35 	va_end(ap);
    36 	exit(EXIT_FAILURE);
    37 }
    38 
    39 static int
    40 getline(int fd, unsigned int len, char *buf) {
    41 	unsigned int i = 0;
    42 	char c;
    43 
    44 	do {
    45 		if(read(fd, &c, sizeof(char)) != sizeof(char))
    46 			return -1;
    47 		buf[i++] = c;
    48 	}
    49 	while(c != '\n' && i < len);
    50 	buf[i - 1] = 0;
    51 	return 0;
    52 }
    53 
    54 static void
    55 pout(char *channel, char *msg) {
    56 	static char timestr[18];
    57 	time_t t = time(0);
    58 
    59 	strftime(timestr, sizeof timestr, "%D %R", localtime(&t));
    60 	fprintf(stdout, "%-12.12s: %s %s\n", channel, timestr, msg);
    61 }
    62 
    63 static void
    64 privmsg(char *channel, char *msg) {
    65 	if(channel[0] == 0)
    66 		return;
    67 	snprintf(bufout, sizeof bufout, "<%s> %s", nick, msg);
    68 	pout(channel, bufout);
    69 	snprintf(bufout, sizeof bufout, "PRIVMSG %s :%s\r\n", channel, msg);
    70 	write(srv, bufout, strlen(bufout));
    71 }
    72 
    73 static void
    74 parsein(char *msg) {
    75 	char *p;
    76 
    77 	if(msg[0] == 0)
    78 		return;
    79 	if(msg[0] != ':') {
    80 		privmsg(channel, msg);
    81 		return;
    82 	}
    83 	if(!strncmp(msg + 1, "j ", 2) && (msg[3] == '#'))
    84 		snprintf(bufout, sizeof bufout, "JOIN %s\r\n", msg + 3);
    85 	else if(!strncmp(msg + 1, "l ", 2))
    86 		snprintf(bufout, sizeof bufout, "PART %s :sic - 250 LOC are too much!\r\n", msg + 3);
    87 	else if(!strncmp(msg + 1, "m ", 2)) {
    88 		if((p = strchr(msg + 3, ' ')))
    89 			*(p++) = 0;
    90 		privmsg(msg + 3, p);
    91 		return;
    92 	}
    93 	else if(!strncmp(msg + 1, "s ", 2)) {
    94 		strncpy(channel, msg + 3, sizeof channel);
    95 		return;
    96 	}
    97 	else
    98 		snprintf(bufout, sizeof bufout, "%s\r\n", msg + 1);
    99 	write(srv, bufout, strlen(bufout));
   100 }
   101 
   102 static void
   103 parsesrv(char *msg) {
   104 	char *chan, *cmd, *p, *txt, *usr; 
   105 
   106 	txt = NULL;
   107 	usr = host;
   108 	if(!msg || !(*msg))
   109 		return;
   110 	if(msg[0] != ':')
   111 		cmd = msg;
   112 	else {
   113 		if(!(p = strchr(msg, ' ')))
   114 			return;
   115 		*p = 0;
   116 		usr = msg + 1;
   117 		cmd = ++p;
   118 		if((p = strchr(usr, '!')))
   119 			*p = 0;
   120 	}
   121 	for(p = cmd; *p; p++) /* remove CRLFs */
   122 		if(*p == '\r' || *p == '\n')
   123 			*p = 0;
   124 	if((p = strchr(cmd, ':'))) {
   125 		*p = 0;
   126 		txt = ++p;
   127 	}
   128 	if(!strncmp("PONG", cmd, 4))
   129 		return;
   130 	if(!strncmp("PRIVMSG", cmd, 7) && txt) {
   131 		if(!(p = strchr(cmd, ' ')))
   132 			return;
   133 		*p = 0;
   134 		chan = ++p;
   135 		for(; *p && *p != ' '; p++);
   136 		*p = 0;
   137 		snprintf(bufout, sizeof bufout, "<%s> %s", usr, txt);
   138 		pout(chan, bufout);
   139 	}
   140 	else if(!strncmp("PING", cmd, 4) && txt) {
   141 		snprintf(bufout, sizeof bufout, "PONG %s\r\n", txt);
   142 		write(srv, bufout, strlen(bufout));
   143 	}
   144 	else {
   145 		snprintf(bufout, sizeof bufout, ">< %s: %s", cmd, txt ? txt : "");
   146 		pout(usr, bufout);
   147 		if(!strncmp("NICK", cmd, 4) && !strncmp(usr, nick, sizeof nick) && txt)
   148 			strncpy(nick, txt, sizeof nick);
   149 	}
   150 }
   151 
   152 int
   153 main(int argc, char *argv[]) {
   154 	int i;
   155 	struct timeval tv;
   156 	struct hostent *hp;
   157 	static struct sockaddr_in addr;  /* initially filled with 0's */
   158 	char ping[256];
   159 	fd_set rd;
   160 
   161 	strncpy(nick, getenv("USER"), sizeof nick);
   162 	for(i = 1; i < argc; i++)
   163 		if(!strncmp(argv[i], "-h", 3)) {
   164 			if(++i < argc) host = argv[i];
   165 		}
   166 		else if(!strncmp(argv[i], "-p", 3)) {
   167 			if(++i < argc) port = (unsigned short)atoi(argv[i]);
   168 		}
   169 		else if(!strncmp(argv[i], "-n", 3)) {
   170 			if(++i < argc) strncpy(nick, argv[i], sizeof nick);
   171 		}
   172 		else if(!strncmp(argv[i], "-k", 3)) {
   173 			if(++i < argc) password = argv[i];
   174 		}
   175 		else if(!strncmp(argv[i], "-v", 3))
   176 			eprint("sic-"VERSION", © 2005-2007 Anselm R. Garbe, Nico Golde\n");
   177 		else
   178 			eprint("usage: sic [-h host] [-p port] [-n nick] [-k keyword] [-v]\n");
   179 
   180 	/* init */
   181 	if((srv = socket(AF_INET, SOCK_STREAM, 0)) < 0)
   182 		eprint("sic: cannot connect host '%s'\n", host);
   183 	if(NULL == (hp = gethostbyname(host)))
   184 		eprint("sic: cannot resolve hostname '%s'\n", host);
   185 	addr.sin_family = AF_INET;
   186 	addr.sin_port = htons(port);
   187 	memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
   188 	if(connect(srv, (struct sockaddr *) &addr, sizeof(struct sockaddr_in))) {
   189 		close(srv);
   190 		eprint("sic: cannot connect host '%s'\n", host);
   191 	}
   192 	/* login */
   193 	if(password)
   194 		snprintf(bufout, sizeof bufout,
   195 				"PASS %s\r\nNICK %s\r\nUSER %s localhost %s :%s\r\n",
   196 				password, nick, nick, host, nick);
   197 	else
   198 		snprintf(bufout, sizeof bufout, "NICK %s\r\nUSER %s localhost %s :%s\r\n",
   199 				 nick, nick, host, nick);
   200 	write(srv, bufout, strlen(bufout));
   201 	snprintf(ping, sizeof ping, "PING %s\r\n", host);
   202 	channel[0] = 0;
   203 	setbuf(stdout, NULL); /* unbuffered stdout */
   204 
   205 	for(;;) { /* main loop */
   206 		FD_ZERO(&rd);
   207 		FD_SET(0, &rd);
   208 		FD_SET(srv, &rd);
   209 		tv.tv_sec = 120;
   210 		tv.tv_usec = 0;
   211 		i = select(srv + 1, &rd, 0, 0, &tv);
   212 		if(i < 0) {
   213 			if(errno == EINTR)
   214 				continue;
   215 			eprint("sic: error on select()");
   216 		}
   217 		else if(i == 0) {
   218 			if(time(NULL) - trespond >= PINGTIMEOUT)
   219 				eprint("sic shutting down: parse timeout");
   220 			write(srv, ping, strlen(ping));
   221 			continue;
   222 		}
   223 		if(FD_ISSET(srv, &rd)) {
   224 			if(getline(srv, sizeof bufin, bufin) == -1)
   225 				eprint("sic: remote host closed connection");
   226 			parsesrv(bufin);
   227 			trespond = time(NULL);
   228 		}
   229 		if(FD_ISSET(0, &rd)) {
   230 			if(getline(0, sizeof bufin, bufin) == -1)
   231 				eprint("sic: broken pipe");
   232 			parsein(bufin);
   233 		}
   234 	}
   235 	return 0;
   236 }