1 /* dircproxy
2  * Copyright (C) 2000-2003 Scott James Remnant <scott at netsplit dot com>
3  *
4  * Copyright (C) 2004-2008 Francois Harvey <contact at francoisharvey dot ca>
5  *
6  * Copyright (C) 2008-2009 Noel Shrum <noel dot w8tvi at gmail dot com>
7  *                         Francois Harvey <contact at francoisharvey dot ca>
8  *
9  *
10  * irc_client.c
11  *  - Handling of clients connected to the proxy
12  *  - Functions to send data to the client in the correct protocol format
13  * --
14  * @(#) $Id: irc_client.c,v 1.96 2004/04/24 09:34:37 fharvey Exp $
15  *
16  * This file is distributed according to the GNU General Public
17  * License.  For full details, read the top of 'main.c' or the
18  * file called COPYING that was distributed with this code.
19  */
20 
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <time.h>
28 
29 #include <dircproxy.h>
30 
31 #ifdef HAVE_CRYPT_H
32 # include <crypt.h>
33 #else /* HAVE_CRYPT_H */
34 # include <unistd.h>
35 #endif /* HAVE_CRYPT_H */
36 
37 #include "sprintf.h"
38 #include "net.h"
39 #include "dns.h"
40 #include "timers.h"
41 #include "dcc_net.h"
42 #include "irc_log.h"
43 #include "irc_net.h"
44 #include "irc_prot.h"
45 #include "irc_string.h"
46 #include "irc_server.h"
47 #include "irc_client.h"
48 #include "logo.h"
49 
50 /* forward declarations */
51 static void _ircclient_connected2(struct ircproxy *, void *, const char *,
52                                   const char *);
53 static void _ircclient_data(struct ircproxy *, int);
54 static void _ircclient_error(struct ircproxy *, int, int);
55 static int _ircclient_detach(struct ircproxy *, const char *);
56 static int _ircclient_gotmsg(struct ircproxy *, const char *);
57 static int _ircclient_authenticate(struct ircproxy *, const char *);
58 static void _ircclient_resetnick(struct ircproxy *, void *);
59 static int _ircclient_got_details(struct ircproxy *, const char *,
60                                   const char *, const char *, const char *);
61 static int _ircclient_motd(struct ircproxy *);
62 static void _ircclient_timedout(struct ircproxy *, void *);
63 static int _ircclient_send_dccreject(struct ircproxy *, const char *,
64                                      const char *);
65 int  _ircclient_handle_privmsg(struct ircproxy *, struct ircmessage);
66 void _ircclient_handle_recall(struct ircproxy *, struct ircmessage);
67 void _ircclient_handle_users(struct ircproxy *, struct ircmessage);
68 void _ircclient_handle_kill(struct ircproxy *, struct ircmessage);
69 void _ircclient_handle_notify(struct ircproxy *, struct ircmessage);
70 int  _ircclient_handle_jump(struct ircproxy *, struct ircmessage);
71 void _ircclient_handle_status(struct ircproxy *, struct ircmessage);
72 void _ircclient_handle_help(struct ircproxy *, struct ircmessage);
73 
74 /* New user mode bits */
75 #define RFC2812_MODE_W 0x04
76 #define RFC2812_MODE_I 0x08
77 
78 /* Time/date format for strftime(3) */
79 #define START_TIMEDATE_FORMAT "%a, %d %b %Y %H:%M:%S %z"
80 
81 /* Define MIN() */
82 #ifndef MIN
83 #define MIN(x, y) ((x) < (y) ? (x) : (y))
84 #endif /* MIN */
85 
86 /* Called when a new client has connected */
ircclient_connected(struct ircproxy * p)87 int ircclient_connected(struct ircproxy *p) {
88   char ip[DNS_MAX_HOSTLEN];
89 
90   ircclient_send_notice(p, "Looking up your hostname...");
91 
92   net_ntop(&p->client_addr, ip, sizeof(ip));
93   dns_hostfromaddr(p, 0, ip, (dns_fun_t) _ircclient_connected2);
94 
95   return 0;
96 }
97 
98 /* Called once a client DNS lookup has completed */
_ircclient_connected2(struct ircproxy * p,void * data,const char * ip,const char * name)99 static void _ircclient_connected2(struct ircproxy *p, void *data,
100                                   const char *ip, const char *name) {
101   p->client_host = x_strdup(name);
102   if (!p->hostname)
103     p->hostname = x_strdup(name);
104   ircclient_send_notice(p, "Got your hostname.");
105 
106   p->client_status |= IRC_CLIENT_CONNECTED;
107   net_hook(p->client_sock, SOCK_NORMAL, (void *)p,
108            ACTIVITY_FUNCTION(_ircclient_data),
109            ERROR_FUNCTION(_ircclient_error));
110 
111   debug("Client connected from %s", p->client_host);
112 
113   timer_new((void *)p, "client_auth", g.client_timeout,
114             TIMER_FUNCTION(_ircclient_timedout), (void *)0);
115 }
116 
117 /* Called when a client sends us stuff. */
_ircclient_data(struct ircproxy * p,int sock)118 static void _ircclient_data(struct ircproxy *p, int sock) {
119   char *str;
120 
121   if (sock != p->client_sock) {
122     error("Unexpected socket %d in _ircclient_data, expected %d", sock,
123           p->client_sock);
124     net_close(&sock);
125     return;
126   }
127 
128   str = 0;
129   while (!p->dead && (p->client_status & IRC_CLIENT_CONNECTED)
130          && net_gets(p->client_sock, &str, "\r\n") > 0) {
131     debug(">> '%s'", str);
132     _ircclient_gotmsg(p, str);
133     free(str);
134   }
135 }
136 
137 /* Called on client disconnection or error */
_ircclient_error(struct ircproxy * p,int sock,int bad)138 static void _ircclient_error(struct ircproxy *p, int sock, int bad) {
139   if (sock != p->client_sock) {
140     error("Unexpected socket %d in _ircclient_error, expected %d", sock,
141           p->client_sock);
142     net_close(&sock);
143     return;
144   }
145 
146   if (bad) {
147     debug("Socket error");
148   } else {
149     debug("Client disconnect");
150   }
151 
152   _ircclient_detach(p, 0);
153 }
154 
155 /* Called to detach an irc client */
_ircclient_detach(struct ircproxy * p,const char * message)156 static int _ircclient_detach(struct ircproxy *p, const char *message) {
157   if (p->die_on_close) {
158     debug("Killing proxy");
159 
160     if (message) {
161       ircserver_send_command(p, "QUIT", ":%s", message);
162     } else if (p->conn_class && p->conn_class->quit_message) {
163       ircserver_send_command(p, "QUIT", ":%s", p->conn_class->quit_message);
164     } else {
165       ircserver_send_command(p, "QUIT", ":Leaving IRC - %s %s",
166                              PACKAGE, VERSION);
167     }
168     ircserver_close_sock(p);
169 
170     p->conn_class = 0;
171     ircclient_close(p);
172 
173   } else {
174     debug("Detaching proxy");
175     if (p->client_status == IRC_CLIENT_ACTIVE)
176       irclog_log(p, IRC_LOG_CLIENT, IRC_LOGFILE_ALL, PACKAGE,
177                  "You disconnected");
178 
179     /* Drop modes */
180     if ((p->client_status == IRC_CLIENT_ACTIVE)
181         && p->conn_class->drop_modes) {
182       char *mode;
183 
184       mode = x_sprintf("-%s", p->conn_class->drop_modes);
185       debug("Auto-mode-change '%s'", mode);
186 
187       ircclient_change_mode(p, mode);
188       if (p->server_status == IRC_SERVER_ACTIVE)
189         ircserver_send_command(p, "MODE", "%s %s", p->nickname, mode);
190 
191       free(mode);
192     }
193 
194     /* Send detach message to all channels we're on */
195     if ((p->server_status == IRC_SERVER_ACTIVE)
196         && (p->client_status == IRC_CLIENT_ACTIVE)) {
197       if (p->conn_class->detach_message) {
198         struct ircchannel *c;
199         int slashme;
200         char *msg;
201 
202         msg = p->conn_class->detach_message;
203         if ((strlen(msg) >= 5) && !strncasecmp(msg, "/me ", 4)) {
204           /* Starts with /me */
205           slashme = 1;
206           msg += 4;
207         } else {
208           slashme = 0;
209         }
210 
211         c = p->channels;
212         while (c) {
213           if (!c->inactive && !c->unjoined) {
214             if (slashme) {
215               ircserver_send_command(p, "PRIVMSG", "%s :\001ACTION %s\001",
216                                      c->name, msg);
217             } else {
218               ircserver_send_command(p, "PRIVMSG", "%s :%s", c->name, msg);
219             }
220           }
221           c = c->next;
222         }
223       }
224     }
225 
226     /* Leave channels until they come back */
227     if ((p->server_status == IRC_SERVER_ACTIVE)
228         && (p->client_status == IRC_CLIENT_ACTIVE)) {
229       if (p->conn_class->channel_leave_on_detach) {
230         struct ircchannel *c;
231 
232         c = p->channels;
233         while (c) {
234           struct ircchannel *t;
235 
236           t = c;
237           c = c->next;
238 
239           /* Leave the channel and decide whether to delete it or rejoin */
240           if (!t->inactive && !t->unjoined) {
241             ircserver_send_command(p, "PART", ":%s", t->name);
242             if (p->conn_class->channel_rejoin_on_attach) {
243               t->unjoined = 1;
244             } else {
245               ircnet_delchannel(p, t->name);
246             }
247           }
248         }
249       }
250     }
251 
252     /* Set away message */
253     if ((p->server_status == IRC_SERVER_ACTIVE)
254         && (p->client_status == IRC_CLIENT_ACTIVE)) {
255       if (message) {
256         ircserver_send_command(p, "AWAY", ":%s", message);
257       } else if (!p->awaymessage && p->conn_class->away_message) {
258         ircserver_send_command(p, "AWAY", ":%s", p->conn_class->away_message);
259       }
260     }
261 
262     /* Change Nickname */
263     if ((p->client_status == IRC_CLIENT_ACTIVE)
264         && p->conn_class->detach_nickname) {
265       char *nick, *ptr;
266 
267       nick = x_strdup(p->conn_class->detach_nickname);
268       ptr = strchr(nick, '*');
269       if (ptr) {
270         char *newnick;
271 
272         *(ptr++) = 0;
273         newnick = x_sprintf("%s%s%s", nick, p->nickname, ptr);
274         free(nick);
275         nick = newnick;
276       }
277       debug("Auto-nick-change '%s'", nick);
278 
279       /* We need to remember what the setnickname is now so when the client
280          comes back we can reset it again.  So put it in oldnickname. */
281       if (p->oldnickname)
282         free(p->oldnickname);
283       p->oldnickname = p->setnickname;
284       p->setnickname = 0;
285 
286       ircclient_change_nick(p, nick);
287 
288       free(nick);
289     }
290 
291     /* Open private_log */
292     if ((p->client_status == IRC_CLIENT_ACTIVE)
293         && p->conn_class->private_log_enabled
294         && !p->conn_class->private_log_always) {
295       if (irclog_open(p, p->nickname))
296         ircclient_send_notice(p, "(warning) Unable to log private messages");
297     }
298 
299     /* Open server log */
300     if ((p->client_status == IRC_CLIENT_ACTIVE)
301         && p->conn_class->server_log_enabled
302         && !p->conn_class->server_log_always) {
303       if (irclog_open(p, 0))
304         ircclient_send_notice(p, "(warning) Unable to log server messages");
305     }
306 
307     /* Open channel logs */
308     if ((p->client_status == IRC_CLIENT_ACTIVE)
309         && p->conn_class->chan_log_enabled
310         && !p->conn_class->chan_log_always) {
311       struct ircchannel *c;
312 
313       c = p->channels;
314       while (c) {
315         if (irclog_open(p, c->name))
316           ircclient_send_notice(p, "(warning) Unable to log channel: %s",
317                                 c->name);
318         c = c->next;
319       }
320     }
321 
322     /* Close the socket */
323     ircclient_close(p);
324   }
325 
326   return 0;
327 }
328 
329 /* Called when we get an irc protocol data from a client */
_ircclient_gotmsg(struct ircproxy * p,const char * str)330 static int _ircclient_gotmsg(struct ircproxy *p, const char *str) {
331   struct ircmessage msg;
332 
333   if (ircprot_parsemsg(str, &msg) == -1)
334     return -1;
335 
336   debug("c=%02x, s=%02x", p->client_status, p->server_status);
337 
338   if (!(p->client_status & IRC_CLIENT_AUTHED)) {
339     /* Accept PASS, NICK and USER commands only until we've authenticated */
340     if (!irc_strcasecmp(msg.cmd, "PASS")) {
341       if (msg.numparams >= 1) {
342         if (p->password)
343           free(p->password);
344         p->password = x_strdup(msg.params[0]);
345         p->client_status |= IRC_CLIENT_GOTPASS;
346       } else {
347         ircclient_send_numeric(p, 461, ":Not enough parameters");
348       }
349 
350     } else if (!irc_strcasecmp(msg.cmd, "NICK")) {
351       if (msg.numparams >= 1) {
352         if (!(p->client_status & IRC_CLIENT_GOTNICK)
353             || strcmp(p->nickname, msg.params[0]))
354           ircclient_change_nick(p, msg.params[0]);
355       } else {
356         ircclient_send_numeric(p, 431, ":No nickname given");
357       }
358 
359     } else if (!irc_strcasecmp(msg.cmd, "USER")) {
360       if (msg.numparams >= 4) {
361         if (!(p->client_status & IRC_CLIENT_GOTUSER))
362           _ircclient_got_details(p, msg.params[0], msg.params[1],
363                                  msg.params[2], msg.params[3]);
364       } else {
365         ircclient_send_numeric(p, 461, ":Not enough parameters");
366       }
367 
368     } else if (!(p->client_status & IRC_CLIENT_GOTPASS)) {
369       ircclient_send_notice(p, "Please send /QUOTE PASS <password> to login");
370 
371     } else {
372       ircclient_send_notice(p, "Please send /QUOTE NICK and /QUOTE USER");
373     }
374 
375   } else if (!(p->client_status & IRC_CLIENT_GOTNICK)) {
376     /* We've lost the nickname */
377     if (!irc_strcasecmp(msg.cmd, "NICK")) {
378       if (msg.numparams >= 1) {
379         ircclient_change_nick(p, msg.params[0]);
380       } else {
381         ircclient_send_numeric(p, 431, ":No nickname given");
382       }
383 
384     } else {
385       ircclient_send_notice(p, "Please send a /NICK command");
386     }
387 
388   } else {
389     /* The server MUST be active to use most of the commands.  The only
390        exception is /DIRCPROXY. */
391 
392     if (p->server_status == IRC_SERVER_ACTIVE) {
393       /* By default we squelch everything, but the else clause turns this off.
394          Effectively it means that all handled commands are not passed to the
395          server unless you set squelch to 0 */
396       int squelch = 1;
397 
398       if (!irc_strcasecmp(msg.cmd, "PASS")) {
399         /* Ignore PASS */
400       } else if (!irc_strcasecmp(msg.cmd, "USER")) {
401         /* Ignore USER */
402       } else if (!irc_strcasecmp(msg.cmd, "DIRCPROXY")) {
403         /* Ignore DIRCPROXY (handled in a minute) */
404       } else if (!irc_strcasecmp(msg.cmd, "QUIT")) {
405         /* User wants to detach */
406         ircnet_announce_status(p);
407         ircclient_send_error(p, "Detached from %s %s", PACKAGE, VERSION);
408         _ircclient_detach(p, 0);
409         ircprot_freemsg(&msg);
410         return 0;
411 
412       } else if (!irc_strcasecmp(msg.cmd, "PONG")) {
413         /* Ignore PONG */
414 
415       } else if (!irc_strcasecmp(msg.cmd, "NICK")) {
416         /* User changing their nickname */
417         if (msg.numparams >= 1) {
418           ircclient_change_nick(p, msg.params[0]);
419         } else {
420           ircclient_send_numeric(p, 431, ":No nickname given");
421         }
422 
423       } else if (!irc_strcasecmp(msg.cmd, "AWAY")) {
424         /* User marking themselves as away or back */
425         squelch = 0;
426 
427         /* ircII sends an empty parameter to mark back *grr* */
428         if ((msg.numparams >= 1) && strlen(msg.params[0])) {
429           free(p->awaymessage);
430           p->awaymessage = x_strdup(msg.params[0]);
431         } else {
432           free(p->awaymessage);
433           p->awaymessage = 0;
434         }
435 
436       } else if (!irc_strcasecmp(msg.cmd, "MOTD")) {
437         /* User requesting the message of the day from the server */
438         p->allow_motd = 1;
439         squelch = 0;
440 
441       } else if (!irc_strcasecmp(msg.cmd, "PING")) {
442         /* User requesting a ping from the server */
443         p->allow_pong = 1;
444         squelch = 0;
445 
446       } else if (!irc_strcasecmp(msg.cmd, "PRIVMSG")) {
447         /* All PRIVMSGs go to the server unless we fiddle */
448         squelch = _ircclient_handle_privmsg(p, msg);
449 
450       } else if (!irc_strcasecmp(msg.cmd, "NOTICE")) {
451         /* Notices from us get logged */
452         if (msg.numparams >= 2) {
453           char *str;
454 
455           ircprot_stripctcp(msg.params[1], &str, 0);
456 
457           if (str && strlen(str)) {
458             char *tmp;
459 
460             tmp = x_sprintf("%s!%s@%s", p->nickname, p->username,
461                             p->hostname);
462             irclog_log(p, IRC_LOG_NOTICE, msg.params[0], tmp, "%s", str);
463             free(tmp);
464           }
465           free(str);
466         }
467 
468         if (p->conn_class->idle_maxtime)
469           ircserver_resetidle(p);
470         squelch = 0;
471 
472       } else {
473         squelch = 0;
474       }
475 
476       /* Send command up to server? (We know there is one at this point) */
477       if (!squelch)
478         net_send(p->server_sock, "%s\r\n", msg.orig);
479 
480     } else if (irc_strcasecmp(msg.cmd, "DIRCPROXY")) {
481       /* Command didn't (and won't be) handled.  We better stick to the
482          RFC and send a RPL_TRYAGAIN back. */
483       ircclient_send_numeric(p, 263, "%s :Please wait a while and try again.",
484                              msg.cmd);
485     }
486 
487     /* /DIRCPROXY can be used at *any* time, if it ever sends anything to the
488        server it has to do it explicitly (no automatic sending) and has to
489        check there is a server there */
490     if (!irc_strcasecmp(msg.cmd, "DIRCPROXY")) {
491       if (msg.numparams >= 1) {
492         if (!irc_strcasecmp(msg.params[0], "RECALL")) {
493           _ircclient_handle_recall(p, msg);
494 
495         } else if (p->conn_class->allow_persist
496                    && !irc_strcasecmp(msg.params[0], "PERSIST")) {
497           /* User wants a die_on_close proxy to persist */
498           if (p->die_on_close) {
499             if (p->conn_class->disconnect_on_detach) {
500               /* Its die_on_close because of configuration, can't dedicate! */
501               p->die_on_close = 0;
502               ircnet_announce_dedicated(p);
503             } else if (!ircnet_dedicate(p)) {
504               /* Okay, it was inetd - we can dedicate this */
505               p->die_on_close = 0;
506             } else {
507               ircclient_send_notice(p, "Could not persist");
508             }
509           } else {
510             ircnet_announce_dedicated(p);
511           }
512 	} else if (!irc_strcasecmp(msg.params[0], "GET")) {
513 	   /* User want to get a configuration item */
514 	   if (p->conn_class->allow_dynamic >= 1) {
515 	      // todo
516 	   } else {
517 	      ircclient_send_notice(p, "You are not authorized to use GET command");
518 	   }
519 	} else if (!irc_strcasecmp(msg.params[0], "SET")) {
520 	   /* User want to set a configuration item */
521 	   if (p->conn_class->allow_dynamic == 2) {
522 	      // todo
523 	   } else {
524 	      ircclient_send_notice(p, "You are not authorized to use SET command");
525 	   }
526         } else if (!irc_strcasecmp(msg.params[0], "RELOAD")) {
527           /* User wants to reload the configuration file */
528           ircclient_send_notice(p, "RELOAD in progress");
529           reload();
530 
531         } else if (!irc_strcasecmp(msg.params[0], "DETACH")) {
532           /* User wants to detach and can't be bothered to use /QUIT */
533           ircnet_announce_status(p);
534           ircclient_send_error(p, "Detached from %s %s", PACKAGE, VERSION);
535 
536           /* Optional AWAY message can be supplied */
537           if ((msg.numparams >= 2) && strlen(msg.paramstarts[1])) {
538             _ircclient_detach(p, msg.paramstarts[1]);
539           } else {
540             _ircclient_detach(p, 0);
541           }
542           ircprot_freemsg(&msg);
543           return 0;
544 
545         } else if (!irc_strcasecmp(msg.params[0], "QUIT")) {
546           /* User wants to detach and end their proxy session */
547 
548           if (IS_SERVER_READY(p)) {
549             /* Optional QUIT message can be supplied */
550             if ((msg.numparams >= 2) && strlen(msg.paramstarts[1])) {
551               ircserver_send_command(p, "QUIT", ":%s", msg.paramstarts[1]);
552             } else if (p->conn_class->quit_message) {
553               ircserver_send_command(p, "QUIT", ":%s",
554                                      p->conn_class->quit_message);
555             } else {
556               ircserver_send_command(p, "QUIT", ":Leaving IRC - %s %s",
557                                      PACKAGE, VERSION);
558             }
559           }
560 
561           ircserver_close_sock(p);
562           p->conn_class = 0;
563           ircclient_close(p);
564           ircprot_freemsg(&msg);
565           return 0;
566 
567         } else if (!irc_strcasecmp(msg.params[0], "MOTD")) {
568           /* Display message of the day file */
569           _ircclient_motd(p);
570 
571         } else if (p->conn_class->allow_die
572                    && !irc_strcasecmp(msg.params[0], "DIE")) {
573           /* User wants to kill us :( */
574           ircclient_send_notice(p, "I'm melting!");
575           stop();
576 
577         } else if (p->conn_class->allow_users
578                    && !irc_strcasecmp(msg.params[0], "USERS")) {
579 					_ircclient_handle_users(p, msg);
580 
581         } else if (p->conn_class->allow_kill
582                    && !irc_strcasecmp(msg.params[0], "KILL")) {
583           _ircclient_handle_kill(p, msg);
584 
585 	} else if (p->conn_class->allow_notify
586 		   && !irc_strcasecmp(msg.params[0], "NOTIFY")) {
587 	  _ircclient_handle_notify(p, msg);
588 
589         } else if (!irc_strcasecmp(msg.params[0], "SERVERS")) {
590           struct strlist *s;
591           int i;
592 
593           s = p->conn_class->servers;
594           i = 0;
595 
596           /* User wants a server list */
597           if (s) {
598             ircclient_send_notice(p, "You can connect to:");
599           } else {
600             ircclient_send_notice(p, "No servers");
601           }
602 
603           while (s) {
604             ircclient_send_notice(p, "-%s %2d. %s",
605                                   (s == p->conn_class->next_server ? ">" : " "),
606                                   ++i, s->str);
607             s = s->next;
608           }
609 
610         } else if (p->conn_class->allow_jump
611                    && (!irc_strcasecmp(msg.params[0], "JUMP")
612                        || !irc_strcasecmp(msg.params[0], "CONNECT"))) {
613           if (_ircclient_handle_jump(p, msg))
614             return 0;
615 
616         } else if (p->conn_class->allow_host
617                    && !irc_strcasecmp(msg.params[0], "HOST")) {
618           /* User wants to change their hostname */
619           free(p->conn_class->local_address);
620           p->conn_class->local_address = 0;
621 
622           if (msg.numparams >= 2) {
623             if (irc_strcasecmp(msg.params[1], "none"))
624               p->conn_class->local_address = x_strdup(msg.params[1]);
625 
626           } else if (p->conn_class->orig_local_address) {
627             p->conn_class->local_address =
628                 x_strdup(p->conn_class->orig_local_address);
629           }
630 
631           ircserver_connectagain(p);
632 
633           /* We have no server now, so need to get out of here */
634           ircprot_freemsg(&msg);
635           return 0;
636 
637         } else if (!irc_strcasecmp(msg.params[0], "STATUS")) {
638           _ircclient_handle_status(p, msg);
639 
640         } else if (!irc_strcasecmp(msg.params[0], "HELP")) {
641           /* User needs a little help */
642           _ircclient_handle_help(p, msg);
643 
644         } else {
645           /* Invalid command */
646           ircclient_send_numeric(p, 421, "%s :Unknown DIRCPROXY command",
647                                  msg.params[0]);
648 
649         }
650       } else {
651         ircclient_send_numeric(p, 461, ":Not enough parameters");
652       }
653     }
654   }
655 
656   /* Do we have enough information to authenticate them? */
657   if (!(p->client_status & IRC_CLIENT_AUTHED)
658       && (p->client_status & IRC_CLIENT_GOTPASS)
659       && (p->client_status & IRC_CLIENT_GOTNICK)
660       && (p->client_status & IRC_CLIENT_GOTUSER))
661   {
662     _ircclient_authenticate(p, p->password);
663     free(p->password);
664     p->password = 0;
665     p->client_status &= ~(IRC_CLIENT_GOTPASS);
666   }
667 
668   /* Do we have enough information to connect to a server? */
669   if (IS_CLIENT_READY(p) && !p->dead) {
670     if (p->server_status != IRC_SERVER_ACTIVE) {
671       if (!(p->server_status & IRC_SERVER_CREATED)) {
672         if (p->conn_class && p->conn_class->server_autoconnect) {
673           ircserver_connect(p);
674         } else {
675           ircclient_send_notice(p, "Please send /DIRCPROXY JUMP "
676                                 "<hostname>[:[port][:[password]]] to choose a "
677                                 "server");
678 
679           /* This won't delete an existing timer */
680           timer_new((void *)p, "client_connect", g.connect_timeout,
681                     TIMER_FUNCTION(_ircclient_timedout), (void *)1);
682         }
683       } else if (!IS_SERVER_READY(p)) {
684         ircclient_send_notice(p, "Connection to server is in progress...");
685       }
686     } else if (!(p->client_status & IRC_CLIENT_SENTWELCOME)) {
687       ircclient_welcome(p);
688     }
689   }
690 
691   ircprot_freemsg(&msg);
692   return 0;
693 }
694 
695 /* Got a password */
_ircclient_authenticate(struct ircproxy * p,const char * password)696 static int _ircclient_authenticate(struct ircproxy *p, const char *password) {
697   struct ircconnclass *cc;
698 
699   cc = connclasses;
700   while (cc) {
701 #ifdef ENCRYPTED_PASSWORDS
702     char *cmp;
703 
704     cmp = crypt(password, cc->password);
705 
706     if (!strcmp(cc->password, cmp)) {
707 #else
708     if (!strcmp(cc->password, password)) {
709 #endif
710       if (cc->masklist) {
711         struct strlist *m;
712         const char *ip;
713         char buf[40];
714 
715         ip = net_ntop(&p->client_addr, buf, sizeof(buf));
716 
717         m = cc->masklist;
718         while (m) {
719           if (strcasematch(ip, m->str) || strcasematch(p->client_host, m->str))
720             break;
721 
722           m = m->next;
723         }
724 
725         /* We got a matching masklist, so this one's ok */
726         if (m)
727           break;
728       } else {
729         break;
730       }
731     }
732 
733     cc = cc->next;
734   }
735 
736   if (cc) {
737     struct ircproxy *tmp_p;
738 
739     tmp_p = ircnet_fetchclass(cc);
740     if (tmp_p && (tmp_p->client_status & IRC_CLIENT_CONNECTED)) {
741       if (tmp_p->conn_class->disconnect_existing) {
742         debug("Already connected, disconnecting existing");
743 
744         ircclient_send_error(tmp_p, "Collided with new user");
745         ircclient_close(tmp_p);
746 
747         if (tmp_p->dead) {
748           debug("Kicked off client, and they died");
749           tmp_p = 0;
750         }
751       } else {
752         debug("Already connected, disconnecting incoming");
753         ircclient_send_error(p, "Already connected");
754         ircclient_close(p);
755         return -1;
756       }
757     }
758 
759     /* Check again, in case killing existing user killed the proxy */
760     if (tmp_p) {
761       debug("Attaching new client to old server session");
762 
763       tmp_p->client_sock = p->client_sock;
764       tmp_p->client_status |= IRC_CLIENT_CONNECTED | IRC_CLIENT_AUTHED;
765       tmp_p->client_addr = p->client_addr;
766       net_hook(tmp_p->client_sock, SOCK_NORMAL, (void *)tmp_p,
767                ACTIVITY_FUNCTION(_ircclient_data),
768                ERROR_FUNCTION(_ircclient_error));
769 
770       /* If the connecting client doesn't agree with the proxy about its
771          nickname, then correct it. */
772       if (strcmp(p->nickname, tmp_p->nickname))
773         ircclient_send_selfcmd(p, "NICK", ":%s", tmp_p->nickname);
774 
775       /* If we've got to restore a different nickname, then do that now */
776       if (tmp_p->oldnickname && strcmp(tmp_p->oldnickname, tmp_p->nickname))
777         ircclient_change_nick(tmp_p, tmp_p->oldnickname);
778 
779       /* We don't need this anymore */
780       free(tmp_p->oldnickname);
781       tmp_p->oldnickname = 0;
782 
783       /* Notify nickserv */
784       if (tmp_p->conn_class->nickserv_password)
785 	 ircserver_send_command(tmp_p, "PRIVMSG", " %s :IDENTIFY %s", "NICKSERV",tmp_p->conn_class->nickserv_password);
786 
787       /* Unset any away message if we set one */
788       if (!tmp_p->awaymessage && (tmp_p->server_status == IRC_SERVER_ACTIVE)
789           && tmp_p->conn_class->away_message)
790         ircserver_send_command(tmp_p, "AWAY", "");
791 
792       /* Rejoin any channels we parted */
793       if ((tmp_p->server_status == IRC_SERVER_ACTIVE) && tmp_p->channels) {
794         struct ircchannel *c;
795 
796         c = tmp_p->channels;
797         while (c) {
798           if (c->unjoined) {
799             if (c->key) {
800               ircserver_send_command(tmp_p, "JOIN", "%s :%s", c->name, c->key);
801             } else {
802               ircserver_send_command(tmp_p, "JOIN", ":%s", c->name);
803             }
804           }
805 
806           c = c->next;
807         }
808       }
809 
810       /* Send attach message to all channels we're on */
811       if (tmp_p->server_status == IRC_SERVER_ACTIVE) {
812         if (tmp_p->conn_class->attach_message) {
813           struct ircchannel *c;
814           int slashme;
815           char *msg;
816 
817           msg = tmp_p->conn_class->attach_message;
818           if ((strlen(msg) >= 5) && !strncasecmp(msg, "/me ", 4)) {
819             /* Starts with /me */
820             slashme = 1;
821             msg += 4;
822           } else {
823             slashme = 0;
824           }
825 
826           c = tmp_p->channels;
827           while (c) {
828             if (!c->inactive) {
829               if (slashme) {
830                 ircserver_send_command(tmp_p, "PRIVMSG",
831                                        "%s :\001ACTION %s\001", c->name, msg);
832               } else {
833                 ircserver_send_command(tmp_p, "PRIVMSG", "%s :%s",
834                                        c->name, msg);
835               }
836             }
837             c = c->next;
838           }
839         }
840       }
841 
842       if ((tmp_p->server_status == IRC_SERVER_ACTIVE)
843           && !(tmp_p->client_status & IRC_CLIENT_SENTWELCOME))
844         ircclient_welcome(tmp_p);
845 
846       p->client_status = IRC_CLIENT_NONE;
847       p->client_sock = -1;
848       p->dead = 1;
849 
850     } else {
851       struct strlist *s;
852 
853       p->conn_class = cc;
854       p->client_status |= IRC_CLIENT_AUTHED;
855       time(&(p->start));
856 
857       if (p->conn_class->disconnect_on_detach)
858         p->die_on_close = 1;
859 
860       /* Okay, they've authed for the first time, make the log directory
861          here */
862       if (p->conn_class->chan_log_enabled
863           || p->conn_class->private_log_enabled
864           || p->conn_class->server_log_enabled) {
865         if (irclog_maketempdir(p))
866           ircclient_send_notice(p, "(warning) Unable to create log "
867                                    "directory, logging disabled");
868       }
869 
870       /* Initialise the private message log */
871       irclog_init(p, "");
872 
873       /* Open a log file if we're always logging */
874       if (p->conn_class->private_log_enabled
875           && p->conn_class->private_log_always) {
876         if (irclog_open(p, ""))
877           ircclient_send_notice(p, "(warning) Unable to log private messages");
878       }
879 
880       /* Initialise the server message log */
881       irclog_init(p, 0);
882 
883       /* Open a log file if we're always logging */
884       if (p->conn_class->server_log_enabled
885           && p->conn_class->server_log_always) {
886         if (irclog_open(p, 0))
887           ircclient_send_notice(p, "(warning) Unable to log server messages");
888       }
889 
890       /* Join initial channels */
891       s = p->conn_class->channels;
892       while (s) {
893         struct ircchannel *c;
894         char *name, *key;
895 
896         name = x_strdup(s->str);
897         key = strchr(name, ' ');
898         if (key)
899           *(key++) = 0;
900 
901         ircnet_addchannel(p, name);
902         c = ircnet_fetchchannel(p, name);
903         if (c) {
904           c->inactive = 1;
905           if (key)
906             c->key = x_strdup(key);
907         }
908 
909         free(name);
910 
911         s = s->next;
912       }
913 
914       /* Set initial modes */
915       if (p->conn_class->initial_modes)
916         ircclient_change_mode(p, p->conn_class->initial_modes);
917 
918       /* Notify nickserv */
919       // currently broken i will look in next revision
920       /* if (p->conn_class->nickserv_password) */
921       /* ircserver_send_command(p, "PRIVMSG", " %s :IDENTIFY %s", "NICKSERV",p->conn_class->nickserv_password); */
922     }
923 
924     return 0;
925   }
926 
927   ircclient_send_numeric(p, 464, ":You are not permitted to use this proxy");
928   ircclient_send_error(p, "Permission Denied");
929   ircclient_close(p);
930   return -1;
931 }
932 
933 /* Request a nickname change */
934 int ircclient_change_nick(struct ircproxy *p, const char *newnick) {
935   /* If a server is ready to accept a NICK command, send it */
936   if (IS_SERVER_READY(p)) {
937     debug("Requesting nick change from '%s' to '%s'",
938           (p->nickname ? p->nickname : ""), newnick);
939     ircserver_send_command(p, "NICK", ":%s", newnick);
940   }
941 
942   /* If we have a nickname already then the server will confirm that, otherwise
943      we should remember it ourselves */
944   if (p->client_status & IRC_CLIENT_GOTNICK) {
945     debug("Server will change it for us");
946     p->expecting_nick = 1;
947 
948     return 0;
949   } else {
950     int ret;
951 
952     /* Because we're not expecting a server confirmation, then we better
953        do the confirm for the client ourselves */
954     if ((p->client_status & IRC_CLIENT_CONNECTED) &&
955         (p->client_status & IRC_CLIENT_AUTHED))
956       ircclient_send_selfcmd(p, "NICK", ":%s", newnick);
957 
958     /* Make the change in the wings */
959     ret = ircclient_nick_changed(p, newnick);
960     ircclient_setnickname(p);
961     ircclient_checknickname(p);
962 
963     return ret;
964   }
965 }
966 
967 /* Nickname has now definitly been changed */
968 int ircclient_nick_changed(struct ircproxy *p, const char *newnick) {
969   if (p->nickname)
970     debug("nickname WAS '%s'", p->nickname);
971   free(p->nickname);
972 
973   p->nickname = x_strdup(newnick);
974   p->client_status |= IRC_CLIENT_GOTNICK;
975   debug("nickname NOW '%s'", p->nickname);
976 
977   return 0;
978 }
979 
980 /* Make the current nickname the set one */
981 int ircclient_setnickname(struct ircproxy *p) {
982   /* Update setnickname too */
983   if (p->setnickname)
984     free(p->setnickname);
985   p->setnickname = x_strdup(p->nickname);
986   debug("Changed setnickname to '%s'", p->setnickname);
987 
988   return 0;
989 }
990 
991 /* Check whether we need to restore the nickname later */
992 int ircclient_checknickname(struct ircproxy *p) {
993   if (p->conn_class && p->conn_class->nick_keep
994       && strcmp(p->nickname, p->setnickname))
995     timer_new((void *)p, "client_resetnick", NICK_GUARD_TIME,
996               TIMER_FUNCTION(_ircclient_resetnick), (void *)0);
997 
998   return 0;
999 }
1000 
1001 /* Change the nickname to something we generate ourselves */
1002 int ircclient_generate_nick(struct ircproxy *p, const char *tried) {
1003   char *c, *nick;
1004   int ret;
1005 
1006   c = nick = (char *)malloc(strlen(tried) + 2);
1007   strcpy(nick, tried);
1008   c += strlen(nick) - 1;
1009 
1010   /* We add -'s until we can't, then we move back through them cycling them
1011      0..9 then finally _ until the whole nickname is _________.  Once that
1012      happens we just use 'dircproxy' and do it all over again */
1013   if (strlen(nick) < 9) {
1014     *(++c) = '-';
1015     *(++c) = 0;
1016   } else {
1017     while (c >= nick) {
1018       if (*c == '-') {
1019         *c = '0';
1020         break;
1021       } else if ((*c >= '0') && (*c < '9')) {
1022         (*c)++;
1023         break;
1024       } else if (*c == '9') {
1025         *c = '_';
1026         break;
1027       } else if (*c == '_') {
1028         c--;
1029       } else {
1030         *c = '-';
1031         break;
1032       }
1033     }
1034 
1035     if (c < nick) {
1036       free(nick);
1037       nick = x_strdup(FALLBACK_NICKNAME);
1038     }
1039   }
1040 
1041   /* Ask the server to change the nickname */
1042   if (IS_SERVER_READY(p)) {
1043     debug("Requesting nick change from '%s' to '%s'",
1044           (p->nickname ? p->nickname : ""), nick);
1045     ircserver_send_command(p, "NICK", ":%s", nick);
1046   }
1047 
1048   /* If we don't have a nickname yet, make the change ourselves */
1049   if (!(p->client_status & IRC_CLIENT_GOTNICK)) {
1050     /* We know that there is no client connected, otherwise this would
1051        never have been called, so no point sending a nickname to the client.
1052 
1053        Just change it in our memory */
1054     ret = ircclient_nick_changed(p, nick);
1055     ircclient_checknickname(p);
1056   } else {
1057     debug("Server will change it for us");
1058   }
1059 
1060   free(nick);
1061   return 0;
1062 }
1063 
1064 /* Timer hook to restore a lost nickname */
1065 static void _ircclient_resetnick(struct ircproxy *p, void *data) {
1066   /* We don't have a server anymore, setnickname will be restored on
1067      connection attempt */
1068   if (!IS_SERVER_READY(p))
1069     return;
1070 
1071   /* Is it worth doing this? */
1072   if (!strcmp(p->nickname, p->setnickname))
1073     return;
1074 
1075   /* Ask the server to change the nickname */
1076   debug("Attempting to restore nickname to '%s'", p->setnickname);
1077   ircclient_change_nick(p, p->setnickname);
1078 }
1079 
1080 /* Got some details */
1081 static int _ircclient_got_details(struct ircproxy *p, const char *newusername,
1082                                   const char *newmode, const char *unused,
1083                                   const char *newrealname) {
1084   int mode;
1085 
1086   if (!p->username)
1087     p->username = x_strdup(newusername);
1088 
1089   if (!p->realname)
1090     p->realname = x_strdup(newrealname);
1091 
1092   /* RFC2812 states that the second parameter to USER is a numeric stating
1093      what default modes to set.  This disagrees with RFC1459.  We follow
1094      the newer one if we can, as the old hostname/servername combo were
1095      useless and ignored anyway. */
1096   mode = atoi(newmode);
1097   if (mode & RFC2812_MODE_W)
1098     ircclient_change_mode(p, "+w");
1099   if (mode & RFC2812_MODE_I)
1100     ircclient_change_mode(p, "+w");
1101 
1102   /* Okay we have the username now */
1103   p->client_status |= IRC_CLIENT_GOTUSER;
1104 
1105   return 0;
1106 }
1107 
1108 /* Got a personal mode change */
1109 int ircclient_change_mode(struct ircproxy *p, const char *change) {
1110   char *ptr, *str;
1111   int add = 1;
1112 
1113   ptr = str = x_strdup(change);
1114   debug("Mode change from '%s', '%s'", (p->modes ? p->modes : ""), str);
1115 
1116   while (*ptr) {
1117     switch (*ptr) {
1118       case '+':
1119         add = 1;
1120         break;
1121       case '-':
1122         add = 0;
1123         break;
1124       default:
1125         if (add) {
1126           if (!p->modes || !strchr(p->modes, *ptr)) {
1127             if (p->modes) {
1128               p->modes = (char *)realloc(p->modes, strlen(p->modes) + 2);
1129             } else {
1130               p->modes = (char *)malloc(2);
1131               p->modes[0] = 0;
1132             }
1133             p->modes[strlen(p->modes) + 1] = 0;
1134             p->modes[strlen(p->modes)] = *ptr;
1135           }
1136         } else if (p->modes) {
1137           char *pos;
1138 
1139           pos = strchr(p->modes, *ptr);
1140           if (pos) {
1141             char *tmp;
1142 
1143             tmp = p->modes;
1144             p->modes = (char *)malloc(strlen(p->modes));
1145             *(pos++) = 0;
1146             strcpy(p->modes, tmp);
1147             strcpy(p->modes + strlen(p->modes), pos);
1148             free(tmp);
1149 
1150             if (!strlen(p->modes)) {
1151               free(p->modes);
1152               p->modes = 0;
1153             }
1154           }
1155         }
1156     }
1157 
1158     ptr++;
1159   }
1160 
1161   debug("    now '%s'", (p->modes ? p->modes : ""));
1162   free(str);
1163   return 0;
1164 }
1165 
1166 /* Close the client socket */
1167 int ircclient_close(struct ircproxy *p) {
1168   timer_del((void *)p, "client_auth");
1169   timer_del((void *)p, "client_connect");
1170 
1171   net_close(&(p->client_sock));
1172   p->client_sock = -1;
1173   p->client_status &= ~(IRC_CLIENT_CONNECTED | IRC_CLIENT_AUTHED
1174                         | IRC_CLIENT_SENTWELCOME);
1175 
1176   /* No connection class, or no nick or user? Die! */
1177   if (!p->conn_class || !(p->client_status & IRC_CLIENT_GOTNICK)
1178       || !(p->client_status & IRC_CLIENT_GOTUSER)) {
1179     if (p->server_status & IRC_SERVER_CREATED) {
1180       ircserver_send_command(p, "QUIT", ":I shouldn't really be here - %s %s",
1181                              PACKAGE, VERSION);
1182       ircserver_close_sock(p);
1183     }
1184     p->dead = 1;
1185   }
1186 
1187   return p->dead;
1188 }
1189 
1190 /* send message of the day to the user */
1191 static int _ircclient_motd(struct ircproxy *p) {
1192   FILE *motd_file;
1193 
1194   if (p->conn_class->motd_file) {
1195     motd_file = fopen(p->conn_class->motd_file, "r");
1196     if (!motd_file)
1197       syscall_fail("fopen", p->conn_class->motd_file, 0);
1198   } else {
1199     motd_file = (FILE *)0;
1200   }
1201 
1202   /* Check whether to do anything, and send appropriate numerics */
1203   if (!p->conn_class->motd_logo && !p->conn_class->motd_stats && !motd_file) {
1204     if (p->conn_class->motd_file) {
1205       ircclient_send_numeric(p, 422, ":MOTD File is missing");
1206     } else {
1207       ircclient_send_numeric(p, 422, ":No MOTD");
1208     }
1209     return 0;
1210   } else {
1211     ircclient_send_numeric(p, 375, ":- %s Message of the Day -", PACKAGE);
1212   }
1213 
1214   /* Send the pretty dircproxy logo */
1215   if (p->conn_class->motd_logo) {
1216     char *ver;
1217     int line;
1218 
1219     line = 0;
1220     while (logo[line]) {
1221       ircclient_send_numeric(p, 372, ":- %s", logo[line]);
1222       line++;
1223     }
1224 
1225     ver = x_sprintf(verstr, VERSION);
1226     ircclient_send_numeric(p, 372, ":- %s", ver);
1227     ircclient_send_numeric(p, 372, ":-");
1228     free(ver);
1229   }
1230 
1231   /* Send from file */
1232   if (motd_file) {
1233     char buff[512];
1234 
1235     while (fgets(buff, 512, motd_file)) {
1236       char *ptr;
1237 
1238       ptr = buff + strlen(buff);
1239       while ((ptr >= buff) && (!ptr || strchr(" \t\r\n", *ptr))) *(ptr--) = 0;
1240       ircclient_send_numeric(p, 372, ":- %s", buff);
1241     }
1242 
1243     ircclient_send_numeric(p, 372, ":-");
1244   }
1245 
1246   /* Send some stats */
1247   if (p->conn_class->motd_stats) {
1248     /* Private messages */
1249     if (p->private_log.filename) {
1250       char *s;
1251 
1252       if (p->conn_class->private_log_recall == -1) {
1253         s = x_strdup(p->private_log.nlines ? "all" : "none");
1254       } else if (p->conn_class->private_log_recall == 0) {
1255         s = x_strdup("none");
1256       } else if (p->conn_class->private_log_recall == p->private_log.nlines) {
1257         s = x_strdup("all");
1258       } else {
1259         s = x_sprintf("%ld", p->conn_class->private_log_recall);
1260       }
1261 
1262       ircclient_send_numeric(p, 372, ":- %ld private message%s "
1263                              "(%s will be sent)", p->private_log.nlines,
1264                              (p->private_log.nlines == 1 ? "" : "s"), s);
1265       ircclient_send_numeric(p, 372, ":-");
1266 
1267       free(s);
1268     }
1269 
1270     /* Server messages */
1271     if (p->server_log.filename) {
1272       char *s;
1273 
1274       if (p->conn_class->server_log_recall == -1) {
1275         s = x_strdup(p->server_log.nlines ? "all" : "none");
1276       } else if (p->conn_class->server_log_recall == 0) {
1277         s = x_strdup("none");
1278       } else if (p->conn_class->server_log_recall == p->server_log.nlines) {
1279         s = x_strdup("all");
1280       } else {
1281         s = x_sprintf("%ld", p->conn_class->server_log_recall);
1282       }
1283 
1284       ircclient_send_numeric(p, 372, ":- %ld server message%s "
1285                              "(%s will be sent)", p->server_log.nlines,
1286                              (p->server_log.nlines == 1 ? "" : "s"), s);
1287       ircclient_send_numeric(p, 372, ":-");
1288 
1289       free(s);
1290     }
1291 
1292 
1293     /* Channels they were on */
1294     if (p->channels) {
1295       struct ircchannel *c;
1296 
1297       c = p->channels;
1298       while (c) {
1299         if (c->inactive) {
1300           if (c->log.nlines) {
1301             ircclient_send_numeric(p, 372, ":- was on %s but removed by force",
1302                                    c->name);
1303           } else {
1304             ircclient_send_numeric(p, 372, ":- yet to join %s", c->name);
1305           }
1306         } else if (c->unjoined) {
1307           ircclient_send_numeric(p, 372, ":- was on %s, yet to rejoin",
1308                                  c->name);
1309         } else if (c->log.filename) {
1310           char *s;
1311 
1312           if (p->conn_class->chan_log_recall == -1) {
1313             s = x_strdup(c->log.nlines ? "all" : "none");
1314           } else if (p->conn_class->chan_log_recall == 0) {
1315             s = x_strdup("none");
1316           } else if (p->conn_class->chan_log_recall == c->log.nlines) {
1317             s = x_strdup("all");
1318           } else {
1319             s = x_sprintf("%ld", MIN(c->log.nlines,
1320                                      p->conn_class->chan_log_recall));
1321           }
1322 
1323           ircclient_send_numeric(p, 372, ":- %s. %ld line%s logged. "
1324                                  "(%s will be sent)", c->name, c->log.nlines,
1325                                  (c->log.nlines == 1 ? "" : "s"), s);
1326 
1327           free(s);
1328         } else {
1329           ircclient_send_numeric(p, 372, ":- %s (not logged)", c->name);
1330         }
1331         c = c->next;
1332       }
1333       ircclient_send_numeric(p, 372, ":-");
1334     }
1335   }
1336 
1337   /* Done */
1338   ircclient_send_numeric(p, 376, ":End of /MOTD command");
1339   if (motd_file)
1340     fclose(motd_file);
1341 
1342   return 1;
1343 }
1344 
1345 /* send welcome headers to the user */
1346 int ircclient_welcome(struct ircproxy *p) {
1347   char tbuf[40];
1348 
1349   strftime(tbuf, sizeof(tbuf), START_TIMEDATE_FORMAT, localtime(&(p->start)));
1350 
1351   ircclient_send_numeric(p, 1, ":Welcome to the Internet Relay Network %s",
1352                          p->nickname);
1353   ircclient_send_numeric(p, 2, ":Your host is %s running %s via %s %s",
1354                          p->servername,
1355                          (p->serverver ? p->serverver : "(unknown)"),
1356                          PACKAGE, VERSION);
1357   ircclient_send_numeric(p, 3, ":This proxy has been running since %s", tbuf);
1358   if (p->serverver)
1359     ircclient_send_numeric(p, 4, "%s %s %s %s",
1360                            p->servername, p->serverver,
1361                            p->serverumodes, p->servercmodes);
1362   struct strlist *s = p->serversupported;
1363   while (s) {
1364     ircclient_send_numeric(p, 5, "%s", s->str);
1365     s = s->next;
1366   }
1367 
1368   _ircclient_motd(p);
1369 
1370   if (p->modes)
1371     ircclient_send_selfcmd(p, "MODE", "%s +%s", p->nickname, p->modes);
1372 
1373   if (p->awaymessage) {
1374     /* Ack.  There's no reason for a client to expect AWAY from a server,
1375        so we cheat and send a 306, reminding them what their away message
1376        was in the text.  This might not trick the client either, but hey,
1377        I can't do anything about that. */
1378     ircclient_send_numeric(p, 306, ":%s: %s",
1379                            "You left yourself away.  Your message was",
1380                            p->awaymessage);
1381   }
1382 
1383   /* Recall other log file */
1384   if (p->conn_class->server_log_enabled) {
1385     irclog_autorecall(p, 0);
1386     if (!p->conn_class->server_log_always)
1387       irclog_close(p, 0);
1388   }
1389 
1390   /* Recall channel log files, and get channel topic and members from server */
1391   if (p->channels) {
1392     struct ircchannel *c;
1393 
1394     c = p->channels;
1395     while (c) {
1396       if (!c->inactive && !c->unjoined) {
1397         ircclient_send_selfcmd(p, "JOIN", ":%s", c->name);
1398         ircserver_send_command(p, "TOPIC", ":%s", c->name);
1399         ircserver_send_command(p, "NAMES", ":%s", c->name);
1400 
1401         if (p->conn_class->chan_log_enabled) {
1402           irclog_autorecall(p, c->name);
1403           if (!p->conn_class->chan_log_always)
1404             irclog_close(p, c->name);
1405         }
1406       }
1407 
1408       c = c->next;
1409     }
1410   }
1411 
1412   /* Recall private log file */
1413   if (p->conn_class->private_log_enabled) {
1414     irclog_autorecall(p, p->nickname);
1415     if (!p->conn_class->private_log_always)
1416       irclog_close(p, p->nickname);
1417   }
1418 
1419   irclog_log(p, IRC_LOG_CLIENT, IRC_LOGFILE_ALL, PACKAGE, "You connected");
1420   ircnet_announce_status(p);
1421 
1422   p->client_status |= IRC_CLIENT_SENTWELCOME;
1423   return 0;
1424 }
1425 
1426 /* Timer hook when something's timed out */
1427 static void _ircclient_timedout(struct ircproxy *p, void *data) {
1428   int connect;
1429 
1430   /* These are always called after the timeout if the client's still
1431      connected, check the event they were looking for has happened */
1432   connect = (int)data;
1433   if (connect && (p->server_status & IRC_SERVER_CREATED)) {
1434     /* Connecting to server, and a socket has been created to do it */
1435     debug("Server has been chosen");
1436     return;
1437   } else if (!connect && IS_CLIENT_READY(p)) {
1438     /* Authorization, and client is ready to accept data */
1439     debug("They are authorized");
1440     return;
1441   }
1442 
1443   /* Timeout! */
1444   debug("Timed out");
1445   ircclient_send_error(p, "%s Timeout", (connect ? "Connect" : "Login"));
1446   ircclient_close(p);
1447 }
1448 
1449 /* send a numeric to the user */
1450 int ircclient_send_numeric(struct ircproxy *p, short numeric,
1451                            const char *format, ...) {
1452   va_list ap;
1453   char *msg;
1454   int ret;
1455 
1456   va_start(ap, format);
1457   msg = x_vsprintf(format, ap);
1458   va_end(ap);
1459 
1460   ret = net_send(p->client_sock, ":%s %03d %s %s\r\n",
1461                  (p->servername ? p->servername : PACKAGE), numeric,
1462                  (p->nickname ? p->nickname : "*"), msg);
1463   debug("<- ':%s %03d %s %s'", (p->servername ? p->servername : PACKAGE),
1464         numeric, (p->nickname ? p->nickname : "*"), msg);
1465 
1466   free(msg);
1467   return ret;
1468 }
1469 
1470 /* send a notice to the user */
1471 int ircclient_send_notice(struct ircproxy *p, const char *format, ...) {
1472   va_list ap;
1473   char *msg;
1474   int ret;
1475 
1476   va_start(ap, format);
1477   msg = x_vsprintf(format, ap);
1478   va_end(ap);
1479 
1480   ret = net_send(p->client_sock, ":%s %s %s :%s\r\n", PACKAGE, "NOTICE",
1481                  (p->nickname ? p->nickname : "AUTH"), msg);
1482   debug("<- ':%s %s %s :%s'", PACKAGE, "NOTICE",
1483         (p->nickname ? p->nickname : "AUTH"), msg);
1484 
1485   free(msg);
1486   return ret;
1487 }
1488 
1489 /* send a notice to a channel */
1490 int ircclient_send_channotice(struct ircproxy *p, const char *channel,
1491                               const char *format, ...) {
1492   va_list ap;
1493   char *msg;
1494   int ret;
1495 
1496   va_start(ap, format);
1497   msg = x_vsprintf(format, ap);
1498   va_end(ap);
1499 
1500   ret = net_send(p->client_sock, ":%s %s %s :%s\r\n",
1501                  (p->servername ? p->servername : PACKAGE), "NOTICE",
1502                  channel, msg);
1503   debug("<- ':%s %s %s :%s'", (p->servername ? p->servername : PACKAGE),
1504         "NOTICE", channel, msg);
1505 
1506   free(msg);
1507   return ret;
1508 }
1509 
1510 /* send a command to the user from the server */
1511 int ircclient_send_command(struct ircproxy *p, const char *command,
1512                            const char *format, ...) {
1513   va_list ap;
1514   char *msg;
1515   int ret;
1516 
1517   va_start(ap, format);
1518   msg = x_vsprintf(format, ap);
1519   va_end(ap);
1520 
1521   ret = net_send(p->client_sock, ":%s %s %s\r\n",
1522                  (p->servername ? p->servername : PACKAGE), command, msg);
1523   debug("<- ':%s %s %s'", (p->servername ? p->servername : PACKAGE),
1524         command, msg);
1525 
1526   free(msg);
1527   return ret;
1528 }
1529 
1530 /* send a command to the user making it look like its from them */
1531 int ircclient_send_selfcmd(struct ircproxy *p, const char *command,
1532                            const char *format, ...) {
1533   char *msg, *prefix;
1534   va_list ap;
1535   int ret;
1536 
1537   va_start(ap, format);
1538   msg = x_vsprintf(format, ap);
1539   va_end(ap);
1540 
1541   if (p->nickname && p->username && p->hostname) {
1542     prefix = x_sprintf(":%s!%s@%s ", p->nickname, p->username, p->hostname);
1543   } else if (p->nickname) {
1544     prefix = x_sprintf(":%s ", p->nickname);
1545   } else {
1546     prefix = (char *)malloc(1);
1547     prefix[0] = 0;
1548   }
1549 
1550   ret = net_send(p->client_sock, "%s%s %s\r\n", prefix, command, msg);
1551   debug("<- '%s%s %s'", prefix, command, msg);
1552 
1553   free(prefix);
1554   free(msg);
1555   return ret;
1556 }
1557 
1558 /* send an error to the user */
1559 int ircclient_send_error(struct ircproxy *p, const char *format, ...) {
1560   char *msg, *nick, *user, *host;
1561   va_list ap;
1562   int ret;
1563 
1564   va_start(ap, format);
1565   msg = x_vsprintf(format, ap);
1566   va_end(ap);
1567 
1568   nick = p->nickname ? p->nickname : "";
1569   user = p->username ? p->username : "user";
1570   host = p->hostname ? p->hostname : "host";
1571 
1572   ret = net_send(p->client_sock, "%s :%s: %s[%s@%s] (%s)\r\n",
1573                  "ERROR", "Closing Link", nick, user, host, msg);
1574   debug("<- '%s :%s: %s[%s@%s] (%s)'", "ERROR", "Closing Link",
1575         nick, user, host, msg);
1576 
1577   free(msg);
1578   return ret;
1579 }
1580 
1581 /* Send a DCC reject message */
1582 static int _ircclient_send_dccreject(struct ircproxy *p, const char *msg,
1583                                      const char *reason) {
1584   int ret = 1;
1585 
1586   if (p && p->conn_class && p->conn_class->dcc_proxy_sendreject &&
1587       (p->client_status == IRC_CLIENT_ACTIVE)) {
1588     if (reason) {
1589       ret = net_send(p->client_sock, "%s (%s: %s)\001\r\n", msg,
1590                      PACKAGE, reason);
1591       debug("<- '%s (%s: %s)\001'", msg, PACKAGE, reason);
1592     } else {
1593       ret = net_send(p->client_sock, "%s\001\r\n", msg);
1594       debug("<- '%s\001'", msg);
1595     }
1596   }
1597 
1598   return ret;
1599 }
1600 
1601   /* /DIRCPROXY STATUS handler */
1602 void _ircclient_handle_status(struct ircproxy *p, struct ircmessage msg) {
1603   struct ircchannel *c;
1604   struct strlist *s;
1605 
1606   ircclient_send_notice(p, "%s %s status:", PACKAGE, VERSION);
1607   ircclient_send_notice(p, "- Nickname on server: %s", p->nickname);
1608   ircclient_send_notice(p, "- Nickname to guard: %s", p->setnickname);
1609   ircclient_send_notice(p, "- Username for server: %s", p->username);
1610   ircclient_send_notice(p, "- Hostname for server: %s", p->hostname);
1611   ircclient_send_notice(p, "- Real name for server: %s", p->realname);
1612   ircclient_send_notice(p, "-");
1613 
1614   ircclient_send_notice(p, "- Client status: %s",
1615                         IS_CLIENT_READY(p) ? "Ready" : "");
1616   if (p->client_status != IRC_CLIENT_ACTIVE) {
1617     if (p->client_status & IRC_CLIENT_CONNECTED)
1618       ircclient_send_notice(p, "-   Connected");
1619     if (p->client_status & IRC_CLIENT_GOTPASS)
1620       ircclient_send_notice(p, "-   Received password");
1621     if (p->client_status & IRC_CLIENT_GOTNICK)
1622       ircclient_send_notice(p, "-   Received nickname");
1623     if (p->client_status & IRC_CLIENT_GOTUSER)
1624       ircclient_send_notice(p, "-   Received user information");
1625     if (p->client_status & IRC_CLIENT_AUTHED)
1626       ircclient_send_notice(p, "-   Authorised");
1627     if (p->client_status & IRC_CLIENT_SENTWELCOME)
1628       ircclient_send_notice(p, "-   Welcomed");
1629   }
1630   ircclient_send_notice(p, "-");
1631 
1632   ircclient_send_notice(p, "- Server status: %s",
1633                         IS_SERVER_READY(p) ? "Ready" : "");
1634   if (p->server_status != IRC_SERVER_ACTIVE) {
1635     if (p->server_status & IRC_SERVER_CREATED)
1636       ircclient_send_notice(p, "-   Created");
1637     if (p->server_status & IRC_SERVER_SEEN)
1638       ircclient_send_notice(p, "-   Seen");
1639     if (p->server_status & IRC_SERVER_CONNECTED)
1640       ircclient_send_notice(p, "-   Connected");
1641     if (p->server_status & IRC_SERVER_INTRODUCED)
1642       ircclient_send_notice(p, "-   Introduced ourselves");
1643     if (p->server_status & IRC_SERVER_GOTWELCOME)
1644       ircclient_send_notice(p, "-   Have been welcomed");
1645   }
1646   ircclient_send_notice(p, "-");
1647 
1648   ircclient_send_notice(p,  "- Servers.  Current marked by '->'");
1649   s = p->conn_class->servers;
1650   while (s) {
1651     ircclient_send_notice(p, "-%s  %s",
1652                           (s == p->conn_class->next_server ? ">" : " "),
1653                           s->str);
1654     s = s->next;
1655   }
1656   ircclient_send_notice(p, "-");
1657 
1658   ircclient_send_notice(p, "- Channels");
1659   c = p->channels;
1660   while (c) {
1661     ircclient_send_notice(p, "-   %s%s%s%s%s%s",
1662                           c->name,
1663                           c->key ? " (key: " : "",
1664                           c->key ? c->key : "",
1665                           c->key ? ")" : "",
1666                           c->inactive ? " (removed by force)" : "",
1667                           c->unjoined ? " (left on detach)" : "");
1668     c = c->next;
1669   }
1670   ircclient_send_notice(p, "-");
1671 
1672   ircclient_send_notice(p, "- Advanced:");
1673   ircclient_send_notice(p, "-   Allow MOTD count: %d", p->allow_motd);
1674   ircclient_send_notice(p, "-   Allow PONG count: %d", p->allow_pong);
1675   ircclient_send_notice(p, "-   411 Squelch count: %d", p->squelch_411);
1676   ircclient_send_notice(p, "-   Expecting NICK count: %d",
1677                         p->expecting_nick);
1678 
1679   if (p->squelch_modes)
1680     ircclient_send_notice(p, "-   Squelching mode changes:");
1681   s = p->squelch_modes;
1682   while (s) {
1683     ircclient_send_notice(p, "-     %s", s->str);
1684     s = s->next;
1685   }
1686 }
1687 
1688   /* /DIRCPROXY JUMP handler */
1689 int _ircclient_handle_jump(struct ircproxy *p, struct ircmessage msg) {
1690   struct strlist *server;
1691 
1692   /* User wants to jump to a new server */
1693   if (msg.numparams >= 2) {
1694     struct strlist *s;
1695     int i;
1696 
1697     /* Check the server list to see whether its a plain jump */
1698     server = 0;
1699     s = p->conn_class->servers;
1700     i = 0;
1701     while (s) {
1702       if ((atoi(msg.params[1]) == ++i)
1703           || !irc_strcasecmp(msg.params[1], s->str)) {
1704         server = s;
1705         break;
1706       }
1707 
1708       s = s->next;
1709     }
1710 
1711   } else {
1712     /* User wants to jump to the next server */
1713     server = 0;
1714     if (p->conn_class->next_server)
1715       server = p->conn_class->next_server->next;
1716     if (!server)
1717       server = p->conn_class->servers;
1718   }
1719 
1720   /* Allocate new server if jump_new */
1721   if (!server && p->conn_class->allow_jump_new
1722       && (msg.numparams >= 2)) {
1723     debug("New server");
1724 
1725     server = (struct strlist *)malloc(sizeof(struct strlist));
1726     server->str = x_strdup(msg.params[1]);
1727     server->next = 0;
1728 
1729     if (p->conn_class->servers) {
1730       struct strlist *ss;
1731 
1732       ss = p->conn_class->servers;
1733       while (ss->next)
1734         ss = ss->next;
1735 
1736       ss->next = server;
1737     } else {
1738       p->conn_class->servers = server;
1739     }
1740 
1741   } else if (!server) {
1742     ircclient_send_numeric(p, 402, "No such server, "
1743                            "use /DIRCPROXY SERVERS to see them");
1744   }
1745 
1746   if (server) {
1747     debug("Jumping to %s", server->str);
1748 
1749     p->conn_class->next_server = server;
1750     ircserver_connectagain(p);
1751 
1752     /* We have no server now, so need to get out of here */
1753     ircprot_freemsg(&msg);
1754     return 1;
1755   } else {
1756     return 0;
1757   }
1758 }
1759 
1760   /* /DIRCPROXY KILL handler */
1761 void _ircclient_handle_kill(struct ircproxy *p, struct ircmessage msg) {
1762   struct ircproxy *proxy;
1763 
1764   /* User wants to kill a user */
1765   if (msg.numparams >= 2) {
1766     struct ircconnclass *c;
1767     struct ircproxy *cp;
1768     int i;
1769 
1770     /* Check the user list */
1771     proxy = 0;
1772     c = connclasses;
1773     i = 0;
1774     while (c) {
1775       cp = ircnet_fetchclass(c);
1776       if (!cp) {
1777         c = c->next;
1778         continue;
1779       }
1780 
1781       if ((atoi(msg.params[1]) == ++i)
1782           || (cp->client_host
1783               && !irc_strcasecmp(msg.params[1], cp->client_host))
1784           || (cp->servername
1785               && !irc_strcasecmp(msg.params[1], cp->servername))
1786           || (cp->nickname
1787               && !irc_strcasecmp(msg.params[1], cp->nickname))) {
1788         proxy = cp;
1789         break;
1790       }
1791 
1792       c = c->next;
1793     }
1794 
1795     if (proxy && (proxy == p)) {
1796       ircclient_send_notice(p, "Use /DIRCPROXY QUIT to kill yourself");
1797     } else if (proxy) {
1798       if (IS_SERVER_READY(proxy)) {
1799         ircserver_send_command(proxy, "QUIT",
1800                                ":Killed by adminstrator - %s %s",
1801                                PACKAGE, VERSION);
1802       }
1803       if (IS_CLIENT_READY(proxy)) {
1804         ircclient_send_error(proxy, "Killed by administrator");
1805       }
1806 
1807       ircserver_close_sock(proxy);
1808       proxy->conn_class = 0;
1809       ircclient_close(proxy);
1810 
1811     } else {
1812       ircclient_send_numeric(p, 401, "No such user, "
1813                              "use /DIRCPROXY USERS to see them");
1814     }
1815 
1816   } else {
1817     ircclient_send_numeric(p, 461, ":Not enough parameters");
1818   }
1819 }
1820 
1821   /* /DIRCPROXY NOTIFY handler */
1822 void _ircclient_handle_notify(struct ircproxy *p, struct ircmessage msg)
1823 {
1824    struct ircproxy *proxy;
1825    /* User wants to kill a user */
1826    if (msg.numparams >= 2) {
1827       struct ircconnclass *c;
1828       struct ircproxy *cp;
1829       int i;
1830 
1831       /* Check the user list */
1832       proxy = 0;
1833       c = connclasses;
1834       i = 0;
1835       while (c)  {
1836          cp = ircnet_fetchclass(c);
1837          if (!cp) {
1838             c = c->next;
1839             continue;
1840          }
1841          if ((atoi(msg.params[1]) == ++i)
1842              || (cp->client_host
1843                  && !irc_strcasecmp(msg.params[1], cp->client_host))
1844              || (cp->servername
1845                  && !irc_strcasecmp(msg.params[1], cp->servername))
1846              || (cp->nickname
1847                  && !irc_strcasecmp(msg.params[1], cp->nickname))) {
1848             proxy = cp;
1849             break;
1850          }
1851          c = c->next;
1852       }
1853 
1854       if (proxy) {
1855          net_send(proxy->client_sock, ":dircproxy!dircproxy@localhost NOTICE %s :%s\r\n",cp->nickname, msg.paramstarts[2]);
1856       } else {
1857          ircclient_send_numeric(p, 401, "No such user, use /DIRCPROXY USERS to see them");
1858       }
1859    }
1860 
1861 }
1862 
1863 
1864   /* /DIRCPROXY USERS handler */
1865 void _ircclient_handle_users(struct ircproxy *p, struct ircmessage msg) {
1866   struct ircconnclass *c;
1867   struct ircproxy *cp;
1868   int i;
1869 
1870   c = connclasses;
1871   i = 0;
1872 
1873   ircclient_send_notice(p, "Connection classes:");
1874 
1875   while (c) {
1876     cp = ircnet_fetchclass(c);
1877     if (!cp) {
1878       c = c->next;
1879       continue;
1880     }
1881 
1882     ircclient_send_notice(p, "-%s %2d. %s -> %s (%s)",
1883                           (cp == p ? ">" : " "), ++i,
1884                           cp->client_host ? cp->client_host : "(none)",
1885                           cp->servername ? cp->servername : "(none)",
1886                           cp->nickname ? cp->nickname : "no nickname");
1887     c = c->next;
1888   }
1889 }
1890 
1891   /* /DIRCPROXY RECALL handler */
1892 void _ircclient_handle_recall(struct ircproxy *p, struct ircmessage msg) {
1893   char *src, *filter;
1894   long start, lines;
1895 
1896   /* User wants to recall stuff from log files */
1897   src = filter = 0;
1898   start = -1;
1899   lines = 0;
1900 
1901   if (msg.numparams >= 4) {
1902     src = msg.params[1];
1903     start = atol(msg.params[2]);
1904     lines = atol(msg.params[3]);
1905   } else if (msg.numparams >= 3) {
1906     if (!irc_strcasecmp(msg.params[2], "ALL")) {
1907       src = msg.params[1];
1908       lines = -1;
1909     } else if (strspn(msg.params[1], "0123456789")
1910                == strlen(msg.params[1])) {
1911       start = atol(msg.params[1]);
1912       lines = atol(msg.params[2]);
1913     } else {
1914       src = msg.params[1];
1915       lines = atol(msg.params[2]);
1916     }
1917   } else if (msg.numparams >= 2) {
1918     if (!irc_strcasecmp(msg.params[1], "ALL")) {
1919       lines = -1;
1920     } else {
1921       lines = atol(msg.params[1]);
1922     }
1923   } else {
1924     ircclient_send_numeric(p, 461, ":Not enough parameters");
1925   }
1926 
1927   if (src && !irc_strcasecmp(src, "SERVER")) {
1928     src = 0;
1929   } else if (src) {
1930     struct ircchannel *c;
1931 
1932     c = ircnet_fetchchannel(p, src);
1933     if (!c) {
1934       filter = src;
1935       src = p->nickname;
1936     }
1937   } else {
1938     src = p->nickname;
1939   }
1940 
1941   irclog_recall(p, src, start, lines, filter);
1942 }
1943 
1944   /* PRIVMSG handler */
1945 int _ircclient_handle_privmsg(struct ircproxy *p, struct ircmessage msg) {
1946    int squelch = 0;
1947 
1948   if (msg.numparams >= 2) {
1949     struct strlist *list, *s;
1950     char *str;
1951 
1952     ircprot_stripctcp(msg.params[1], &str, &list);
1953 
1954     /* Privmsgs from us get logged */
1955     if (str && strlen(str)) {
1956       char *tmp;
1957 
1958       tmp = x_sprintf("%s!%s@%s", p->nickname, p->username,
1959                       p->hostname);
1960       irclog_log(p, IRC_LOG_MSG, msg.params[0], tmp, "%s", str);
1961       free(tmp);
1962     }
1963     free(str);
1964 
1965     /* Handle CTCP */
1966     str = x_strdup(msg.params[1]);
1967     s = list;
1968     while (s) {
1969       struct ctcpmessage cmsg;
1970       struct strlist *n;
1971       char *unquoted;
1972       int r;
1973 
1974       n = s->next;
1975       r = ircprot_parsectcp(s->str, &cmsg);
1976       unquoted = s->str;
1977       free(s);
1978       s = n;
1979       if (r == -1) {
1980         free(unquoted);
1981         continue;
1982       }
1983 
1984       if (!strcmp(cmsg.cmd, "ACTION")) {
1985         char *tmp;
1986 
1987         tmp = x_sprintf("%s!%s@%s", p->nickname, p->username,
1988                         p->hostname);
1989 	if (cmsg.paramstarts)
1990 	   irclog_log(p, IRC_LOG_ACTION, msg.params[0], tmp, "%s", cmsg.paramstarts[0]);
1991 	else
1992 	   irclog_log(p, IRC_LOG_ACTION, msg.params[0], tmp, NULL);
1993         free(tmp);
1994 
1995       } else if (!strcmp(cmsg.cmd, "DCC")
1996                  && p->conn_class->dcc_proxy_outgoing) {
1997         struct sockaddr_in vis_addr;
1998         int len;
1999 
2000         /* We need our local address to do anything DCC related */
2001         len = sizeof(struct sockaddr_in);
2002         if (getsockname(p->server_sock, (struct sockaddr *)&vis_addr,
2003                          &len)) {
2004           syscall_fail("getsockname", "", 0);
2005 
2006         } else if ((cmsg.numparams >= 4)
2007                    && (!irc_strcasecmp(cmsg.params[0], "CHAT")
2008                        || !irc_strcasecmp(cmsg.params[0], "SEND"))) {
2009           char *tmp, *ptr, *dccmsg, *rejmsg;
2010           struct in_addr l_addr, r_addr;
2011           int l_port, r_port, t_port;
2012           char *rest = 0;
2013           int type = 0;
2014 
2015           /* Find out what type of DCC request this is */
2016           if (!irc_strcasecmp(cmsg.params[0], "CHAT")) {
2017             type = DCC_CHAT;
2018           } else if (!irc_strcasecmp(cmsg.params[0], "SEND")) {
2019             if (p->conn_class->dcc_send_fast) {
2020               type = DCC_SEND_FAST;
2021             } else {
2022               type = DCC_SEND_SIMPLE;
2023             }
2024           }
2025 
2026           /* Check whether there's a tunnel port */
2027           t_port = 0;
2028           if (p->conn_class->dcc_tunnel_outgoing)
2029             t_port = dns_portfromserv(p->conn_class->dcc_tunnel_outgoing);
2030 
2031           /* Eww, host order, how the hell does this even work
2032              between machines of a different byte order? */
2033           if (!t_port) {
2034             r_addr.s_addr = strtoul(cmsg.params[2], (char **)NULL, 10);
2035             r_port = atoi(cmsg.params[3]);
2036           } else {
2037             r_addr.s_addr = INADDR_LOOPBACK;
2038             r_port = ntohs(t_port);
2039           }
2040           l_addr.s_addr = ntohl(vis_addr.sin_addr.s_addr);
2041           if (cmsg.numparams >= 5)
2042             rest = cmsg.paramstarts[4];
2043 
2044           /* Strip out this CTCP from the message, replacing it in
2045              a moment with dccmsg */
2046           tmp = x_sprintf("\001%s\001", unquoted);
2047           ptr = strstr(str, tmp);
2048           dccmsg = 0;
2049 
2050           /* Save this in case we need it later */
2051           rejmsg = x_sprintf(":%s NOTICE %s :\001DCC REJECT %s %s",
2052                              msg.params[0], p->nickname,
2053                              cmsg.params[0], cmsg.params[1]);
2054 
2055           /* Set up a dcc proxy */
2056           if (ptr && !dccnet_new(type, p->conn_class->dcc_proxy_timeout,
2057                                  p->conn_class->dcc_proxy_ports,
2058                                  p->conn_class->dcc_proxy_ports_sz,
2059                                  &l_port, r_addr, r_port, 0, 0,
2060                                  DCCN_FUNCTION(_ircclient_send_dccreject),
2061                                  p, rejmsg, 0)) {
2062             char *me_tmp;
2063 
2064             dccmsg = x_sprintf("\001DCC %s %s %lu %u%s%s\001",
2065                                cmsg.params[0], cmsg.params[1],
2066                                l_addr.s_addr, l_port,
2067                                (rest ? " " : ""), (rest ? rest : ""));
2068 
2069             me_tmp = x_sprintf("%s!%s@%s", p->nickname, p->username,
2070                                p->hostname);
2071             irclog_log(p, IRC_LOG_CTCP, msg.params[0], me_tmp,
2072                        "Sent DCC %s Request", cmsg.params[0]);
2073             free(me_tmp);
2074 
2075           } else if (ptr) {
2076             dccmsg = x_strdup("");
2077             _ircclient_send_dccreject(p, rejmsg,
2078                                       "Couldn't establish proxy");
2079           }
2080 
2081           /* Don't need this now */
2082           free(rejmsg);
2083 
2084           /* Cut out the old CTCP and replace with dccmsg */
2085           if (ptr) {
2086             char *oldstr;
2087 
2088             *ptr = 0;
2089             ptr += strlen(tmp);
2090 
2091             oldstr = str;
2092             str = x_sprintf("%s%s%s", oldstr, dccmsg, ptr);
2093 
2094             free(oldstr);
2095             free(dccmsg);
2096           }
2097 
2098           free(tmp);
2099 
2100         } else {
2101           /* Unknown DCC */
2102 	   if (cmsg.numparams > 0)
2103 	     debug("Unknown or Unimplemented DCC request - %s", cmsg.params[0]);
2104         }
2105 
2106       } else {
2107         char *tmp;
2108 
2109         tmp = x_sprintf("%s!%s@%s", p->nickname, p->username,
2110                         p->hostname);
2111 	if (cmsg.numparams > 0)
2112 	   irclog_log(p, IRC_LOG_CTCP, msg.params[0], tmp, "Sent CTCP %s", cmsg.params[0]);
2113         free(tmp);
2114       }
2115 
2116       ircprot_freectcp(&cmsg);
2117       free(unquoted);
2118     }
2119 
2120     /* Send str */
2121     if (strlen(str))
2122       net_send(p->server_sock, ":%s PRIVMSG %s :%s\r\n",
2123                (msg.src.orig ? msg.src.orig : p->nickname),
2124                msg.params[0], str);
2125     squelch = 1;
2126     free(str);
2127   }
2128 
2129   if (p->conn_class->idle_maxtime)
2130     ircserver_resetidle(p);
2131 
2132   return squelch;
2133 }
2134 
2135   /* /DIRCPROXY HELP handler */
2136 void _ircclient_handle_help(struct ircproxy *p, struct ircmessage msg) {
2137   char **help_page;
2138 
2139   help_page = 0;
2140 
2141   if ((msg.numparams >= 2) && strlen(msg.params[1])) {
2142     if (!irc_strcasecmp(msg.params[1], "RECALL")) {
2143       help_page = command_help[I_HELP_RECALL];
2144     } else if (p->conn_class->allow_persist
2145                && !irc_strcasecmp(msg.params[1], "PERSIST")) {
2146       help_page = command_help[I_HELP_PERSIST];
2147     } else if (!irc_strcasecmp(msg.params[1], "RELOAD")) {
2148       help_page = command_help[I_HELP_RELOAD];
2149     } else if (!irc_strcasecmp(msg.params[1], "DETACH")) {
2150       help_page = command_help[I_HELP_DETACH];
2151     } else if (!irc_strcasecmp(msg.params[1], "QUIT")) {
2152       help_page = command_help[I_HELP_QUIT];
2153     } else if (!irc_strcasecmp(msg.params[1], "MOTD")) {
2154       help_page = command_help[I_HELP_MOTD];
2155     } else if (p->conn_class->allow_die
2156                && !irc_strcasecmp(msg.params[1], "DIE")) {
2157       help_page = command_help[I_HELP_DIE];
2158     } else if (!irc_strcasecmp(msg.params[1], "SERVERS")) {
2159       help_page = command_help[I_HELP_SERVERS];
2160     } else if (p->conn_class->allow_jump
2161                && !irc_strcasecmp(msg.params[1], "JUMP")) {
2162       help_page = (p->conn_class->allow_jump_new
2163                    ? command_help[I_HELP_JUMP_NEW] : command_help[I_HELP_JUMP]);
2164     } else if (p->conn_class->allow_host
2165                && !irc_strcasecmp(msg.params[1], "HOST")) {
2166       help_page = command_help[I_HELP_HOST];
2167     } else if (!irc_strcasecmp(msg.params[1], "STATUS")) {
2168       help_page = command_help[I_HELP_STATUS];
2169     } else if (!irc_strcasecmp(msg.params[1], "USERS")) {
2170       help_page = command_help[I_HELP_USERS];
2171     } else if (!irc_strcasecmp(msg.params[1], "KILL")) {
2172       help_page = command_help[I_HELP_KILL];
2173     } else if (!irc_strcasecmp(msg.params[1], "NOTIFY")) {
2174       help_page = command_help[I_HELP_NOTIFY];
2175     } else if (!irc_strcasecmp(msg.params[1], "HELP")) {
2176       help_page = command_help[I_HELP_HELP];
2177     } else {
2178       help_page = command_help[I_HELP_INDEX];
2179     }
2180 
2181   } else {
2182     help_page = command_help[I_HELP_INDEX];
2183   }
2184 
2185   if (help_page) {
2186     int i;
2187 
2188     i = 0;
2189     ircclient_send_notice(p, "%s %s help", PACKAGE, VERSION);
2190     while (help_page[i]) {
2191       ircclient_send_notice(p, "- %s", help_page[i]);
2192       i++;
2193     }
2194 
2195     if (help_page == command_help[I_HELP_INDEX]) {
2196       ircclient_send_notice(p, "-     HELP      "
2197                            "(help on /dircproxy commands)");
2198       ircclient_send_notice(p, "-     MOTD      "
2199                            "(show dircproxy message of the day)");
2200       ircclient_send_notice(p, "-     STATUS    "
2201                             "(show dircproxy status information)");
2202       ircclient_send_notice(p, "-     RECALL    "
2203                             "(recall text from log files)");
2204       ircclient_send_notice(p, "-     GET    "
2205 			    "(Get the value of a configuration item)");
2206       ircclient_send_notice(p, "-     SET    "
2207 			     "(Set the value of a configuration item)");
2208       ircclient_send_notice(p, "-     RELOAD    "
2209                             "(reload configuration file)");
2210       ircclient_send_notice(p, "-     DETACH    "
2211                             "(detach from dircproxy)");
2212       if (p->conn_class->allow_persist)
2213         ircclient_send_notice(p, "-     PERSIST   "
2214                               "(keep session after detach)");
2215       ircclient_send_notice(p, "-     QUIT      "
2216                             "(end dircproxy session)");
2217       if (p->conn_class->allow_die)
2218         ircclient_send_notice(p, "-     DIE       "
2219                               "(terminate dircproxy)");
2220       if (p->conn_class->allow_users)
2221         ircclient_send_notice(p, "-     USERS     "
2222                               "(show users using dircproxy)");
2223       if (p->conn_class->allow_kill)
2224         ircclient_send_notice(p, "-     KILL      "
2225                               "(terminate a user's session)");
2226       if (p->conn_class->allow_notify)
2227 	ircclient_send_notice(p, "-     NOTIFY    "
2228 			      "(send a notice to a user's session");
2229       ircclient_send_notice(p, "-     SERVERS   "
2230                             "(show servers list)");
2231       if (p->conn_class->allow_jump)
2232         ircclient_send_notice(p, "-     JUMP      "
2233                               "(jump to a different server)");
2234       if (p->conn_class->allow_host)
2235         ircclient_send_notice(p, "-     HOST      "
2236                               "(change your visible hostname)");
2237 
2238       i = 0;
2239       while (command_help[I_HELP_INDEX_END][i]) {
2240         ircclient_send_notice(p, "- %s", command_help[I_HELP_INDEX_END][i]);
2241         i++;
2242       }
2243     }
2244 
2245     ircclient_send_notice(p, "-");
2246   }
2247 }
2248