2 irc.c - FireTalk IRC protocol driver
3 Copyright (C) 2000 Ian Gulliver
4 Copyright 2002-2006 Daniel Reed <n@ml.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of version 2 of the GNU General Public License as
8 published by the Free Software Foundation.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <sys/types.h>
24 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
36 #define ROOMSTARTS "#+&"
39 static char irc_tolower(const char c
) {
40 if ((c
>= 'A') && (c
<= 'Z'))
41 return((c
- 'A') + 'a');
51 static int irc_compare_nicks_int(const char *const nick1
, const char *const nick2
) {
54 while (nick1
[i
] != '\0') {
55 if (irc_tolower(nick1
[i
]) != irc_tolower(nick2
[i
]))
66 struct s_irc_whois
*next
;
74 typedef struct irc_conn_t
*client_t
;
75 #define _HAVE_CLIENT_T
76 #include "firetalk-int.h"
78 typedef struct irc_conn_t
{
84 int passchange
; /* whether we are currently changing our pass */
86 usesilence
:1, /* are we on a network that understands SILENCE */
87 identified
:1; /* are we we identified */
91 static const char *const irc_normalize_user_nick(const char *const name
) {
96 if (strchr(name
, '!') == NULL
)
99 for (i
= 0; (i
< sizeof(buf
)) && (name
[i
] != '!'); i
++)
105 static const char *const irc_normalize_user_mask(const char *const name
) {
109 if (strchr(name
, '!') != NULL
)
112 snprintf(buf
, sizeof(buf
), "%s!*@*", name
);
117 static void irc_disc_user_rem(irc_conn_t
*c
, const char *disc
, const char *name
) {
118 struct s_firetalk_handle
*fchandle
;
120 fchandle
= firetalk_find_handle(c
);
122 if (firetalk_user_visible_but(fchandle
, disc
, name
) == FE_NOMATCH
)
123 firetalk_callback_im_buddyonline(c
, name
, 0);
126 static void irc_disc_rem(irc_conn_t
*c
, const char *disc
) {
127 struct s_firetalk_handle
*fchandle
;
128 struct s_firetalk_room
*iter
;
129 struct s_firetalk_member
*mem
;
131 fchandle
= firetalk_find_handle(c
);
132 iter
= firetalk_find_room(fchandle
, disc
);
133 assert(iter
!= NULL
);
135 for (mem
= iter
->member_head
; mem
!= NULL
; mem
= mem
->next
)
136 irc_disc_user_rem(c
, disc
, mem
->nickname
);
139 static const char *irc_normalize_room_name(const char *const name
) {
140 static char newname
[2048];
142 if (strchr(ROOMSTARTS
, *name
))
144 snprintf(newname
, sizeof(newname
), "#%s", name
);
150 #include "firetalk.h"
155 extern void *curconn
;
156 extern void status_echof(void *conn
, const unsigned char *format
, ...);
158 static void irc_echof(irc_conn_t
*c
, const char *const where
, const char *const format
, ...) {
161 void statrefresh(void);
163 va_start(ap
, format
);
164 vsnprintf(buf
, sizeof(buf
), format
, ap
);
167 while (buf
[strlen(buf
)-1] == '\n')
168 buf
[strlen(buf
)-1] = 0;
170 status_echof(curconn
, firetalk_htmlentities(buf
));
171 // firetalk_callback_chat_getmessage(c, ":RAW", where, 0, buf);
177 static fte_t
irc_compare_nicks(const char *const nick1
, const char *const nick2
) {
178 if (irc_compare_nicks_int(nick1
, nick2
) == 0)
184 irc_isprint(const int c
) {
187 return(FE_INVALIDFORMAT
);
191 irc_isnickfirst(const int c
) {
192 return(isalpha(c
) || (c
== '[') || (c
== ']') || (c
== '\\') || (c
== '`') || (c
== '^') || (c
== '{') || (c
== '}'));
196 irc_isnick(const int c
) {
197 return(irc_isnickfirst(c
) || isdigit(c
) || (c
== '-'));
200 static char *irc_html_to_irc(const char *const string
) {
203 static char *output = NULL;
209 output = realloc(output, (l * 4) + 1);
214 assert(o < ((l * 4) + 1));
217 if (!strncasecmp(&string[i],"&",5)) {
220 } else if (!strncasecmp(&string[i],">",4)) {
223 } else if (!strncasecmp(&string[i],"<",4)) {
226 } else if (!strncasecmp(&string[i]," ",6)) {
230 output[o++] = string[i++];
233 if (!strncasecmp(&string[i],"<b>",3)) {
234 output[o++] = (char) 2;
236 } else if (!strncasecmp(&string[i],"</b>",4)) {
237 output[o++] = (char) 2;
239 } else if (!strncasecmp(&string[i],"<i>",3)) {
240 output[o++] = (char) 22;
242 } else if (!strncasecmp(&string[i],"</i>",4)) {
243 output[o++] = (char) 22;
245 } else if (!strncasecmp(&string[i],"<u>",3)) {
246 output[o++] = (char) 31;
248 } else if (!strncasecmp(&string[i],"</u>",4)) {
249 output[o++] = (char) 31;
251 } else if (!strncasecmp(&string[i],"<br>",4)) {
252 output[o++] = '\020';
254 output[o++] = '\020';
258 output[o++] = string[i++];
261 output[o++] = '\020';
266 output[o++] = '\020';
271 output[o++] = '\020';
272 output[o++] = '\020';
276 output[o++] = string[i++];
280 assert(o <= ((l * 4) + 1));
286 static const char *mIRCar
[] = {
287 "#FFFFFF", // 0 white
288 "#000000", // 1 black
289 "#0000FF", // 2 blue (navy)
290 "#00FF00", // 3 green
292 "#4E2F2F", // 5 brown (maroon)
293 "#AA00FF", // 6 purple
294 "#FF7700", // 7 orange (olive)
295 "#FFFF00", // 8 yellow
296 "#32CD32", // 9 lt.green (lime)
297 "#349F79", // 10 teal (a kinda green/blue cyan)
298 "#70DB93", // 11 lt.cyan (cyan ?) (aqua)
299 "#3333FF", // 12 lt.blue (royal)
300 "#FF00AA", // 13 pink (light purple) (fuchsia)
301 "#A8A8A8", // 14 grey
302 "#E6E8FA" // 15 lt.grey (silver)
305 static const char *ANSIar
[] = {
306 "#000000", // 30 black
308 "#00FF00", // 32 green
309 "#FFFF00", // 33 yellow
310 "#0000FF", // 34 blue
311 "#FF00FF", // 35 magenta (purple)
312 "#00FFFF", // 36 cyan (aqua)
313 "#FFFFFF", // 37 white
316 static const char *irc_mIRC_to_html(const char *const string
, size_t *pos
) {
319 for (i
= 0; (i
< 2) && isdigit(string
[*pos
]); i
++, (*pos
)++) {
321 col
+= string
[*pos
] - '0';
323 if (string
[*pos
] == ',') {
325 for (i
= 0; (i
< 2) && isdigit(string
[*pos
]); i
++, (*pos
)++)
328 if ((col
>= 0) && (col
<= 15))
334 static char *irc_irc_to_html(const char *const string
) {
337 static char *output = NULL;
340 int infont = 0, inbold = 0, initalics = 0, inunderline = 0;
342 assert(string != NULL);
346 s = l*(sizeof("<font color=\"#RRGGBB\">")-1) + 1;
347 output = realloc(output, s);
355 memcpy(&output[o],"</B>",4);
359 memcpy(&output[o],"<B>",3);
365 if (isdigit(string[i+1])) {
367 sprintf(output+o, "<font color=\"%s\">", irc_mIRC_to_html(string, &i));
368 o += sizeof("<font color=\"#RRGGBB\">")-1;
371 } else if (infont == 1) {
372 strcpy(output+o, "</font>");
373 o += sizeof("</font>")-1;
379 strcpy(output+o, "</font>");
380 o += sizeof("</font>")-1;
384 strcpy(output+o, "</B>");
385 o += sizeof("</B>")-1;
388 if (initalics == 1) {
389 strcpy(output+o, "</I>");
390 o += sizeof("</I>")-1;
393 if (inunderline == 1) {
394 strcpy(output+o, "</U>");
395 o += sizeof("</U>")-1;
400 if (string[i+1] == '[') {
402 while ((string[i] != 0) && (string[i] != 'm')) {
405 if (!isdigit(string[i]))
407 while (isdigit(string[i])) {
409 num += string[i] - '0';
412 if (string[i] == ';')
417 strcpy(output+o, "</font>");
418 o += sizeof("</font>")-1;
422 strcpy(output+o, "</B>");
423 o += sizeof("</B>")-1;
426 if (initalics == 1) {
427 strcpy(output+o, "</I>");
428 o += sizeof("</I>")-1;
431 if (inunderline == 1) {
432 strcpy(output+o, "</U>");
433 o += sizeof("</U>")-1;
439 strcpy(output+o, "<B>");
440 o += sizeof("<B>")-1;
446 strcpy(output+o, "</B>");
447 o += sizeof("</B>")-1;
452 if (inunderline == 0) {
453 strcpy(output+o, "<U>");
454 o += sizeof("<U>")-1;
459 if (initalics == 0) {
460 strcpy(output+o, "<I>");
461 o += sizeof("<I>")-1;
465 case 30: case 31: case 32: case 33:
466 case 34: case 35: case 36: case 37:
467 sprintf(output+o, "<font color=\"%s\">", ANSIar[num-30]);
468 o += sizeof("<font color=\"#RRGGBB\">")-1;
478 if (initalics == 1) {
479 memcpy(&output[o],"</I>",4);
483 memcpy(&output[o],"<I>",3);
489 if (inunderline == 1) {
490 memcpy(&output[o],"</U>",4);
494 memcpy(&output[o],"<U>",3);
500 memcpy(&output[o],"&",5);
504 memcpy(&output[o],"<",4);
508 memcpy(&output[o],">",4);
512 switch(string[++i]) {
514 output[o++] = '\020';
517 if (string[i+1] == '\020' && string[i+2] == 'n') {
519 memcpy(&output[o],"<br>",4);
528 output[o++] = string[i];
533 if (string[i+1] == ' ') {
534 memcpy(&output[o], " ", 6);
539 output[o++] = string[i];
550 static int irc_internal_disconnect(irc_conn_t
*c
, const int error
) {
551 struct s_irc_whois
*whois_iter
, *whois_iter2
;
553 if (c
->nickname
!= NULL
) {
557 if (c
->password
!= NULL
) {
561 for (whois_iter
= c
->whois_head
; whois_iter
!= NULL
; whois_iter
= whois_iter2
) {
562 whois_iter2
= whois_iter
->next
;
563 if (whois_iter
->nickname
!= NULL
) {
564 free(whois_iter
->nickname
);
565 whois_iter
->nickname
= NULL
;
567 if (whois_iter
->info
!= NULL
) {
568 free(whois_iter
->info
);
569 whois_iter
->info
= NULL
;
573 c
->whois_head
= NULL
;
579 firetalk_callback_disconnect(c
, error
);
584 static int irc_send_printf(irc_conn_t
*c
, const char *const format
, ...) {
590 va_start(ap
, format
);
591 for (i
= 0; format
[i
] != 0; i
++) {
592 if (format
[i
] == '%') {
593 switch (format
[++i
]) {
604 *s
= irc_html_to_irc(va_arg(ap
, char *));
605 size_t slen
= strlen(s
);
607 if ((datai
+slen
) > (sizeof(data
)-2-1))
608 return(FE_PACKETSIZE
);
609 strcpy(data
+datai
, s
);
618 data
[datai
++] = format
[i
];
619 if (datai
> (sizeof(data
)-2-1))
620 return(FE_PACKETSIZE
);
627 irc_echof(c
, "send_printf", "%s", data
);
630 strcpy(data
+datai
, "\r\n");
634 struct s_firetalk_handle
637 fchandle
= firetalk_find_handle(c
);
638 firetalk_internal_send_data(fchandle
, data
, datai
);
644 static char **irc_recv_parse(irc_conn_t
*c
, unsigned char *buffer
, unsigned short *bufferpos
) {
645 static char *args
[256];
646 static char data
[513];
653 assert(*bufferpos
< sizeof(data
));
654 memcpy(data
, buffer
, *bufferpos
);
655 data
[*bufferpos
] = '\0';
657 tempchr
= strchr(data
, '\n');
660 if ((tempchr
> data
) && (tempchr
[-1] == '\r'))
664 *bufferpos
-= (tempchr
- data
+ 1);
665 memmove(buffer
, &buffer
[tempchr
- data
+ 1], *bufferpos
);
668 irc_echof(c
, "recv_parse", "%s", data
);
676 args
[curarg
++] = ":SERVER";
678 while ((curarg
< sizeof(args
)/sizeof(*args
)) && ((tempchr2
= strchr(tempchr
, ' ')) != NULL
)) {
679 args
[curarg
++] = tempchr
;
681 tempchr
= tempchr2
+ 1;
682 if (*tempchr
== ':') {
687 args
[curarg
++] = tempchr
;
692 static char *irc_get_nickname(const char *const hostmask
) {
693 static char data
[512];
696 strncpy(data
, hostmask
, sizeof(data
)-1);
697 data
[sizeof(data
)-1] = 0;
699 if ((tempchr
= strchr(data
, '!')) != NULL
)
705 irc_set_nickname(irc_conn_t
*c
, const char *const nickname
) {
706 return(irc_send_printf(c
,"NICK %s",nickname
));
710 irc_set_password(irc_conn_t
*c
, const char *const oldpass
, const char *const newpass
) {
712 return(irc_send_printf(c
,"PRIVMSG NickServ :SET PASSWORD %s",newpass
));
716 irc_destroy_handle(irc_conn_t
*c
) {
717 irc_send_printf(c
,"QUIT :Handle destroyed");
718 irc_internal_disconnect(c
,FE_USERDISCONNECT
);
723 irc_disconnect(irc_conn_t
*c
) {
724 irc_send_printf(c
,"QUIT :User disconnected");
725 return(irc_internal_disconnect(c
,FE_USERDISCONNECT
));
729 *irc_create_handle(void) {
732 c
= calloc(1, sizeof(*c
));
740 #if defined(HAVE_GETPWUID) && defined(HAVE_GETUID)
745 irc_signon(irc_conn_t
*c
, const char *const nickname
) {
750 firetalk_callback_needpass(c
, pass
, sizeof(pass
));
753 if(irc_send_printf(c
, "PASS %s", pass
) != FE_SUCCESS
)
756 #if defined(HAVE_GETPWUID) && defined(HAVE_GETUID)
757 struct passwd
*pw
= getpwuid(getuid());
761 for (i
= 0; (pw
->pw_gecos
[i
] != 0) && (pw
->pw_gecos
[i
] != ',') && (i
< sizeof(buf
)-1); i
++)
762 buf
[i
] = pw
->pw_gecos
[i
];
764 snprintf(buf
, sizeof(buf
), "http://www.centerim.org user");
769 if (irc_send_printf(c
, "USER %s %s %s :%s", pw
->pw_name
, nickname
, nickname
, buf
) != FE_SUCCESS
)
772 if (irc_send_printf(c
, "USER %s %s %s :%s", nickname
, nickname
, nickname
, nickname
) != FE_SUCCESS
)
776 if (irc_send_printf(c
, "NICK %s", nickname
) != FE_SUCCESS
)
780 c
->nickname
= strdup(nickname
);
781 if (c
->nickname
== NULL
)
788 irc_preselect(irc_conn_t
*c
, fd_set
*read
, fd_set
*write
, fd_set
*except
, int *n
) {
793 irc_postselect(irc_conn_t
*c
, fd_set
*read
, fd_set
*write
, fd_set
*except
) {
797 static void irc_addwhois(irc_conn_t
*c
, const char *const name
, const char *const format
, ...) {
798 struct s_irc_whois
*whoisiter
;
802 va_start(msg
, format
);
803 vsnprintf(buf
, sizeof(buf
), format
, msg
);
806 for (whoisiter
= c
->whois_head
; whoisiter
!= NULL
; whoisiter
= whoisiter
->next
)
807 if (irc_compare_nicks(name
, whoisiter
->nickname
) == 0) {
808 int len
= whoisiter
->info
?strlen(whoisiter
->info
):0;
810 whoisiter
->info
= realloc(whoisiter
->info
, len
+strlen(buf
)+1);
811 if (whoisiter
->info
== NULL
)
813 strcpy(whoisiter
->info
+len
, buf
);
818 static fte_t
irc_got_data_parse(irc_conn_t
*c
, char **args
) {
825 static unsigned int inwhois
= 0;
830 if (strcmp(args
[1], "PING") == 0) {
831 if (args
[2] != NULL
) {
832 if (irc_send_printf(c
, "PONG :%s", args
[2]) != 0) {
833 irc_internal_disconnect(c
, FE_PACKET
);
837 if (irc_send_printf(c
, "PONG") != 0) {
838 irc_internal_disconnect(c
, FE_PACKET
);
842 } else if (strcmp(args
[1], "QUIT") == 0) {
843 const char *name
= irc_get_nickname(args
[0]);
845 firetalk_callback_im_buddyonline(c
, name
, 0);
846 if (irc_compare_nicks(c
->nickname
, name
) == 0)
847 irc_internal_disconnect(c
, FE_DISCONNECT
);
849 firetalk_callback_chat_user_quit(c
, name
, irc_irc_to_html(args
[2]));
856 numeric
= atoi(args
[1]);
862 if (strcmp(args
[1], "JOIN") == 0) {
863 const char *name
= irc_get_nickname(args
[0]);
865 firetalk_callback_im_buddyonline(c
, name
, 1);
866 if (irc_compare_nicks(c
->nickname
, name
) == 0) {
867 firetalk_callback_chat_joined(c
, args
[2]);
869 if (c
->identified
== 1) {
870 if (irc_send_printf(c
, "PRIVMSG ChanServ :OP %s %s", args
[2], c
->nickname
) != FE_SUCCESS
) {
871 irc_internal_disconnect(c
, FE_PACKET
);
876 char *extra
= strchr(args
[0], '!');
878 firetalk_callback_chat_user_joined(c
, args
[2], name
, (extra
!= NULL
)?(extra
+1):NULL
);
880 } else if (strcmp(args
[1], "PART") == 0) {
881 const char *name
= irc_get_nickname(args
[0]);
883 if (irc_compare_nicks(c
->nickname
, name
) == 0) {
884 irc_disc_rem(c
, args
[2]);
885 firetalk_callback_chat_left(c
, args
[2]);
887 irc_disc_user_rem(c
, args
[2], name
);
888 firetalk_callback_chat_user_left(c
, args
[2], name
, (args
[3] != NULL
)?irc_irc_to_html(args
[3]):NULL
);
890 } else if (strcmp(args
[1],"NICK") == 0) {
891 const char *name
= irc_get_nickname(args
[0]);
893 if (irc_compare_nicks(c
->nickname
, name
) == 0) {
895 c
->nickname
= strdup(args
[2]);
896 if (c
->nickname
== NULL
)
898 firetalk_callback_newnick(c
, c
->nickname
);
900 firetalk_callback_user_nickchanged(c
, name
, args
[2]);
911 if (strcmp(args
[1],"PRIVMSG") == 0) {
913 while ((tempchr
= strchr(args
[3], 1))) {
916 if ((sp
= strchr(tempchr
+1, 1))) {
920 if (strchr(ROOMSTARTS
, args
[2][0])) {
921 /* chat room subcode */
922 if (strncasecmp(&tempchr
[1],"ACTION ",7) == 0)
923 firetalk_callback_chat_getaction(c
, args
[2], irc_get_nickname(args
[0]), 0, irc_irc_to_html(tempchr
+8));
927 endcommand
= strchr(&tempchr
[1], ' ');
931 firetalk_callback_subcode_request(c
, irc_get_nickname(args
[0]), &tempchr
[1], endcommand
);
933 firetalk_callback_subcode_request(c
, irc_get_nickname(args
[0]), &tempchr
[1], NULL
);
935 memmove(tempchr
, sp
+1, strlen(sp
+1) + 1);
939 if (args
[3][0] != '\0') {
940 if (strchr(ROOMSTARTS
, args
[2][0]))
941 firetalk_callback_chat_getmessage(c
, args
[2], irc_get_nickname(args
[0]), 0, irc_irc_to_html(args
[3]));
943 firetalk_callback_im_getmessage(c
, irc_get_nickname(args
[0]), 0, irc_irc_to_html(args
[3]));
945 } else if (strcmp(args
[1],"NOTICE") == 0) {
946 const char *name
= irc_get_nickname(args
[0]);
948 /* scan for CTCP's */
949 while ((tempchr
= strchr(args
[3], 1))) {
952 if ((sp
= strchr(tempchr
+1, 1)))
955 if (strchr(ROOMSTARTS
, args
[2][0]) == NULL
) {
957 endcommand
= strchr(&tempchr
[1], ' ');
961 firetalk_callback_subcode_reply(c
, name
, &tempchr
[1], endcommand
);
963 firetalk_callback_subcode_reply(c
, name
, &tempchr
[1], NULL
);
966 memcpy(tempchr
, sp
+1, strlen(sp
+1) + 1);
970 if (strcasecmp(name
, "NickServ") == 0) {
971 if ((strstr(args
[3],"IDENTIFY") != NULL
) && (strstr(args
[3],"/msg") != NULL
) && (strstr(args
[3],"HELP") == NULL
)) {
973 /* nickserv seems to be asking us to identify ourselves, and we have a password */
975 c
->password
= calloc(1, 128);
976 if (c
->password
== NULL
)
978 firetalk_callback_needpass(c
,c
->password
,128);
980 if ((c
->password
!= NULL
) && irc_send_printf(c
,"PRIVMSG NickServ :IDENTIFY %s",c
->password
) != 0) {
981 irc_internal_disconnect(c
,FE_PACKET
);
984 } else if ((strstr(args
[3],"Password changed") != NULL
) && (c
->passchange
!= 0)) {
985 /* successful change */
987 firetalk_callback_passchanged(c
);
988 } else if ((strstr(args
[3],"authentication required") != NULL
) && (c
->passchange
!= 0)) {
989 /* didn't log in with the right password initially, not happening */
992 firetalk_callback_error(c
,FE_NOCHANGEPASS
,NULL
,args
[3]);
993 } else if ((strstr(args
[3],"isn't registered") != NULL
) && (c
->passchange
!= 0)) {
994 /* nick not registered, fail */
996 firetalk_callback_error(c
,FE_NOCHANGEPASS
,NULL
,args
[3]);
997 } else if (strstr(args
[3],"Password accepted") != NULL
) {
998 /* we're recognized */
1000 if (irc_send_printf(c
,"PRIVMSG ChanServ :OP ALL") != FE_SUCCESS
) {
1001 irc_internal_disconnect(c
,FE_PACKET
);
1004 } else if (strchr(ROOMSTARTS
, args
[2][0]))
1005 firetalk_callback_chat_getmessage(c
, args
[2], name
, 1, irc_irc_to_html(args
[3]));
1007 firetalk_callback_im_getmessage(c
, name
, 1, irc_irc_to_html(args
[3]));
1008 } else if (strchr(name
, '.') != NULL
) {
1009 if (strncmp(args
[3], "*** Notice -- ", sizeof("*** Notice -- ")-1) == 0)
1010 firetalk_callback_chat_getmessage(c
, ":RAW", name
, 1, irc_irc_to_html(args
[3]+sizeof("*** Notice -- ")-1));
1012 firetalk_callback_chat_getmessage(c
, ":RAW", name
, 1, irc_irc_to_html(args
[3]));
1013 } else if (args
[3][0] != '\0') {
1014 if (strchr(ROOMSTARTS
, args
[2][0]))
1015 firetalk_callback_chat_getmessage(c
, args
[2], name
, 1, irc_irc_to_html(args
[3]));
1017 firetalk_callback_im_getmessage(c
, name
, 1, irc_irc_to_html(args
[3]));
1019 } else if (strcmp(args
[1], "TOPIC") == 0) {
1020 firetalk_callback_chat_gottopic(c
, args
[2], irc_irc_to_html(args
[3]), irc_get_nickname(args
[0]));
1021 } else if (strcmp(args
[1], "KICK") == 0) {
1022 const char *name
= irc_get_nickname(args
[3]);
1024 if (irc_compare_nicks(c
->nickname
, name
) == 0) {
1025 irc_disc_rem(c
, args
[2]);
1026 firetalk_callback_chat_kicked(c
, args
[2], irc_get_nickname(args
[0]), irc_irc_to_html(args
[4]));
1028 irc_disc_user_rem(c
, args
[2], name
);
1030 tempchr
= strdup(name
);
1031 if (tempchr
== NULL
)
1033 firetalk_callback_chat_user_kicked(c
, args
[2], tempchr
, irc_get_nickname(args
[0]), irc_irc_to_html(args
[4]));
1043 if (numeric
== 311) // RPL_WHOISUSER
1045 else if (numeric
== 318)// RPL_ENDOFWHOIS
1050 case 333: /* :PREFIX 333 sn channel topicsetuser topicsettime */
1051 case 306: /* :PREFIX 306 sn :You have been marked as being away */
1052 case 305: /* :PREFIX 305 sn :You are no longer marked as being away */
1053 case 396: /* :PREFIX 396 sn whoishost :is now your hidden host */
1059 case 366: /* :PREFIX 366 sn channel :End of /NAMES list. */
1060 firetalk_callback_chat_user_joined(c
, args
[3], NULL
, NULL
);
1062 case 301: /* RPL_AWAY */
1064 case 313: /* RPL_WHOISOPER */
1065 for (whoisiter
= c
->whois_head
; whoisiter
!= NULL
; whoisiter
= whoisiter
->next
)
1066 if (irc_compare_nicks(args
[3],whoisiter
->nickname
) == 0) {
1067 whoisiter
->flags
|= FF_ADMIN
;
1071 case 318: /* RPL_ENDOFWHOIS */
1073 for (whoisiter
= c
->whois_head
; whoisiter
!= NULL
; whoisiter
= whoisiter
->next
) {
1074 if (irc_compare_nicks(args
[3], whoisiter
->nickname
) == 0) {
1076 firetalk_callback_gotinfo(c
, whoisiter
->nickname
, whoisiter
->info
, 0, whoisiter
->online
, whoisiter
->idle
, whoisiter
->flags
);
1077 free(whoisiter
->nickname
);
1078 whoisiter
->nickname
= NULL
;
1079 if (whoisiter
->info
!= NULL
) {
1080 free(whoisiter
->info
);
1081 whoisiter
->info
= NULL
;
1084 whoisiter2
->next
= whoisiter
->next
;
1086 c
->whois_head
= whoisiter
->next
;
1091 whoisiter2
= whoisiter
;
1094 case 401: /* ERR_NOSUCHNICK */
1095 firetalk_callback_im_buddyonline(c
, args
[3], 0);
1096 case 441: /* ERR_USERNOTINCHANNEL */
1097 case 443: /* ERR_USERONCHANNEL */
1098 if (!strcasecmp(args
[3], "NickServ") && c
->passchange
)
1101 for (whoisiter
= c
->whois_head
; whoisiter
!= NULL
; whoisiter
= whoisiter
->next
) {
1102 if (irc_compare_nicks(args
[3], whoisiter
->nickname
) == 0) {
1103 free(whoisiter
->nickname
);
1104 whoisiter
->nickname
= NULL
;
1105 if (whoisiter
->info
!= NULL
) {
1106 free(whoisiter
->info
);
1107 whoisiter
->info
= NULL
;
1110 whoisiter2
->next
= whoisiter
->next
;
1112 c
->whois_head
= whoisiter
->next
;
1115 firetalk_callback_error(c
, FE_BADUSER
, args
[3], args
[4]);
1118 whoisiter2
= whoisiter
;
1121 case 403: /* ERR_NOSUCHCHANNEL */
1122 case 442: /* ERR_NOTONCHANNEL */
1123 firetalk_callback_error(c
, FE_BADROOM
, args
[3], args
[4]);
1125 case 404: /* ERR_CANNOTSENDTOCHAN */
1126 case 405: /* ERR_TOOMANYCHANNELS */
1127 case 471: /* ERR_CHANNELISFULL */
1128 case 473: /* ERR_INVITEONLYCHAN */
1129 case 474: /* ERR_BANNEDFROMCHAN */
1130 case 475: /* ERR_BADCHANNELKEY */
1131 firetalk_callback_error(c
, FE_ROOMUNAVAILABLE
, args
[3], args
[4]);
1133 case 412: /* ERR_NOTEXTTOSEND */
1134 firetalk_callback_error(c
, FE_BADMESSAGE
, NULL
, args
[4]);
1136 case 421: /* ERR_UNKNOWNCOMMAND */
1137 if (strcmp(args
[3], "SILENCE") == 0)
1141 case 433: /* ERR_NICKNAMEINUSE */
1142 firetalk_callback_error(c
, FE_BADUSER
, NULL
, "Nickname in use.");
1144 case 482: /* ERR_CHANOPRIVSNEEDED */
1145 firetalk_callback_error(c
, FE_NOPERMS
, args
[3], "You need to be a channel operator.");
1154 if (args
[4] == NULL
)
1157 if (strcmp(args
[1],"MODE") == 0) {
1159 *source
= irc_get_nickname(args
[0]);
1167 strcpy(buf
, args
[3]);
1168 for (i
= 4; args
[i
] != NULL
; i
++) {
1170 strcat(buf
, args
[i
]);
1172 firetalk_callback_chat_modechanged(c
, args
[2], buf
, source
);
1175 while ((args
[arg
] != NULL
) && (args
[3][loc
] != '\0')) {
1176 switch (args
[3][loc
++]) {
1185 firetalk_callback_chat_user_opped(c
, args
[2], args
[arg
++], source
);
1186 if (irc_compare_nicks(args
[arg
-1], c
->nickname
) == FE_SUCCESS
)
1187 firetalk_callback_chat_opped(c
, args
[2], source
);
1188 } else if (dir
== -1) {
1189 firetalk_callback_chat_user_deopped(c
, args
[2], args
[arg
++], source
);
1190 if (irc_compare_nicks(args
[arg
-1], c
->nickname
) == FE_SUCCESS
) {
1191 firetalk_callback_chat_deopped(c
, args
[2], source
);
1192 if (c
->identified
== 1) {
1193 /* this is us, and we're identified, so we can request a reop */
1194 if (irc_send_printf(c
,"PRIVMSG ChanServ :OP %s %s",args
[2],c
->nickname
) != FE_SUCCESS
) {
1195 irc_internal_disconnect(c
,FE_PACKET
);
1204 firetalk_callback_chat_keychanged(c
, args
[2], args
[arg
], source
);
1206 firetalk_callback_chat_keychanged(c
, args
[2], NULL
, source
);
1223 case 317: /* RPL_WHOISIDLE */
1224 for (whoisiter
= c
->whois_head
; whoisiter
!= NULL
; whoisiter
= whoisiter
->next
)
1225 if (irc_compare_nicks(args
[3], whoisiter
->nickname
) == 0) {
1226 whoisiter
->online
= atol(args
[5]);
1227 whoisiter
->idle
= atol(args
[4])/60;
1230 case 312: /* RPL_WHOISSERVER */
1231 irc_addwhois(c
, args
[3], "<br>On server %s (%s)", args
[4], irc_irc_to_html(args
[5]));
1233 case 319: /* RPL_WHOISCHANNELS */
1234 irc_addwhois(c
, args
[3], "<br>On channels %s", irc_irc_to_html(args
[4]));
1236 case 332: /* RPL_TOPIC */
1237 firetalk_callback_chat_gottopic(c
, args
[3], irc_irc_to_html(args
[4]), NULL
);
1246 if (args
[5] == NULL
)
1249 if (numeric
== 353) { /* RPL_NAMREPLY */
1250 char *str
= args
[5];
1252 while ((str
!= NULL
) && (*str
!= 0)) {
1257 if ((sp
= strchr(str
, ' ')) != NULL
)
1260 while ((*str
!= 0) && !irc_isnickfirst(*str
)) {
1263 else if (*str
== '+')
1268 firetalk_callback_chat_user_joined(c
, args
[4], str
, NULL
);
1269 firetalk_callback_im_buddyonline(c
, str
, 1);
1271 firetalk_callback_chat_user_opped(c
, args
[4], str
, NULL
);
1272 if (irc_compare_nicks(str
, c
->nickname
) == FE_SUCCESS
)
1273 firetalk_callback_chat_opped(c
, args
[4], NULL
);
1284 if ((args
[6] == NULL
) || (args
[7] == NULL
))
1287 if (numeric
== 311) { /* RPL_WHOISUSER */
1288 char *gecos
= irc_irc_to_html(args
[7]);
1291 for (i
= 0; gecos
[i
] != 0; i
++)
1292 if (strncasecmp(gecos
+i
, "<HTML>", sizeof("<HTML>")-1) == 0)
1295 irc_addwhois(c
, args
[3], "%s@%s (%s)", args
[4], args
[5], firetalk_htmlentities(gecos
));
1297 irc_addwhois(c
, args
[3], "%s@%s (<HTML>%s</HTML>)", args
[4], args
[5], gecos
);
1299 } else if (numeric
== 352) { /* WHO output */
1300 char buf
[1024], buf2
[1024];
1303 snprintf(buf
, sizeof(buf
), "%s %s %s %s", args
[7], args
[4], args
[5], args
[6]);
1304 for (i
= 8; args
[i
] != NULL
; i
++)
1306 snprintf(buf2
, sizeof(buf2
), ", %s", args
[i
]);
1307 strncat(buf
, buf2
, sizeof(buf
)-strlen(buf
)-1);
1309 firetalk_callback_chat_getmessage(c
, ":RAW", args
[3], 0, buf
);
1318 snprintf(buf
, sizeof(buf
), "<br>%s", args
[3]);
1319 for (i
= numeric
?4:3; args
[i
+1] != NULL
; i
++)
1321 snprintf(buf
+strlen(buf
), sizeof(buf
)-strlen(buf
), " %s", args
[i
]);
1322 for (i
= numeric
?4:3; args
[i
+1] != NULL
; i
++)
1323 snprintf(buf
+strlen(buf
), sizeof(buf
)-strlen(buf
), " %s", args
[i
]);
1324 snprintf(buf
+strlen(buf
), sizeof(buf
)-strlen(buf
), " (%s)", args
[1]);
1325 irc_addwhois(c
, args
[3], "%s", buf
);
1327 char buf
[1024], buf2
[1024];
1331 for (i
= 1; args
[i
+1] != NULL
; i
++)
1333 snprintf(buf2
, sizeof(buf2
), "<B>%s</B>, ", args
[i
]);
1334 strncat(buf
, buf2
, sizeof(buf
)-strlen(buf
)-1);
1336 strncat(buf
, args
[i
], sizeof(buf
)-strlen(buf
)-1);
1337 firetalk_callback_chat_getmessage(c
, ":RAW", irc_get_nickname(args
[0]), 0, buf
);
1345 irc_got_data(irc_conn_t
*c
, unsigned char *buffer
, unsigned short *bufferpos
) {
1348 while (((args
= irc_recv_parse(c
, buffer
, bufferpos
)) != NULL
) && (args
[1] != NULL
)) {
1351 if ((fte
= irc_got_data_parse(c
, args
)) != FE_SUCCESS
)
1359 irc_got_data_connecting(irc_conn_t
*c
, unsigned char *buffer
, unsigned short *bufferpos
) {
1362 while (((args
= irc_recv_parse(c
, buffer
, bufferpos
)) != NULL
) && (args
[1] != NULL
)) {
1363 if (strcmp(args
[1], "ERROR") == 0) {
1364 irc_send_printf(c
,"QUIT :error");
1365 if (args
[2] == NULL
)
1366 firetalk_callback_connectfailed(c
, FE_PACKET
, "Server returned ERROR");
1368 firetalk_callback_connectfailed(c
, FE_PACKET
, args
[2]);
1371 switch (atoi(args
[1])) {
1372 case 1: /* :PREFIX 001 sn :Welcome message */
1373 if (strcmp(c
->nickname
, args
[2]) != 0) {
1374 firetalk_callback_user_nickchanged(c
, c
->nickname
, args
[2]);
1376 c
->nickname
= strdup(args
[2]);
1377 if (c
->nickname
== NULL
)
1379 firetalk_callback_newnick(c
, args
[2]);
1382 case 376: /* End of MOTD */
1383 case 422: /* MOTD is missing */
1384 firetalk_callback_doinit(c
,c
->nickname
);
1385 firetalk_callback_connected(c
);
1391 irc_send_printf(c
,"QUIT :Invalid nickname");
1392 firetalk_callback_connectfailed(c
,FE_BADUSER
,"Invalid nickname");
1395 irc_send_printf(c
,"QUIT :Invalid nickname");
1396 firetalk_callback_connectfailed(c
,FE_BADUSER
,"Nickname in use");
1399 irc_send_printf(c
,"QUIT :banned");
1400 firetalk_callback_connectfailed(c
,FE_BLOCKED
,"You are banned");
1405 if ((fte
= irc_got_data_parse(c
, args
)) != FE_SUCCESS
)
1416 static fte_t
irc_chat_join(irc_conn_t
*c
, const char *const room
) {
1417 return(irc_send_printf(c
,"JOIN %s",room
));
1420 static fte_t
irc_chat_part(irc_conn_t
*c
, const char *const room
) {
1421 return(irc_send_printf(c
,"PART %s",room
));
1424 static fte_t
irc_chat_send_message(irc_conn_t
*c
, const char *const room
, const char *const message
, const int auto_flag
) {
1425 if (strcasecmp(room
, ":RAW") == 0)
1426 return(irc_send_printf(c
, "%s", message
));
1429 irc_echof(c
, "chat_send_message", "c=%#p, room=%#p \"%s\", message=%#p \"%s\", auto_flag=%i\n", c
, room
, room
, message
, message
, auto_flag
);
1433 return(irc_send_printf(c
, "NOTICE %s :%s", room
, message
));
1435 return(irc_send_printf(c
, "PRIVMSG %s :%s", room
, message
));
1438 static fte_t
irc_chat_send_action(irc_conn_t
*c
, const char *const room
, const char *const message
, const int auto_flag
) {
1439 return(irc_send_printf(c
,"PRIVMSG %s :\001ACTION %s\001",room
,message
));
1442 static fte_t
irc_chat_invite(irc_conn_t
*c
, const char *const room
, const char *const who
, const char *const message
) {
1443 return(irc_send_printf(c
,"INVITE %s %s",who
,room
));
1446 static fte_t
irc_im_send_message(irc_conn_t
*c
, const char *const dest
, const char *const message
, const int auto_flag
) {
1447 struct s_firetalk_handle
*fchandle
;
1450 if (strcasecmp(dest
, ":RAW") == 0)
1451 return(irc_send_printf(c
, "%s", message
));
1453 fchandle
= firetalk_find_handle(c
);
1455 snprintf(buf
, sizeof(buf
), "NOTICE %s :%s", dest
, message
);
1456 firetalk_queue_append(buf
, sizeof(buf
), &(fchandle
->subcode_replies
), dest
);
1458 snprintf(buf
, sizeof(buf
), "PRIVMSG %s :%s", dest
, message
);
1459 firetalk_queue_append(buf
, sizeof(buf
), &(fchandle
->subcode_requests
), dest
);
1461 return(irc_send_printf(c
, "%s", buf
));
1464 static fte_t
irc_im_send_action(irc_conn_t
*c
, const char *const dest
, const char *const message
, const int auto_flag
) {
1465 return(irc_send_printf(c
, "PRIVMSG %s :\001ACTION %s\001", dest
, message
));
1468 static fte_t
irc_chat_set_topic(irc_conn_t
*c
, const char *const room
, const char *const topic
) {
1469 return(irc_send_printf(c
,"TOPIC %s :%s",room
,topic
));
1472 static fte_t
irc_chat_op(irc_conn_t
*c
, const char *const room
, const char *const who
) {
1473 return(irc_send_printf(c
,"MODE %s +o %s",room
,who
));
1476 static fte_t
irc_chat_deop(irc_conn_t
*c
, const char *const room
, const char *const who
) {
1477 return(irc_send_printf(c
,"MODE %s -o %s",room
,who
));
1480 static fte_t
irc_chat_kick(irc_conn_t
*c
, const char *const room
, const char *const who
, const char *const reason
) {
1482 return(irc_send_printf(c
,"KICK %s %s :%s",room
,who
,reason
));
1484 return(irc_send_printf(c
,"KICK %s %s",room
,who
));
1487 static fte_t
irc_chat_requestextended(client_t c
, const char * const room
) {
1488 return (irc_send_printf(c
,"WHO %s", room
));
1491 static fte_t
irc_im_add_buddy(irc_conn_t
*c
, const char *const name
, const char *const group
, const char *const friendly
) {
1492 struct s_firetalk_handle
*fchandle
;
1494 fchandle
= firetalk_find_handle(c
);
1496 if (firetalk_user_visible(fchandle
, name
) == FE_SUCCESS
)
1497 firetalk_callback_im_buddyonline(c
, name
, 1);
1501 static fte_t
irc_im_remove_buddy(irc_conn_t
*c
, const char *const name
, const char *const group
) {
1502 firetalk_callback_im_buddyonline(c
, name
, 0);
1507 static fte_t
irc_im_add_deny(irc_conn_t
*c
, const char *const nickname
) {
1508 if (c
->usesilence
== 1)
1509 return(irc_send_printf(c
,"SILENCE +%s!*@*",nickname
));
1514 static fte_t
irc_im_remove_deny(irc_conn_t
*c
, const char *const nickname
) {
1515 if (c
->usesilence
== 1)
1516 return(irc_send_printf(c
,"SILENCE -%s!*@*",nickname
));
1521 static fte_t
irc_im_upload_buddies(irc_conn_t
*c
) {
1525 static fte_t
irc_im_upload_denies(irc_conn_t
*c
) {
1529 static fte_t
irc_im_evil(irc_conn_t
*c
, const char *const who
) {
1533 static fte_t
irc_set_privacy(client_t c
, const char *const mode
) {
1537 static fte_t
irc_get_info(irc_conn_t
*c
, const char *const nickname
) {
1538 struct s_irc_whois
*whoistemp
;
1540 whoistemp
= c
->whois_head
;
1541 c
->whois_head
= calloc(1, sizeof(struct s_irc_whois
));
1542 if (c
->whois_head
== NULL
)
1544 c
->whois_head
->nickname
= strdup(nickname
);
1545 if (c
->whois_head
->nickname
== NULL
)
1547 c
->whois_head
->flags
= 0;
1548 c
->whois_head
->online
= 0;
1549 c
->whois_head
->idle
= 0;
1550 c
->whois_head
->info
= NULL
;
1551 c
->whois_head
->next
= whoistemp
;
1552 return(irc_send_printf(c
, "WHOIS %s", nickname
));
1556 irc_set_info(irc_conn_t
*c
, const char *const info
) {
1561 irc_set_away(irc_conn_t
*c
, const char *const message
, const int auto_flag
) {
1563 return(irc_send_printf(c
,"AWAY :%s",message
));
1565 return(irc_send_printf(c
,"AWAY"));
1568 static fte_t
irc_periodic(struct s_firetalk_handle
*const conn
) {
1573 static fte_t
irc_subcode_send_request(irc_conn_t
*c
, const char *const to
, const char *const command
, const char *const args
) {
1575 if (irc_send_printf(c
,"PRIVMSG %s :\001%s\001",to
,command
) != 0) {
1576 irc_internal_disconnect(c
,FE_PACKET
);
1580 if (irc_send_printf(c
,"PRIVMSG %s :\001%s %s\001",to
,command
,args
) != 0) {
1581 irc_internal_disconnect(c
,FE_PACKET
);
1588 static fte_t
irc_subcode_send_reply(irc_conn_t
*c
, const char *const to
, const char *const command
, const char *const args
) {
1593 if (irc_send_printf(c
,"NOTICE %s :\001%s\001",to
,command
) != 0) {
1594 irc_internal_disconnect(c
,FE_PACKET
);
1598 if (irc_send_printf(c
,"NOTICE %s :\001%s %s\001",to
,command
,args
) != 0) {
1599 irc_internal_disconnect(c
,FE_PACKET
);
1607 static char *irc_ctcp_encode(irc_conn_t
*c
, const char *const command
, const char *const message
) {
1610 if (message
!= NULL
) {
1611 str
= malloc(1 + strlen(command
) + 1 + strlen(message
) + 1 + 1);
1614 sprintf(str
, "\001%s %s\001", command
, message
);
1616 str
= malloc(1 + strlen(command
) + 1 + 1);
1619 sprintf(str
, "\001%s\001", command
);
1625 const firetalk_protocol_t firetalk_protocol_irc
= {
1627 default_server
: "irc.n.ml.org",
1629 default_buffersize
: 1024/2,
1630 periodic
: irc_periodic
,
1631 preselect
: irc_preselect
,
1632 postselect
: irc_postselect
,
1633 got_data
: irc_got_data
,
1634 got_data_connecting
: irc_got_data_connecting
,
1635 comparenicks
: irc_compare_nicks
,
1636 isprintable
: irc_isprint
,
1637 disconnect
: irc_disconnect
,
1639 get_info
: irc_get_info
,
1640 set_info
: irc_set_info
,
1641 set_away
: irc_set_away
,
1642 set_nickname
: irc_set_nickname
,
1643 set_password
: irc_set_password
,
1644 im_add_buddy
: irc_im_add_buddy
,
1645 im_remove_buddy
: irc_im_remove_buddy
,
1646 im_add_deny
: irc_im_add_deny
,
1647 im_remove_deny
: irc_im_remove_deny
,
1648 im_upload_buddies
: irc_im_upload_buddies
,
1649 im_upload_denies
: irc_im_upload_denies
,
1650 im_send_message
: irc_im_send_message
,
1651 im_send_action
: irc_im_send_action
,
1652 im_evil
: irc_im_evil
,
1653 chat_join
: irc_chat_join
,
1654 chat_part
: irc_chat_part
,
1655 chat_invite
: irc_chat_invite
,
1656 chat_set_topic
: irc_chat_set_topic
,
1657 chat_op
: irc_chat_op
,
1658 chat_deop
: irc_chat_deop
,
1659 chat_kick
: irc_chat_kick
,
1660 chat_send_message
: irc_chat_send_message
,
1661 chat_send_action
: irc_chat_send_action
,
1662 // subcode_send_request: irc_subcode_send_request,
1663 // subcode_send_reply: irc_subcode_send_reply,
1664 subcode_encode
: irc_ctcp_encode
,
1665 set_privacy
: irc_set_privacy
,
1666 room_normalize
: irc_normalize_room_name
,
1667 create_handle
: irc_create_handle
,
1668 destroy_handle
: irc_destroy_handle
,