ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/trunk/src/irc.c
(Generate patch)

Comparing hopm/trunk/src/irc.c (file contents):
Revision 5367 by michael, Mon Jan 12 20:13:22 2015 UTC vs.
Revision 8178 by michael, Thu Apr 13 14:55:40 2017 UTC

# Line 1 | Line 1
1   /*
2   *  Copyright (c) 2002-2003 Erik Fears
3 < *  Copyright (c) 2014-2015 ircd-hybrid development team
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
# Line 36 | Line 36
36   #include <errno.h>
37   #include <stdarg.h>
38   #include <regex.h>
39 + #include <assert.h>
40  
41   #include "config.h"
42   #include "irc.h"
# Line 50 | Line 51
51   #include "negcache.h"
52   #include "memory.h"
53   #include "main.h"
54 <
54 <
55 < static void irc_init(void);
56 < static void irc_connect(void);
57 < static void irc_reconnect(void);
58 < static void irc_read(void);
59 < static void irc_parse(void);
60 <
61 < static struct ChannelConf *get_channel(const char *);
62 <
63 < static struct UserInfo *userinfo_create(char *);
64 < static void userinfo_free(struct UserInfo *source);
65 <
66 < static void m_ping(char *[], unsigned int, char *, const struct UserInfo *);
67 < static void m_invite(char *[], unsigned int, char *, const struct UserInfo *);
68 < static void m_privmsg(char *[], unsigned int, char *, const struct UserInfo *);
69 < static void m_ctcp(char *[], unsigned int, char *, const struct UserInfo *);
70 < static void m_notice(char *[], unsigned int, char *, const struct UserInfo *);
71 < static void m_perform(char *[], unsigned int, char *, const struct UserInfo *);
72 < static void m_userhost(char *[], unsigned int, char *, const struct UserInfo *);
73 < static void m_cannot_join(char *[], unsigned int, char *, const struct UserInfo *);
74 < static void m_kill(char *[], unsigned int, char *, const struct UserInfo *);
54 > #include "serno.h"
55  
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 + static char         IRC_RAW[MSGLENMAX];  /* Buffer to read data into               */
62 + static unsigned int IRC_RAW_LEN;         /* Position of IRC_RAW                    */
63 + static int          IRC_FD = -1;         /* File descriptor for IRC client         */
64 +
65 + static struct sockaddr_storage IRC_SVR;  /* Sock Address Struct for IRC server     */
66 + static socklen_t svr_addrlen;
67 + static time_t IRC_LAST;                  /* Last full line of data from irc server */
68 + static time_t IRC_LASTRECONNECT;         /* Time of last reconnection              */
69  
82 static char          IRC_RAW[MSGLENMAX];         /* Buffer to read data into              */
83 static unsigned int  IRC_RAW_LEN    = 0;         /* Position of IRC_RAW                   */
84 static int           IRC_FD         = 0;         /* File descriptor for IRC client        */
70  
71 < static struct sockaddr_storage IRC_SVR;          /* Sock Address Struct for IRC server    */
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 > static const struct ChannelConf *
82 > get_channel(const char *name)
83 > {
84 >  node_t *node;
85  
86 < static time_t IRC_LAST = 0;                      /* Last full line of data from irc server*/
87 < static time_t IRC_LASTRECONNECT = 0;             /* Time of last reconnection */
86 >  LIST_FOREACH(node, IRCItem->channels->head)
87 >  {
88 >    struct ChannelConf *item = node->data;
89  
90 < /*
91 < * Table should be ordered with most occuring (or priority)
92 < * commands at the top of the list.
90 >    if (strcasecmp(item->name, name) == 0)
91 >      return item;
92 >  }
93 >
94 >  return NULL;
95 > }
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 < static struct CommandHash COMMAND_TABLE[] =
109 > static void
110 > m_perform(char *parv[], unsigned int parc, const char *msg, const char *source_p)
111   {
112 <  { "NOTICE",   m_notice      },
98 <  { "PRIVMSG",  m_privmsg     },
99 <  { "PING",     m_ping        },
100 <  { "INVITE",   m_invite      },
101 <  { "001",      m_perform     },
102 <  { "302",      m_userhost    },
103 <  { "471",      m_cannot_join },
104 <  { "473",      m_cannot_join },
105 <  { "474",      m_cannot_join },
106 <  { "475",      m_cannot_join },
107 <  { "KILL",     m_kill        },
108 <  { NULL,       NULL          }
109 < };
112 >  node_t *node;
113  
114 < /* irc_cycle
114 >  log_printf("IRC -> Connected to %s/%d", IRCItem->server, IRCItem->port);
115 >
116 >  /* Identify to nickserv if needed */
117 >  if (!EmptyString(IRCItem->nickserv))
118 >    irc_send("%s", IRCItem->nickserv);
119 >
120 >  /* Oper */
121 >  irc_send("OPER %s", IRCItem->oper);
122 >
123 >  /* Set modes */
124 >  irc_send("MODE %s %s", IRCItem->nick, IRCItem->mode);
125 >
126 >  /* Set Away */
127 >  if (!EmptyString(IRCItem->away))
128 >    irc_send("AWAY :%s", IRCItem->away);
129 >
130 >  /* Perform */
131 >  LIST_FOREACH(node, IRCItem->performs->head)
132 >    irc_send("%s", node->data);
133 >
134 >  /* Join all listed channels. */
135 >  LIST_FOREACH(node, IRCItem->channels->head)
136 >  {
137 >    const struct ChannelConf *channel = node->data;
138 >
139 >    if (EmptyString(channel->name))
140 >      continue;
141 >
142 >    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 > }
148 >
149 > /* m_ping
150   *
151 < *    Pass control to the IRC portion of HOPM to handle any awaiting IRC events.
151 > * parv[0]  = source
152 > * parv[1]  = PING
153 > * parv[2]  = PING TS/Package
154   *
155 < * Parameters:
156 < *    None
155 > * source_p: UserInfo struct of the source user, or NULL if
156 > * the source (parv[0]) is a server.
157 > */
158 > static void
159 > m_ping(char *parv[], unsigned int parc, const char *msg, const char *source_p)
160 > {
161 >  if (parc < 3)
162 >    return;
163 >
164 >  if (OPT_DEBUG >= 2)
165 >    log_printf("IRC -> PING? PONG!");
166 >
167 >  irc_send("PONG %s", parv[2]);
168 > }
169 >
170 > /* m_invite
171   *
172 < * Return:
173 < *    None
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 < void
181 < irc_cycle(void)
180 > static void
181 > m_invite(char *parv[], unsigned int parc, const char *msg, const char *source_p)
182   {
183 <  static struct pollfd pfd;
183 >  const struct ChannelConf *channel = NULL;
184 >
185 >  if (parc < 4)
186 >    return;
187 >
188 >  log_printf("IRC -> Invited to %s by %s", parv[3], parv[0]);
189  
190 <  if (IRC_FD <= 0)
190 >  if ((channel = get_channel(parv[3])) == NULL)
191 >    return;
192 >
193 >  irc_send("JOIN %s %s", channel->name, channel->key);
194 > }
195 >
196 > /* 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 > m_ctcp(char *parv[], unsigned int parc, const char *msg, const char *source_p)
207 > {
208 >  if (strncasecmp(parv[3], "\001VERSION\001", 9) == 0)
209 >    irc_send("NOTICE %s :\001VERSION Hybrid Open Proxy Monitor %s(%s)\001",
210 >             source_p, VERSION, SERIALNUM);
211 > }
212 >
213 > /* 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 > static void
224 > m_privmsg(char *parv[], unsigned int parc, const char *msg, const char *source_p)
225 > {
226 >  const struct ChannelConf *channel = NULL;
227 >
228 >  if (source_p == NULL)
229 >    return;
230 >
231 >  if (parc < 4)
232 >    return;
233 >
234 >  /* CTCP */
235 >  if (*parv[3] == '\001')
236    {
237 <    /* Initialise negative cache. */
238 <    if (OptionsItem->negcache > 0)
239 <      nc_init(&nc_head);
237 >    m_ctcp(parv, parc, msg, source_p);
238 >    return;
239 >  }
240  
241 <    /* Resolve remote host. */
242 <    irc_init();
241 >  /* Only interested in privmsg to channels */
242 >  if (*parv[2] != '#' && *parv[2] != '&')
243 >    return;
244  
245 <    /* Connect to remote host. */
246 <    irc_connect();
247 <    return;  /* In case connect() immediately failed */
245 >  /* Get a target */
246 >  if ((channel = get_channel(parv[2])) == NULL)
247 >    return;
248 >
249 >  int hit = strncasecmp(parv[3], "!all ", 5) == 0;
250 >  if (hit == 0)
251 >  {
252 >    size_t nick_len = strlen(IRCItem->nick);
253 >
254 >    if (strncasecmp(parv[3], IRCItem->nick, nick_len) == 0)
255 >      hit = *(parv[3] + nick_len) == ' ';
256    }
257  
258 <  pfd.fd = IRC_FD;
259 <  pfd.events = POLLIN;
258 >  if (hit)
259 >    /* XXX command_parse will alter parv[3]. */
260 >    command_parse(parv[3], channel, source_p);
261 > }
262  
263 <  /* Block .025 seconds to avoid excessive CPU use on poll(). */
264 <  switch (poll(&pfd, 1, 25))
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 <    case  0:
147 <    case -1:
148 <      break;
149 <    default:
150 <      /* Check if IRC data is available. */
151 <      if (pfd.revents & POLLIN)
152 <        irc_read();
293 >    preg = xcalloc(sizeof(*preg));
294  
295 <      break;
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:
455 > * Parameters:
456   *    None
457 < *
457 > *
458   * Return:
459   *    None
460   */
# Line 170 | Line 463 | irc_init(void)
463   {
464    const void *address = NULL;
465  
466 <  if (IRC_FD)
174 <    close(IRC_FD);
466 >  assert(IRC_FD == -1);
467  
468    memset(&IRC_SVR, 0, sizeof(IRC_SVR));
469  
# Line 180 | Line 472 | irc_init(void)
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));
# Line 188 | Line 481 | irc_init(void)
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));
# Line 195 | Line 489 | irc_init(void)
489    else
490    {
491      log_printf("IRC -> firedns_resolveip(\"%s\"): %s", IRCItem->server,
492 <               firedns_strerror(fdns_errno));
492 >               firedns_strerror(firedns_errno));
493      exit(EXIT_FAILURE);
494    }
495  
# Line 212 | Line 506 | irc_init(void)
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_PASSIVE | AI_NUMERICHOST;
515 >    hints.ai_flags = AI_NUMERICHOST;
516  
517 <    if (getaddrinfo(IRCItem->vhost, NULL, &hints, &res))
517 >    if ((n = getaddrinfo(IRCItem->vhost, NULL, &hints, &res)))
518      {
519 <      log_printf("IRC -> bind(): %s is an invalid address", IRCItem->vhost);
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 -> bind(): error binding to %s: %s", IRCItem->vhost, strerror(errno));
524 >      log_printf("IRC -> error binding to %s: %s", IRCItem->vhost, strerror(errno));
525        exit(EXIT_FAILURE);
526      }
527  
# Line 234 | Line 529 | irc_init(void)
529    }
530   }
531  
532 < /* irc_send
532 > /* irc_reconnect
533   *
534 < *    Send data to remote IRC host.
534 > *    Close connection to IRC server.
535 > *
536 > * Parameters: NONE
537   *
241 * Parameters:
242 *    data: Format of data to send
243 *    ...: varargs to format with
244 *
538   * Return: NONE
539   */
540 < void
541 < irc_send(const char *data, ...)
540 > static void
541 > irc_reconnect(void)
542   {
543 <  va_list arglist;
251 <  char buf[MSGLENMAX];
252 <  size_t len = 0;
253 <
254 <  va_start(arglist, data);
255 <  len = vsnprintf(buf, sizeof(buf), data, arglist);
256 <  va_end(arglist);
257 <
258 <  if (OPT_DEBUG >= 2)
259 <    log_printf("IRC SEND -> %s", buf);
260 <
261 <  if (len > 510)
262 <    len = 510;
543 >  time_t present;
544  
545 <  buf[len++] = '\r';
265 <  buf[len++] = '\n';
545 >  time(&present);
546  
547 <  if (send(IRC_FD, buf, len, 0) == -1)
547 >  /* Only try to reconnect every IRCItem->reconnectinterval seconds */
548 >  if ((present - IRC_LASTRECONNECT) < IRCItem->reconnectinterval)
549    {
550 <    /* Return of -1 indicates error sending data; we reconnect. */
551 <    log_printf("IRC -> Error sending data to server: %s", strerror(errno));
552 <    irc_reconnect();
550 >    /* Sleep to avoid excessive CPU */
551 >    sleep(1);
552 >    return;
553    }
273 }
274
275 /* irc_send
276 *
277 *    Send privmsg to all channels.
278 *
279 * Parameters:
280 *    data: Format of data to send
281 *    ...: varargs to format with
282 *
283 * Return: NONE
284 */
285 void
286 irc_send_channels(const char *data, ...)
287 {
288  const node_t *node;
289  va_list arglist;
290  char buf[MSGLENMAX];
554  
555 <  va_start(arglist, data);
293 <  vsnprintf(buf, sizeof(buf), data, arglist);
294 <  va_end(arglist);
555 >  time(&IRC_LASTRECONNECT);
556  
557 <  LIST_FOREACH(node, IRCItem->channels->head)
557 >  if (IRC_FD > -1)
558    {
559 <    const struct ChannelConf *chan = node->data;
560 <
300 <    irc_send("PRIVMSG %s :%s", chan->name, buf);
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
# Line 313 | Line 575 | static void
575   irc_connect(void)
576   {
577    /* Connect to IRC server as client. */
578 <  if (connect(IRC_FD, (struct sockaddr *)&IRC_SVR, sizeof(IRC_SVR)) == -1)
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));
# Line 339 | Line 601 | irc_connect(void)
601    time(&IRC_LAST);
602   }
603  
342 /* irc_reconnect
343 *
344 *    Close connection to IRC server.
345 *
346 * Parameters: NONE
347 *
348 * Return: NONE
349 */
350 static void
351 irc_reconnect(void)
352 {
353  time_t present;
354
355  time(&present);
356
357  /* Only try to reconnect every RECONNECT_INTERVAL seconds */
358  if ((present - IRC_LASTRECONNECT) < RECONNECTINTERVAL)
359  {
360    /* Sleep to avoid excessive CPU */
361    sleep(1);
362    return;
363  }
364
365  time(&IRC_LASTRECONNECT);
366
367  if (IRC_FD > 0)
368    close(IRC_FD);
369
370  /* Set IRC_FD 0 for reconnection on next irc_cycle(). */
371  IRC_FD = 0;
372
373  log_printf("IRC -> Connection to (%s) failed, reconnecting.", IRCItem->server);
374 }
375
376 /* irc_read
377 *
378 *    irc_read is called my irc_cycle when new data is ready to be
379 *    read from the irc server.
380 *
381 * Parameters: NONE
382 * Return: NONE
383 */
384 static void
385 irc_read(void)
386 {
387  int len;
388  char c;
389
390  while ((len = read(IRC_FD, &c, 1)) > 0)
391  {
392    if (c == '\r')
393      continue;
394
395    if (c == '\n')
396    {
397      /* Null string. */
398      IRC_RAW[IRC_RAW_LEN] = '\0';
399
400      /* Parse line. */
401      irc_parse();
402
403      /* Reset counter. */
404      IRC_RAW_LEN = 0;
405      break;
406    }
407
408    if (c != '\0')
409      IRC_RAW[IRC_RAW_LEN++] = c;
410  }
411
412  if ((len <= 0) && (errno != EAGAIN))
413  {
414    if (OPT_DEBUG >= 2)
415      log_printf("irc_read -> errno=%d len=%d", errno, len);
416
417    irc_reconnect();
418    IRC_RAW_LEN = 0;
419    return;
420  }
421 }
422
604   /* irc_parse
605   *
606   *    irc_parse is called by irc_read when a full line of data
# Line 431 | Line 612 | irc_read(void)
612   static void
613   irc_parse(void)
614   {
434  struct UserInfo *source_p;
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 source
622 <   * did not exist
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 <  char            *parv[17];
639 <  unsigned int     parc = 1;
640 <  char             msg[MSGLENMAX];    /* Temporarily stores IRC msg to pass to handlers */
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;
# Line 493 | Line 699 | irc_parse(void)
699      pos++;
700    }
701  
496  /* Generate a UserInfo struct from the source */
497  source_p = userinfo_create(parv[0]);
498
702    /*
703     * Determine which command this is from the command table
704     * and let the handler for that command take control
# Line 504 | Line 707 | irc_parse(void)
707    {
708      if (strcasecmp(cmd->command, parv[1]) == 0)
709      {
710 <      cmd->handler(parv, parc, msg, source_p);
710 >      cmd->handler(parv, parc, msg, userinfo_create(parv[0]));
711        break;
712      }
713    }
511
512  userinfo_free(source_p);
714   }
715  
716 < /* irc_timer
716 > /* irc_read
717   *
718 < *    Functions to be performed every ~seconds.
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 < void
725 < irc_timer(void)
724 > static void
725 > irc_read(void)
726   {
727 <  time_t present, delta;
727 >  ssize_t len;
728 >  char c;
729  
730 <  time(&present);
730 >  while ((len = read(IRC_FD, &c, 1)) > 0)
731 >  {
732 >    if (c == '\r')
733 >      continue;
734  
735 <  delta = present - IRC_LAST;
735 >    if (c == '\n')
736 >    {
737 >      /* Null string. */
738 >      IRC_RAW[IRC_RAW_LEN] = '\0';
739  
740 <  /* No data in IRCItem->readtimeout seconds */
741 <  if (delta >= IRCItem->readtimeout)
533 <  {
534 <    log_printf("IRC -> Timeout awaiting data from server.");
535 <    irc_reconnect();
740 >      /* Parse line. */
741 >      irc_parse();
742  
743 <    /* Make sure we don't do this again for a while */
744 <    time(&IRC_LAST);
745 <  }
746 <  else if (delta >= IRCItem->readtimeout / 2)
541 <  {
542 <    /*
543 <     * Generate some data so high ping times don't cause uneeded
544 <     * reconnections
545 <     */
546 <    irc_send("PING :HOPM");
547 <  }
548 < }
743 >      /* Reset counter. */
744 >      IRC_RAW_LEN = 0;
745 >      break;
746 >    }
747  
748 < /* get_channel
749 < *
750 < *    Check if a channel is defined in our conf. If so return
553 < *    a pointer to it.
554 < *
555 < * Parameters:
556 < *    channel: channel to search conf for
557 < *
558 < * Return: Pointer to ChannelConf containing the channel
559 < */
560 < static struct ChannelConf *
561 < get_channel(const char *channel)
562 < {
563 <  node_t *node;
748 >    if (c != '\0')
749 >      IRC_RAW[IRC_RAW_LEN++] = c;
750 >  }
751  
752 <  LIST_FOREACH(node, IRCItem->channels->head)
752 >  if ((len <= 0) && (errno != EAGAIN))
753    {
754 <    struct ChannelConf *item = node->data;
754 >    if (errno != EINTR)
755 >      log_printf("IRC -> Error reading data from server: %s", strerror(errno));
756  
757 <    if (strcasecmp(item->name, channel) == 0)
758 <      return item;
757 >    irc_reconnect();
758 >    IRC_RAW_LEN = 0;
759 >    return;
760    }
572
573  return NULL;
761   }
762  
763 < /* userinfo_create
763 > /* irc_cycle
764   *
765 < *    Parse a nick!user@host into a UserInfo struct
579 < *    and return a pointer to the new struct.
765 > *    Pass control to the IRC portion of HOPM to handle any awaiting IRC events.
766   *
767   * Parameters:
768 < *    source: nick!user@host to parse
768 > *    None
769   *
770   * Return:
771 < *    pointer to new UserInfo struct, or NULL if parsing failed
771 > *    None
772   */
773 < static struct UserInfo *
774 < userinfo_create(char *source)
773 > void
774 > irc_cycle(void)
775   {
776 <  struct UserInfo *ret;
591 <  char *nick;
592 <  char *username;
593 <  char *hostname;
594 <  char *tmp;
595 <  int i, len;
596 <
597 <  nick = username = hostname = NULL;
598 <  tmp = xstrdup(source);
599 <  len = strlen(tmp);
600 <  nick = tmp;
776 >  static struct pollfd pfd;
777  
778 <  for (i = 0; i < len; ++i)
778 >  if (IRC_FD == -1)
779    {
780 <    if (tmp[i] == '!')
781 <    {
782 <      tmp[i] = '\0';
607 <      username = tmp + i + 1;
608 <    }
780 >    /* Initialize negative cache. */
781 >    if (OptionsItem->negcache)
782 >      negcache_init();
783  
784 <    if (tmp[i] == '@')
785 <    {
612 <      tmp[i] = '\0';
613 <      hostname = tmp + i + 1;
614 <    }
615 <  }
784 >    /* Resolve remote host. */
785 >    irc_init();
786  
787 <  if (nick == NULL || username == NULL || hostname == NULL)
788 <  {
789 <    MyFree(tmp);
620 <    return NULL;
787 >    /* Connect to remote host. */
788 >    irc_connect();
789 >    return;  /* In case connect() immediately failed */
790    }
791  
792 <  ret = xcalloc(sizeof *ret);
793 <  ret->irc_nick     = xstrdup(nick);
625 <  ret->irc_username = xstrdup(username);
626 <  ret->irc_hostname = xstrdup(hostname);
627 <
628 <  MyFree(tmp);
629 <
630 <  return ret;
631 < };
792 >  pfd.fd = IRC_FD;
793 >  pfd.events = POLLIN;
794  
795 < /* userinfo_free
796 < *
797 < *    Free a UserInfo struct created with userinfo_create.
798 < *
799 < * Parameters:
800 < *    source: struct to free
801 < *
802 < * Return: None
803 < */
804 < static void
805 < userinfo_free(struct UserInfo *source_p)
806 < {
645 <  if (source_p == NULL)
646 <    return;
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 <  MyFree(source_p->irc_nick);
809 <  MyFree(source_p->irc_username);
650 <  MyFree(source_p->irc_hostname);
651 <  MyFree(source_p);
808 >      break;
809 >  }
810   }
811  
812 < /* m_perform
812 > /* irc_send
813   *
814 < *    actions to perform on IRC connection
814 > *    Send data to remote IRC host.
815   *
816   * Parameters:
817 < * parv[0]  = source
818 < * parv[1]  = PING
661 < * parv[2]  = PING TS/Package
817 > *    data: Format of data to send
818 > *    ...: varargs to format with
819   *
820 < * source_p: UserInfo struct of the source user, or NULL if
664 < * the source (parv[0]) is a server.
820 > * Return: NONE
821   */
822 < static void
823 < m_perform(char *parv[], unsigned int parc, char *msg, const struct UserInfo *notused)
822 > void
823 > irc_send(const char *data, ...)
824   {
825 <  node_t *node;
826 <
827 <  log_printf("IRC -> Connected to %s/%d", IRCItem->server, IRCItem->port);
672 <
673 <  /* Identify to nickserv if needed */
674 <  if (!EmptyString(IRCItem->nickserv))
675 <    irc_send("%s", IRCItem->nickserv);
825 >  va_list arglist;
826 >  char buf[MSGLENMAX];
827 >  size_t len = 0;
828  
829 <  /* Oper */
830 <  irc_send("OPER %s", IRCItem->oper);
829 >  va_start(arglist, data);
830 >  len = vsnprintf(buf, sizeof(buf), data, arglist);
831 >  va_end(arglist);
832  
833 <  /* Set modes */
834 <  irc_send("MODE %s %s", IRCItem->nick, IRCItem->mode);
833 >  if (OPT_DEBUG >= 2)
834 >    log_printf("IRC SEND -> %s", buf);
835  
836 <  /* Set Away */
837 <  if (!EmptyString(IRCItem->away))
685 <    irc_send("AWAY :%s", IRCItem->away);
836 >  if (len > 510)
837 >    len = 510;
838  
839 <  /* Perform */
840 <  LIST_FOREACH(node, IRCItem->performs->head)
689 <    irc_send("%s", node->data);
839 >  buf[len++] = '\r';
840 >  buf[len++] = '\n';
841  
842 <  /* Join all listed channels. */
692 <  LIST_FOREACH(node, IRCItem->channels->head)
842 >  if (send(IRC_FD, buf, len, 0) == -1)
843    {
844 <    const struct ChannelConf *channel = node->data;
845 <
846 <    if (EmptyString(channel->name))
697 <      continue;
698 <
699 <    if (!EmptyString(channel->key))
700 <      irc_send("JOIN %s %s", channel->name, channel->key);
701 <    else
702 <      irc_send("JOIN %s", channel->name);
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 < /* m_ping
707 < *
708 < * parv[0]  = source
709 < * parv[1]  = PING
710 < * parv[2]  = PING TS/Package
711 < *
712 < * source_p: UserInfo struct of the source user, or NULL if
713 < * the source (parv[0]) is a server.
714 < */
715 < static void
716 < m_ping(char *parv[], unsigned int parc, char *msg, const struct UserInfo *source_p)
717 < {
718 <  if (parc < 3)
719 <    return;
720 <
721 <  if (OPT_DEBUG >= 2)
722 <    log_printf("IRC -> PING? PONG!");
723 <
724 <  irc_send("PONG %s", parv[2]);
725 < }
726 <
727 < /* m_invite
728 < *
729 < * parv[0]  = source
730 < * parv[1]  = INVITE
731 < * parv[2]  = target
732 < * parv[3]  = channel
850 > /* irc_send_channels
851   *
852 < * source_p: UserInfo struct of the source user, or NULL if
735 < * the source (parv[0]) is a server.
736 < */
737 < static void
738 < m_invite(char *parv[], unsigned int parc, char *msg, const struct UserInfo *source_p)
739 < {
740 <  struct ChannelConf *channel = NULL;
741 <
742 <  if (parc < 4)
743 <    return;
744 <
745 <  log_printf("IRC -> Invited to %s by %s", parv[3], parv[0]);
746 <
747 <  if ((channel = get_channel(parv[3])) == NULL)
748 <    return;
749 <
750 <  irc_send("JOIN %s %s", channel->name, channel->key);
751 < }
752 <
753 < /* m_privmsg
852 > *    Send privmsg to all channels.
853   *
854 < * parv[0]  = source
855 < * parv[1]  = PRIVMSG
856 < * parv[2]  = target (channel or user)
758 < * parv[3]  = message
854 > * Parameters:
855 > *    data: Format of data to send
856 > *    ...: varargs to format with
857   *
858 < * source_p: UserInfo struct of the source user, or NULL if
761 < * the source (parv[0]) is a server.
858 > * Return: NONE
859   */
860 < static void
861 < m_privmsg(char *parv[], unsigned int parc, char *msg, const struct UserInfo *source_p)
860 > void
861 > irc_send_channels(const char *data, ...)
862   {
863 <  struct ChannelConf *channel = NULL;
864 <  size_t nick_len;
865 <
769 <  if (source_p == NULL)
770 <    return;
771 <
772 <  if (parc < 4)
773 <    return;
774 <
775 <  /* CTCP */
776 <  if (parv[3][0] == '\001')
777 <    m_ctcp(parv, parc, msg, source_p);
778 <
779 <  /* Only interested in privmsg to channels */
780 <  if (parv[2][0] != '#' && parv[2][0] != '&')
781 <    return;
782 <
783 <  /* Get a target */
784 <  if ((channel = get_channel(parv[2])) == NULL)
785 <    return;
786 <
787 <  /* Find a suitable length to compare with */
788 <  nick_len = strcspn(parv[3], " :,");
863 >  const node_t *node;
864 >  va_list arglist;
865 >  char buf[MSGLENMAX];
866  
867 <  if (nick_len < 3 && strlen(IRCItem->nick) >= 3)
868 <    nick_len = 3;
867 >  va_start(arglist, data);
868 >  vsnprintf(buf, sizeof(buf), data, arglist);
869 >  va_end(arglist);
870  
871 <  /* message is a command */
794 <  if (strncasecmp(parv[3], IRCItem->nick, nick_len) == 0  ||
795 <      strncasecmp(parv[3], "!all", 4) == 0)
871 >  LIST_FOREACH(node, IRCItem->channels->head)
872    {
873 <    /* XXX command_parse will alter parv[3]. */
798 <    command_parse(parv[3], channel, source_p);
799 <  }
800 < }
873 >    const struct ChannelConf *chan = node->data;
874  
875 < /* m_ctcp
876 < * parv[0]  = source
804 < * parv[1]  = PRIVMSG
805 < * parv[2]  = target (channel or user)
806 < * parv[3]  = message
807 < *
808 < * source_p: UserInfo struct of the source user, or NULL if
809 < * the source (parv[0]) is a server.
810 < *
811 < */
812 < static void
813 < m_ctcp(char *parv[], unsigned int parc, char *msg, const struct UserInfo *source_p)
814 < {
815 <  if (strncasecmp(parv[3], "\001VERSION\001", 9) == 0)
816 <    irc_send("NOTICE %s :\001VERSION Hybrid Open Proxy Monitor %s\001",
817 <             source_p->irc_nick, VERSION);
875 >    irc_send("PRIVMSG %s :%s", chan->name, buf);
876 >  }
877   }
878  
879 < /* m_notice
821 < *
822 < * parv[0]  = source
823 < * parv[1]  = NOTICE
824 < * parv[2]  = target
825 < * parv[3]  = message
826 < *
879 > /* irc_timer
880   *
881 < * source_p: UserInfo struct of the source user, or NULL if
829 < * the source (parv[0]) is a server.
881 > *    Functions to be performed every ~seconds.
882   *
883 + * Parameters: NONE
884 + * Return: NONE
885   */
886 < static void
887 < m_notice(char *parv[], unsigned int parc, char *msg, const struct UserInfo *source_p)
886 > void
887 > irc_timer(void)
888   {
889 <  static regex_t *preg = NULL;
836 <  regmatch_t pmatch[5];
837 <  int errnum;
838 <  const char *user[4];
889 >  time_t present, delta;
890  
891 <  /* Not interested in notices from users */
841 <  if (source_p)
842 <    return;
891 >  time(&present);
892  
893 <  if (parc < 4)
845 <    return;
893 >  delta = present - IRC_LAST;
894  
895 <  /* Compile the regular expression if it has not been already */
896 <  if (preg == NULL)
895 >  /* No data in IRCItem->readtimeout seconds */
896 >  if (delta >= IRCItem->readtimeout)
897    {
898 <    preg = xcalloc(sizeof *preg);
899 <
852 <    if ((errnum = regcomp(preg, IRCItem->connregex, REG_ICASE | REG_EXTENDED)))
853 <    {
854 <      char errmsg[256];
855 <
856 <      regerror(errnum, preg, errmsg, sizeof(errmsg));
857 <      log_printf("IRC REGEX -> Error when compiling regular expression");
858 <      log_printf("IRC REGEX -> %s", errmsg);
859 <
860 <      MyFree(preg);
861 <      preg = NULL;
862 <      return;
863 <    }
864 <  }
865 <
866 <  /* Match the expression against the possible connection notice */
867 <  if (regexec(preg, parv[3], 5, pmatch, 0))
868 <    return;
869 <
870 <  if (OPT_DEBUG > 0)
871 <    log_printf("IRC REGEX -> Regular expression caught connection notice. Parsing.");
898 >    log_printf("IRC -> Timeout awaiting data from server.");
899 >    irc_reconnect();
900  
901 <  if (pmatch[4].rm_so == -1)
902 <  {
875 <    log_printf("IRC REGEX -> pmatch[4].rm_so is -1 while parsing??? Aborting.");
876 <    return;
901 >    /* Make sure we don't do this again for a while */
902 >    time(&IRC_LAST);
903    }
904 <
879 <  /*
880 <   *   Offsets for data in the connection notice:
881 <   *
882 <   *   NICKNAME: pmatch[1].rm_so  TO  pmatch[1].rm_eo
883 <   *   USERNAME: pmatch[2].rm_so  TO  pmatch[2].rm_eo
884 <   *   HOSTNAME: pmatch[3].rm_so  TO  pmatch[3].rm_eo
885 <   *   IP      : pmatch[4].rm_so  TO  pmatch[4].rm_eo
886 <   */
887 <  for (unsigned int i = 0; i < 4; ++i)
904 >  else if (delta >= IRCItem->readtimeout / 2)
905    {
906 <    user[i] = (parv[3] + pmatch[i + 1].rm_so);
907 <    *(parv[3] + pmatch[i + 1].rm_eo) = '\0';
906 >    /*
907 >     * Generate some data so high ping times don't cause uneeded
908 >     * reconnections
909 >     */
910 >    irc_send("PING :HOPM");
911    }
892
893  if (OPT_DEBUG > 0)
894    log_printf("IRC REGEX -> Parsed %s!%s@%s [%s] from connection notice.",
895               user[0], user[1], user[2], user[3]);
896
897  /*FIXME (reminder) In the case of any rehash to the regex, preg MUST be freed first.
898      regfree(preg);
899   */
900
901  /* Pass this information off to scan.c */
902  scan_connect(user, msg);
903
904  /* Record the connect for stats purposes */
905  stats_connect();
906 }
907
908 /* m_userhost
909 *
910 * parv[0]  = source
911 * parv[1]  = USERHOST
912 * parv[2]  = target (hopm)
913 * parv[3]  = :nick=(flags)user@host
914 *
915 *
916 * source_p: UserInfo struct of the source user, or NULL if
917 * the source (parv[0]) is a server.
918 *
919 */
920 static void
921 m_userhost(char *parv[], unsigned int parc, char *msg, const struct UserInfo *source_p)
922 {
923  if (parc < 4)
924    return;
925
926  command_userhost(parv[3]);
927 }
928
929 /* m_cannot_join
930 *
931 * parv[0]  = source
932 * parv[1]  = numeric
933 * parv[2]  = target (hopm)
934 * parv[3]  = channel
935 * parv[4]  = error text
936 *
937 */
938 static void
939 m_cannot_join(char *parv[], unsigned int parc, char *msg, const struct UserInfo *source_p)
940 {
941  const struct ChannelConf *channel = NULL;
942
943  if (parc < 5)
944    return;
945
946  /* Is it one of our channels? */
947  if ((channel = get_channel(parv[3])) == NULL)
948    return;
949
950  if (EmptyString(channel->invite))
951    return;
952
953  irc_send("%s", channel->invite);
954 }
955
956 /* m_kill
957 *
958 * parv[0]  = source
959 * parv[1]  = numeric
960 * parv[2]  = target (hopm)
961 * parv[3]  = channel
962 * parv[4]  = error text
963 *
964 */
965 static void
966 m_kill(char *parv[], unsigned int parc, char *msg, const struct UserInfo *source_p)
967 {
968  /* Restart hopm to rehash */
969  main_restart();
912   }

Diff Legend

Removed lines
+ Added lines
< Changed lines (old)
> Changed lines (new)