]> git.armaanb.net Git - sic.git/blob - sic.c
Initial config changes
[sic.git] / sic.c
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 }