ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/trunk/src/irc.c
Revision: 8193
Committed: Fri Apr 14 12:38:22 2017 UTC (8 years, 4 months ago) by michael
Content type: text/x-csrc
File size: 20044 byte(s)
Log Message:
- Just pass a char pointer to the opercommand handlers instead of a struct ChannelConf pointer

File Contents

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

Properties

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