ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/branches/1.0.x/src/irc.c
Revision: 5125
Committed: Thu Dec 25 17:23:34 2014 UTC (9 years, 3 months ago) by michael
Content type: text/x-csrc
Original Path: hopm/trunk/src/irc.c
File size: 23341 byte(s)
Log Message:
- irc.c: made IRC_RAW_LEN an unsigned int

File Contents

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