ircc.c - ircc - Simple IRC client
(HTM) git clone git://r-36.net/ircc
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) LICENSE
---
ircc.c (18168B)
---
1 /*
2 * Copy me if you can.
3 * by 20h
4 */
5
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <stdarg.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <time.h>
13 #include <pthread.h>
14 #include <netdb.h>
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 #include <openssl/bio.h>
20 #include <openssl/ssl.h>
21 #include <openssl/err.h>
22 #include "arg.h"
23
24 #define VERSION "ircc 3rd ed. Linux 1st ed."
25 #define nil NULL
26
27 int tls, debug, ignoreflood, anickused, havenickserv, doautojoin;
28 char *argv0, *lastchan, *clientinfo, *nick, *anick, *passwd;
29
30 typedef struct command command;
31 struct command {
32 char **params;
33 int len;
34 int isalloc;
35 };
36
37 command *ignorel, *joinl;
38
39 void *
40 realloci(void *p, int i, int d)
41 {
42
43 p = realloc(p, i);
44 if(p == nil) {
45 perror("realloc");
46 exit(1);
47 }
48
49 if(d != 0)
50 memset(p, 0, i);
51
52 return (void *)p;
53 }
54
55 int
56 swrite(void *sock, char *buf, int l)
57 {
58 int ret;
59
60 if(!tls)
61 ret = write(*(int *)sock, buf, l);
62 else {
63 ret = BIO_write((BIO *)sock, buf, l);
64 (void)BIO_flush((BIO *)sock);
65 }
66
67 return ret;
68 }
69
70 int
71 sread(void *sock, char *buf, int l)
72 {
73 int ret;
74
75 if(!tls)
76 ret = read(*(int *)sock, buf, l);
77 else {
78 ret = BIO_read((BIO *)sock, buf, l);
79 (void)BIO_flush((BIO *)sock);
80 }
81
82 return ret;
83 }
84
85 void
86 tprintf(void *sock, char *fmt, ...)
87 {
88 va_list fmtargs;
89 char buf[8192];
90
91 va_start(fmtargs, fmt);
92 vsnprintf(buf, sizeof(buf) - 1, fmt, fmtargs);
93 va_end(fmtargs);
94
95 if(swrite(sock, buf, strlen(buf)) < 0)
96 perror("write");
97
98 return;
99 }
100
101 int
102 connecttcp(char *host, char *service)
103 {
104 int sock;
105 struct addrinfo *ai, *a;
106
107 sock = -1;
108
109 if(getaddrinfo(host, service, nil, &ai) < 0) {
110 perror("getaddrinfo");
111 return -1;
112 }
113
114 for(a = ai; a; a = a->ai_next) {
115 sock = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
116 if(sock < 0) {
117 perror("socket");
118 sock = -1;
119 break;
120 }
121
122 if(connect(sock, a->ai_addr, a->ai_addrlen) < 0) {
123 perror("connect");
124 sock = -1;
125 break;
126 } else
127 break;
128 }
129
130 freeaddrinfo(ai);
131
132 return sock;
133 }
134
135 void
136 freecmd(command *cmd)
137 {
138 int i;
139
140 i = -1;
141 if(cmd != nil) {
142 if(cmd->isalloc)
143 while(++i < cmd->len)
144 if(cmd->params[i] != nil)
145 free(cmd->params[i]);
146 if(cmd->params != nil)
147 free(cmd->params);
148 free(cmd);
149 }
150
151 return;
152 }
153
154 int
155 addlist(command *c, char *p)
156 {
157 int i;
158
159 i = -1;
160 if(c != nil) {
161 if(c->isalloc) {
162 p = strdup(p);
163 while(++i < c->len) {
164 if(c->params[i] == nil) {
165 c->params[i] = p;
166
167 return 2;
168 }
169 }
170 }
171
172 c->len++;
173 c->params = realloc(c->params, sizeof(char *) * c->len);
174 c->params[c->len - 1] = p;
175
176 return 1;
177 }
178
179 return 0;
180 }
181
182 int
183 searchlist(command *c, char *p)
184 {
185 int i;
186
187 i = -1;
188 if(c != nil)
189 while(++i < c->len)
190 if(c->params[i] != nil)
191 if(!strcasecmp(c->params[i], p))
192 return i;
193
194 return -1;
195 }
196
197 int
198 dellist(command *c, char *p)
199 {
200 int i;
201
202 if(c != nil) {
203 i = searchlist(c, p);
204 if(i != -1) {
205 free(c->params[i]);
206 c->params[i] = nil;
207 c->len--;
208 return 1;
209 }
210 }
211
212 return 0;
213 }
214
215 command *
216 parsecmd(command *cmd, char *data, int cmds)
217 {
218 command *ret;
219 char *next, *last, *temp;
220
221 temp = nil;
222
223 if(cmd == nil) {
224 ret = realloci(nil, sizeof(command), 2);
225 last = data;
226 } else {
227 ret = cmd;
228 if(ret->isalloc) {
229 temp = strdup(ret->params[ret->len - 1]);
230 dellist(ret, ret->params[ret->len - 1]);
231 } else {
232 ret->len--;
233 temp = ret->params[ret->len];
234 }
235 last = temp;
236 }
237 while((next = strchr(last, ' ')) != nil && ret->len < cmds) {
238 *next = '\0';
239 next++;
240 if(last[0] == ':')
241 last++;
242 addlist(ret, last);
243 last = next;
244 }
245 if(last[0] == ':')
246 last++;
247 if(last != nil)
248 addlist(ret, last);
249
250 if(temp != nil && cmd->isalloc)
251 free(temp);
252
253 return ret;
254 }
255
256 char *
257 mktimestamp(char bord, char bord_e)
258 {
259 time_t tim;
260 struct tm tm;
261 char *ret;
262
263 time(&tim);
264 localtime_r(&tim, &tm);
265
266 ret = malloc(31);
267 snprintf(ret, 30, "%c%.2d:%.2d%c", bord, tm.tm_hour, tm.tm_min, bord_e);
268
269 return ret;
270 }
271
272 void
273 setchan(char *new)
274 {
275
276 if(lastchan != nil)
277 free(lastchan);
278
279 lastchan = realloci(nil, strlen(new) + 1, 2);
280 strcpy(lastchan, new);
281 }
282
283 void
284 autojoin(void *sock)
285 {
286 int i;
287
288 for(i = 0; i < joinl->len; i++)
289 tprintf(sock, "JOIN %s\r\n", joinl->params[i]);
290 setchan(joinl->params[joinl->len - 1]);
291 }
292
293 int
294 gotcommand(void *sock, char *data, int len)
295 {
296 command *cmd;
297 char *last, *next, *send, *cpy, *test, *tmstmp;
298 int clen;
299 time_t tim;
300
301 clen = len;
302 last = data;
303 send = realloci(nil, 513, 2);
304
305 while((next = strchr(last, '\n')) != nil) {
306 *next++ = '\0';
307 clen -= (next - last);
308 if(*(next - 2) == '\r')
309 *(next - 2) = '\0';
310
311 cpy = strdup(last);
312 cmd = parsecmd(nil, last, 3);
313 tmstmp = mktimestamp('(', ')');
314
315 if(cmd->len > 1) {
316 if(!strncasecmp(cmd->params[0], "PING", 4)) {
317 tprintf(sock, "PONG %s\r\n", cmd->params[1]);
318 goto c_end;
319 }
320 if(!strncasecmp(cmd->params[0], "ERROR", 5)) {
321 printf("%sERROR%% %s %s %s\n", tmstmp, cmd->params[1], cmd->params[2], cmd->params[3]);
322 goto c_end;
323 }
324 }
325 if(cmd->len > 2) {
326 if(!strncasecmp(cmd->params[1], "JOIN", 4)) {
327 printf("%s(%s) has joined %s\n", tmstmp, cmd->params[0], cmd->params[2]);
328 goto c_end;
329 }
330 if(!strncasecmp(cmd->params[1], "PART", 4)) {
331 printf("%s(%s) has parted %s\n", tmstmp, cmd->params[0], cmd->params[2]);
332 goto c_end;
333 }
334 if(!strncasecmp(cmd->params[1], "MODE", 4)) {
335 printf("%s(%s)-m-(%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
336 goto c_end;
337 }
338 if(!strncasecmp(cmd->params[1], "QUIT", 4)) {
339 printf("%s(%s) has quit (%s %s)\n", tmstmp, cmd->params[0], cmd->params[2], (cmd->len > 3) ? cmd->params[3] : "");
340 goto c_end;
341 }
342 if((test = strchr(cmd->params[0], '!')) != nil)
343 *test = '\0';
344 if(!strncasecmp(cmd->params[1], "NICK", 4)) {
345 printf("%s(%s) changed nick to %s\n", tmstmp, cmd->params[0], cmd->params[2]);
346 if(searchlist(ignorel, cmd->params[0]) != -1) {
347 dellist(ignorel, cmd->params[0]);
348 addlist(ignorel, cmd->params[2]);
349 }
350 goto c_end;
351 }
352 }
353 if(cmd->len > 3) {
354 if(strlen(cmd->params[1]) == 3) {
355 if(ignoreflood) {
356 if(!strncasecmp(cmd->params[1], "001", 3) ||
357 !strncasecmp(cmd->params[1], "002", 3) ||
358 !strncasecmp(cmd->params[1], "003", 3) ||
359 !strncasecmp(cmd->params[1], "004", 3) ||
360 !strncasecmp(cmd->params[1], "005", 3) ||
361 !strncasecmp(cmd->params[1], "250", 3) ||
362 !strncasecmp(cmd->params[1], "251", 3) ||
363 !strncasecmp(cmd->params[1], "252", 3) ||
364 !strncasecmp(cmd->params[1], "253", 3) ||
365 !strncasecmp(cmd->params[1], "254", 3) ||
366 !strncasecmp(cmd->params[1], "255", 3) ||
367 !strncasecmp(cmd->params[1], "265", 3) ||
368 !strncasecmp(cmd->params[1], "266", 3) ||
369 !strncasecmp(cmd->params[1], "317", 3) ||
370 !strncasecmp(cmd->params[1], "318", 3) ||
371 !strncasecmp(cmd->params[1], "366", 3) ||
372 !strncasecmp(cmd->params[1], "375", 3) ||
373 !strncasecmp(cmd->params[1], "372", 3))
374 goto c_end;
375 }
376 if(!strncasecmp(cmd->params[1], "376", 3)) {
377 if(doautojoin && (!havenickserv || anickused))
378 autojoin(sock);
379 goto c_end;
380 }
381
382 printf("%s(%s)(%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[1], cmd->params[3]);
383 if(!strncasecmp(cmd->params[1], "433", 3) && anick != nil) {
384 if(anickused) {
385 printf("%s(IRCC)%% Sorry, but nick and anick are used. Try something different.\n", tmstmp);
386 goto c_end;
387 }
388 tprintf(sock, "NICK %s\r\n", anick);
389 anickused = 1;
390 }
391 goto c_end;
392 }
393 if(!strncasecmp(cmd->params[1], "INVITE", 6)) {
394 printf("%s(%s) invited you to %s\n", tmstmp, cmd->params[0], cmd->params[3]);
395 goto c_end;
396 }
397 if(!strncasecmp(cmd->params[1], "TOPIC", 5)) {
398 printf("%s(%s) topicchange in %s to \"%s\"\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
399 goto c_end;
400 }
401 if(!strncasecmp(cmd->params[1], "KICK", 4)) {
402 if(parsecmd(cmd, nil, 4) != nil)
403 printf("%s(%s) kicked %s out of %s \"%s\"\n", tmstmp, cmd->params[0], cmd->params[3], cmd->params[2], cmd->params[4]);
404 else
405 printf("%s(%s) kicked %s out of %s\n", tmstmp, cmd->params[0], cmd->params[3], cmd->params[2]);
406 goto c_end;
407 }
408 if(searchlist(ignorel, cmd->params[0]) == -1 &&
409 searchlist(ignorel, cmd->params[2]) == -1) {
410 if(!strncasecmp(cmd->params[1], "NOTICE", 6)) {
411 printf("%s(%s|%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
412 if(!strncasecmp(cmd->params[0], "NickServ", 8) && havenickserv) {
413 printf("in case NickServ\n");
414 tprintf(sock, "PRIVMSG NickServ :IDENTIFY %s\r\n", passwd);
415 memset(passwd, 0, strlen(passwd));
416 havenickserv = 0;
417 if(doautojoin)
418 autojoin(sock);
419 }
420 goto c_end;
421 }
422 if(!strncasecmp(cmd->params[1], "PRIVMSG", 7)) {
423 if(*(cmd->params[3]) == '\x01') {
424 if(!strncasecmp(cmd->params[3]+1, "VERSION", 7))
425 tprintf(sock, "NOTICE %s :\x01VERSION " VERSION "\x01\r\n", cmd->params[0]);
426 if(!strncasecmp(cmd->params[3]+1, "TIME", 4)) {
427 tim = time(0);
428 test = ctime(&tim);
429 test[strlen(test) - 1] = '\0';
430 tprintf(sock, "NOTICE %s :\x01TIME %s\x01\r\n", cmd->params[0], test);
431 }
432 if(!strncasecmp(cmd->params[3]+1, "PING", 4))
433 tprintf(sock, "NOTICE %s :\x01PONG %s\r\n", cmd->params[0], cmd->params[3]+6);
434 if(!strncasecmp(cmd->params[3]+1, "ACTION", 6)) {
435 *(cmd->params[3] + strlen(cmd->params[3])-1) = '\0';
436 printf("%s(%s+%s) %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]+8);
437 goto c_end;
438 }
439 if(!strncasecmp(cmd->params[3]+1, "CLIENTINFO", 10))
440 tprintf(sock, "NOTICE %s :\x01CLIENTINFO PING VERSION TIME USERINFO CLIENTINFO\x01\r\n", cmd->params[0]);
441 if(!strncasecmp(cmd->params[3]+1, "USERINFO", 8))
442 tprintf(sock, "NOTICE %s :\x01USERINFO %s\x01\r\n", cmd->params[0], (clientinfo != nil) ? clientinfo : "<nil>");
443 }
444 printf("%s(%s-%s)%% %s\n", tmstmp, cmd->params[0], cmd->params[2], cmd->params[3]);
445 goto c_end;
446 }
447 if(!strncasecmp(cmd->params[0], "NOTICE", 6)) {
448 printf("%sNOTICE%% %s\n", tmstmp, cmd->params[3]);
449 goto c_end;
450 }
451 } else
452 goto c_end;
453 }
454 printf("%s\n",cpy);
455 c_end:
456 free(tmstmp);
457 free(cpy);
458 freecmd(cmd);
459 last = next;
460 }
461
462 if(clen > 0)
463 strcpy(data, data + len - clen);
464 if(send != nil)
465 free(send);
466
467 return clen;
468 }
469
470 void *
471 recvproc(void *sock)
472 {
473 char *recv;
474 int len, overl;
475
476 recv = realloci(nil, 1025, 2);
477 len = 1;
478 overl = 0;
479
480 while(len > 0 && sock != nil) {
481 len = sread(sock, recv + overl, 1024 - overl);
482
483 if(debug)
484 printf("%s\n", recv);
485
486 if(len > 1)
487 overl = gotcommand(sock, recv, len + overl);
488 }
489
490 free(recv);
491
492 return nil;
493 }
494
495 void
496 usage(void)
497 {
498
499 fprintf(stdout, "usage: %s [-dit] [-a anick] [-c clientinfo]"
500 " [-j #channel ...]"
501 " [-k passwd]"
502 " [-n nick] [-o port] [-p pass] [-u user]"
503 " server\n",
504 argv0);
505
506 exit(1);
507 }
508
509 int
510 main(int argc, char *argv[])
511 {
512 void *sock;
513 int i, so;
514 char *send, *rad, *tmstmp, *user, *pass, *port;
515 command *cmd;
516 pthread_t th;
517 BIO *bio;
518 SSL_CTX *ctx;
519 SSL *ssl;
520
521 ctx = nil;
522 user = "root";
523 pass = nil;
524 nick = "root";
525 anick = nil;
526 port = nil;
527 tls = 0;
528 clientinfo = "The user has not specified his details.";
529 joinl = realloci(nil, sizeof(command), 2);
530 joinl->isalloc = 1;
531
532 ARGBEGIN {
533 case 'u':
534 user = EARGF(usage());
535 break;
536 case 'p':
537 pass = EARGF(usage());
538 break;
539 case 't':
540 tls = 1;
541 break;
542 case 'd':
543 debug = 1;
544 break;
545 case 'c':
546 clientinfo = EARGF(usage());
547 break;
548 case 'i':
549 ignoreflood = 1;
550 break;
551 case 'n':
552 nick = EARGF(usage());
553 break;
554 case 'o':
555 port = EARGF(usage());
556 break;
557 case 'a':
558 anick = EARGF(usage());
559 break;
560 case 'k':
561 havenickserv = 1;
562 passwd = EARGF(usage());
563 break;
564 case 'j':
565 addlist(joinl, EARGF(usage()));
566 doautojoin = 1;
567 break;
568 default:
569 usage();
570 } ARGEND;
571
572
573 if(argc < 1)
574 usage();
575
576 if(!tls) {
577 so = connecttcp(argv[0], (port == nil)? "6667" : port);
578 if(so < 0) {
579 perror("connecttcp");
580 exit(1);
581 }
582
583 sock = &so;
584 } else {
585 ERR_load_crypto_strings();
586 SSL_library_init();
587
588 ctx = SSL_CTX_new(SSLv23_client_method());
589 if(ctx == nil) {
590 perror("SSL_CTX_new");
591 ERR_print_errors_fp(stderr);
592 exit(1);
593 }
594 bio = BIO_new_ssl_connect(ctx);
595 if(bio == nil) {
596 perror("BIO_new_ssl_connect");
597 ERR_print_errors_fp(stderr);
598 exit(1);
599 }
600
601 BIO_get_ssl(bio, &ssl);
602 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
603
604 BIO_set_conn_port(bio, (port == nil)? "994" : port);
605 BIO_set_conn_hostname(bio, argv[0]);
606 if(BIO_do_connect(bio) <= 0) {
607 perror("BIO_do_connect");
608 ERR_print_errors_fp(stderr);
609 exit(1);
610 }
611
612 sock = bio;
613 }
614
615 if(user == nil)
616 user = nick;
617
618 if(pass != nil)
619 tprintf(sock, "PASS %s\r\n", pass);
620 tprintf(sock, "NICK %s\r\n", nick);
621 tprintf(sock, "USER %s localhost %s :%s\r\n", user, nick, user);
622
623 if(pthread_create(&th, nil, recvproc, sock) < 0) {
624 perror("pthread_create");
625 exit(1);
626 }
627
628 ignorel = realloci(nil, sizeof(command), 2);
629 ignorel->isalloc = 1;
630 send = realloci(nil, 513, 2);
631 rad = realloci(nil, 513, 2);
632
633 while(sock > 0) {
634 i = -1;
635 cmd = nil;
636 memset(send, 0, 513);
637 memset(rad, 0, 513);
638
639 while(read(0, &rad[++i], 1) && i < 400 && sock != nil) {
640 if(rad[i] == '\n') {
641 rad[i] = '\0';
642 break;
643 }
644 }
645 rad[i + 1] = '\0';
646
647 tmstmp = mktimestamp('(', ')');
648 if(rad[0] == '/')
649 cmd = parsecmd(nil, rad, 2);
650
651 if(cmd != nil) {
652 if(cmd->len > 0) {
653 switch(cmd->params[0][1]) {
654 case 'H':
655 case 'h':
656 printf("%s Help for ircc:\n", tmstmp);
657 printf("%s /a chan text - send /me text to chan\n", tmstmp);
658 printf("%s /c chan what - send CTCP what to chan\n", tmstmp);
659 printf("%s /d - turn debugging on or off\n", tmstmp);
660 printf("%s /h - show this help\n", tmstmp);
661 printf("%s /i chan - ignore/unignore chan\n", tmstmp);
662 printf("%s /j chan - join chan\n", tmstmp);
663 printf("%s /k user [readon] - kick a user\n", tmstmp);
664 printf("%s /l [chan] - list channels on server\n", tmstmp);
665 printf("%s /m chan text - send text to chan\n", tmstmp);
666 printf("%s /n nick - change nickname to nick\n", tmstmp);
667 printf("%s /o chan mode [user] - set mode on chan\n", tmstmp);
668 printf("%s /p chan - part chan\n", tmstmp);
669 printf("%s /q [msg] - quit ircc\n", tmstmp);
670 printf("%s /s chan - set active chan\n", tmstmp);
671 printf("%s /t chan [topic] - set/show topic of chan\n", tmstmp);
672 printf("%s /u chan - WHO of chan\n", tmstmp);
673 printf("%s /w chan - WHOIS of chan\n", tmstmp);
674 goto end_e;
675 case 'D':
676 case 'd':
677 if(debug)
678 debug = 0;
679 else
680 debug = 1;
681 printf("%s debug %d\n", tmstmp, debug);
682 goto end_e;
683 case 'Q':
684 case 'q':
685 sprintf(send, "QUIT :\r\n");
686 if(cmd->len == 2)
687 sprintf(send + 6, "%s\r\n", cmd->params[1]);
688 if(cmd->len > 2)
689 sprintf(send + 6, "%s %s\r\n", cmd->params[1], cmd->params[2]);
690 swrite(sock, send, strlen(send));
691 if(tls)
692 BIO_free_all(sock);
693 else
694 close(*(int *)sock);
695 sock = nil;
696 goto end_e;
697 case 'L':
698 case 'l':
699 sprintf(send, "LIST\r\n");
700 if(cmd->len == 2)
701 sprintf(send + 4, " %s\r\n", cmd->params[1]);
702 if(cmd->len > 2)
703 sprintf(send + 4, " %s %s\r\n", cmd->params[1], cmd->params[2]);
704 swrite(sock, send, strlen(send));
705 goto end_e;
706 case 'S':
707 case 's':
708 if(cmd->len > 1)
709 setchan(cmd->params[1]);
710 else
711 printf("%s\n", (lastchan == nil) ? "<nil>": lastchan);
712 goto end_e;
713 default:
714 break;
715 }
716 }
717 if(cmd->len > 1) {
718 switch(cmd->params[0][1]) {
719 case 'J':
720 case 'j':
721 tprintf(sock, "JOIN %s\r\n", cmd->params[1]);
722 setchan(cmd->params[1]);
723 goto end_e;
724 case 'P':
725 case 'p':
726 tprintf(sock, "PART %s\r\n", cmd->params[1]);
727 goto end_e;
728 case 'N':
729 case 'n':
730 tprintf(sock, "NICK %s\r\n", cmd->params[1]);
731 goto end_e;
732 case 'W':
733 case 'w':
734 tprintf(sock, "WHOIS %s\r\n", cmd->params[1]);
735 goto end_e;
736 case 'U':
737 case 'u':
738 tprintf(sock, "WHO %s\r\n", cmd->params[1]);
739 goto end_e;
740 case 'T':
741 case 't':
742 sprintf(send, "TOPIC %s\r\n", cmd->params[1]);
743 if(cmd->len > 2)
744 sprintf(send + strlen(send) - 2, " :%s\r\n", cmd->params[2]);
745 tprintf(sock, send);
746 goto end_e;
747 case 'I':
748 case 'i':
749 if(searchlist(ignorel, cmd->params[1]) != -1) {
750 dellist(ignorel, cmd->params[1]);
751 printf("%s no more ignored. %d\n", cmd->params[1], searchlist(ignorel, cmd->params[1]));
752 } else {
753 addlist(ignorel, cmd->params[1]);
754 printf("%s will be ignored.\n", cmd->params[1]);
755 }
756 goto end_e;
757 default:
758 break;
759 }
760 }
761 if(cmd->len > 2) {
762 switch(cmd->params[0][1]) {
763 case 'M':
764 case 'm':
765 tprintf(sock, "PRIVMSG %s :%s\r\n", cmd->params[1], cmd->params[2]);
766 goto end_e;
767 case 'A':
768 case 'a':
769 tprintf(sock, "PRIVMSG %s :%sACTION %s\x01\r\n", "\x01", cmd->params[1], cmd->params[2]);
770 goto end_e;
771 case 'O':
772 case 'o':
773 if(cmd->len > 3)
774 tprintf(sock, "MODE %s %s %s\r\n", cmd->params[1], cmd->params[2], cmd->params[3]);
775 else
776 tprintf(sock, "MODE %s %s\r\n", cmd->params[1], cmd->params[2]);
777 goto end_e;
778 case 'C':
779 case 'c':
780 printf("%s(%s) ctcp %s\n", tmstmp, cmd->params[1], cmd->params[2]);
781 tprintf(sock, "PRIVMSG %s :\x01%s\x01\r\n", cmd->params[1], cmd->params[2]);
782 goto end_e;
783 case 'K':
784 case 'k':
785 sprintf(send, "KICK %s %s\r\n", cmd->params[1], cmd->params[2]);
786 if(cmd->len > 3)
787 sprintf(send + strlen(send) - 2, " :%s\r\n", cmd->params[3]);
788 tprintf(sock, send);
789 goto end_e;
790 default:
791 break;
792 }
793 }
794 }
795 if(lastchan != nil)
796 tprintf(sock, "PRIVMSG %s :%s\r\n", lastchan, rad);
797 else
798 printf(".");
799 end_e:
800 printf("%s\n", tmstmp);
801 free(tmstmp);
802 freecmd(cmd);
803 }
804
805 if(lastchan != nil)
806 free(lastchan);
807 free(send);
808 free(rad);
809 freecmd(ignorel);
810 freecmd(joinl);
811 if(tls)
812 SSL_CTX_free(ctx);
813
814 return 0;
815 }
816