ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/branches/1.0.x/src/irc.c
Revision: 5114
Committed: Wed Dec 24 22:12:05 2014 UTC (9 years, 3 months ago) by michael
Content type: text/x-csrc
Original Path: hopm/trunk/src/irc.c
File size: 23905 byte(s)
Log Message:
- Style corrections

File Contents

# Content
1 /* vim: set shiftwidth=3 softtabstop=3 expandtab: */
2
3 /*
4 * Copyright (C) 2002-2003 Erik Fears
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to
18 *
19 * The Free Software Foundation, Inc.
20 * 59 Temple Place - Suite 330
21 * Boston, MA 02111-1307, USA.
22 *
23 *
24 */
25
26 #include "setup.h"
27
28 #include <stdio.h>
29 #include <unistd.h>
30
31 #ifdef STDC_HEADERS
32 # include <stdlib.h>
33 # include <string.h>
34 #endif
35
36 #ifdef HAVE_STRINGS_H
37 # include <strings.h>
38 #endif
39
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44
45 #ifdef TIME_WITH_SYS_TIME
46 # include <sys/time.h>
47 # include <time.h>
48 #else
49 # ifdef HAVE_SYS_TIME_H
50 # include <sys/time.h>
51 # else
52 # include <time.h>
53 # endif
54 #endif
55
56 #include <errno.h>
57 #include <stdarg.h>
58 #include <regex.h>
59
60 #include "config.h"
61 #include "irc.h"
62 #include "log.h"
63 #include "opercmd.h"
64 #include "scan.h"
65 #include "dnsbl.h"
66 #include "stats.h"
67 #include "extern.h"
68 #include "options.h"
69 #include "match.h"
70 #include "compat.h"
71 #include "negcache.h"
72 #include "malloc.h"
73 #include "main.h"
74
75
76 static void irc_init(void);
77 static void irc_connect(void);
78 static void irc_reconnect(void);
79 static void irc_read(void);
80 static void irc_parse(void);
81
82 static struct ChannelConf *get_channel(const char *);
83
84 static struct UserInfo *userinfo_create(char *);
85 static void userinfo_free(struct UserInfo *source);
86
87 static void m_ping(char **, unsigned int, char *, struct UserInfo *);
88 static void m_invite(char **, unsigned int, char *, struct UserInfo *);
89 static void m_privmsg(char **, unsigned int, char *, struct UserInfo *);
90 static void m_ctcp(char **, unsigned int, char *, struct UserInfo *);
91 static void m_notice(char **, unsigned int, char *, struct UserInfo *);
92 static void m_perform(char **, unsigned int, char *, struct UserInfo *);
93 static void m_userhost(char **, unsigned int, char *, struct UserInfo *);
94 static void m_cannot_join(char **, unsigned int, char *, struct UserInfo *);
95 static void m_kill(char **, unsigned int, char *, struct UserInfo *);
96
97 extern struct cnode *nc_head;
98
99 /*
100 * Certain variables we don't want to allocate memory for over and over
101 * again so global scope is given.
102 */
103
104 char IRC_RAW[MSGLENMAX]; /* Buffer to read data into */
105 char IRC_SENDBUFF[MSGLENMAX]; /* Send buffer */
106 int IRC_RAW_LEN = 0; /* Position of IRC_RAW */
107
108 int IRC_FD = 0; /* File descriptor for IRC client */
109
110 struct bopm_sockaddr IRC_SVR; /* Sock Address Struct for IRC server */
111 struct bopm_ircaddr IRC_LOCAL; /* Sock Address Struct for Bind */
112
113 fd_set IRC_READ_FDSET; /* fd_set for IRC (read) data for select()*/
114 struct timeval IRC_TIMEOUT; /* timeval struct for select() timeout */
115
116 time_t IRC_LAST = 0; /* Last full line of data from irc server*/
117 time_t IRC_LASTRECONNECT = 0; /* Time of last reconnection */
118
119 /*
120 * Table should be ordered with most occuring (or priority)
121 * commands at the top of the list.
122 */
123 static struct CommandHash COMMAND_TABLE[] =
124 {
125 { "NOTICE", m_notice },
126 { "PRIVMSG", m_privmsg },
127 { "PING", m_ping },
128 { "INVITE", m_invite },
129 { "001", m_perform },
130 { "302", m_userhost },
131 { "471", m_cannot_join },
132 { "473", m_cannot_join },
133 { "474", m_cannot_join },
134 { "475", m_cannot_join },
135 { "KILL", m_kill }
136 };
137
138 /* irc_cycle
139 *
140 * Pass control to the IRC portion of HOPM to handle any awaiting IRC events.
141 *
142 * Parameters:
143 * None
144 *
145 * Return:
146 * None
147 */
148 void
149 irc_cycle(void)
150 {
151 if (IRC_FD <= 0)
152 {
153 /* Initialise negative cache. */
154 if (OptionsItem->negcache > 0)
155 nc_init(&nc_head);
156
157 /* Resolve remote host. */
158 irc_init();
159
160 /* Connect to remote host. */
161 irc_connect();
162 return; /* In case connect() immediately failed */
163 }
164
165 IRC_TIMEOUT.tv_sec = 0;
166
167 /* Block .025 seconds to avoid excessive CPU use on select(). */
168 IRC_TIMEOUT.tv_usec = 25000;
169
170 FD_ZERO(&IRC_READ_FDSET);
171 FD_SET(IRC_FD, &IRC_READ_FDSET);
172
173 switch (select((IRC_FD + 1), &IRC_READ_FDSET, 0, 0, &IRC_TIMEOUT))
174 {
175 case -1:
176 return;
177 break;
178 case 0:
179 break;
180 default:
181 /* Check if IRC data is available. */
182 if (FD_ISSET(IRC_FD, &IRC_READ_FDSET))
183 irc_read();
184
185 break;
186 }
187 }
188
189 /* irc_init
190 *
191 * Resolve IRC host and perform other initialization.
192 *
193 * Parameters:
194 * None
195 *
196 * Return:
197 * None
198 */
199 static void
200 irc_init(void)
201 {
202 node_t *node;
203 struct ChannelConf *chan;
204 struct bopm_sockaddr bsaddr;
205 struct in_addr *irc_host;
206
207 if (IRC_FD)
208 close(IRC_FD);
209
210 memset(&IRC_SVR, 0, sizeof(IRC_SVR));
211 memset(&IRC_LOCAL, 0, sizeof(IRC_LOCAL));
212 memset(&bsaddr, 0, sizeof(struct bopm_sockaddr));
213
214 /* Resolve IRC host. */
215 if ((irc_host = firedns_resolveip4(IRCItem->server)) == NULL)
216 {
217 log_printf("IRC -> firedns_resolveip4(\"%s\"): %s", IRCItem->server,
218 firedns_strerror(fdns_errno));
219 exit(EXIT_FAILURE);
220 }
221
222 IRC_SVR.sa4.sin_family = AF_INET;
223 IRC_SVR.sa4.sin_port = htons(IRCItem->port);
224 IRC_SVR.sa4.sin_addr = *irc_host;
225
226 if (IRC_SVR.sa4.sin_addr.s_addr == INADDR_NONE)
227 {
228 log_printf("IRC -> Unknown error resolving remote host (%s)",
229 IRCItem->server);
230 exit(EXIT_FAILURE);
231 }
232
233 /* Request file desc for IRC client socket */
234 IRC_FD = socket(AF_INET, SOCK_STREAM, 0);
235
236 if (IRC_FD == -1)
237 {
238 switch(errno)
239 {
240 case EINVAL:
241 case EPROTONOSUPPORT:
242 log_printf("IRC -> socket(): SOCK_STREAM is not "
243 "supported on this domain");
244 break;
245 case ENFILE:
246 log_printf("IRC -> socket(): Not enough free file "
247 "descriptors to allocate IRC socket");
248 break;
249 case EMFILE:
250 log_printf("IRC -> socket(): Process table overflow when "
251 "requesting file descriptor");
252 break;
253 case EACCES:
254 log_printf("IRC -> socket(): Permission denied to create "
255 "socket of type SOCK_STREAM");
256 break;
257 case ENOMEM:
258 log_printf("IRC -> socket(): Insufficient memory to "
259 "allocate socket");
260 break;
261 default:
262 log_printf("IRC -> socket(): Unknown error allocating "
263 "socket");
264 break;
265 }
266
267 exit(EXIT_FAILURE);
268 }
269
270 /* Bind */
271 if (!EmptyString(IRCItem->vhost))
272 {
273 int bindret = 0;
274
275 if (!inet_pton(AF_INET, IRCItem->vhost, &(IRC_LOCAL.in4.s_addr)))
276 {
277 log_printf("IRC -> bind(): %s is an invalid address", IRCItem->vhost);
278 exit(EXIT_FAILURE);
279 }
280
281 bsaddr.sa4.sin_addr.s_addr = IRC_LOCAL.in4.s_addr;
282 bsaddr.sa4.sin_family = AF_INET;
283 bsaddr.sa4.sin_port = htons(0);
284
285 bindret = bind(IRC_FD, (struct sockaddr *) &(bsaddr.sa4), sizeof(bsaddr.sa4));
286
287 if (bindret)
288 {
289 switch(errno)
290 {
291 case EACCES:
292 log_printf("IRC -> bind(): No access to bind to %s",
293 IRCItem->vhost);
294 break;
295 default:
296 log_printf("IRC -> bind(): Error binding to %s (%d)",
297 IRCItem->vhost, errno);
298 break;
299 }
300 exit(EXIT_FAILURE);
301 }
302 }
303 }
304
305 /* irc_send
306 *
307 * Send data to remote IRC host.
308 *
309 * Parameters:
310 * data: Format of data to send
311 * ...: varargs to format with
312 *
313 * Return: NONE
314 */
315 void
316 irc_send(const char *data, ...)
317 {
318 va_list arglist;
319 char data2[MSGLENMAX];
320 char tosend[MSGLENMAX];
321
322 va_start(arglist, data);
323 vsnprintf(data2, MSGLENMAX, data, arglist);
324 va_end(arglist);
325
326 if (OPT_DEBUG >= 2)
327 log_printf("IRC SEND -> %s", data2);
328
329 snprintf(tosend, MSGLENMAX, "%s\n", data2);
330
331 if (send(IRC_FD, tosend, strlen(tosend), 0) == -1)
332 {
333 /* Return of -1 indicates error sending data; we reconnect. */
334 log_printf("IRC -> Error sending data to server\n");
335 irc_reconnect();
336 }
337 }
338
339 /* irc_send
340 *
341 * Send privmsg to all channels.
342 *
343 * Parameters:
344 * data: Format of data to send
345 * ...: varargs to format with
346 *
347 * Return: NONE
348 */
349 void
350 irc_send_channels(const char *data, ...)
351 {
352 const node_t *node;
353 va_list arglist;
354 char buf[MSGLENMAX];
355
356 va_start(arglist, data);
357 vsnprintf(buf, sizeof(buf), data, arglist);
358 va_end(arglist);
359
360
361 LIST_FOREACH(node, IRCItem->channels->head)
362 {
363 const struct ChannelConf *chan = node->data;
364
365 irc_send("PRIVMSG %s :%s", chan->name, buf);
366 }
367 }
368
369 /* irc_connect
370 *
371 * Connect to IRC server.
372 * XXX: FD allocation done here
373 *
374 * Parameters: NONE
375 * Return: NONE
376 */
377 static void
378 irc_connect(void)
379 {
380 /* Connect to IRC server as client. */
381 if (connect(IRC_FD, (struct sockaddr *)&IRC_SVR, sizeof(IRC_SVR)) == -1)
382 {
383 switch (errno)
384 {
385 case EISCONN:
386 /* Already connected */
387 return;
388 case ECONNREFUSED:
389 log_printf("IRC -> connect(): Connection refused by (%s)",
390 IRCItem->server);
391 break;
392 case ETIMEDOUT:
393 log_printf("IRC -> connect(): Timed out connecting to (%s)",
394 IRCItem->server);
395 break;
396 case ENETUNREACH:
397 log_printf("IRC -> connect(): Network unreachable");
398 break;
399 case EALREADY:
400 /* Previous attempt not complete */
401 return;
402 default:
403 log_printf("IRC -> connect(): Unknown error connecting to (%s)",
404 IRCItem->server);
405
406 if (OPT_DEBUG >= 1)
407 log_printf("%s", strerror(errno));
408 }
409
410 /* Try to connect again */
411 irc_reconnect();
412 return;
413 }
414
415 irc_send("NICK %s", IRCItem->nick);
416
417 if (!EmptyString(IRCItem->password))
418 irc_send("PASS %s", IRCItem->password);
419
420 irc_send("USER %s %s %s :%s",
421 IRCItem->username,
422 IRCItem->username,
423 IRCItem->username,
424 IRCItem->realname);
425 time(&IRC_LAST);
426 }
427
428 /* irc_reconnect
429 *
430 * Close connection to IRC server.
431 *
432 * Parameters: NONE
433 *
434 * Return: NONE
435 */
436 static void
437 irc_reconnect(void)
438 {
439 time_t present;
440
441 time(&present);
442
443 /* Only try to reconnect every RECONNECT_INTERVAL seconds */
444 if ((present - IRC_LASTRECONNECT) < RECONNECTINTERVAL)
445 {
446 /* Sleep to avoid excessive CPU */
447 sleep(1);
448 return;
449 }
450
451 time(&IRC_LASTRECONNECT);
452
453 if (IRC_FD > 0)
454 close(IRC_FD);
455
456 /* Set IRC_FD 0 for reconnection on next irc_cycle(). */
457 IRC_FD = 0;
458
459 log_printf("IRC -> Connection to (%s) failed, reconnecting.", IRCItem->server);
460 }
461
462 /* irc_read
463 *
464 * irc_read is called my irc_cycle when new data is ready to be
465 * read from the irc server.
466 *
467 * Parameters: NONE
468 * Return: NONE
469 */
470 static void
471 irc_read(void)
472 {
473 int len;
474 char c;
475
476 while ((len = read(IRC_FD, &c, 1)) > 0)
477 {
478 if (c == '\r')
479 continue;
480
481 if (c == '\n')
482 {
483 /* Null string. */
484 IRC_RAW[IRC_RAW_LEN] = '\0';
485 /* Parse line. */
486 irc_parse();
487 /* Reset counter. */
488 IRC_RAW_LEN = 0;
489 break;
490 }
491
492 if (c != '\r' && c != '\n' && c != '\0')
493 IRC_RAW[IRC_RAW_LEN++] = c;
494 }
495
496 if((len <= 0) && (errno != EAGAIN))
497 {
498 if(OPT_DEBUG >= 2)
499 log_printf("irc_read -> errno=%d len=%d", errno, len);
500 irc_reconnect();
501 IRC_RAW_LEN = 0;
502 return;
503 }
504 }
505
506 /* irc_parse
507 *
508 * irc_parse is called by irc_read when a full line of data
509 * is ready to be parsed.
510 *
511 * Parameters: NONE
512 * Return: NONE
513 */
514 static void
515 irc_parse(void)
516 {
517 struct UserInfo *source_p;
518 char *pos;
519 unsigned int i;
520
521 /*
522 parv stores the parsed token, parc is the count of the parsed
523 tokens
524
525 parv[0] is ALWAYS the source, and is the server name of the source
526 did not exist
527 */
528
529 static char *parv[17];
530 static unsigned int parc;
531 static char msg[MSGLENMAX]; /* Temporarily stores IRC msg to pass to handlers */
532
533 parc = 1;
534
535 if(IRC_RAW_LEN <= 0)
536 return;
537
538 if (OPT_DEBUG >= 2)
539 log_printf("IRC READ -> %s", IRC_RAW);
540
541 time(&IRC_LAST);
542
543 /* Store a copy of IRC_RAW for the handlers (for functions that need PROOF) */
544 strlcpy(msg, IRC_RAW, sizeof(msg));
545
546 /* parv[0] is always the source */
547 if(IRC_RAW[0] == ':')
548 parv[0] = IRC_RAW + 1;
549 else
550 {
551 parv[0] = IRCItem->server;
552 parv[parc++] = IRC_RAW;
553 }
554
555 pos = IRC_RAW;
556
557 while((pos = strchr(pos, ' ')) && parc <= 17)
558 {
559
560 /* Avoid excessive spaces and end of IRC_RAW */
561 if(*(pos + 1) == ' ' && *(pos + 1) == '\0')
562 {
563 pos++;
564 continue;
565 }
566
567 /* Anything after a : is considered the final string of the
568 message */
569 if(*(pos + 1) == ':')
570 {
571 parv[parc++] = pos + 2;
572 *pos = '\0';
573 break;
574 }
575
576 /* Set the next parv at this position and replace the space with a
577 \0 for the previous parv */
578 parv[parc++] = pos + 1;
579 *pos = '\0';
580 pos++;
581 }
582
583 /* Generate a UserInfo struct from the source */
584
585 source_p = userinfo_create(parv[0]);
586
587 /* Determine which command this is from the command table
588 and let the handler for that command take control */
589
590 for(i = 0; i < (sizeof(COMMAND_TABLE) / sizeof(struct CommandHash)); i++)
591 if(strcasecmp(COMMAND_TABLE[i].command, parv[1]) == 0)
592 {
593 (*COMMAND_TABLE[i].handler)(parv, parc, msg, source_p);
594 break;
595 }
596
597 userinfo_free(source_p);
598 }
599
600 /* irc_timer
601 *
602 * Functions to be performed every ~seconds.
603 *
604 * Parameters: NONE
605 * Return: NONE
606 */
607 void
608 irc_timer(void)
609 {
610 time_t present, delta;
611
612 time(&present);
613
614 delta = present - IRC_LAST;
615
616 /* No data in NODATA_TIMEOUT minutes (set in options.h). */
617 if (delta >= NODATA_TIMEOUT)
618 {
619 log_printf("IRC -> Timeout awaiting data from server.");
620 irc_reconnect();
621
622 /* Make sure we dont do this again for a while */
623 time(&IRC_LAST);
624 }
625 else if (delta >= NODATA_TIMEOUT / 2)
626 {
627 /*
628 * Generate some data so high ping times or bugs in certain
629 * ircds (*cough* unreal *cough*) don't cause uneeded
630 * reconnections
631 */
632 irc_send("PING :HOPM");
633 }
634 }
635
636 /* get_channel
637 *
638 * Check if a channel is defined in our conf. If so return
639 * a pointer to it.
640 *
641 * Parameters:
642 * channel: channel to search conf for
643 *
644 * Return: Pointer to ChannelConf containing the channel
645 */
646 static struct ChannelConf *
647 get_channel(const char *channel)
648 {
649 node_t *node;
650 struct ChannelConf *item;
651
652 LIST_FOREACH(node, IRCItem->channels->head)
653 {
654 item = node->data;
655
656 if (strcasecmp(item->name, channel) == 0)
657 return item;
658 }
659
660 return NULL;
661 }
662
663 /* userinfo_create
664 *
665 * Parse a nick!user@host into a UserInfo struct
666 * and return a pointer to the new struct.
667 *
668 * Parameters:
669 * source: nick!user@host to parse
670 *
671 * Return:
672 * pointer to new UserInfo struct, or NULL if parsing failed
673 */
674 static struct UserInfo *
675 userinfo_create(char *source)
676 {
677 struct UserInfo *ret;
678 char *nick;
679 char *username;
680 char *hostname;
681 char *tmp;
682 int i, len;
683
684 nick = username = hostname = NULL;
685 tmp = xstrdup(source);
686 len = strlen(tmp);
687 nick = tmp;
688
689 for (i = 0; i < len; ++i)
690 {
691 if (tmp[i] == '!')
692 {
693 tmp[i] = '\0';
694 username = tmp + i + 1;
695 }
696
697 if (tmp[i] == '@')
698 {
699 tmp[i] = '\0';
700 hostname = tmp + i + 1;
701 }
702 }
703
704 if (nick == NULL || username == NULL || hostname == NULL)
705 {
706 MyFree(tmp);
707 return NULL;
708 }
709
710 ret = MyMalloc(sizeof *ret);
711 ret->irc_nick = xstrdup(nick);
712 ret->irc_username = xstrdup(username);
713 ret->irc_hostname = xstrdup(hostname);
714
715 MyFree(tmp);
716
717 return ret;
718 };
719
720 /* userinfo_free
721 *
722 * Free a UserInfo struct created with userinfo_create.
723 *
724 * Parameters:
725 * source: struct to free
726 *
727 * Return: None
728 */
729 static void
730 userinfo_free(struct UserInfo *source_p)
731 {
732 if (source_p == NULL)
733 return;
734
735 MyFree(source_p->irc_nick);
736 MyFree(source_p->irc_username);
737 MyFree(source_p->irc_hostname);
738 MyFree(source_p);
739 }
740
741 /* m_perform
742 *
743 * actions to perform on IRC connection
744 *
745 * Parameters:
746 * parv[0] = source
747 * parv[1] = PING
748 * parv[2] = PING TS/Package
749 *
750 * source_p: UserInfo struct of the source user, or NULL if
751 * the source (parv[0]) is a server.
752 */
753 static void
754 m_perform(char **parv, unsigned int parc, char *msg, struct UserInfo *notused)
755 {
756 node_t *node;
757
758 log_printf("IRC -> Connected to %s:%d", IRCItem->server, IRCItem->port);
759
760 /* Identify to nickserv if needed */
761 if (!EmptyString(IRCItem->nickserv))
762 irc_send("%s", IRCItem->nickserv);
763
764 /* Oper */
765 irc_send("OPER %s", IRCItem->oper);
766
767 /* Set modes */
768 irc_send("MODE %s %s", IRCItem->nick, IRCItem->mode);
769
770 /* Set Away */
771 if (!EmptyString(IRCItem->away))
772 irc_send("AWAY :%s", IRCItem->away);
773
774 /* Perform */
775 LIST_FOREACH(node, IRCItem->performs->head)
776 irc_send("%s", node->data);
777
778 /* Join all listed channels. */
779 LIST_FOREACH(node, IRCItem->channels->head)
780 {
781 struct ChannelConf *channel = node->data;
782
783 if (EmptyString(channel->name))
784 continue;
785
786 if (!EmptyString(channel->key))
787 irc_send("JOIN %s %s", channel->name, channel->key);
788 else
789 irc_send("JOIN %s", channel->name);
790 }
791 }
792
793 /* m_ping
794 *
795 * parv[0] = source
796 * parv[1] = PING
797 * parv[2] = PING TS/Package
798 *
799 * source_p: UserInfo struct of the source user, or NULL if
800 * the source (parv[0]) is a server.
801 */
802 static void
803 m_ping(char **parv, unsigned int parc, char *msg, struct UserInfo *source_p)
804 {
805 if (parc < 3)
806 return;
807
808 if (OPT_DEBUG >= 2)
809 log_printf("IRC -> PING? PONG!");
810
811 irc_send("PONG %s", parv[2]);
812 }
813
814 /* m_invite
815 *
816 * parv[0] = source
817 * parv[1] = INVITE
818 * parv[2] = target
819 * parv[3] = channel
820 *
821 * source_p: UserInfo struct of the source user, or NULL if
822 * the source (parv[0]) is a server.
823 */
824 static void
825 m_invite(char **parv, unsigned int parc, char *msg, struct UserInfo *source_p)
826 {
827 struct ChannelConf *channel;
828
829 if (parc < 4)
830 return;
831
832 log_printf("IRC -> Invited to %s by %s", parv[3], parv[0]);
833
834 if ((channel = get_channel(parv[3])) == NULL)
835 return;
836
837 irc_send("JOIN %s %s", channel->name, channel->key);
838 }
839
840 /* m_privmsg
841 *
842 * parv[0] = source
843 * parv[1] = PRIVMSG
844 * parv[2] = target (channel or user)
845 * parv[3] = message
846 *
847 * source_p: UserInfo struct of the source user, or NULL if
848 * the source (parv[0]) is a server.
849 */
850 static void
851 m_privmsg(char **parv, unsigned int parc, char *msg, struct UserInfo *source_p)
852 {
853 struct ChannelConf *channel;
854 size_t nick_len;
855
856 if (source_p == NULL)
857 return;
858
859 if (parc < 4)
860 return;
861
862 /* CTCP */
863 if (parv[3][0] == '\001')
864 m_ctcp(parv, parc, msg, source_p);
865
866 /* Only interested in privmsg to channels */
867 if (parv[2][0] != '#' && parv[2][0] != '&')
868 return;
869
870 /* Get a target */
871 if ((channel = get_channel(parv[2])) == NULL)
872 return;
873
874 /* Find a suitable length to compare with */
875 nick_len = strcspn(parv[3], " :,");
876
877 if (nick_len < 3 && strlen(IRCItem->nick) >= 3)
878 nick_len = 3;
879
880 /* message is a command */
881 if (strncasecmp(parv[3], IRCItem->nick, nick_len) == 0 ||
882 strncasecmp(parv[3], "!all", 4) == 0)
883 {
884 /* XXX command_parse will alter parv[3]. */
885 command_parse(parv[3], msg, channel, source_p);
886 }
887 }
888
889 /* m_ctcp
890 * parv[0] = source
891 * parv[1] = PRIVMSG
892 * parv[2] = target (channel or user)
893 * parv[3] = message
894 *
895 * source_p: UserInfo struct of the source user, or NULL if
896 * the source (parv[0]) is a server.
897 *
898 */
899 static void
900 m_ctcp(char **parv, unsigned int parc, char *msg, struct UserInfo *source_p)
901 {
902 if (strncasecmp(parv[3], "\001VERSION\001", 9) == 0)
903 irc_send("NOTICE %s :\001VERSION Hybrid Open Proxy Monitor %s\001",
904 source_p->irc_nick, VERSION);
905 }
906
907 /* m_notice
908 *
909 * parv[0] = source
910 * parv[1] = NOTICE
911 * parv[2] = target
912 * parv[3] = message
913 *
914 *
915 * source_p: UserInfo struct of the source user, or NULL if
916 * the source (parv[0]) is a server.
917 *
918 */
919 static void
920 m_notice(char **parv, unsigned int parc, char *msg, struct UserInfo *source_p)
921 {
922 static regex_t *preg = NULL;
923 regmatch_t pmatch[5];
924
925 static char errmsg[256];
926 int errnum, i;
927 char *user[4];
928
929 if (parc < 4)
930 return;
931
932 /* Not interested in notices from users */
933 if (source_p)
934 return;
935
936 /* Compile the regular expression if it has not been already */
937 if (preg == NULL)
938 {
939 preg = MyMalloc(sizeof *preg);
940
941 if ((errnum = regcomp(preg, IRCItem->connregex, REG_ICASE | REG_EXTENDED)))
942 {
943 regerror(errnum, preg, errmsg, 256);
944 log_printf("IRC REGEX -> Error when compiling regular expression");
945 log_printf("IRC REGEX -> %s", errmsg);
946
947 MyFree(preg);
948 preg = NULL;
949 return;
950 }
951 }
952
953 /* Match the expression against the possible connection notice */
954 if (regexec(preg, parv[3], 5, pmatch, 0))
955 return;
956
957 if (OPT_DEBUG > 0)
958 log_printf("IRC REGEX -> Regular expression caught connection notice. Parsing.");
959
960 if (pmatch[4].rm_so == -1)
961 {
962 log_printf("IRC REGEX -> pmatch[4].rm_so is -1 while parsing??? Aborting.");
963 return;
964 }
965
966 /*
967 * Offsets for data in the connection notice:
968 *
969 * NICKNAME: pmatch[1].rm_so TO pmatch[1].rm_eo
970 * USERNAME: pmatch[2].rm_so TO pmatch[2].rm_eo
971 * HOSTNAME: pmatch[3].rm_so TO pmatch[3].rm_eo
972 * IP : pmatch[4].rm_so TO pmatch[4].rm_eo
973 */
974 for (i = 0; i < 4; ++i)
975 {
976 user[i] = (parv[3] + pmatch[i + 1].rm_so);
977 *(parv[3] + pmatch[i + 1].rm_eo) = '\0';
978 }
979
980 if (OPT_DEBUG > 0)
981 log_printf("IRC REGEX -> Parsed %s!%s@%s [%s] from connection notice.",
982 user[0], user[1], user[2], user[3]);
983
984 /*FIXME (reminder) In the case of any rehash to the regex, preg MUST be freed first.
985 regfree(preg);
986 */
987
988 /* Pass this information off to scan.c */
989 scan_connect(user, msg);
990
991 /* Record the connect for stats purposes */
992 stats_connect();
993 }
994
995 /* m_userhost
996 *
997 * parv[0] = source
998 * parv[1] = USERHOST
999 * parv[2] = target (hopm)
1000 * parv[3] = :nick=(flags)user@host
1001 *
1002 *
1003 * source_p: UserInfo struct of the source user, or NULL if
1004 * the source (parv[0]) is a server.
1005 *
1006 */
1007 static void
1008 m_userhost(char **parv, unsigned int parc, char *msg, struct UserInfo *source_p)
1009 {
1010 if (parc < 4)
1011 return;
1012
1013 command_userhost(parv[3]);
1014 }
1015
1016 /* m_cannot_join
1017 *
1018 * parv[0] = source
1019 * parv[1] = numeric
1020 * parv[2] = target (hopm)
1021 * parv[3] = channel
1022 * parv[4] = error text
1023 *
1024 */
1025 static void
1026 m_cannot_join(char **parv, unsigned int parc, char *msg, struct UserInfo *source_p)
1027 {
1028 struct ChannelConf *channel;
1029
1030 if (parc < 5)
1031 return;
1032
1033 /* Is it one of our channels? */
1034 if ((channel = get_channel(parv[3])) == NULL)
1035 return;
1036
1037 if (EmptyString(channel->invite))
1038 return;
1039
1040 irc_send("%s", channel->invite);
1041 }
1042
1043 /* m_kill
1044 *
1045 * parv[0] = source
1046 * parv[1] = numeric
1047 * parv[2] = target (hopm)
1048 * parv[3] = channel
1049 * parv[4] = error text
1050 *
1051 */
1052 static void
1053 m_kill(char **parv, unsigned int parc, char *msg, struct UserInfo *source_p)
1054 {
1055 /* Restart hopm to rehash */
1056 main_restart();
1057 }