ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/trunk/src/irc.c
Revision: 5226
Committed: Wed Dec 31 14:08:39 2014 UTC (9 years, 3 months ago) by michael
Content type: text/x-csrc
File size: 22813 byte(s)
Log Message:
- irc.c:irc_cycle(): replaced select() with poll()

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

Properties

Name Value
svn:eol-style native
svn:keywords Id