Introduction
Introduction Statistics Contact Development Disclaimer Help
ii.c - ii - irc it, simple FIFO based irc client
git clone git://git.suckless.org/ii
Log
Files
Refs
README
LICENSE
---
ii.c (20148B)
---
1 /* See LICENSE file for license details. */
2 #include <sys/select.h>
3 #include <sys/socket.h>
4 #include <sys/stat.h>
5 #include <sys/types.h>
6 #include <sys/un.h>
7
8 #include <ctype.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <limits.h>
12 #include <netdb.h>
13 #include <netinet/in.h>
14 #include <pwd.h>
15 #include <signal.h>
16 #include <stdarg.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <time.h>
21 #include <unistd.h>
22
23 char *argv0;
24
25 #include "arg.h"
26
27 #ifdef NEED_STRLCPY
28 size_t strlcpy(char *, const char *, size_t);
29 #endif /* NEED_STRLCPY */
30
31 #define IRC_CHANNEL_MAX 200
32 #define IRC_MSG_MAX 512 /* guaranteed to be <= than PIPE_BUF */
33 #define PING_TIMEOUT 600
34
35 enum { TOK_NICKSRV = 0, TOK_USER, TOK_CMD, TOK_CHAN, TOK_ARG, TOK_TEXT, …
36
37 typedef struct Channel Channel;
38 struct Channel {
39 int fdin;
40 char name[IRC_CHANNEL_MAX]; /* channel name (normalized) */
41 char inpath[PATH_MAX]; /* input path */
42 char outpath[PATH_MAX]; /* output path */
43 Channel *next;
44 };
45
46 static Channel * channel_add(const char *);
47 static Channel * channel_find(const char *);
48 static Channel * channel_join(const char *);
49 static void channel_leave(Channel *);
50 static Channel * channel_new(const char *);
51 static void channel_normalize_name(char *);
52 static void channel_normalize_path(char *);
53 static int channel_open(Channel *);
54 static void channel_print(Channel *, const char *);
55 static int channel_reopen(Channel *);
56 static void channel_rm(Channel *);
57 static void cleanup(void);
58 static void create_dirtree(const char *);
59 static void create_filepath(char *, size_t, const char *, const cha…
60 static void die(const char *, ...);
61 static void ewritestr(int, const char *);
62 static void handle_channels_input(int, Channel *);
63 static void handle_server_output(int);
64 static int isnumeric(const char *);
65 static void loginkey(int, const char *);
66 static void loginuser(int, const char *, const char *);
67 static void proc_channels_input(int, Channel *, char *);
68 static void proc_channels_privmsg(int, Channel *, char *);
69 static void proc_server_cmd(int, char *);
70 static int read_line(int, char *, size_t);
71 static void run(int, const char *);
72 static void setup(void);
73 static void sighandler(int);
74 static int tcpopen(const char *, const char *);
75 static size_t tokenize(char **, size_t, char *, int);
76 static int udsopen(const char *);
77 static void usage(void);
78
79 static int isrunning = 1;
80 static time_t last_response = 0;
81 static Channel *channels = NULL;
82 static Channel *channelmaster = NULL;
83 static char nick[32]; /* active nickname at runtime */
84 static char _nick[32]; /* nickname at startup */
85 static char ircpath[PATH_MAX]; /* irc dir (-i) */
86 static char msg[IRC_MSG_MAX]; /* message buf used for communication…
87
88 static void
89 die(const char *fmt, ...)
90 {
91 va_list ap;
92
93 va_start(ap, fmt);
94 vfprintf(stderr, fmt, ap);
95 va_end(ap);
96
97 cleanup();
98 exit(1);
99 }
100
101 static void
102 usage(void)
103 {
104 die("usage: %s -s host [-p port | -u sockname] [-i ircdir]\n"
105 " [-n nickname] [-f fullname] [-k env_pass]\n", argv0…
106 }
107
108 static void
109 ewritestr(int fd, const char *s)
110 {
111 size_t len, off = 0;
112 int w = -1;
113
114 len = strlen(s);
115 for (off = 0; off < len; off += w) {
116 if ((w = write(fd, s + off, len - off)) == -1)
117 break;
118 }
119 if (w == -1)
120 die("%s: write: %s\n", argv0, strerror(errno));
121 }
122
123 /* creates directories bottom-up, if necessary */
124 static void
125 create_dirtree(const char *dir)
126 {
127 char tmp[PATH_MAX], *p;
128 struct stat st;
129 size_t len;
130
131 strlcpy(tmp, dir, sizeof(tmp));
132 len = strlen(tmp);
133 if (len > 0 && tmp[len - 1] == '/')
134 tmp[len - 1] = '\0';
135
136 if ((stat(tmp, &st) != -1) && S_ISDIR(st.st_mode))
137 return; /* dir exists */
138
139 for (p = tmp + 1; *p; p++) {
140 if (*p != '/')
141 continue;
142 *p = '\0';
143 mkdir(tmp, S_IRWXU);
144 *p = '/';
145 }
146 mkdir(tmp, S_IRWXU);
147 }
148
149 static void
150 channel_normalize_path(char *s)
151 {
152 for (; *s; s++) {
153 if (isalpha((unsigned char)*s))
154 *s = tolower((unsigned char)*s);
155 else if (!isdigit((unsigned char)*s) && !strchr(".#&+!-"…
156 *s = '_';
157 }
158 }
159
160 static void
161 channel_normalize_name(char *s)
162 {
163 char *p;
164
165 while (*s == '&' || *s == '#')
166 s++;
167 for (p = s; *s; s++) {
168 if (!strchr(" ,&#\x07", *s)) {
169 *p = *s;
170 p++;
171 }
172 }
173 *p = '\0';
174 }
175
176 static void
177 cleanup(void)
178 {
179 Channel *c, *tmp;
180
181 if (channelmaster)
182 channel_leave(channelmaster);
183
184 for (c = channels; c; c = tmp) {
185 tmp = c->next;
186 channel_leave(c);
187 }
188 }
189
190 static void
191 create_filepath(char *filepath, size_t len, const char *path,
192 const char *channel, const char *suffix)
193 {
194 int r;
195
196 if (channel[0]) {
197 r = snprintf(filepath, len, "%s/%s", path, channel);
198 if (r < 0 || (size_t)r >= len)
199 goto error;
200 create_dirtree(filepath);
201 r = snprintf(filepath, len, "%s/%s/%s", path, channel, s…
202 if (r < 0 || (size_t)r >= len)
203 goto error;
204 } else {
205 r = snprintf(filepath, len, "%s/%s", path, suffix);
206 if (r < 0 || (size_t)r >= len)
207 goto error;
208 }
209 return;
210
211 error:
212 die("%s: path to irc directory too long\n", argv0);
213 }
214
215 static int
216 channel_open(Channel *c)
217 {
218 int fd;
219 struct stat st;
220
221 /* make "in" fifo if it doesn't exist already. */
222 if (lstat(c->inpath, &st) != -1) {
223 if (!(st.st_mode & S_IFIFO))
224 return -1;
225 } else if (mkfifo(c->inpath, S_IRWXU)) {
226 return -1;
227 }
228 c->fdin = -1;
229 fd = open(c->inpath, O_RDONLY | O_NONBLOCK, 0);
230 if (fd == -1)
231 return -1;
232 c->fdin = fd;
233
234 return 0;
235 }
236
237 static int
238 channel_reopen(Channel *c)
239 {
240 if (c->fdin > 2) {
241 close(c->fdin);
242 c->fdin = -1;
243 }
244 return channel_open(c);
245 }
246
247 static Channel *
248 channel_new(const char *name)
249 {
250 Channel *c;
251 char channelpath[PATH_MAX];
252
253 strlcpy(channelpath, name, sizeof(channelpath));
254 channel_normalize_path(channelpath);
255
256 if (!(c = calloc(1, sizeof(Channel))))
257 die("%s: calloc: %s\n", argv0, strerror(errno));
258
259 strlcpy(c->name, name, sizeof(c->name));
260 channel_normalize_name(c->name);
261
262 create_filepath(c->inpath, sizeof(c->inpath), ircpath,
263 channelpath, "in");
264 create_filepath(c->outpath, sizeof(c->outpath), ircpath,
265 channelpath, "out");
266 return c;
267 }
268
269 static Channel *
270 channel_find(const char *name)
271 {
272 Channel *c;
273 char chan[IRC_CHANNEL_MAX];
274
275 strlcpy(chan, name, sizeof(chan));
276 channel_normalize_name(chan);
277 for (c = channels; c; c = c->next) {
278 if (!strcmp(chan, c->name))
279 return c; /* already handled */
280 }
281 return NULL;
282 }
283
284 static Channel *
285 channel_add(const char *name)
286 {
287 Channel *c;
288
289 c = channel_new(name);
290 if (channel_open(c) == -1) {
291 fprintf(stderr, "%s: cannot create channel: %s: %s\n",
292 argv0, name, strerror(errno));
293 free(c);
294 return NULL;
295 }
296 if (!channels) {
297 channels = c;
298 } else {
299 c->next = channels;
300 channels = c;
301 }
302 return c;
303 }
304
305 static Channel *
306 channel_join(const char *name)
307 {
308 Channel *c;
309
310 if (!(c = channel_find(name)))
311 c = channel_add(name);
312 return c;
313 }
314
315 static void
316 channel_rm(Channel *c)
317 {
318 Channel *p;
319
320 if (channels == c) {
321 channels = channels->next;
322 } else {
323 for (p = channels; p && p->next != c; p = p->next)
324 ;
325 if (p && p->next == c)
326 p->next = c->next;
327 }
328 free(c);
329 }
330
331 static void
332 channel_leave(Channel *c)
333 {
334 if (c->fdin > 2) {
335 close(c->fdin);
336 c->fdin = -1;
337 }
338 /* remove "in" file on leaving the channel */
339 unlink(c->inpath);
340 channel_rm(c);
341 }
342
343 static void
344 loginkey(int ircfd, const char *key)
345 {
346 snprintf(msg, sizeof(msg), "PASS %s\r\n", key);
347 ewritestr(ircfd, msg);
348 }
349
350 static void
351 loginuser(int ircfd, const char *host, const char *fullname)
352 {
353 snprintf(msg, sizeof(msg), "NICK %s\r\nUSER %s localhost %s :%s\…
354 nick, nick, host, fullname);
355 puts(msg);
356 ewritestr(ircfd, msg);
357 }
358
359 static int
360 udsopen(const char *uds)
361 {
362 struct sockaddr_un sun;
363 size_t len;
364 int fd;
365
366 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
367 die("%s: socket: %s\n", argv0, strerror(errno));
368
369 sun.sun_family = AF_UNIX;
370 if (strlcpy(sun.sun_path, uds, sizeof(sun.sun_path)) >= sizeof(s…
371 die("%s: UNIX domain socket path truncation\n", argv0);
372
373 len = strlen(sun.sun_path) + 1 + sizeof(sun.sun_family);
374 if (connect(fd, (struct sockaddr *)&sun, len) == -1)
375 die("%s: connect: %s\n", argv0, strerror(errno));
376
377 return fd;
378 }
379
380 static int
381 tcpopen(const char *host, const char *service)
382 {
383 struct addrinfo hints, *res = NULL, *rp;
384 int fd = -1, e;
385
386 memset(&hints, 0, sizeof(hints));
387 hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */
388 hints.ai_flags = AI_NUMERICSERV; /* avoid name lookup for port */
389 hints.ai_socktype = SOCK_STREAM;
390
391 if ((e = getaddrinfo(host, service, &hints, &res)))
392 die("%s: getaddrinfo: %s\n", argv0, gai_strerror(e));
393
394 for (rp = res; rp; rp = rp->ai_next) {
395 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_proto…
396 if (fd == -1)
397 continue;
398 if (connect(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
399 close(fd);
400 fd = -1;
401 continue;
402 }
403 break; /* success */
404 }
405 if (fd == -1)
406 die("%s: could not connect to %s:%s: %s\n",
407 argv0, host, service, strerror(errno));
408
409 freeaddrinfo(res);
410 return fd;
411 }
412
413 static int
414 isnumeric(const char *s)
415 {
416 errno = 0;
417 strtol(s, NULL, 10);
418 return errno == 0;
419 }
420
421 static size_t
422 tokenize(char **result, size_t reslen, char *str, int delim)
423 {
424 char *p = NULL, *n = NULL;
425 size_t i = 0;
426
427 for (n = str; *n == ' '; n++)
428 ;
429 p = n;
430 while (*n != '\0') {
431 if (i >= reslen)
432 return 0;
433 if (i > TOK_CHAN - TOK_CMD && result[0] && isnumeric(res…
434 delim = ':'; /* workaround non-RFC compliant mes…
435 if (*n == delim) {
436 *n = '\0';
437 result[i++] = p;
438 p = ++n;
439 } else {
440 n++;
441 }
442 }
443 /* add last entry */
444 if (i < reslen && p < n && p && *p)
445 result[i++] = p;
446 return i; /* number of tokens */
447 }
448
449 static void
450 channel_print(Channel *c, const char *buf)
451 {
452 FILE *fp = NULL;
453 time_t t = time(NULL);
454
455 if (!(fp = fopen(c->outpath, "a")))
456 return;
457 fprintf(fp, "%lu %s\n", (unsigned long)t, buf);
458 fclose(fp);
459 }
460
461 static void
462 proc_channels_privmsg(int ircfd, Channel *c, char *buf)
463 {
464 snprintf(msg, sizeof(msg), "<%s> %s", nick, buf);
465 channel_print(c, msg);
466 snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", c->name, buf);
467 ewritestr(ircfd, msg);
468 }
469
470 static void
471 proc_channels_input(int ircfd, Channel *c, char *buf)
472 {
473 char *p = NULL;
474 size_t buflen;
475
476 if (buf[0] == '\0')
477 return;
478 if (buf[0] != '/') {
479 proc_channels_privmsg(ircfd, c, buf);
480 return;
481 }
482
483 msg[0] = '\0';
484 if ((buflen = strlen(buf)) < 2)
485 return;
486 if (buf[2] == ' ' || buf[2] == '\0') {
487 switch (buf[1]) {
488 case 'j': /* join */
489 if (buflen < 3)
490 return;
491 if ((p = strchr(&buf[3], ' '))) /* password para…
492 *p = '\0';
493 if ((buf[3] == '#') || (buf[3] == '&') || (buf[3…
494 (buf[3] == '!'))
495 {
496 /* password protected channel */
497 if (p)
498 snprintf(msg, sizeof(msg), "JOIN…
499 else
500 snprintf(msg, sizeof(msg), "JOIN…
501 channel_join(&buf[3]);
502 } else if (p) {
503 if ((c = channel_join(&buf[3])))
504 proc_channels_privmsg(ircfd, c, …
505 return;
506 }
507 break;
508 case 't': /* topic */
509 if (buflen >= 3)
510 snprintf(msg, sizeof(msg), "TOPIC %s :%s…
511 break;
512 case 'a': /* away */
513 if (buflen >= 3) {
514 snprintf(msg, sizeof(msg), "-!- %s is aw…
515 channel_print(c, msg);
516 }
517 if (buflen >= 3)
518 snprintf(msg, sizeof(msg), "AWAY :%s\r\n…
519 else
520 snprintf(msg, sizeof(msg), "AWAY\r\n");
521 break;
522 case 'n': /* change nick */
523 if (buflen >= 3) {
524 strlcpy(_nick, &buf[3], sizeof(_nick));
525 snprintf(msg, sizeof(msg), "NICK %s\r\n"…
526 }
527 break;
528 case 'l': /* leave */
529 if (c == channelmaster)
530 return;
531 if (buflen >= 3)
532 snprintf(msg, sizeof(msg), "PART %s :%s\…
533 else
534 snprintf(msg, sizeof(msg),
535 "PART %s :leaving\r\n", c->name…
536 ewritestr(ircfd, msg);
537 channel_leave(c);
538 return;
539 break;
540 case 'q': /* quit */
541 if (buflen >= 3)
542 snprintf(msg, sizeof(msg), "QUIT :%s\r\n…
543 else
544 snprintf(msg, sizeof(msg),
545 "QUIT %s\r\n", "bye");
546 ewritestr(ircfd, msg);
547 isrunning = 0;
548 return;
549 break;
550 default: /* raw IRC command */
551 snprintf(msg, sizeof(msg), "%s\r\n", &buf[1]);
552 break;
553 }
554 } else {
555 /* raw IRC command */
556 snprintf(msg, sizeof(msg), "%s\r\n", &buf[1]);
557 }
558 if (msg[0] != '\0')
559 ewritestr(ircfd, msg);
560 }
561
562 static void
563 proc_server_cmd(int fd, char *buf)
564 {
565 Channel *c;
566 const char *channel;
567 char *argv[TOK_LAST], *cmd = NULL, *p = NULL;
568 unsigned int i;
569
570 if (!buf || buf[0] == '\0')
571 return;
572
573 /* clear tokens */
574 for (i = 0; i < TOK_LAST; i++)
575 argv[i] = NULL;
576
577 /* check prefix */
578 if (buf[0] == ':') {
579 if (!(p = strchr(buf, ' ')))
580 return;
581 *p = '\0';
582 for (++p; *p == ' '; p++)
583 ;
584 cmd = p;
585 argv[TOK_NICKSRV] = &buf[1];
586 if ((p = strchr(buf, '!'))) {
587 *p = '\0';
588 argv[TOK_USER] = ++p;
589 }
590 } else {
591 cmd = buf;
592 }
593
594 /* remove CRLFs */
595 for (p = cmd; p && *p != '\0'; p++) {
596 if (*p == '\r' || *p == '\n')
597 *p = '\0';
598 }
599
600 if ((p = strchr(cmd, ':'))) {
601 *p = '\0';
602 argv[TOK_TEXT] = ++p;
603 }
604
605 tokenize(&argv[TOK_CMD], TOK_LAST - TOK_CMD, cmd, ' ');
606
607 if (!argv[TOK_CMD] || !strcmp("PONG", argv[TOK_CMD])) {
608 return;
609 } else if (!strcmp("PING", argv[TOK_CMD])) {
610 snprintf(msg, sizeof(msg), "PONG %s\r\n", argv[TOK_TEXT]…
611 ewritestr(fd, msg);
612 return;
613 } else if (!argv[TOK_NICKSRV] || !argv[TOK_USER]) {
614 /* server command */
615 snprintf(msg, sizeof(msg), "%s%s",
616 argv[TOK_ARG] ? argv[TOK_ARG] : "",
617 argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
618 channel_print(channelmaster, msg);
619 return; /* don't process further */
620 } else if (!strcmp("ERROR", argv[TOK_CMD]))
621 snprintf(msg, sizeof(msg), "-!- error %s",
622 argv[TOK_TEXT] ? argv[TOK_TEXT] : "unkno…
623 else if (!strcmp("JOIN", argv[TOK_CMD]) && (argv[TOK_CHAN] || ar…
624 if (argv[TOK_TEXT])
625 argv[TOK_CHAN] = argv[TOK_TEXT];
626 snprintf(msg, sizeof(msg), "-!- %s(%s) has joined %s",
627 argv[TOK_NICKSRV], argv[TOK_USER], argv[…
628 } else if (!strcmp("PART", argv[TOK_CMD]) && argv[TOK_CHAN]) {
629 snprintf(msg, sizeof(msg), "-!- %s(%s) has left %s",
630 argv[TOK_NICKSRV], argv[TOK_USER], argv[…
631 /* if user itself leaves, don't write to channel (don't …
632 if (!strcmp(argv[TOK_NICKSRV], nick))
633 return;
634 } else if (!strcmp("MODE", argv[TOK_CMD])) {
635 snprintf(msg, sizeof(msg), "-!- %s changed mode/%s -> %s…
636 argv[TOK_NICKSRV],
637 argv[TOK_CHAN] ? argv[TOK_CHAN] : "",
638 argv[TOK_ARG] ? argv[TOK_ARG] : "",
639 argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
640 } else if (!strcmp("QUIT", argv[TOK_CMD])) {
641 snprintf(msg, sizeof(msg), "-!- %s(%s) has quit \"%s\"",
642 argv[TOK_NICKSRV], argv[TOK_USER],
643 argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
644 } else if (!strncmp("NICK", argv[TOK_CMD], 5) && argv[TOK_TEXT] …
645 !strcmp(_nick, argv[TOK_TEXT])) {
646 strlcpy(nick, _nick, sizeof(nick));
647 snprintf(msg, sizeof(msg), "-!- changed nick to \"%s\"",…
648 channel_print(channelmaster, msg);
649 } else if (!strcmp("NICK", argv[TOK_CMD]) && argv[TOK_TEXT]) {
650 snprintf(msg, sizeof(msg), "-!- %s changed nick to %s",
651 argv[TOK_NICKSRV], argv[TOK_TEXT]);
652 } else if (!strcmp("TOPIC", argv[TOK_CMD])) {
653 snprintf(msg, sizeof(msg), "-!- %s changed topic to \"%s…
654 argv[TOK_NICKSRV],
655 argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
656 } else if (!strcmp("KICK", argv[TOK_CMD]) && argv[TOK_ARG]) {
657 snprintf(msg, sizeof(msg), "-!- %s kicked %s (\"%s\")",
658 argv[TOK_NICKSRV], argv[TOK_ARG],
659 argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
660 } else if (!strcmp("NOTICE", argv[TOK_CMD])) {
661 snprintf(msg, sizeof(msg), "-!- \"%s\"",
662 argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
663 } else if (!strcmp("PRIVMSG", argv[TOK_CMD])) {
664 snprintf(msg, sizeof(msg), "<%s> %s", argv[TOK_NICKSRV],
665 argv[TOK_TEXT] ? argv[TOK_TEXT] : "");
666 } else {
667 return; /* can't read this message */
668 }
669 if (argv[TOK_CHAN] && !strcmp(argv[TOK_CHAN], nick))
670 channel = argv[TOK_NICKSRV];
671 else
672 channel = argv[TOK_CHAN];
673
674 if (!channel || channel[0] == '\0')
675 c = channelmaster;
676 else
677 c = channel_join(channel);
678 if (c)
679 channel_print(c, msg);
680 }
681
682 static int
683 read_line(int fd, char *buf, size_t bufsiz)
684 {
685 size_t i = 0;
686 char c = '\0';
687
688 do {
689 if (read(fd, &c, sizeof(char)) != sizeof(char))
690 return -1;
691 buf[i++] = c;
692 } while (c != '\n' && i < bufsiz);
693 buf[i - 1] = '\0'; /* eliminates '\n' */
694 return 0;
695 }
696
697 static void
698 handle_channels_input(int ircfd, Channel *c)
699 {
700 /*
701 * Do not allow to read this fully, since commands will be
702 * prepended. It will result in too long lines sent to the
703 * server.
704 * TODO: Make this depend on the maximum metadata given by the
705 * server at the beginning of the connection.
706 */
707 char buf[IRC_MSG_MAX-64];
708
709 if (read_line(c->fdin, buf, sizeof(buf)) == -1) {
710 if (channel_reopen(c) == -1)
711 channel_rm(c);
712 return;
713 }
714 proc_channels_input(ircfd, c, buf);
715 }
716
717 static void
718 handle_server_output(int ircfd)
719 {
720 char buf[IRC_MSG_MAX];
721
722 if (read_line(ircfd, buf, sizeof(buf)) == -1)
723 die("%s: remote host closed connection: %s\n", argv0, st…
724
725 fprintf(stdout, "%lu %s\n", (unsigned long)time(NULL), buf);
726 fflush(stdout);
727 proc_server_cmd(ircfd, buf);
728 }
729
730 static void
731 sighandler(int sig)
732 {
733 if (sig == SIGTERM || sig == SIGINT)
734 isrunning = 0;
735 }
736
737 static void
738 setup(void)
739 {
740 struct sigaction sa;
741
742 memset(&sa, 0, sizeof(sa));
743 sa.sa_handler = sighandler;
744 sigaction(SIGTERM, &sa, NULL);
745 sigaction(SIGINT, &sa, NULL);
746 }
747
748 static void
749 run(int ircfd, const char *host)
750 {
751 Channel *c, *tmp;
752 fd_set rdset;
753 struct timeval tv;
754 char ping_msg[IRC_MSG_MAX];
755 int r, maxfd;
756
757 snprintf(ping_msg, sizeof(ping_msg), "PING %s\r\n", host);
758 while (isrunning) {
759 maxfd = ircfd;
760 FD_ZERO(&rdset);
761 FD_SET(ircfd, &rdset);
762 for (c = channels; c; c = c->next) {
763 if (c->fdin > maxfd)
764 maxfd = c->fdin;
765 FD_SET(c->fdin, &rdset);
766 }
767 memset(&tv, 0, sizeof(tv));
768 tv.tv_sec = 120;
769 r = select(maxfd + 1, &rdset, 0, 0, &tv);
770 if (r < 0) {
771 if (errno == EINTR)
772 continue;
773 die("%s: select: %s\n", argv0, strerror(errno));
774 } else if (r == 0) {
775 if (time(NULL) - last_response >= PING_TIMEOUT) {
776 channel_print(channelmaster, "-!- ii shu…
777 cleanup();
778 exit(2); /* status code 2 for timeout */
779 }
780 ewritestr(ircfd, ping_msg);
781 continue;
782 }
783 if (FD_ISSET(ircfd, &rdset)) {
784 handle_server_output(ircfd);
785 last_response = time(NULL);
786 }
787 for (c = channels; c; c = tmp) {
788 tmp = c->next;
789 if (FD_ISSET(c->fdin, &rdset))
790 handle_channels_input(ircfd, c);
791 }
792 }
793 }
794
795 int
796 main(int argc, char *argv[])
797 {
798 struct passwd *spw;
799 const char *key = NULL, *fullname = NULL, *host = "";
800 const char *uds = NULL, *service = "6667";
801 char prefix[PATH_MAX];
802 int ircfd, r;
803
804 /* use nickname and home dir of user by default */
805 if (!(spw = getpwuid(getuid())))
806 die("%s: getpwuid: %s\n", argv0, strerror(errno));
807
808 strlcpy(nick, spw->pw_name, sizeof(nick));
809 snprintf(prefix, sizeof(prefix), "%s/irc", spw->pw_dir);
810
811 ARGBEGIN {
812 case 'f':
813 fullname = EARGF(usage());
814 break;
815 case 'i':
816 strlcpy(prefix, EARGF(usage()), sizeof(prefix));
817 break;
818 case 'k':
819 key = getenv(EARGF(usage()));
820 break;
821 case 'n':
822 strlcpy(nick, EARGF(usage()), sizeof(nick));
823 break;
824 case 'p':
825 service = EARGF(usage());
826 break;
827 case 's':
828 host = EARGF(usage());
829 break;
830 case 'u':
831 uds = EARGF(usage());
832 break;
833 default:
834 usage();
835 break;
836 } ARGEND
837
838 if (!*host)
839 usage();
840
841 if (uds)
842 ircfd = udsopen(uds);
843 else
844 ircfd = tcpopen(host, service);
845
846 #ifdef __OpenBSD__
847 /* OpenBSD pledge(2) support */
848 if (pledge("stdio rpath wpath cpath dpath", NULL) == -1)
849 die("%s: pledge: %s\n", argv0, strerror(errno));
850 #endif
851
852 r = snprintf(ircpath, sizeof(ircpath), "%s/%s", prefix, host);
853 if (r < 0 || (size_t)r >= sizeof(ircpath))
854 die("%s: path to irc directory too long\n", argv0);
855 create_dirtree(ircpath);
856
857 channelmaster = channel_add(""); /* master channel */
858 if (key)
859 loginkey(ircfd, key);
860 loginuser(ircfd, host, fullname && *fullname ? fullname : nick);
861 setup();
862 run(ircfd, host);
863 cleanup();
864
865 return 0;
866 }
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.