summary refs log tree commit diff stats
path: root/IRC.cc
diff options
context:
space:
mode:
Diffstat (limited to 'IRC.cc')
-rwxr-xr-xIRC.cc877
1 files changed, 877 insertions, 0 deletions
diff --git a/IRC.cc b/IRC.cc new file mode 100755 index 0000000..b6b4da4 --- /dev/null +++ b/IRC.cc
@@ -0,0 +1,877 @@
1/*
2 cpIRC - C++ class based IRC protocol wrapper
3 Copyright (C) 2003 Iain Sheppard
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library 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 GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Contacting the author:
20 ~~~~~~~~~~~~~~~~~~~~~~
21
22 email: iainsheppard@yahoo.co.uk
23 IRC: #magpie @ irc.quakenet.org
24*/
25
26#include "IRC.h"
27#ifdef WIN32
28#include <windows.h>
29#else
30#include <stdlib.h>
31#include <unistd.h>
32#include <errno.h>
33#include <string.h>
34#include <netdb.h>
35#include <sys/types.h>
36#include <netinet/in.h>
37#include <sys/socket.h>
38#define closesocket(s) close(s)
39#define SOCKET_ERROR -1
40#define INVALID_SOCKET -1
41#endif
42
43IRC::IRC()
44{
45 hooks=0;
46 chan_users=0;
47 connected=false;
48 sentnick=false;
49 sentpass=false;
50 sentuser=false;
51 cur_nick=0;
52}
53
54IRC::~IRC()
55{
56 if (hooks)
57 delete_irc_command_hook(hooks);
58}
59
60void IRC::insert_irc_command_hook(irc_command_hook* hook, char* cmd_name, int (*function_ptr)(char*, irc_reply_data*, void*))
61{
62 if (hook->function)
63 {
64 if (!hook->next)
65 {
66 hook->next=new irc_command_hook;
67 hook->next->function=0;
68 hook->next->irc_command=0;
69 hook->next->next=0;
70 }
71 insert_irc_command_hook(hook->next, cmd_name, function_ptr);
72 }
73 else
74 {
75 hook->function=function_ptr;
76 hook->irc_command=new char[strlen(cmd_name)+1];
77 strcpy(hook->irc_command, cmd_name);
78 }
79}
80
81void IRC::hook_irc_command(char* cmd_name, int (*function_ptr)(char*, irc_reply_data*, void*))
82{
83 if (!hooks)
84 {
85 hooks=new irc_command_hook;
86 hooks->function=0;
87 hooks->irc_command=0;
88 hooks->next=0;
89 insert_irc_command_hook(hooks, cmd_name, function_ptr);
90 }
91 else
92 {
93 insert_irc_command_hook(hooks, cmd_name, function_ptr);
94 }
95}
96
97void IRC::delete_irc_command_hook(irc_command_hook* cmd_hook)
98{
99 if (cmd_hook->next)
100 delete_irc_command_hook(cmd_hook->next);
101 if (cmd_hook->irc_command)
102 delete cmd_hook->irc_command;
103 delete cmd_hook;
104}
105
106int IRC::start(char* server, int port, char* nick, char* user, char* name, char* pass)
107{
108 #ifdef WIN32
109 HOSTENT* resolv;
110 #else
111 hostent* resolv;
112 #endif
113 sockaddr_in rem;
114
115 if (connected)
116 return 1;
117
118 irc_socket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
119 if (irc_socket==INVALID_SOCKET)
120 {
121 return 1;
122 }
123 resolv=gethostbyname(server);
124 if (!resolv)
125 {
126 closesocket(irc_socket);
127 return 1;
128 }
129 memcpy(&rem.sin_addr, resolv->h_addr, 4);
130 rem.sin_family=AF_INET;
131 rem.sin_port=htons(port);
132
133 if (connect(irc_socket, (const sockaddr*)&rem, sizeof(rem))==SOCKET_ERROR)
134 {
135 #ifdef WIN32
136 printf("Failed to connect: %d\n", WSAGetLastError());
137 #endif
138 closesocket(irc_socket);
139 return 1;
140 }
141
142 dataout=fdopen(irc_socket, "w");
143 //datain=fdopen(irc_socket, "r");
144
145 if (!dataout /*|| !datain*/)
146 {
147 printf("Failed to open streams!\n");
148 closesocket(irc_socket);
149 return 1;
150 }
151
152 connected=true;
153
154 cur_nick=new char[strlen(nick)+1];
155 strcpy(cur_nick, nick);
156
157 fprintf(dataout, "PASS %s\r\n", pass);
158 fprintf(dataout, "NICK %s\r\n", nick);
159 fprintf(dataout, "USER %s * 0 :%s\r\n", user, name);
160 fflush(dataout);
161
162 return 0;
163}
164
165void IRC::disconnect()
166{
167 if (connected)
168 {
169 fclose(dataout);
170 printf("Disconnected from server.\n");
171 connected=false;
172 quit("Leaving");
173 #ifdef WIN32
174 shutdown(irc_socket, 2);
175 #endif
176 closesocket(irc_socket);
177 }
178}
179
180int IRC::quit(char* quit_message)
181{
182 if (connected)
183 {
184 if (quit_message)
185 fprintf(dataout, "QUIT %s\r\n", quit_message);
186 else
187 fprintf(dataout, "QUIT\r\n");
188 if (fflush(dataout))
189 return 1;
190 }
191 return 0;
192}
193
194int IRC::message_loop()
195{
196 char buffer[1024];
197 int ret_len;
198
199 if (!connected)
200 {
201 printf("Not connected!\n");
202 return 1;
203 }
204
205 while (1)
206 {
207 ret_len=recv(irc_socket, buffer, 1023, 0);
208 if (ret_len==SOCKET_ERROR || !ret_len)
209 {
210 return 1;
211 }
212 buffer[ret_len]='\0';
213 split_to_replies(buffer);
214 }
215
216 return 0;
217}
218
219void IRC::split_to_replies(char* data)
220{
221 char* p;
222
223 while (p=strstr(data, "\r\n"))
224 {
225 *p='\0';
226 parse_irc_reply(data);
227 data=p+2;
228 }
229}
230
231int IRC::is_op(char* channel, char* nick)
232{
233 channel_user* cup;
234
235 cup=chan_users;
236
237 while (cup)
238 {
239 if (!strcmp(cup->channel, channel) && !strcmp(cup->nick, nick))
240 {
241 return cup->flags&IRC_USER_OP;
242 }
243 cup=cup->next;
244 }
245
246 return 0;
247}
248
249int IRC::is_voice(char* channel, char* nick)
250{
251 channel_user* cup;
252
253 cup=chan_users;
254
255 while (cup)
256 {
257 if (!strcmp(cup->channel, channel) && !strcmp(cup->nick, nick))
258 {
259 return cup->flags&IRC_USER_VOICE;
260 }
261 cup=cup->next;
262 }
263
264 return 0;
265}
266
267void IRC::parse_irc_reply(char* data)
268{
269 char* hostd;
270 char* cmd;
271 char* params;
272 char buffer[514];
273 irc_reply_data hostd_tmp;
274 channel_user* cup;
275 char* p;
276 char* chan_temp;
277
278 hostd_tmp.target=0;
279
280 printf("%s\n", data);
281
282 if (data[0]==':')
283 {
284 hostd=&data[1];
285 cmd=strchr(hostd, ' ');
286 if (!cmd)
287 return;
288 *cmd='\0';
289 cmd++;
290 params=strchr(cmd, ' ');
291 if (params)
292 {
293 *params='\0';
294 params++;
295 }
296 if (params[0]==':')
297 {
298 *params='\0';
299 params++;
300 }
301 hostd_tmp.nick=hostd;
302 hostd_tmp.ident=strchr(hostd, '!');
303 if (hostd_tmp.ident)
304 {
305 *hostd_tmp.ident='\0';
306 hostd_tmp.ident++;
307 hostd_tmp.host=strchr(hostd_tmp.ident, '@');
308 if (hostd_tmp.host)
309 {
310 *hostd_tmp.host='\0';
311 hostd_tmp.host++;
312 }
313 }
314
315 if (!strcmp(cmd, "JOIN"))
316 {
317
318
319 cup=chan_users;
320 if (cup)
321 {
322 while (cup->nick)
323 {
324 if (!cup->next)
325 {
326 cup->next=new channel_user;
327 cup->next->channel=0;
328 cup->next->flags=0;
329 cup->next->next=0;
330 cup->next->nick=0;
331 }
332 cup=cup->next;
333 }
334 cup->channel=new char[strlen(params)+1];
335 strcpy(cup->channel, params);
336 cup->nick=new char[strlen(hostd_tmp.nick)+1];
337 strcpy(cup->nick, hostd_tmp.nick);
338 }
339 }
340 else if (!strcmp(cmd, "PART"))
341 {
342 channel_user* d;
343 channel_user* prev;
344
345 d=0;
346 prev=0;
347 cup=chan_users;
348 while (cup)
349 {
350 if (!strcmp(cup->channel, params) && !strcmp(cup->nick, hostd_tmp.nick))
351 {
352 d=cup;
353 break;
354 }
355 else
356 {
357 prev=cup;
358 }
359 cup=cup->next;
360 }
361 if (d)
362 {
363 if (d==chan_users)
364 {
365 chan_users=d->next;
366 if (d->channel)
367 delete [] d->channel;
368 if (d->nick)
369 delete [] d->nick;
370 delete d;
371 }
372 else
373 {
374 if (prev)
375 {
376 prev->next=d->next;
377 }
378 chan_users=d->next;
379 if (d->channel)
380 delete [] d->channel;
381 if (d->nick)
382 delete [] d->nick;
383 delete d;
384 }
385 }
386 }
387 else if (!strcmp(cmd, "QUIT"))
388 {
389 channel_user* d;
390 channel_user* prev;
391
392 d=0;
393 prev=0;
394 cup=chan_users;
395 while (cup)
396 {
397 if (!strcmp(cup->nick, hostd_tmp.nick))
398 {
399 d=cup;
400 if (d==chan_users)
401 {
402 chan_users=d->next;
403 if (d->channel)
404 delete [] d->channel;
405 if (d->nick)
406 delete [] d->nick;
407 delete d;
408 }
409 else
410 {
411 if (prev)
412 {
413 prev->next=d->next;
414 }
415 if (d->channel)
416 delete [] d->channel;
417 if (d->nick)
418 delete [] d->nick;
419 delete d;
420 }
421 break;
422 }
423 else
424 {
425 prev=cup;
426 }
427 cup=cup->next;
428 }
429 }
430 else if (!strcmp(cmd, "MODE"))
431 {
432 char* chan;
433 char* changevars;
434 channel_user* cup;
435 channel_user* d;
436 char* tmp;
437 int i;
438 bool plus;
439
440 chan=params;
441 params=strchr(chan, ' ');
442 *params='\0';
443 params++;
444 changevars=params;
445 params=strchr(changevars, ' ');
446 if (!params)
447 {
448 return;
449 }
450 if (chan[0]!='#')
451 {
452 return;
453 }
454 *params='\0';
455 params++;
456
457 plus=false;
458 for (i=0; i<(signed)strlen(changevars); i++)
459 {
460 switch (changevars[i])
461 {
462 case '+':
463 plus=true;
464 break;
465 case '-':
466 plus=false;
467 break;
468 case 'q':
469 break;
470 case 'o':
471 tmp=strchr(params, ' ');
472 if (tmp)
473 {
474 *tmp='\0';
475 tmp++;
476 }
477 tmp=params;
478 if (plus)
479 {
480 // user has been opped (chan, params)
481 cup=chan_users;
482 d=0;
483 while (cup)
484 {
485 if (!strcmp(cup->channel, chan) && !strcmp(cup->nick, tmp))
486 {
487 d=cup;
488 break;
489 }
490 cup=cup->next;
491 }
492 if (d)
493 {
494 d->flags=d->flags|IRC_USER_OP;
495 printf("MODE FOR %s ON %s IS %d\n", tmp, chan, d->flags);
496 }
497 }
498 else
499 {
500 // user has been deopped (chan, params)
501 cup=chan_users;
502 d=0;
503 while (cup)
504 {
505 if (!strcmp(cup->channel, chan) && !strcmp(cup->nick, tmp))
506 {
507 d=cup;
508 break;
509 }
510 cup=cup->next;
511 }
512 if (d)
513 {
514 d->flags=d->flags^IRC_USER_OP;
515 }
516 }
517 params=tmp;
518 break;
519 case 'v':
520 tmp=strchr(params, ' ');
521 if (tmp)
522 {
523 *tmp='\0';
524 tmp++;
525 }
526 tmp=params;
527 if (plus)
528 {
529 // user has been voiced
530 cup=chan_users;
531 d=0;
532 while (cup)
533 {
534 if (!strcmp(cup->channel, params) && !strcmp(cup->nick, hostd_tmp.nick))
535 {
536 d=cup;
537 break;
538 }
539 cup=cup->next;
540 }
541 if (d)
542 {
543 d->flags=d->flags|IRC_USER_VOICE;
544 }
545 }
546 else
547 {
548 // user has been devoiced
549 cup=chan_users;
550 d=0;
551 while (cup)
552 {
553 if (!strcmp(cup->channel, params) && !strcmp(cup->nick, hostd_tmp.nick))
554 {
555 d=cup;
556 break;
557 }
558 cup=cup->next;
559 }
560 if (d)
561 {
562 d->flags=d->flags^IRC_USER_VOICE;
563 }
564 }
565 params=tmp;
566 break;
567 default:
568 return;
569 break;
570 }
571 // ------------ END OF MODE ---------------
572 }
573 }
574 else if (!strcmp(cmd, "353"))
575 {
576 // receiving channel names list
577 if (!chan_users)
578 {
579 chan_users=new channel_user;
580 chan_users->next=0;
581 chan_users->nick=0;
582 chan_users->flags=0;
583 chan_users->channel=0;
584 }
585 cup=chan_users;
586 chan_temp=strchr(params, '#');
587 if (chan_temp)
588 {
589 //chan_temp+=3;
590 p=strstr(chan_temp, " :");
591 if (p)
592 {
593 *p='\0';
594 p+=2;
595 while (strchr(p, ' '))
596 {
597 char* tmp;
598
599 tmp=strchr(p, ' ');
600 *tmp='\0';
601 tmp++;
602 while (cup->nick)
603 {
604 if (!cup->next)
605 {
606 cup->next=new channel_user;
607 cup->next->channel=0;
608 cup->next->flags=0;
609 cup->next->next=0;
610 cup->next->nick=0;
611 }
612 cup=cup->next;
613 }
614 if (p[0]=='@')
615 {
616 cup->flags=cup->flags|IRC_USER_OP;
617 p++;
618 }
619 else if (p[0]=='+')
620 {
621 cup->flags=cup->flags|IRC_USER_VOICE;
622 p++;
623 }
624 cup->nick=new char[strlen(p)+1];
625 strcpy(cup->nick, p);
626 cup->channel=new char[strlen(chan_temp)+1];
627 strcpy(cup->channel, chan_temp);
628 p=tmp;
629 }
630 while (cup->nick)
631 {
632 if (!cup->next)
633 {
634 cup->next=new channel_user;
635 cup->next->channel=0;
636 cup->next->flags=0;
637 cup->next->next=0;
638 cup->next->nick=0;
639 }
640 cup=cup->next;
641 }
642 if (p[0]=='@')
643 {
644 cup->flags=cup->flags|IRC_USER_OP;
645 p++;
646 }
647 else if (p[0]=='+')
648 {
649 cup->flags=cup->flags|IRC_USER_VOICE;
650 p++;
651 }
652 cup->nick=new char[strlen(p)+1];
653 strcpy(cup->nick, p);
654 cup->channel=new char[strlen(chan_temp)+1];
655 strcpy(cup->channel, chan_temp);
656 }
657 }
658 }
659 else if (!strcmp(cmd, "NOTICE"))
660 {
661 hostd_tmp.target=params;
662 params=strchr(hostd_tmp.target, ' ');
663 if (params)
664 *params='\0';
665 params++;
666 #ifdef __IRC_DEBUG__
667 printf("%s >-%s- %s\n", hostd_tmp.nick, hostd_tmp.target, &params[1]);
668 #endif
669 }
670 else if (!strcmp(cmd, "PRIVMSG"))
671 {
672 hostd_tmp.target=params;
673 params=strchr(hostd_tmp.target, ' ');
674 if (!params)
675 return;
676 *(params++)='\0';
677 #ifdef __IRC_DEBUG__
678 printf("%s: <%s> %s\n", hostd_tmp.target, hostd_tmp.nick, &params[1]);
679 #endif
680 }
681 else if (!strcmp(cmd, "NICK"))
682 {
683 if (!strcmp(hostd_tmp.nick, cur_nick))
684 {
685 delete [] cur_nick;
686 cur_nick=new char[strlen(params)+1];
687 strcpy(cur_nick, params);
688 }
689
690 cup=chan_users;
691 while (cup)
692 {
693 if (!strcmp(cup->nick, hostd_tmp.nick))
694 {
695 cup->nick=new char[strlen(params)+1];
696 strcpy(cup->nick, params);
697 }
698 cup=cup->next;
699 }
700 }
701 /* else if (!strcmp(cmd, ""))
702 {
703 #ifdef __IRC_DEBUG__
704 #endif
705 } */
706 call_hook(cmd, params, &hostd_tmp);
707 }
708 else
709 {
710 cmd=data;
711 data=strchr(cmd, ' ');
712 if (!data)
713 return;
714 *data='\0';
715 params=data+1;
716
717 if (!strcmp(cmd, "PING"))
718 {
719 if (!params)
720 return;
721 fprintf(dataout, "PONG %s\r\n", &params[1]);
722 #ifdef __IRC_DEBUG__
723 printf("Ping received, pong sent.\n");
724 #endif
725 fflush(dataout);
726 }
727 else
728 {
729 hostd_tmp.host=0;
730 hostd_tmp.ident=0;
731 hostd_tmp.nick=0;
732 hostd_tmp.target=0;
733 call_hook(cmd, params, &hostd_tmp);
734 }
735 }
736}
737
738void IRC::call_hook(char* irc_command, char* params, irc_reply_data* hostd)
739{
740 irc_command_hook* p;
741
742 if (!hooks)
743 return;
744
745 p=hooks;
746 while (p)
747 {
748 if (!strcmp(p->irc_command, irc_command))
749 {
750 (*(p->function))(params, hostd, this);
751 p=0;
752 }
753 else
754 {
755 p=p->next;
756 }
757 }
758}
759
760int IRC::notice(char* target, char* message)
761{
762 if (!connected)
763 return 1;
764 fprintf(dataout, "NOTICE %s :%s\r\n", target, message);
765 return fflush(dataout);
766}
767
768int IRC::notice(char* fmt, ...)
769{
770 va_list argp;
771 char* target;
772
773 if (!connected)
774 return 1;
775 va_start(argp, fmt);
776 fprintf(dataout, "NOTICE %s :", fmt);
777 vfprintf(dataout, va_arg(argp, char*), argp);
778 va_end(argp);
779 fprintf(dataout, "\r\n");
780 return fflush(dataout);
781}
782
783int IRC::privmsg(char* target, char* message)
784{
785 if (!connected)
786 return 1;
787 fprintf(dataout, "PRIVMSG %s :%s\r\n", target, message);
788 return fflush(dataout);
789}
790
791int IRC::privmsg(char* fmt, ...)
792{
793 va_list argp;
794 char* target;
795
796 if (!connected)
797 return 1;
798 va_start(argp, fmt);
799 fprintf(dataout, "PRIVMSG %s :", fmt);
800 vfprintf(dataout, va_arg(argp, char*), argp);
801 va_end(argp);
802 fprintf(dataout, "\r\n");
803 return fflush(dataout);
804}
805
806
807int IRC::join(char* channel)
808{
809 if (!connected)
810 return 1;
811 fprintf(dataout, "JOIN %s\r\n", channel);
812 return fflush(dataout);
813}
814
815int IRC::part(char* channel)
816{
817 if (!connected)
818 return 1;
819 fprintf(dataout, "PART %s\r\n", channel);
820 return fflush(dataout);
821}
822
823int IRC::kick(char* channel, char* nick)
824{
825 if (!connected)
826 return 1;
827 fprintf(dataout, "KICK %s %s\r\n", channel, nick);
828 return fflush(dataout);
829}
830
831int IRC::raw(char* data)
832{
833 if (!connected)
834 return 1;
835 fprintf(dataout, "%s\r\n", data);
836 return fflush(dataout);
837}
838
839int IRC::kick(char* channel, char* nick, char* message)
840{
841 if (!connected)
842 return 1;
843 fprintf(dataout, "KICK %s %s :%s\r\n", channel, nick, message);
844 return fflush(dataout);
845}
846
847int IRC::mode(char* channel, char* modes, char* targets)
848{
849 if (!connected)
850 return 1;
851 if (!targets)
852 fprintf(dataout, "MODE %s %s\r\n", channel, modes);
853 else
854 fprintf(dataout, "MODE %s %s %s\r\n", channel, modes, targets);
855 return fflush(dataout);
856}
857
858int IRC::mode(char* modes)
859{
860 if (!connected)
861 return 1;
862 mode(cur_nick, modes, 0);
863 return 0;
864}
865
866int IRC::nick(char* newnick)
867{
868 if (!connected)
869 return 1;
870 fprintf(dataout, "NICK %s\r\n", newnick);
871 return fflush(dataout);
872}
873
874char* IRC::current_nick()
875{
876 return cur_nick;
877}