ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/trunk/src/irc.c
Revision: 8750
Committed: Tue Jan 1 11:06:38 2019 UTC (5 years, 3 months ago) by michael
Content type: text/x-csrc
File size: 20085 byte(s)
Log Message:
- Update copyright years

File Contents

# Content
1 /*
2 * Copyright (c) 2002-2003 Erik Fears
3 * Copyright (c) 2014-2019 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 *(parv[3] + nick_len) == ',' ||
255 *(parv[3] + nick_len) == ':';
256 }
257
258 if (hit)
259 /* XXX command_parse will alter parv[3]. */
260 command_parse(parv[3], channel->name, source_p);
261 }
262
263 /* m_notice
264 *
265 * parv[0] = source
266 * parv[1] = NOTICE
267 * parv[2] = target
268 * parv[3] = message
269 *
270 *
271 * source_p: UserInfo struct of the source user, or NULL if
272 * the source (parv[0]) is a server.
273 */
274 static void
275 m_notice(char *parv[], unsigned int parc, const char *msg, const char *source_p)
276 {
277 static regex_t *preg = NULL;
278 regmatch_t pmatch[5];
279 int errnum;
280 const char *user[4];
281 const node_t *node;
282
283 /* Not interested in notices from users */
284 if (source_p)
285 return;
286
287 if (parc < 4)
288 return;
289
290 /* Compile the regular expression if it has not been already */
291 if (preg == NULL)
292 {
293 preg = xcalloc(sizeof(*preg));
294
295 if ((errnum = regcomp(preg, IRCItem.connregex, REG_ICASE | REG_EXTENDED)))
296 {
297 char errmsg[256];
298
299 regerror(errnum, preg, errmsg, sizeof(errmsg));
300 log_printf("IRC REGEX -> Error when compiling regular expression: %s", errmsg);
301
302 xfree(preg);
303 preg = NULL;
304 return;
305 }
306 }
307
308 /* Match the expression against the possible connection notice */
309 if (regexec(preg, parv[3], 5, pmatch, 0))
310 return;
311
312 if (OPT_DEBUG > 0)
313 log_printf("IRC REGEX -> Regular expression caught connection notice. Parsing.");
314
315 if (pmatch[4].rm_so == -1)
316 {
317 log_printf("IRC REGEX -> pmatch[4].rm_so is -1 while parsing??? Aborting.");
318 return;
319 }
320
321 /*
322 * Offsets for data in the connection notice:
323 *
324 * NICKNAME: pmatch[1].rm_so TO pmatch[1].rm_eo
325 * USERNAME: pmatch[2].rm_so TO pmatch[2].rm_eo
326 * HOSTNAME: pmatch[3].rm_so TO pmatch[3].rm_eo
327 * IP : pmatch[4].rm_so TO pmatch[4].rm_eo
328 */
329 for (unsigned int i = 0; i < 4; ++i)
330 {
331 user[i] = (parv[3] + pmatch[i + 1].rm_so);
332 *(parv[3] + pmatch[i + 1].rm_eo) = '\0';
333 }
334
335 if (OPT_DEBUG > 0)
336 log_printf("IRC REGEX -> Parsed %s!%s@%s [%s] from connection notice.",
337 user[0], user[1], user[2], user[3]);
338
339 LIST_FOREACH(node, IRCItem.notices.head)
340 irc_send("NOTICE %s :%s", user[0], node->data);
341
342 /* Pass this information off to scan.c */
343 scan_connect(user, msg);
344
345 /* Record the connect for stats purposes */
346 stats_connect();
347 }
348
349 /* m_userhost
350 *
351 * parv[0] = source
352 * parv[1] = USERHOST
353 * parv[2] = target (hopm)
354 * parv[3] = :nick=(flags)user@host
355 *
356 *
357 * source_p: UserInfo struct of the source user, or NULL if
358 * the source (parv[0]) is a server.
359 */
360 static void
361 m_userhost(char *parv[], unsigned int parc, const char *msg, const char *source_p)
362 {
363 if (parc < 4)
364 return;
365
366 command_userhost(parv[3]);
367 }
368
369 /* m_cannot_join
370 *
371 * parv[0] = source
372 * parv[1] = numeric
373 * parv[2] = target (hopm)
374 * parv[3] = channel
375 * parv[4] = error text
376 */
377 static void
378 m_cannot_join(char *parv[], unsigned int parc, const char *msg, const char *source_p)
379 {
380 const struct ChannelConf *channel = NULL;
381
382 if (parc < 5)
383 return;
384
385 /* Is it one of our channels? */
386 if ((channel = get_channel(parv[3])) == NULL)
387 return;
388
389 if (EmptyString(channel->invite))
390 return;
391
392 irc_send("%s", channel->invite);
393 }
394
395 /* m_kill
396 *
397 * parv[0] = source
398 * parv[1] = numeric
399 * parv[2] = target (hopm)
400 * parv[3] = channel
401 * parv[4] = error text
402 */
403 static void
404 m_kill(char *parv[], unsigned int parc, const char *msg, const char *source_p)
405 {
406 /* Restart hopm to rehash */
407 main_restart();
408 }
409
410 /* userinfo_create
411 *
412 * Parse a nick!user@host into a UserInfo struct
413 * and return a pointer to the new struct.
414 *
415 * Parameters:
416 * source: nick!user@host to parse
417 *
418 * Return:
419 * pointer to new UserInfo struct, or NULL if parsing failed
420 */
421 static const char *
422 userinfo_create(const char *source)
423 {
424 static char name[MSGLENMAX];
425 unsigned int has_user = 0;
426 unsigned int has_host = 0;
427
428 strlcpy(name, source, sizeof(name));
429
430 for (char *p = name; *p; ++p)
431 {
432 if (*p == '!')
433 {
434 *p = '\0';
435 ++has_user;
436 continue;
437 }
438
439 if (*p == '@' && has_user)
440 {
441 ++has_host;
442 continue;
443 }
444 }
445
446 if (has_user == 1 && has_host == 1)
447 return name;
448 return NULL;
449 };
450
451 /* irc_init
452 *
453 * Resolve IRC host and perform other initialization.
454 *
455 * Parameters:
456 * None
457 *
458 * Return:
459 * None
460 */
461 static void
462 irc_init(void)
463 {
464 const void *address = NULL;
465
466 assert(IRC_FD == -1);
467
468 memset(&IRC_SVR, 0, sizeof(IRC_SVR));
469
470 /* Resolve IRC host. */
471 if ((address = firedns_resolveip6(IRCItem.server)))
472 {
473 struct sockaddr_in6 *in = (struct sockaddr_in6 *)&IRC_SVR;
474
475 svr_addrlen = sizeof(*in);
476 IRC_SVR.ss_family = AF_INET6;
477 in->sin6_port = htons(IRCItem.port);
478 memcpy(&in->sin6_addr, address, sizeof(in->sin6_addr));
479 }
480 else if ((address = firedns_resolveip4(IRCItem.server)))
481 {
482 struct sockaddr_in *in = (struct sockaddr_in *)&IRC_SVR;
483
484 svr_addrlen = sizeof(*in);
485 IRC_SVR.ss_family = AF_INET;
486 in->sin_port = htons(IRCItem.port);
487 memcpy(&in->sin_addr, address, sizeof(in->sin_addr));
488 }
489 else
490 {
491 log_printf("IRC -> firedns_resolveip(\"%s\"): %s", IRCItem.server,
492 firedns_strerror(firedns_errno));
493 exit(EXIT_FAILURE);
494 }
495
496 /* Request file desc for IRC client socket */
497 IRC_FD = socket(IRC_SVR.ss_family, SOCK_STREAM, 0);
498
499 if (IRC_FD == -1)
500 {
501 log_printf("IRC -> socket(): error creating socket: %s", strerror(errno));
502 exit(EXIT_FAILURE);
503 }
504
505 /* Bind */
506 if (!EmptyString(IRCItem.vhost))
507 {
508 struct addrinfo hints, *res;
509 int n;
510
511 memset(&hints, 0, sizeof(hints));
512
513 hints.ai_family = AF_UNSPEC;
514 hints.ai_socktype = SOCK_STREAM;
515 hints.ai_flags = AI_NUMERICHOST;
516
517 if ((n = getaddrinfo(IRCItem.vhost, NULL, &hints, &res)))
518 {
519 log_printf("IRC -> error binding to %s: %s", IRCItem.vhost, gai_strerror(n));
520 exit(EXIT_FAILURE);
521 }
522 else if (bind(IRC_FD, res->ai_addr, res->ai_addrlen))
523 {
524 log_printf("IRC -> error binding to %s: %s", IRCItem.vhost, strerror(errno));
525 exit(EXIT_FAILURE);
526 }
527
528 freeaddrinfo(res);
529 }
530 }
531
532 /* irc_reconnect
533 *
534 * Close connection to IRC server.
535 *
536 * Parameters: NONE
537 *
538 * Return: NONE
539 */
540 static void
541 irc_reconnect(void)
542 {
543 time_t present;
544
545 time(&present);
546
547 /* Only try to reconnect every IRCItem.reconnectinterval seconds */
548 if ((present - IRC_LASTRECONNECT) < IRCItem.reconnectinterval)
549 {
550 /* Sleep to avoid excessive CPU */
551 sleep(1);
552 return;
553 }
554
555 time(&IRC_LASTRECONNECT);
556
557 if (IRC_FD > -1)
558 {
559 close(IRC_FD);
560 IRC_FD = -1; /* Set IRC_FD -1 for reconnection on next irc_cycle(). */
561 }
562
563 log_printf("IRC -> Connection to (%s) failed, reconnecting.", IRCItem.server);
564 }
565
566 /* irc_connect
567 *
568 * Connect to IRC server.
569 * XXX: FD allocation done here
570 *
571 * Parameters: NONE
572 * Return: NONE
573 */
574 static void
575 irc_connect(void)
576 {
577 /* Connect to IRC server as client. */
578 if (connect(IRC_FD, (struct sockaddr *)&IRC_SVR, svr_addrlen) == -1)
579 {
580 log_printf("IRC -> connect(): error connecting to %s: %s",
581 IRCItem.server, strerror(errno));
582
583 if (errno == EISCONN /* Already connected */ || errno == EALREADY /* Previous attempt not complete */)
584 return;
585
586 /* Try to connect again */
587 irc_reconnect();
588 return;
589 }
590
591 irc_send("NICK %s", IRCItem.nick);
592
593 if (!EmptyString(IRCItem.password))
594 irc_send("PASS %s", IRCItem.password);
595
596 irc_send("USER %s %s %s :%s",
597 IRCItem.username,
598 IRCItem.username,
599 IRCItem.username,
600 IRCItem.realname);
601 time(&IRC_LAST);
602 }
603
604 /* irc_parse
605 *
606 * irc_parse is called by irc_read when a full line of data
607 * is ready to be parsed.
608 *
609 * Parameters: NONE
610 * Return: NONE
611 */
612 static void
613 irc_parse(void)
614 {
615 char *pos;
616
617 /*
618 * parv stores the parsed token, parc is the count of the parsed
619 * tokens
620 *
621 * parv[0] is ALWAYS the source, and is the server name of the
622 * source did not exist
623 */
624 char *parv[17];
625 unsigned int parc = 1;
626 char msg[MSGLENMAX]; /* Temporarily stores IRC msg to pass to handlers */
627
628 struct CommandHash
629 {
630 const char *command;
631 void (*handler)(char *[], unsigned int, const char *, const char *);
632 };
633
634 /*
635 * Table should be ordered with most occuring (or priority)
636 * commands at the top of the list.
637 */
638 static const struct CommandHash COMMAND_TABLE[] =
639 {
640 { .command = "NOTICE", .handler = m_notice },
641 { .command = "PRIVMSG", .handler = m_privmsg },
642 { .command = "PING", .handler = m_ping },
643 { .command = "INVITE", .handler = m_invite },
644 { .command = "001", .handler = m_perform },
645 { .command = "302", .handler = m_userhost },
646 { .command = "471", .handler = m_cannot_join },
647 { .command = "473", .handler = m_cannot_join },
648 { .command = "474", .handler = m_cannot_join },
649 { .command = "475", .handler = m_cannot_join },
650 { .command = "KILL", .handler = m_kill },
651 { .command = NULL }
652 };
653
654 if (IRC_RAW_LEN == 0)
655 return;
656
657 if (OPT_DEBUG >= 2)
658 log_printf("IRC READ -> %s", IRC_RAW);
659
660 time(&IRC_LAST);
661
662 /* Store a copy of IRC_RAW for the handlers (for functions that need PROOF) */
663 strlcpy(msg, IRC_RAW, sizeof(msg));
664
665 /* parv[0] is always the source */
666 if (IRC_RAW[0] == ':')
667 parv[0] = IRC_RAW + 1;
668 else
669 {
670 parv[0] = IRCItem.server;
671 parv[parc++] = IRC_RAW;
672 }
673
674 pos = IRC_RAW;
675
676 while ((pos = strchr(pos, ' ')) && parc <= 17)
677 {
678 /* Avoid excessive spaces and end of IRC_RAW */
679 if (*(pos + 1) == ' ' || *(pos + 1) == '\0')
680 {
681 pos++;
682 continue;
683 }
684
685 /* Anything after a : is considered the final string of the message */
686 if (*(pos + 1) == ':')
687 {
688 parv[parc++] = pos + 2;
689 *pos = '\0';
690 break;
691 }
692
693 /*
694 * Set the next parv at this position and replace the space with a
695 * \0 for the previous parv
696 */
697 parv[parc++] = pos + 1;
698 *pos = '\0';
699 pos++;
700 }
701
702 /*
703 * Determine which command this is from the command table
704 * and let the handler for that command take control
705 */
706 for (const struct CommandHash *cmd = COMMAND_TABLE; cmd->command; ++cmd)
707 {
708 if (strcasecmp(cmd->command, parv[1]) == 0)
709 {
710 cmd->handler(parv, parc, msg, userinfo_create(parv[0]));
711 break;
712 }
713 }
714 }
715
716 /* irc_read
717 *
718 * irc_read is called by irc_cycle when new data is ready to be
719 * read from the irc server.
720 *
721 * Parameters: NONE
722 * Return: NONE
723 */
724 static void
725 irc_read(void)
726 {
727 ssize_t len;
728 char c;
729
730 while ((len = read(IRC_FD, &c, 1)) > 0)
731 {
732 if (c == '\r')
733 continue;
734
735 if (c == '\n')
736 {
737 /* Null string. */
738 IRC_RAW[IRC_RAW_LEN] = '\0';
739
740 /* Parse line. */
741 irc_parse();
742
743 /* Reset counter. */
744 IRC_RAW_LEN = 0;
745 break;
746 }
747
748 if (c != '\0')
749 IRC_RAW[IRC_RAW_LEN++] = c;
750 }
751
752 if ((len <= 0) && (errno != EAGAIN))
753 {
754 if (errno != EINTR)
755 log_printf("IRC -> Error reading data from server: %s", strerror(errno));
756
757 irc_reconnect();
758 IRC_RAW_LEN = 0;
759 return;
760 }
761 }
762
763 /* irc_cycle
764 *
765 * Pass control to the IRC portion of HOPM to handle any awaiting IRC events.
766 *
767 * Parameters:
768 * None
769 *
770 * Return:
771 * None
772 */
773 void
774 irc_cycle(void)
775 {
776 static struct pollfd pfd;
777
778 if (IRC_FD == -1)
779 {
780 /* Initialize negative cache. */
781 if (OptionsItem.negcache)
782 negcache_init();
783
784 /* Resolve remote host. */
785 irc_init();
786
787 /* Connect to remote host. */
788 irc_connect();
789 return; /* In case connect() immediately failed */
790 }
791
792 pfd.fd = IRC_FD;
793 pfd.events = POLLIN;
794
795 /* Block .050 seconds to avoid excessive CPU use on poll(). */
796 switch (poll(&pfd, 1, 50))
797 {
798 case 0:
799 case -1:
800 break;
801 default:
802 /* Check if IRC data is available. */
803 if (pfd.revents & POLLIN)
804 irc_read();
805 else if (pfd.revents & (POLLERR | POLLHUP))
806 irc_reconnect();
807
808 break;
809 }
810 }
811
812 /* irc_send
813 *
814 * Send data to remote IRC host.
815 *
816 * Parameters:
817 * data: Format of data to send
818 * ...: varargs to format with
819 *
820 * Return: NONE
821 */
822 void
823 irc_send(const char *data, ...)
824 {
825 va_list arglist;
826 char buf[MSGLENMAX];
827 size_t len = 0;
828
829 va_start(arglist, data);
830 len = vsnprintf(buf, sizeof(buf), data, arglist);
831 va_end(arglist);
832
833 if (OPT_DEBUG >= 2)
834 log_printf("IRC SEND -> %s", buf);
835
836 if (len > 510)
837 len = 510;
838
839 buf[len++] = '\r';
840 buf[len++] = '\n';
841
842 if (send(IRC_FD, buf, len, 0) == -1)
843 {
844 /* Return of -1 indicates error sending data; we reconnect. */
845 log_printf("IRC -> Error sending data to server: %s", strerror(errno));
846 irc_reconnect();
847 }
848 }
849
850 /* irc_send_channels
851 *
852 * Send privmsg to all channels.
853 *
854 * Parameters:
855 * data: Format of data to send
856 * ...: varargs to format with
857 *
858 * Return: NONE
859 */
860 void
861 irc_send_channels(const char *data, ...)
862 {
863 const node_t *node;
864 va_list arglist;
865 char buf[MSGLENMAX];
866
867 va_start(arglist, data);
868 vsnprintf(buf, sizeof(buf), data, arglist);
869 va_end(arglist);
870
871 LIST_FOREACH(node, IRCItem.channels.head)
872 {
873 const struct ChannelConf *chan = node->data;
874
875 irc_send("PRIVMSG %s :%s", chan->name, buf);
876 }
877 }
878
879 /* irc_timer
880 *
881 * Functions to be performed every ~seconds.
882 *
883 * Parameters: NONE
884 * Return: NONE
885 */
886 void
887 irc_timer(void)
888 {
889 time_t present, delta;
890
891 time(&present);
892
893 delta = present - IRC_LAST;
894
895 /* No data in IRCItem.readtimeout seconds */
896 if (delta >= IRCItem.readtimeout)
897 {
898 log_printf("IRC -> Timeout awaiting data from server.");
899 irc_reconnect();
900
901 /* Make sure we don't do this again for a while */
902 time(&IRC_LAST);
903 }
904 else if (delta >= IRCItem.readtimeout / 2)
905 {
906 /*
907 * Generate some data so high ping times don't cause uneeded
908 * reconnections
909 */
910 irc_send("PING :HOPM");
911 }
912 }

Properties

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