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 9482 by michael, Sat Jul 4 19:07:21 2020 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-2020 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 30 | Line 30
30   #include <netinet/in.h>
31   #include <arpa/inet.h>
32   #include <poll.h>
33 #include <sys/time.h>
33   #include <time.h>
35
34   #include <errno.h>
35   #include <stdarg.h>
36   #include <regex.h>
37 + #include <assert.h>
38 + #ifdef HAVE_LIBCRYPTO
39 + #include <openssl/ssl.h>
40 + #include <openssl/x509v3.h>
41 + #include <openssl/err.h>
42 + #endif
43  
44   #include "config.h"
45   #include "irc.h"
# Line 50 | Line 54
54   #include "negcache.h"
55   #include "memory.h"
56   #include "main.h"
57 <
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 *);
57 > #include "serno.h"
58  
59  
60   /*
61   * Certain variables we don't want to allocate memory for over and over
62   * again so global scope is given.
63   */
64 + static char         IRC_RAW[MSGLENMAX];  /* Buffer to read data into               */
65 + static unsigned int IRC_RAW_LEN;         /* Position of IRC_RAW                    */
66 + static int          IRC_FD = -1;         /* File descriptor for IRC client         */
67 +
68 + static struct sockaddr_storage IRC_SVR;  /* Sock Address Struct for IRC server     */
69 + static socklen_t svr_addrlen;
70 + static time_t IRC_LAST;                  /* Last full line of data from irc server */
71 + static time_t IRC_LASTRECONNECT;         /* Time of last reconnection              */
72 +
73 + #ifdef HAVE_LIBCRYPTO
74 + static SSL_CTX *ssl_ctx;
75 + static SSL *ssl_handle;
76 + #endif
77 +
78 +
79 + /* get_channel
80 + *
81 + *    Check if a channel is defined in our conf. If so return
82 + *    a pointer to it.
83 + *
84 + * Parameters:
85 + *    channel: channel to search conf for
86 + *
87 + * Return: Pointer to ChannelConf containing the channel
88 + */
89 + static const struct ChannelConf *
90 + get_channel(const char *name)
91 + {
92 +  node_t *node;
93  
94 < static char          IRC_RAW[MSGLENMAX];         /* Buffer to read data into              */
95 < static unsigned int  IRC_RAW_LEN    = 0;         /* Position of IRC_RAW                   */
96 < static int           IRC_FD         = 0;         /* File descriptor for IRC client        */
94 >  LIST_FOREACH(node, IRCItem.channels.head)
95 >  {
96 >    struct ChannelConf *item = node->data;
97  
98 < static struct sockaddr_storage IRC_SVR;          /* Sock Address Struct for IRC server    */
98 >    if (strcasecmp(item->name, name) == 0)
99 >      return item;
100 >  }
101  
102 < static time_t IRC_LAST = 0;                      /* Last full line of data from irc server*/
103 < static time_t IRC_LASTRECONNECT = 0;             /* Time of last reconnection */
102 >  return NULL;
103 > }
104  
105 < /*
106 < * Table should be ordered with most occuring (or priority)
107 < * commands at the top of the list.
105 > /* m_perform
106 > *
107 > *    actions to perform on IRC connection
108 > *
109 > * Parameters:
110 > * parv[0]  = source
111 > * parv[1]  = PING
112 > * parv[2]  = PING TS/Package
113 > *
114 > * source_p: UserInfo struct of the source user, or NULL if
115 > * the source (parv[0]) is a server.
116   */
117 < static struct CommandHash COMMAND_TABLE[] =
117 > static void
118 > m_perform(char *parv[], unsigned int parc, const char *msg, const char *source_p)
119   {
120 <  { "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 < };
120 >  node_t *node;
121  
122 < /* irc_cycle
122 >  log_printf("IRC -> Connected to %s/%d", IRCItem.server, IRCItem.port);
123 >
124 >  /* Identify to nickserv if needed */
125 >  if (!EmptyString(IRCItem.nickserv))
126 >    irc_send("%s", IRCItem.nickserv);
127 >
128 >  /* Oper */
129 >  irc_send("OPER %s", IRCItem.oper);
130 >
131 >  /* Set modes */
132 >  irc_send("MODE %s %s", IRCItem.nick, IRCItem.mode);
133 >
134 >  /* Set Away */
135 >  if (!EmptyString(IRCItem.away))
136 >    irc_send("AWAY :%s", IRCItem.away);
137 >
138 >  /* Perform */
139 >  LIST_FOREACH(node, IRCItem.performs.head)
140 >    irc_send("%s", node->data);
141 >
142 >  /* Join all listed channels. */
143 >  LIST_FOREACH(node, IRCItem.channels.head)
144 >  {
145 >    const struct ChannelConf *channel = node->data;
146 >
147 >    if (EmptyString(channel->name))
148 >      continue;
149 >
150 >    if (!EmptyString(channel->key))
151 >      irc_send("JOIN %s %s", channel->name, channel->key);
152 >    else
153 >      irc_send("JOIN %s", channel->name);
154 >  }
155 > }
156 >
157 > /* m_ping
158   *
159 < *    Pass control to the IRC portion of HOPM to handle any awaiting IRC events.
159 > * parv[0]  = source
160 > * parv[1]  = PING
161 > * parv[2]  = PING TS/Package
162   *
163 < * Parameters:
164 < *    None
163 > * source_p: UserInfo struct of the source user, or NULL if
164 > * the source (parv[0]) is a server.
165 > */
166 > static void
167 > m_ping(char *parv[], unsigned int parc, const char *msg, const char *source_p)
168 > {
169 >  if (parc < 3)
170 >    return;
171 >
172 >  if (OPT_DEBUG >= 2)
173 >    log_printf("IRC -> PING? PONG!");
174 >
175 >  irc_send("PONG %s", parv[2]);
176 > }
177 >
178 > /* m_invite
179   *
180 < * Return:
181 < *    None
180 > * parv[0]  = source
181 > * parv[1]  = INVITE
182 > * parv[2]  = target
183 > * parv[3]  = channel
184 > *
185 > * source_p: UserInfo struct of the source user, or NULL if
186 > * the source (parv[0]) is a server.
187   */
188 < void
189 < irc_cycle(void)
188 > static void
189 > m_invite(char *parv[], unsigned int parc, const char *msg, const char *source_p)
190   {
191 <  static struct pollfd pfd;
191 >  if (parc < 4)
192 >    return;
193 >
194 >  log_printf("IRC -> Invited to %s by %s", parv[3], parv[0]);
195  
196 <  if (IRC_FD <= 0)
196 >  const struct ChannelConf *channel = get_channel(parv[3]);
197 >  if (channel == NULL)
198 >    return;
199 >
200 >  irc_send("JOIN %s %s", channel->name, channel->key);
201 > }
202 >
203 > /* m_ctcp
204 > * parv[0]  = source
205 > * parv[1]  = PRIVMSG
206 > * parv[2]  = target (channel or user)
207 > * parv[3]  = message
208 > *
209 > * source_p: UserInfo struct of the source user, or NULL if
210 > * the source (parv[0]) is a server.
211 > */
212 > static void
213 > m_ctcp(char *parv[], unsigned int parc, const char *msg, const char *source_p)
214 > {
215 >  if (strncasecmp(parv[3], "\001VERSION\001", 9) == 0)
216 >    irc_send("NOTICE %s :\001VERSION Hybrid Open Proxy Monitor %s(%s)\001",
217 >             source_p, VERSION, SERIALNUM);
218 > }
219 >
220 > /* m_privmsg
221 > *
222 > * parv[0]  = source
223 > * parv[1]  = PRIVMSG
224 > * parv[2]  = target (channel or user)
225 > * parv[3]  = message
226 > *
227 > * source_p: UserInfo struct of the source user, or NULL if
228 > * the source (parv[0]) is a server.
229 > */
230 > static void
231 > m_privmsg(char *parv[], unsigned int parc, const char *msg, const char *source_p)
232 > {
233 >  if (source_p == NULL)
234 >    return;
235 >
236 >  if (parc < 4)
237 >    return;
238 >
239 >  /* CTCP */
240 >  if (*parv[3] == '\001')
241    {
242 <    /* Initialise negative cache. */
243 <    if (OptionsItem->negcache > 0)
244 <      nc_init(&nc_head);
242 >    m_ctcp(parv, parc, msg, source_p);
243 >    return;
244 >  }
245  
246 <    /* Resolve remote host. */
247 <    irc_init();
246 >  /* Only interested in privmsg to channels */
247 >  if (*parv[2] != '#' && *parv[2] != '&')
248 >    return;
249  
250 <    /* Connect to remote host. */
251 <    irc_connect();
252 <    return;  /* In case connect() immediately failed */
250 >  /* Get a target */
251 >  const struct ChannelConf *channel = get_channel(parv[2]);
252 >  if (channel == NULL)
253 >    return;
254 >
255 >  int hit = strncasecmp(parv[3], "!all ", 5) == 0;
256 >  if (hit == 0)
257 >  {
258 >    size_t nick_len = strlen(IRCItem.nick);
259 >
260 >    if (strncasecmp(parv[3], IRCItem.nick, nick_len) == 0)
261 >      hit = *(parv[3] + nick_len) == ' ' ||
262 >            *(parv[3] + nick_len) == ',' ||
263 >            *(parv[3] + nick_len) == ':';
264    }
265  
266 <  pfd.fd = IRC_FD;
267 <  pfd.events = POLLIN;
266 >  if (hit)
267 >    /* XXX command_parse will alter parv[3]. */
268 >    command_parse(parv[3], channel->name, source_p);
269 > }
270  
271 <  /* Block .025 seconds to avoid excessive CPU use on poll(). */
272 <  switch (poll(&pfd, 1, 25))
271 > /* m_notice
272 > *
273 > * parv[0]  = source
274 > * parv[1]  = NOTICE
275 > * parv[2]  = target
276 > * parv[3]  = message
277 > *
278 > *
279 > * source_p: UserInfo struct of the source user, or NULL if
280 > * the source (parv[0]) is a server.
281 > */
282 > static void
283 > m_notice(char *parv[], unsigned int parc, const char *msg, const char *source_p)
284 > {
285 >  static regex_t *preg = NULL;
286 >  regmatch_t pmatch[5];
287 >  int errnum;
288 >  const char *user[4];
289 >  const node_t *node;
290 >
291 >  /* Not interested in notices from users */
292 >  if (source_p)
293 >    return;
294 >
295 >  if (parc < 4)
296 >    return;
297 >
298 >  /* Compile the regular expression if it has not been already */
299 >  if (preg == NULL)
300    {
301 <    case  0:
147 <    case -1:
148 <      break;
149 <    default:
150 <      /* Check if IRC data is available. */
151 <      if (pfd.revents & POLLIN)
152 <        irc_read();
301 >    preg = xcalloc(sizeof(*preg));
302  
303 <      break;
303 >    if ((errnum = regcomp(preg, IRCItem.connregex, REG_ICASE | REG_EXTENDED)))
304 >    {
305 >      char errmsg[256];
306 >
307 >      regerror(errnum, preg, errmsg, sizeof(errmsg));
308 >      log_printf("IRC REGEX -> Error when compiling regular expression: %s", errmsg);
309 >
310 >      xfree(preg);
311 >      preg = NULL;
312 >      return;
313 >    }
314    }
315 +
316 +  /* Match the expression against the possible connection notice */
317 +  if (regexec(preg, parv[3], 5, pmatch, 0))
318 +    return;
319 +
320 +  if (OPT_DEBUG > 0)
321 +    log_printf("IRC REGEX -> Regular expression caught connection notice. Parsing.");
322 +
323 +  if (pmatch[4].rm_so == -1)
324 +  {
325 +    log_printf("IRC REGEX -> pmatch[4].rm_so is -1 while parsing??? Aborting.");
326 +    return;
327 +  }
328 +
329 +  /*
330 +   *   Offsets for data in the connection notice:
331 +   *
332 +   *   NICKNAME: pmatch[1].rm_so  TO  pmatch[1].rm_eo
333 +   *   USERNAME: pmatch[2].rm_so  TO  pmatch[2].rm_eo
334 +   *   HOSTNAME: pmatch[3].rm_so  TO  pmatch[3].rm_eo
335 +   *   IP      : pmatch[4].rm_so  TO  pmatch[4].rm_eo
336 +   */
337 +  for (unsigned int i = 0; i < 4; ++i)
338 +  {
339 +    user[i] = (parv[3] + pmatch[i + 1].rm_so);
340 +    *(parv[3] + pmatch[i + 1].rm_eo) = '\0';
341 +  }
342 +
343 +  if (OPT_DEBUG > 0)
344 +    log_printf("IRC REGEX -> Parsed %s!%s@%s [%s] from connection notice.",
345 +               user[0], user[1], user[2], user[3]);
346 +
347 +  LIST_FOREACH(node, IRCItem.notices.head)
348 +    irc_send("NOTICE %s :%s", user[0], node->data);
349 +
350 +  /* Pass this information off to scan.c */
351 +  scan_connect(user, msg);
352 +
353 +  /* Record the connect for stats purposes */
354 +  stats_connect();
355   }
356  
357 + /* m_userhost
358 + *
359 + * parv[0]  = source
360 + * parv[1]  = USERHOST
361 + * parv[2]  = target (hopm)
362 + * parv[3]  = :nick=(flags)user@host
363 + *
364 + *
365 + * source_p: UserInfo struct of the source user, or NULL if
366 + * the source (parv[0]) is a server.
367 + */
368 + static void
369 + m_userhost(char *parv[], unsigned int parc, const char *msg, const char *source_p)
370 + {
371 +  if (parc < 4)
372 +    return;
373 +
374 +  command_userhost(parv[3]);
375 + }
376 +
377 + /* m_cannot_join
378 + *
379 + * parv[0]  = source
380 + * parv[1]  = numeric
381 + * parv[2]  = target (hopm)
382 + * parv[3]  = channel
383 + * parv[4]  = error text
384 + */
385 + static void
386 + m_cannot_join(char *parv[], unsigned int parc, const char *msg, const char *source_p)
387 + {
388 +  if (parc < 5)
389 +    return;
390 +
391 +  /* Is it one of our channels? */
392 +  const struct ChannelConf *channel = get_channel(parv[3]);
393 +  if (channel == NULL)
394 +    return;
395 +
396 +  if (EmptyString(channel->invite))
397 +    return;
398 +
399 +  irc_send("%s", channel->invite);
400 + }
401 +
402 + /* m_kill
403 + *
404 + * parv[0]  = source
405 + * parv[1]  = numeric
406 + * parv[2]  = target (hopm)
407 + * parv[3]  = channel
408 + * parv[4]  = error text
409 + */
410 + static void
411 + m_kill(char *parv[], unsigned int parc, const char *msg, const char *source_p)
412 + {
413 +  /* Restart hopm to rehash */
414 +  main_restart();
415 + }
416 +
417 + /* userinfo_create
418 + *
419 + *    Parse a nick!user@host into a UserInfo struct
420 + *    and return a pointer to the new struct.
421 + *
422 + * Parameters:
423 + *    source: nick!user@host to parse
424 + *
425 + * Return:
426 + *    pointer to new UserInfo struct, or NULL if parsing failed
427 + */
428 + static const char *
429 + userinfo_create(const char *source)
430 + {
431 +  static char name[MSGLENMAX];
432 +  unsigned int has_user = 0;
433 +  unsigned int has_host = 0;
434 +
435 +  strlcpy(name, source, sizeof(name));
436 +
437 +  for (char *p = name; *p; ++p)
438 +  {
439 +    if (*p == '!')
440 +    {
441 +      *p = '\0';
442 +      ++has_user;
443 +      continue;
444 +    }
445 +
446 +    if (*p == '@' && has_user)
447 +    {
448 +      ++has_host;
449 +      continue;
450 +    }
451 +  }
452 +
453 +  if (has_user == 1 && has_host == 1)
454 +    return name;
455 +  return NULL;
456 + };
457 +
458   /* irc_init
459   *
460   *    Resolve IRC host and perform other initialization.
461   *
462 < * Parameters:
462 > * Parameters:
463   *    None
464 < *
464 > *
465   * Return:
466   *    None
467   */
468   static void
469   irc_init(void)
470   {
471 <  const void *address = NULL;
471 >  const void *address;
472  
473 <  if (IRC_FD)
174 <    close(IRC_FD);
473 >  assert(IRC_FD == -1);
474  
475    memset(&IRC_SVR, 0, sizeof(IRC_SVR));
476  
477    /* Resolve IRC host. */
478 <  if ((address = firedns_resolveip6(IRCItem->server)))
478 >  if ((address = firedns_resolveip6(IRCItem.server)))
479    {
480      struct sockaddr_in6 *in = (struct sockaddr_in6 *)&IRC_SVR;
481  
482 +    svr_addrlen = sizeof(*in);
483      IRC_SVR.ss_family = AF_INET6;
484 <    in->sin6_port = htons(IRCItem->port);
484 >    in->sin6_port = htons(IRCItem.port);
485      memcpy(&in->sin6_addr, address, sizeof(in->sin6_addr));
486    }
487 <  else if ((address = firedns_resolveip4(IRCItem->server)))
487 >  else if ((address = firedns_resolveip4(IRCItem.server)))
488    {
489      struct sockaddr_in *in = (struct sockaddr_in *)&IRC_SVR;
490  
491 +    svr_addrlen = sizeof(*in);
492      IRC_SVR.ss_family = AF_INET;
493 <    in->sin_port = htons(IRCItem->port);
493 >    in->sin_port = htons(IRCItem.port);
494      memcpy(&in->sin_addr, address, sizeof(in->sin_addr));
495    }
496    else
497    {
498 <    log_printf("IRC -> firedns_resolveip(\"%s\"): %s", IRCItem->server,
499 <               firedns_strerror(fdns_errno));
498 >    log_printf("IRC -> firedns_resolveip(\"%s\"): %s", IRCItem.server,
499 >               firedns_strerror(firedns_errno));
500      exit(EXIT_FAILURE);
501    }
502  
# Line 209 | Line 510 | irc_init(void)
510    }
511  
512    /* Bind */
513 <  if (!EmptyString(IRCItem->vhost))
513 >  if (!EmptyString(IRCItem.vhost))
514    {
515      struct addrinfo hints, *res;
516  
# Line 217 | Line 518 | irc_init(void)
518  
519      hints.ai_family = AF_UNSPEC;
520      hints.ai_socktype = SOCK_STREAM;
521 <    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
521 >    hints.ai_flags = AI_NUMERICHOST;
522  
523 <    if (getaddrinfo(IRCItem->vhost, NULL, &hints, &res))
523 >    int n = getaddrinfo(IRCItem.vhost, NULL, &hints, &res);
524 >    if (n)
525      {
526 <      log_printf("IRC -> bind(): %s is an invalid address", IRCItem->vhost);
526 >      log_printf("IRC -> error binding to %s: %s", IRCItem.vhost, gai_strerror(n));
527        exit(EXIT_FAILURE);
528      }
529      else if (bind(IRC_FD, res->ai_addr, res->ai_addrlen))
530      {
531 <      log_printf("IRC -> bind(): error binding to %s: %s", IRCItem->vhost, strerror(errno));
531 >      log_printf("IRC -> error binding to %s: %s", IRCItem.vhost, strerror(errno));
532        exit(EXIT_FAILURE);
533      }
534  
535      freeaddrinfo(res);
536    }
235 }
236
237 /* irc_send
238 *
239 *    Send data to remote IRC host.
240 *
241 * Parameters:
242 *    data: Format of data to send
243 *    ...: varargs to format with
244 *
245 * Return: NONE
246 */
247 void
248 irc_send(const char *data, ...)
249 {
250  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;
263
264  buf[len++] = '\r';
265  buf[len++] = '\n';
537  
538 <  if (send(IRC_FD, buf, len, 0) == -1)
538 >  if (IRCItem.tls)
539    {
540 <    /* Return of -1 indicates error sending data; we reconnect. */
541 <    log_printf("IRC -> Error sending data to server: %s", strerror(errno));
542 <    irc_reconnect();
543 <  }
544 < }
545 <
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];
540 > #ifdef HAVE_LIBCRYPTO
541 >    /* Initialize SSL */
542 >    static int tls_init = 0;
543 >    if (tls_init == 0)
544 >    {
545 >      tls_init = 1;
546  
547 <  va_start(arglist, data);
548 <  vsnprintf(buf, sizeof(buf), data, arglist);
549 <  va_end(arglist);
547 >      ssl_ctx = SSL_CTX_new(SSLv23_client_method());
548 >      if (ssl_ctx == NULL)
549 >      {
550 >        log_printf("IRC -> unable to create SSL context");
551 >        exit(EXIT_FAILURE);
552 >      }
553  
554 <  LIST_FOREACH(node, IRCItem->channels->head)
555 <  {
298 <    const struct ChannelConf *chan = node->data;
554 >      SSL_CTX_set_default_verify_paths(ssl_ctx);
555 >    }
556  
557 <    irc_send("PRIVMSG %s :%s", chan->name, buf);
558 <  }
559 < }
557 >    ssl_handle = SSL_new(ssl_ctx);
558 >    SSL_set_fd(ssl_handle, IRC_FD);
559 >    SSL_set_hostflags(ssl_handle, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
560  
561 < /* irc_connect
562 < *
563 < *    Connect to IRC server.
564 < *    XXX: FD allocation done here
565 < *
566 < * Parameters: NONE
310 < * Return: NONE
311 < */
312 < static void
313 < irc_connect(void)
314 < {
315 <  /* Connect to IRC server as client. */
316 <  if (connect(IRC_FD, (struct sockaddr *)&IRC_SVR, sizeof(IRC_SVR)) == -1)
317 <  {
318 <    log_printf("IRC -> connect(): error connecting to %s: %s",
319 <               IRCItem->server, strerror(errno));
320 <
321 <    if (errno == EISCONN /* Already connected */ || errno == EALREADY /* Previous attempt not complete */)
322 <      return;
561 >    if (!SSL_set1_host(ssl_handle, IRCItem.server))
562 >    {
563 >      log_printf("IRC -> unable to set expected DNS hostname");
564 >      /* OpenSSL is unable to verify the server hostname at this point, so we exit. */
565 >      exit(EXIT_FAILURE);
566 >    }
567  
568 <    /* Try to connect again */
569 <    irc_reconnect();
570 <    return;
568 >    SSL_set_verify(ssl_handle, SSL_VERIFY_PEER, NULL);
569 > #else
570 >    log_printf("IRC -> HOPM is not compiled with OpenSSL support");
571 >    exit(EXIT_FAILURE);
572 > #endif
573    }
328
329  irc_send("NICK %s", IRCItem->nick);
330
331  if (!EmptyString(IRCItem->password))
332    irc_send("PASS %s", IRCItem->password);
333
334  irc_send("USER %s %s %s :%s",
335           IRCItem->username,
336           IRCItem->username,
337           IRCItem->username,
338           IRCItem->realname);
339  time(&IRC_LAST);
574   }
575  
576   /* irc_reconnect
# Line 354 | Line 588 | irc_reconnect(void)
588  
589    time(&present);
590  
591 <  /* Only try to reconnect every RECONNECT_INTERVAL seconds */
592 <  if ((present - IRC_LASTRECONNECT) < RECONNECTINTERVAL)
591 >  /* Only try to reconnect every IRCItem.reconnectinterval seconds */
592 >  if ((present - IRC_LASTRECONNECT) < IRCItem.reconnectinterval)
593    {
594      /* Sleep to avoid excessive CPU */
595      sleep(1);
# Line 364 | Line 598 | irc_reconnect(void)
598  
599    time(&IRC_LASTRECONNECT);
600  
601 <  if (IRC_FD > 0)
602 <    close(IRC_FD);
601 >  if (IRC_FD > -1)
602 >  {
603 > #ifdef HAVE_LIBCRYPTO
604 >    if (ssl_handle)
605 >    {
606 >      SSL_shutdown(ssl_handle);
607 >      SSL_free(ssl_handle);
608 >      ssl_handle = NULL;
609 >    }
610 > #endif
611  
612 <  /* Set IRC_FD 0 for reconnection on next irc_cycle(). */
613 <  IRC_FD = 0;
612 >    close(IRC_FD);
613 >    IRC_FD = -1;  /* Set IRC_FD -1 for reconnection on next irc_cycle(). */
614 >  }
615  
616 <  log_printf("IRC -> Connection to (%s) failed, reconnecting.", IRCItem->server);
616 >  log_printf("IRC -> Connection to (%s) failed, reconnecting.", IRCItem.server);
617   }
618  
619 < /* irc_read
619 > /* irc_connect
620   *
621 < *    irc_read is called my irc_cycle when new data is ready to be
622 < *    read from the irc server.
621 > *    Connect to IRC server.
622 > *    XXX: FD allocation done here
623   *
624   * Parameters: NONE
625   * Return: NONE
626   */
627   static void
628 < irc_read(void)
628 > irc_connect(void)
629   {
630 <  int len;
631 <  char c;
389 <
390 <  while ((len = read(IRC_FD, &c, 1)) > 0)
630 >  /* Connect to IRC server as client. */
631 >  if (connect(IRC_FD, (struct sockaddr *)&IRC_SVR, svr_addrlen) == -1)
632    {
633 <    if (c == '\r')
634 <      continue;
394 <
395 <    if (c == '\n')
396 <    {
397 <      /* Null string. */
398 <      IRC_RAW[IRC_RAW_LEN] = '\0';
399 <
400 <      /* Parse line. */
401 <      irc_parse();
633 >    log_printf("IRC -> connect(): error connecting to %s: %s",
634 >               IRCItem.server, strerror(errno));
635  
636 <      /* Reset counter. */
637 <      IRC_RAW_LEN = 0;
405 <      break;
406 <    }
636 >    if (errno == EISCONN /* Already connected */ || errno == EALREADY /* Previous attempt not complete */)
637 >      return;
638  
639 <    if (c != '\0')
640 <      IRC_RAW[IRC_RAW_LEN++] = c;
639 >    /* Try to connect again */
640 >    irc_reconnect();
641 >    return;
642    }
643  
644 <  if ((len <= 0) && (errno != EAGAIN))
644 > #ifdef HAVE_LIBCRYPTO
645 >  if (ssl_handle)
646    {
647 <    if (OPT_DEBUG >= 2)
648 <      log_printf("irc_read -> errno=%d len=%d", errno, len);
647 >    int ret = SSL_connect(ssl_handle);
648 >    if (ret != 1)
649 >    {
650 >      const char *error = ERR_error_string(ERR_get_error(), NULL);
651 >      log_printf("IRC -> connect(): error performing TLS handshake with %s: %s",
652 >                 IRCItem.server, error);
653  
654 <    irc_reconnect();
655 <    IRC_RAW_LEN = 0;
656 <    return;
654 >      /* Try to connect again */
655 >      irc_reconnect();
656 >      return;
657 >    }
658    }
659 + #endif
660 +
661 +  irc_send("NICK %s", IRCItem.nick);
662 +
663 +  if (!EmptyString(IRCItem.password))
664 +    irc_send("PASS %s", IRCItem.password);
665 +
666 +  irc_send("USER %s %s %s :%s",
667 +           IRCItem.username,
668 +           IRCItem.username,
669 +           IRCItem.username,
670 +           IRCItem.realname);
671 +  time(&IRC_LAST);
672   }
673  
674   /* irc_parse
# Line 431 | Line 682 | irc_read(void)
682   static void
683   irc_parse(void)
684   {
434  struct UserInfo *source_p;
685    char *pos;
686  
687    /*
688     * parv stores the parsed token, parc is the count of the parsed
689     * tokens
690     *
691 <   * parv[0] is ALWAYS the source, and is the server name of the source
692 <   * did not exist
691 >   * parv[0] is ALWAYS the source, and is the server name of the
692 >   * source did not exist
693     */
694 <  char            *parv[17];
695 <  unsigned int     parc = 1;
696 <  char             msg[MSGLENMAX];    /* Temporarily stores IRC msg to pass to handlers */
694 >  char        *parv[17];
695 >  unsigned int parc = 1;
696 >  char         msg[MSGLENMAX];  /* Temporarily stores IRC msg to pass to handlers */
697 >
698 >  struct CommandHash
699 >  {
700 >    const char *command;
701 >    void (*handler)(char *[], unsigned int, const char *, const char *);
702 >  };
703 >
704 >  /*
705 >   * Table should be ordered with most occuring (or priority)
706 >   * commands at the top of the list.
707 >   */
708 >  static const struct CommandHash COMMAND_TABLE[] =
709 >  {
710 >    { .command = "NOTICE",  .handler = m_notice      },
711 >    { .command = "PRIVMSG", .handler = m_privmsg     },
712 >    { .command = "PING",    .handler = m_ping        },
713 >    { .command = "INVITE",  .handler = m_invite      },
714 >    { .command = "001",     .handler = m_perform     },
715 >    { .command = "302",     .handler = m_userhost    },
716 >    { .command = "471",     .handler = m_cannot_join },
717 >    { .command = "473",     .handler = m_cannot_join },
718 >    { .command = "474",     .handler = m_cannot_join },
719 >    { .command = "475",     .handler = m_cannot_join },
720 >    { .command = "KILL",    .handler = m_kill        },
721 >    { .command = NULL }
722 >  };
723  
724    if (IRC_RAW_LEN == 0)
725      return;
# Line 461 | Line 737 | irc_parse(void)
737      parv[0] = IRC_RAW + 1;
738    else
739    {
740 <    parv[0] = IRCItem->server;
740 >    parv[0] = IRCItem.server;
741      parv[parc++] = IRC_RAW;
742    }
743  
# Line 493 | Line 769 | irc_parse(void)
769      pos++;
770    }
771  
496  /* Generate a UserInfo struct from the source */
497  source_p = userinfo_create(parv[0]);
498
772    /*
773     * Determine which command this is from the command table
774     * and let the handler for that command take control
# Line 504 | Line 777 | irc_parse(void)
777    {
778      if (strcasecmp(cmd->command, parv[1]) == 0)
779      {
780 <      cmd->handler(parv, parc, msg, source_p);
780 >      cmd->handler(parv, parc, msg, userinfo_create(parv[0]));
781        break;
782      }
783    }
511
512  userinfo_free(source_p);
784   }
785  
786 < /* irc_timer
786 > /* irc_read
787   *
788 < *    Functions to be performed every ~seconds.
788 > *    irc_read is called by irc_cycle when new data is ready to be
789 > *    read from the irc server.
790   *
791   * Parameters: NONE
792   * Return: NONE
793   */
794 < void
795 < irc_timer(void)
794 > static void
795 > irc_read(void)
796   {
797 <  time_t present, delta;
797 >  ssize_t len;
798 >  char c;
799  
800 <  time(&present);
800 >  while (1)
801 >  {
802 > #ifdef HAVE_LIBCRYPTO
803 >    if (ssl_handle)
804 >      len = SSL_read(ssl_handle, &c, 1);
805 >    else
806 > #endif
807 >      len = recv(IRC_FD, &c, 1, 0);
808  
809 <  delta = present - IRC_LAST;
809 >    if (len <= 0)
810 >        break;
811 >    if (c == '\r')
812 >      continue;
813  
814 <  /* No data in IRCItem->readtimeout seconds */
815 <  if (delta >= IRCItem->readtimeout)
816 <  {
817 <    log_printf("IRC -> Timeout awaiting data from server.");
535 <    irc_reconnect();
814 >    if (c == '\n')
815 >    {
816 >      /* Null string. */
817 >      IRC_RAW[IRC_RAW_LEN] = '\0';
818  
819 <    /* Make sure we don't do this again for a while */
820 <    time(&IRC_LAST);
539 <  }
540 <  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 < }
819 >      /* Parse line. */
820 >      irc_parse();
821  
822 < /* get_channel
823 < *
824 < *    Check if a channel is defined in our conf. If so return
825 < *    a pointer to it.
826 < *
827 < * Parameters:
828 < *    channel: channel to search conf for
829 < *
558 < * Return: Pointer to ChannelConf containing the channel
559 < */
560 < static struct ChannelConf *
561 < get_channel(const char *channel)
562 < {
563 <  node_t *node;
822 >      /* Reset counter. */
823 >      IRC_RAW_LEN = 0;
824 >      break;
825 >    }
826 >
827 >    if (c != '\0')
828 >      IRC_RAW[IRC_RAW_LEN++] = c;
829 >  }
830  
831 <  LIST_FOREACH(node, IRCItem->channels->head)
831 >  if ((len <= 0) && (errno != EAGAIN))
832    {
833 <    struct ChannelConf *item = node->data;
833 >    if (errno != EINTR)
834 >      log_printf("IRC -> Error reading data from server: %s", strerror(errno));
835  
836 <    if (strcasecmp(item->name, channel) == 0)
837 <      return item;
836 >    irc_reconnect();
837 >    IRC_RAW_LEN = 0;
838 >    return;
839    }
572
573  return NULL;
840   }
841  
842 < /* userinfo_create
842 > /* irc_cycle
843   *
844 < *    Parse a nick!user@host into a UserInfo struct
579 < *    and return a pointer to the new struct.
844 > *    Pass control to the IRC portion of HOPM to handle any awaiting IRC events.
845   *
846   * Parameters:
847 < *    source: nick!user@host to parse
847 > *    None
848   *
849   * Return:
850 < *    pointer to new UserInfo struct, or NULL if parsing failed
850 > *    None
851   */
852 < static struct UserInfo *
853 < userinfo_create(char *source)
852 > void
853 > irc_cycle(void)
854   {
855 <  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;
855 >  static struct pollfd pfd;
856  
857 <  for (i = 0; i < len; ++i)
857 >  if (IRC_FD == -1)
858    {
859 <    if (tmp[i] == '!')
860 <    {
861 <      tmp[i] = '\0';
607 <      username = tmp + i + 1;
608 <    }
859 >    /* Initialize negative cache. */
860 >    if (OptionsItem.negcache)
861 >      negcache_init();
862  
863 <    if (tmp[i] == '@')
864 <    {
612 <      tmp[i] = '\0';
613 <      hostname = tmp + i + 1;
614 <    }
615 <  }
863 >    /* Resolve remote host. */
864 >    irc_init();
865  
866 <  if (nick == NULL || username == NULL || hostname == NULL)
867 <  {
868 <    MyFree(tmp);
620 <    return NULL;
866 >    /* Connect to remote host. */
867 >    irc_connect();
868 >    return;  /* In case connect() immediately failed */
869    }
870  
871 <  ret = xcalloc(sizeof *ret);
872 <  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 < };
871 >  pfd.fd = IRC_FD;
872 >  pfd.events = POLLIN;
873  
874 < /* userinfo_free
875 < *
876 < *    Free a UserInfo struct created with userinfo_create.
877 < *
878 < * Parameters:
879 < *    source: struct to free
880 < *
881 < * Return: None
882 < */
883 < static void
884 < userinfo_free(struct UserInfo *source_p)
885 < {
645 <  if (source_p == NULL)
646 <    return;
874 >  /* Block .050 seconds to avoid excessive CPU use on poll(). */
875 >  switch (poll(&pfd, 1, 50))
876 >  {
877 >    case  0:
878 >    case -1:
879 >      break;
880 >    default:
881 >      /* Check if IRC data is available. */
882 >      if (pfd.revents & POLLIN)
883 >        irc_read();
884 >      else if (pfd.revents & (POLLERR | POLLHUP))
885 >        irc_reconnect();
886  
887 <  MyFree(source_p->irc_nick);
888 <  MyFree(source_p->irc_username);
650 <  MyFree(source_p->irc_hostname);
651 <  MyFree(source_p);
887 >      break;
888 >  }
889   }
890  
891 < /* m_perform
891 > /* irc_send
892   *
893 < *    actions to perform on IRC connection
893 > *    Send data to remote IRC host.
894   *
895   * Parameters:
896 < * parv[0]  = source
897 < * parv[1]  = PING
661 < * parv[2]  = PING TS/Package
896 > *    data: Format of data to send
897 > *    ...: varargs to format with
898   *
899 < * source_p: UserInfo struct of the source user, or NULL if
664 < * the source (parv[0]) is a server.
899 > * Return: NONE
900   */
901 < static void
902 < m_perform(char *parv[], unsigned int parc, char *msg, const struct UserInfo *notused)
901 > void
902 > irc_send(const char *data, ...)
903   {
904 <  node_t *node;
905 <
671 <  log_printf("IRC -> Connected to %s/%d", IRCItem->server, IRCItem->port);
904 >  va_list arglist;
905 >  char buf[MSGLENMAX];
906  
907 <  /* Identify to nickserv if needed */
908 <  if (!EmptyString(IRCItem->nickserv))
909 <    irc_send("%s", IRCItem->nickserv);
907 >  va_start(arglist, data);
908 >  ssize_t len = vsnprintf(buf, sizeof(buf), data, arglist);
909 >  va_end(arglist);
910  
911 <  /* Oper */
912 <  irc_send("OPER %s", IRCItem->oper);
911 >  if (OPT_DEBUG >= 2)
912 >    log_printf("IRC SEND -> %s", buf);
913  
914 <  /* Set modes */
915 <  irc_send("MODE %s %s", IRCItem->nick, IRCItem->mode);
914 >  if (len > 510)
915 >    len = 510;
916  
917 <  /* Set Away */
918 <  if (!EmptyString(IRCItem->away))
685 <    irc_send("AWAY :%s", IRCItem->away);
917 >  buf[len++] = '\r';
918 >  buf[len++] = '\n';
919  
920 <  /* Perform */
921 <  LIST_FOREACH(node, IRCItem->performs->head)
922 <    irc_send("%s", node->data);
920 >  ssize_t sent;
921 > #ifdef HAVE_LIBCRYPTO
922 >  if (ssl_handle)
923 >    sent = SSL_write(ssl_handle, buf, len);
924 >  else
925 > #endif
926 >    sent = send(IRC_FD, buf, len, 0);
927  
928 <  /* Join all listed channels. */
692 <  LIST_FOREACH(node, IRCItem->channels->head)
928 >  if (sent != len)
929    {
930 <    const struct ChannelConf *channel = node->data;
931 <
932 <    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);
930 >    /* Return of -1 indicates error sending data; we reconnect. */
931 >    log_printf("IRC -> Error sending data to server: %s", strerror(errno));
932 >    irc_reconnect();
933    }
934   }
935  
936 < /* m_ping
707 < *
708 < * parv[0]  = source
709 < * parv[1]  = PING
710 < * parv[2]  = PING TS/Package
936 > /* irc_send_channels
937   *
938 < * 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
733 < *
734 < * 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
938 > *    Send privmsg to all channels.
939   *
940 < * parv[0]  = source
941 < * parv[1]  = PRIVMSG
942 < * parv[2]  = target (channel or user)
758 < * parv[3]  = message
940 > * Parameters:
941 > *    data: Format of data to send
942 > *    ...: varargs to format with
943   *
944 < * source_p: UserInfo struct of the source user, or NULL if
761 < * the source (parv[0]) is a server.
944 > * Return: NONE
945   */
946 < static void
947 < m_privmsg(char *parv[], unsigned int parc, char *msg, const struct UserInfo *source_p)
946 > void
947 > irc_send_channels(const char *data, ...)
948   {
949 <  struct ChannelConf *channel = NULL;
950 <  size_t nick_len;
951 <
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], " :,");
949 >  const node_t *node;
950 >  va_list arglist;
951 >  char buf[MSGLENMAX];
952  
953 <  if (nick_len < 3 && strlen(IRCItem->nick) >= 3)
954 <    nick_len = 3;
953 >  va_start(arglist, data);
954 >  vsnprintf(buf, sizeof(buf), data, arglist);
955 >  va_end(arglist);
956  
957 <  /* message is a command */
794 <  if (strncasecmp(parv[3], IRCItem->nick, nick_len) == 0  ||
795 <      strncasecmp(parv[3], "!all", 4) == 0)
957 >  LIST_FOREACH(node, IRCItem.channels.head)
958    {
959 <    /* XXX command_parse will alter parv[3]. */
798 <    command_parse(parv[3], channel, source_p);
799 <  }
800 < }
959 >    const struct ChannelConf *chan = node->data;
960  
961 < /* m_ctcp
962 < * 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);
961 >    irc_send("PRIVMSG %s :%s", chan->name, buf);
962 >  }
963   }
964  
965 < /* m_notice
821 < *
822 < * parv[0]  = source
823 < * parv[1]  = NOTICE
824 < * parv[2]  = target
825 < * parv[3]  = message
826 < *
965 > /* irc_timer
966   *
967 < * source_p: UserInfo struct of the source user, or NULL if
829 < * the source (parv[0]) is a server.
967 > *    Functions to be performed every ~seconds.
968   *
969 + * Parameters: NONE
970 + * Return: NONE
971   */
972 < static void
973 < m_notice(char *parv[], unsigned int parc, char *msg, const struct UserInfo *source_p)
972 > void
973 > irc_timer(void)
974   {
975 <  static regex_t *preg = NULL;
836 <  regmatch_t pmatch[5];
837 <  int errnum;
838 <  const char *user[4];
975 >  time_t present, delta;
976  
977 <  /* Not interested in notices from users */
841 <  if (source_p)
842 <    return;
977 >  time(&present);
978  
979 <  if (parc < 4)
845 <    return;
979 >  delta = present - IRC_LAST;
980  
981 <  /* Compile the regular expression if it has not been already */
982 <  if (preg == NULL)
981 >  /* No data in IRCItem.readtimeout seconds */
982 >  if (delta >= IRCItem.readtimeout)
983    {
984 <    preg = xcalloc(sizeof *preg);
985 <
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);
984 >    log_printf("IRC -> Timeout awaiting data from server.");
985 >    irc_reconnect();
986  
987 <      MyFree(preg);
988 <      preg = NULL;
862 <      return;
863 <    }
987 >    /* Make sure we don't do this again for a while */
988 >    time(&IRC_LAST);
989    }
990 <
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.");
872 <
873 <  if (pmatch[4].rm_so == -1)
990 >  else if (delta >= IRCItem.readtimeout / 2)
991    {
992 <    log_printf("IRC REGEX -> pmatch[4].rm_so is -1 while parsing??? Aborting.");
993 <    return;
994 <  }
995 <
996 <  /*
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)
888 <  {
889 <    user[i] = (parv[3] + pmatch[i + 1].rm_so);
890 <    *(parv[3] + pmatch[i + 1].rm_eo) = '\0';
992 >    /*
993 >     * Generate some data so high ping times don't cause uneeded
994 >     * reconnections
995 >     */
996 >    irc_send("PING :HOPM");
997    }
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();
998   }

Diff Legend

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