sic.c (4393B)
1 /* See LICENSE file for license details. */ 2 #include <sys/select.h> 3 4 #include <ctype.h> 5 #include <errno.h> 6 #include <stdarg.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <time.h> 11 #include <unistd.h> 12 13 #include "arg.h" 14 #include "config.h" 15 16 char *argv0; 17 static char *host = DEFAULT_HOST; 18 static char *port = DEFAULT_PORT; 19 static char *password; 20 static char nick[32]; 21 static char bufin[4096]; 22 static char bufout[4096]; 23 static char channel[256]; 24 static time_t trespond; 25 static FILE *srv; 26 27 #undef strlcpy 28 #include "strlcpy.c" 29 #include "util.c" 30 31 static void 32 pout(char *channel, char *fmt, ...) { 33 static char timestr[80]; 34 time_t t; 35 va_list ap; 36 37 va_start(ap, fmt); 38 vsnprintf(bufout, sizeof bufout, fmt, ap); 39 va_end(ap); 40 t = time(NULL); 41 strftime(timestr, sizeof timestr, TIMESTAMP_FORMAT, localtime(&t)); 42 fprintf(stdout, "%-12s: %s %s\n", channel, timestr, bufout); 43 } 44 45 static void 46 sout(char *fmt, ...) { 47 va_list ap; 48 49 va_start(ap, fmt); 50 vsnprintf(bufout, sizeof bufout, fmt, ap); 51 va_end(ap); 52 fprintf(srv, "%s\r\n", bufout); 53 } 54 55 static void 56 privmsg(char *channel, char *msg) { 57 if(channel[0] == '\0') { 58 pout("", "No channel to send to"); 59 return; 60 } 61 pout(channel, "<%s> %s", nick, msg); 62 sout("PRIVMSG %s :%s", channel, msg); 63 } 64 65 static void 66 parsein(char *s) { 67 char c, *p; 68 69 if(s[0] == '\0') 70 return; 71 skip(s, '\n'); 72 if(s[0] != COMMAND_PREFIX_CHARACTER) { 73 privmsg(channel, s); 74 return; 75 } 76 c = *++s; 77 if(c != '\0' && isspace((unsigned char)s[1])) { 78 p = s + 2; 79 switch(c) { 80 case 'j': 81 sout("JOIN %s", p); 82 if(channel[0] == '\0') 83 strlcpy(channel, p, sizeof channel); 84 return; 85 case 'l': 86 s = eat(p, isspace, 1); 87 p = eat(s, isspace, 0); 88 if(!*s) 89 s = channel; 90 if(*p) 91 *p++ = '\0'; 92 if(!*p) 93 p = DEFAULT_PARTING_MESSAGE; 94 sout("PART %s :%s", s, p); 95 return; 96 case 'm': 97 s = eat(p, isspace, 1); 98 p = eat(s, isspace, 0); 99 if(*p) 100 *p++ = '\0'; 101 privmsg(s, p); 102 return; 103 case 's': 104 strlcpy(channel, p, sizeof channel); 105 return; 106 } 107 } 108 sout("%s", s); 109 } 110 111 static void 112 parsesrv(char *cmd) { 113 char *usr, *par, *txt; 114 115 usr = host; 116 if(!cmd || !*cmd) 117 return; 118 if(cmd[0] == ':') { 119 usr = cmd + 1; 120 cmd = skip(usr, ' '); 121 if(cmd[0] == '\0') 122 return; 123 skip(usr, '!'); 124 } 125 skip(cmd, '\r'); 126 par = skip(cmd, ' '); 127 txt = skip(par, ':'); 128 trim(par); 129 if(!strcmp("PONG", cmd)) 130 return; 131 if(!strcmp("PRIVMSG", cmd)) 132 pout(par, "<%s> %s", usr, txt); 133 else if(!strcmp("PING", cmd)) 134 sout("PONG %s", txt); 135 else { 136 pout(usr, ">< %s (%s): %s", cmd, par, txt); 137 if(!strcmp("NICK", cmd) && !strcmp(usr, nick)) 138 strlcpy(nick, txt, sizeof nick); 139 } 140 } 141 142 143 static void 144 usage(void) { 145 eprint("usage: sic [-h host] [-p port] [-n nick] [-k keyword] [-v]\n", argv0); 146 } 147 148 int 149 main(int argc, char *argv[]) { 150 struct timeval tv; 151 const char *user = getenv("USER"); 152 int n; 153 fd_set rd; 154 155 strlcpy(nick, user ? user : "unknown", sizeof nick); 156 ARGBEGIN { 157 case 'h': 158 host = EARGF(usage()); 159 break; 160 case 'p': 161 port = EARGF(usage()); 162 break; 163 case 'n': 164 strlcpy(nick, EARGF(usage()), sizeof nick); 165 break; 166 case 'k': 167 password = EARGF(usage()); 168 break; 169 case 'v': 170 eprint("sic-"VERSION", © 2005-2014 Kris Maglione, Anselm R. Garbe, Nico Golde\n"); 171 break; 172 default: 173 usage(); 174 } ARGEND; 175 176 /* init */ 177 srv = fdopen(dial(host, port), "r+"); 178 if (!srv) 179 eprint("fdopen:"); 180 /* login */ 181 if(password) 182 sout("PASS %s", password); 183 sout("NICK %s", nick); 184 sout("USER %s localhost %s :%s", nick, host, nick); 185 fflush(srv); 186 setbuf(stdout, NULL); 187 setbuf(srv, NULL); 188 setbuf(stdin, NULL); 189 #ifdef __OpenBSD__ 190 if (pledge("stdio", NULL) == -1) 191 eprint("error: pledge:"); 192 #endif 193 for(;;) { /* main loop */ 194 FD_ZERO(&rd); 195 FD_SET(0, &rd); 196 FD_SET(fileno(srv), &rd); 197 tv.tv_sec = 120; 198 tv.tv_usec = 0; 199 n = select(fileno(srv) + 1, &rd, 0, 0, &tv); 200 if(n < 0) { 201 if(errno == EINTR) 202 continue; 203 eprint("sic: error on select():"); 204 } 205 else if(n == 0) { 206 if(time(NULL) - trespond >= 300) 207 eprint("sic shutting down: parse timeout\n"); 208 sout("PING %s", host); 209 continue; 210 } 211 if(FD_ISSET(fileno(srv), &rd)) { 212 if(fgets(bufin, sizeof bufin, srv) == NULL) 213 eprint("sic: remote host closed connection\n"); 214 parsesrv(bufin); 215 trespond = time(NULL); 216 } 217 if(FD_ISSET(0, &rd)) { 218 if(fgets(bufin, sizeof bufin, stdin) == NULL) 219 eprint("sic: broken pipe\n"); 220 parsein(bufin); 221 } 222 } 223 return 0; 224 }