5 static char nick_self
[128];
6 static char *channels
[128];
7 static unsigned nchannel
;
9 static char *wanted_nick
;
12 static typeof(init_hook
) *init_ptr
;
13 static typeof(privmsg_hook
) *privmsg_ptr
;
14 static typeof(command_hook
) *command_ptr
;
16 static int reload_requested
= 1;
19 static void reload_handler(int signal
)
24 static void handle_connect(struct bio
*b
, const char *prefix
, const char *ident
, const char *host
,
25 const char *const *args
, unsigned nargs
)
28 strncpy(nick_self
, args
[0], sizeof(nick_self
)-1);
29 b
->writeline(b
, "PRIVMSG NICKSERV :identify %s %s", wanted_nick
, password
);
30 for (i
= 0; i
< nchannel
; ++i
)
31 b
->writeline(b
, "JOIN %s", channels
[i
]);
34 static void handle_nick(struct bio
*b
, const char *prefix
, const char *ident
, const char *host
,
35 const char *const *args
, unsigned nargs
)
37 if (!strcmp(nick_self
, prefix
)) {
38 strncpy(nick_self
, args
[0], sizeof(nick_self
)-1);
41 if (!strcasecmp(args
[0], "SweetieBelle") || !strcasecmp(args
[0], wanted_nick
))
42 b
->writeline(b
, "PRIVMSG NICKSERV :ghost %s %s", args
[0], password
);
43 if (dl_mod
&& command_ptr
)
44 command_ptr(b
, prefix
, ident
, host
, "NICK", args
, nargs
);
47 static void handle_ping(struct bio
*b
, const char *prefix
, const char *ident
, const char *host
,
48 const char *const *args
, unsigned nargs
)
50 b
->writeline(b
, "PONG %s", args
[0]);
53 static void mod_unload(struct bio
*b
, const char *target
)
56 if (dlclose(dl_mod
)) {
57 const char *foo
= strchr(dlerror(), ':');
60 b
->writeline(b
, "PRIVMSG %s :UNLOAD ERROR: %s", target
, foo
? foo
: dlerror());
67 static void mod_load(struct bio
*b
, const char *target
)
70 mod_unload(b
, target
);
72 dl_mod
= dlopen("mod.so", RTLD_LOCAL
|RTLD_NOW
);
74 const char *foo
= strchr(dlerror(), ':');
77 b
->writeline(b
, "PRIVMSG %s :LOAD ERROR: %s", target
, foo
? foo
: dlerror());
80 init_ptr
= dlsym(dl_mod
, "init_hook");
81 privmsg_ptr
= dlsym(dl_mod
, "privmsg_hook");
82 command_ptr
= dlsym(dl_mod
, "command_hook");
84 init_ptr(b
, target
, nick_self
, ponify
);
86 b
->writeline(b
, "PRIVMSG %s :I feel smarter", target
);
89 static void handle_privmsg(struct bio
*b
, const char *prefix
, const char *ident
, const char *host
,
90 const char *const *args
, unsigned nargs
)
95 target
= args
[0][0] == '#' ? args
[0] : prefix
;
97 if (!strcmp(args
[1], "crash")) {
100 } else if (!strcmp(args
[1], "load")) {
103 } else if (!strcmp(args
[1], "unload")) {
106 mod_unload(b
, target
);
107 b
->writeline(b
, "PRIVMSG %s :{ I feel different. }", target
);
111 if (!strcmp(args
[1], "bt")) {
112 crash_write_backtrace(b
, prefix
);
116 if (dl_mod
&& privmsg_ptr
)
117 privmsg_ptr(b
, prefix
, ident
, host
, args
, nargs
);
118 if (reload_requested
)
126 void (*ptr
)(struct bio
*b
, const char *prefix
, const char *ident
, const char *host
,
127 const char *const *args
, unsigned nargs
);
130 { "PRIVMSG", 2, 1, handle_privmsg
},
131 { "NICK", 1, 1, handle_nick
},
132 { "PING", 1, 0, handle_ping
, 1 },
133 { "001", 2, 0, handle_connect
, 1 },
137 static void ssl_writeline(struct bio
*b
, const char *fmt
, ...)
138 __attribute__ ((__format__ (__printf__
, 2, 3)));
140 static void plain_writeline(struct bio
*b
, const char *fmt
, ...)
141 __attribute__ ((__format__ (__printf__
, 2, 3)));
143 static void bio_seterr(struct bio
*b
, ssize_t err
)
149 static void bio_fill(struct bio
*b
, ssize_t avail
)
151 if (b
->maxlen
< avail
+ b
->len
) {
152 memmove(b
->priv
, b
->priv
+ b
->ofs
, b
->len
- b
->ofs
);
155 if (b
->maxlen
< b
->len
+ avail
) {
156 b
->priv
= realloc(b
->priv
, b
->len
+ avail
);
157 b
->maxlen
= b
->len
+ avail
;
162 static int plain_fill(struct bio
*b
)
165 if (ioctl(b
->fd
, FIONREAD
, &avail
) < 0) {
166 bio_seterr(b
, -errno
);
170 avail
= read(b
->fd
, b
->priv
+ b
->len
, avail
);
175 static int ssl_fill(struct bio
*b
)
181 ret
= SSL_read(b
->ssl
, b
->priv
+ b
->len
, 1);
186 avail
= SSL_pending(b
->ssl
);
190 avail
= SSL_read(b
->ssl
, b
->priv
+ b
->len
, avail
);
197 static int bio_poll(struct bio
*b
, int timeout
)
200 struct pollfd pfd
= {
202 .events
= POLLIN
|POLLRDHUP
|POLLHUP
204 ret
= poll(&pfd
, 1, timeout
);
205 if (ret
&& pfd
.revents
& ~POLLIN
)
210 static int bio_readline(struct bio
*b
, char **line
)
214 for (i
= b
->ofs
; i
< b
->len
; ++i
) {
217 if (b
->priv
[i
] != '\r' && b
->priv
[i
] != '\n')
224 if (b
->ofs
== b
->len
) {
230 *line
= b
->priv
+ b
->ofs
;
240 static void plain_writeline(struct bio
*b
, const char *fmt
, ...)
246 ret
= vasprintf(&buffer
, fmt
, va
);
252 buffer
[ret
] = '\n'; /* Replace \0 with \n */
256 ret
= write(b
->fd
, ofs
, rem
);
265 static void ssl_writeline(struct bio
*b
, const char *fmt
, ...)
272 ret
= vasprintf(&buffer
, fmt
, va
);
278 buffer
[ret
] = '\n'; /* Replace \0 with \n */
283 ret
= SSL_write(b
->ssl
, ofs
, rem
);
285 bio_seterr(b
, SSL_get_error(b
->ssl
, ret
));
294 static int get_connection(const char *server
, const char *port
)
297 struct addrinfo hints
, *result
, *rp
;
299 memset(&hints
, 0, sizeof(struct addrinfo
));
300 hints
.ai_family
= AF_UNSPEC
; /* Allow IPv4 or IPv6 */
301 hints
.ai_socktype
= SOCK_STREAM
;
303 hints
.ai_protocol
= 0;
305 s
= getaddrinfo(server
, port
, &hints
, &result
);
307 fprintf(stderr
, "getaddrinfo: %s\n", gai_strerror(s
));
311 for (rp
= result
; rp
!= NULL
; rp
= rp
->ai_next
) {
312 sfd
= socket(rp
->ai_family
, rp
->ai_socktype
,
317 if (connect(sfd
, rp
->ai_addr
, rp
->ai_addrlen
) != -1)
324 fprintf(stderr
, "Could not connect\n");
328 freeaddrinfo(result
);
332 static char *token(char **foo
)
334 char *line
= *foo
, *ret
;
336 while (*line
&& *line
!= ' ')
348 static inline void handle_line(struct bio
*b
, char *line
)
350 char *prefix
= NULL
, *command
, *ident
= NULL
, *host
= NULL
;
351 const char *args
[128];
353 if (line
[0] == ':') {
355 prefix
= token(&line
);
356 ident
= strchr(prefix
, '!');
359 host
= strchr(ident
, '@');
366 command
= token(&line
);
368 while (line
&& nargs
< sizeof(args
)/sizeof(*args
)) {
369 if (line
[0] == ':') {
370 args
[nargs
++] = ++line
;
373 args
[nargs
++] = token(&line
);
375 for (i
= 0; i
< sizeof(cmds
)/sizeof(*cmds
)-1; ++i
) {
376 typeof(*cmds
) *c
= cmds
+i
;
377 if (strcasecmp(c
->cmd
, command
))
379 if (nargs
< c
->min_args
) {
380 fprintf(stderr
, "Dropped %s because of %u < %u\n", command
, nargs
, c
->min_args
);
383 if (!prefix
&& c
->prefixed
) {
384 fprintf(stderr
, "Dropped %s because not prefixed\n", command
);
388 int sig
= sigsetjmp(crash_buf
, 1);
391 c
->ptr(b
, prefix
, ident
, host
, args
, nargs
);
395 crash_mod(b
, channels
[0]);
397 c
->ptr(b
, prefix
, ident
, host
, args
, nargs
);
400 if (!cmds
[i
].cmd
&& dl_mod
&& command_ptr
) {
401 int sig
= setjmp(crash_buf
);
404 command_ptr(b
, prefix
, ident
, host
, command
, args
, nargs
);
408 crash_mod(b
, channels
[0]);
412 static void mainloop(struct bio
*b
, const char *server
)
414 int ret
= 0, pinged
= 0;
415 b
->writeline(b
, "NICK %s", wanted_nick
);
417 b
->writeline(b
, "USER %s localhost %s :{ Cutie Mark Acquisition Program }", wanted_nick
, server
);
419 b
->writeline(b
, "USER %s localhost %s :Robotic overlord", wanted_nick
, server
);
422 ret
= b
->poll(b
, pinged
? 50000 : 250000);
426 b
->writeline(b
, "PING %s", nick_self
);
435 while ((ret
= b
->readline(b
, &line
) > 0))
436 handle_line(b
, line
);
440 static void usage(char *prog
, FILE *out
)
442 fprintf(out
, "Usage: %s\n", prog
);
443 fprintf(out
, "\t-s\t --server <server>\n");
444 fprintf(out
, "\t-p\t --port <port>\n");
445 fprintf(out
, "\t-P\t --password <password>\n");
446 fprintf(out
, "\t-c\t --channel <channel>\n");
447 fprintf(out
, "\t-n\t --nick <nick>\n");
448 fprintf(out
, "\t-S\t --ssl\n");
451 int main(int argc
, char *argv
[])
453 char *server
= NULL
, *port
= NULL
;
454 int use_ssl
= 0, c
, option_index
;
455 SSL_CTX
*ssl_ctx
= NULL
;
457 struct option options
[] = {
458 { "server", required_argument
, NULL
, 's' },
459 { "port", required_argument
, NULL
, 'p' },
460 { "password", required_argument
, NULL
, 'P' },
461 { "channel", required_argument
, NULL
, 'c' },
462 { "nick", required_argument
, NULL
, 'n' },
463 { "ssl", no_argument
, NULL
, 'S' },
464 { "help", no_argument
, NULL
, 'h' },
468 while ((c
= getopt_long(argc
, argv
, "s:p:P:c:n:Sh", options
, &option_index
)) >= 0) {
470 case 'c': channels
[nchannel
++] = optarg
; break;
471 case 's': server
= optarg
; break;
472 case 'p': port
= optarg
; break;
473 case 'P': password
= optarg
; break;
474 case 'n': wanted_nick
= optarg
; break;
475 case 'S': use_ssl
= 1; break;
476 case 'h': usage(argv
[0], stdout
); return 0;
477 default: usage(argv
[0], stderr
); return -1;
480 ponify
|= !!strcasestr(optarg
, "pony");
481 ponify
|= !!strcasestr(optarg
, "bron");
485 if (!server
|| !port
|| !nchannel
|| !wanted_nick
|| !password
) {
486 usage(argv
[0], stderr
);
491 ssl_ctx
= SSL_CTX_new(SSLv3_client_method());
492 SSL_CTX_set_verify(ssl_ctx
, SSL_VERIFY_NONE
, NULL
);
494 signal(SIGUSR1
, reload_handler
);
496 int fd
= get_connection(server
, port
);
503 ssl
= SSL_new(ssl_ctx
);
506 ssl_err
= SSL_set_fd(ssl
, fd
);
510 ssl_err
= SSL_get_error(ssl
, SSL_connect(ssl
));
511 if (ssl_err
!= SSL_ERROR_NONE
) {
512 fprintf(stderr
, "error code: %i\n", ssl_err
);
516 .readline
= bio_readline
,
517 .writeline
= ssl_writeline
,
523 mainloop(&b
, server
);
530 .readline
= bio_readline
,
531 .writeline
= plain_writeline
,
536 mainloop(&b
, server
);
543 SSL_CTX_free(ssl_ctx
);