ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/branches/1.1.x/src/irc.c
Revision: 7004
Committed: Fri Jan 1 00:06:28 2016 UTC (10 years, 6 months ago) by michael
Content type: text/x-csrc
File size: 19831 byte(s)
Log Message:
- Update copyright years

File Contents

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

Properties

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