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