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