1 /*
2  * ----------------------------------------------------------------
3  * ircproxy - Client Parser Functions
4  * ----------------------------------------------------------------
5  * Copyright (C) 1997-2009 Jonas Kvinge
6  *
7  * This file is part of ircproxy.
8  *
9  * ircproxy is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * ircproxy is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with ircproxy.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * Additional permission under GNU GPL version 3 section 7
23  *
24  * If you modify ircproxy, or any covered work, by linking or
25  * combining it with openssl (or a modified version of that library),
26  * containing parts covered by the terms of the OpenSSL License and the
27  * SSLeay License, the licensors of ircproxy grant you additional
28  * permission to convey the resulting work.
29  *
30  * $Id: client_parser.c 54 2009-03-18 18:23:29Z jonasio $
31  *
32  */
33 
34 #define CLIENT_PARSER_C
35 
36 #define NEED_SYS_TYPES_H 1		/* Extra types */
37 #define NEED_SYS_PARAM_H 1		/* Some systems need this */
38 #define NEED_LIMITS_H 0			/* Kernel limits */
39 #define NEED_STDARG_H 1			/* va_list, etc */
40 #define NEED_ERRNO_H 1			/* errno */
41 #define NEED_CTYPE_H 1			/* isdigit(), etc */
42 #define NEED_NETINET_IN_H 1		/* in_addr, sockaddr_in, etc */
43 #define NEED_ARPA_INET_H 0		/* inet_ntoa(), inet_aton(), etc */
44 #define NEED_STDIO_H 1			/* Standard C UNIX functions */
45 #define NEED_STDLIB_H 1			/* malloc(), exit(), atoi(), etc */
46 #define NEED_TIME_H 1			/* time(), etc */
47 #define NEED_SYSCTL_H 0			/* sysctl(), etc */
48 #define NEED_SYS_STAT_H 0		/* chmod(), mkdir(), etc */
49 #define NEED_SYS_UIO_H 0		/* iovec, etc */
50 #define NEED_FCNTL_H 1			/* open(), creat(), fcntl(), etc */
51 #define NEED_SYS_IOCTL_H 0		/* ioctl(), etc */
52 #define NEED_SYS_FILIO_H 0		/* Solaris need this for ioctl(), etc */
53 #define NEED_UNISTD_H 1			/* Unix standard functions */
54 #define NEED_STRING_H 1			/* C string functions */
55 #define NEED_SIGNAL_H 0			/* Signal functions */
56 #define NEED_SYS_SOCKET_H 0		/* Socket functions */
57 #define NEED_NETDB_H 0			/* Network database functions */
58 #define NEED_ARPA_NAMESER_H 0		/* Nameserver definitions */
59 #define NEED_GETUSERPW_HEADERS 0 	/* Functions to retrive system passwords */
60 #define NEED_ARES 1			/* Functions needed for ares */
61 #define NEED_SSL 1			/* Needed for SSL support */
62 
63 #include "includes.h"
64 #include "irc.h"
65 
66 #include "conf.h"
67 
68 #if MEMDEBUG
69   #include "memleaks.h"
70 #endif
71 
72 #include "access_conf.h"
73 #include "conn_conf.h"
74 
75 #include "listen.h"
76 
77 #include "client.h"
78 #include "client_connection.h"
79 #include "client_parser.h"
80 #include "client_auth.h"
81 #include "client_handle.h"
82 #include "client_notice.h"
83 
84 #include "conn.h"
85 #include "conn_connection.h"
86 #include "conn_sendq.h"
87 #include "conn_log.h"
88 
89 #include "chan.h"
90 #include "chan_user.h"
91 
92 /* VARIABLES - JONAS (31.07.2001) */
93 
94 #if MEMDEBUG
95   extern unsigned long int G_Leaks;
96   extern unsigned long int G_LeaksSize;
97 #endif
98 
99 #if ARES
100   extern ares_channel Ares_Channel;
101 #endif
102 
103 extern struct Conf_Struct ConfS;
104 extern struct Conn_Struct *Conn_Head;
105 
106 /* CLIENT_PARSE FUNCTION - JONAS (18.07.2001) */
107 
client_parse(struct Client_Struct * ClientS)108 void client_parse(struct Client_Struct *ClientS) {
109 
110   char *BufferPT = NULL;
111   char *LinePT = NULL;
112   unsigned long int Count = 0;
113   unsigned long int Len = 0;
114   char *TempPT = NULL;
115 
116   BufferPT = ClientS->RecvBuffer;
117 
118   for (Count = 1 ; BufferPT != NULL ; Count++) {
119     TempPT = strchr(BufferPT, '\n');
120     if (TempPT == NULL) {
121       if (Count == 1) { return; }
122       Len = strlen(BufferPT) + 1;
123       memmove(ClientS->RecvBuffer, BufferPT, Len);
124       TempPT = realloc(ClientS->RecvBuffer, Len);
125       if (TempPT == NULL) {
126         client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
127         return;
128       }
129       ClientS->RecvBuffer = TempPT;
130       return;
131     }
132     LinePT = strtok(BufferPT, "\n");
133     BufferPT = strtok(NULL, "\0");
134     if (LinePT == NULL) { continue; }
135     Len = strlen(LinePT);
136     if (LinePT[Len-1] == '\r') { LinePT[Len-1] = '\0'; }
137     client_parse_message(ClientS, LinePT);
138     if (ClientS->RecvBuffer == NULL) { return; }
139   }
140   FREE(ClientS->RecvBuffer);
141 
142 }
143 
144 /* CLIENT_PARSE_MESSAGE FUNCTION - JONAS (17.07.2001) */
145 
client_parse_message(struct Client_Struct * ClientS,char * MessagePT)146 void client_parse_message(struct Client_Struct *ClientS, char *MessagePT) {
147 
148   char *PrefixPT = NULL;
149   char *LinePT = NULL;
150   char *CommandPT = NULL;
151   char **ParamsPT = NULL;
152   char **TempPT = NULL;
153   unsigned short int Index = 0;
154   unsigned short int Params = 0;
155   time_t Duration = 0;
156 
157   Duration = (NOW - ClientS->LineTime);
158   if (Duration >= ConfS.ClientLineFloodTTL) { ClientS->Lines = 0; }
159 
160   ++ClientS->Lines;
161   if (ClientS->Lines >= ConfS.ClientMaxLines) {
162     client_close(ClientS, ACCESS_CONF_USERFLOOD);
163     access_conf_adddeny(ClientS->HostName, ACCESS_CONF_USERFLOOD);
164     return;
165   }
166   ClientS->LineTime = NOW;
167 
168   while (MessagePT[0] == ' ') { MessagePT++; }
169   if (MessagePT[0] == '\0') { return; }
170 
171   if (MessagePT[0] == ':') {
172     MessagePT++;
173     PrefixPT = MessagePT;
174     MessagePT = strchr(MessagePT, ' ');
175     if (MessagePT == NULL) { return; }
176     MessagePT[0] = '\0';
177     MessagePT++;
178     while (MessagePT[0] == ' ') { MessagePT++; }
179     if (MessagePT[0] == '\0') { return; }
180   }
181   else {
182     if (ClientS->User == NULL) { PrefixPT = "*"; }
183     else { PrefixPT = ClientS->User; }
184   }
185 
186   LinePT = strdup(MessagePT);
187   if (LinePT == NULL) {
188     client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
189     return;
190   }
191 
192   CommandPT = MessagePT;
193   MessagePT = strchr(MessagePT, ' ');
194   if (MessagePT != NULL) {
195     MessagePT[0] = '\0';
196     MessagePT++;
197     while (MessagePT[0] == ' ') { MessagePT++; }
198     if (MessagePT[0] == '\0') {
199       free(LinePT);
200       return;
201     }
202 
203     FOREVERLOOP {
204       if (MessagePT[0] == ':') {
205         MessagePT++;
206         ++Params;
207         TempPT = realloc(ParamsPT, (sizeof(char *) * Params));
208         if (TempPT == NULL) {
209           client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
210           free(ParamsPT);
211           free(LinePT);
212           return;
213         }
214         ParamsPT = TempPT;
215         ParamsPT[Index] = MessagePT;
216         break;
217       }
218       else {
219         ++Params;
220         TempPT = realloc(ParamsPT, (sizeof(char *) * Params));
221         if (TempPT == NULL) {
222           client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
223           free(ParamsPT);
224           free(LinePT);
225           return;
226         }
227         ParamsPT = TempPT;
228         ParamsPT[Index] = MessagePT;
229         ++Index;
230         MessagePT = strchr(MessagePT, ' ');
231         if (MessagePT == NULL) { break; }
232         MessagePT[0] = '\0';
233         MessagePT++;
234         while (MessagePT[0] == ' ') { MessagePT++; }
235         if (MessagePT[0] == '\0') { break; }
236       }
237     }
238   }
239 
240   strupper(CommandPT);
241 
242   client_parse_command(ClientS, LinePT, CommandPT, ParamsPT, Params);
243 
244   free(LinePT);
245   free(ParamsPT);
246 
247 }
248 
249 /* CLIENT_PARSE_COMMAND FUNCTION - JONAS (17.07.2001) */
250 
client_parse_command(struct Client_Struct * ClientS,char * LinePT,char * CommandPT,char ** ParamsPT,const unsigned short int Params)251 void client_parse_command(struct Client_Struct *ClientS, char *LinePT, char *CommandPT, char **ParamsPT, const unsigned short int Params) {
252 
253   if (!Client_IsVerified(ClientS)) {
254     if (strcasecmp(CommandPT, "PASS") == FALSE) {
255       if (Params < 1) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; }
256       ClientS->Pass = strrealloc(ClientS->Pass, ParamsPT[0]);
257     }
258     else if (strcasecmp(CommandPT, "NICK") == FALSE) {
259       if (Params < 1) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; }
260       ClientS->Nick = strrealloc(ClientS->Nick, ParamsPT[0]);
261       if ((ClientS->Pass == NULL) && (ClientS->User != NULL)) {
262         client_notice(ClientS, "Your IRC client did not send a password, type: /PASS <password> to connect.");
263       }
264     }
265     else if (strcasecmp(CommandPT, "USER") == FALSE) {
266       if (Params < 4) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; }
267       ClientS->User = strrealloc(ClientS->User, ParamsPT[0]);
268       if ((ClientS->Pass == NULL) && (ClientS->Nick != NULL)) {
269         client_notice(ClientS, "Your IRC client did not send a password, type: /PASS <password> to connect.");
270       }
271     }
272     else { client_addsend(ClientS, ":%s %.3d * :You have not registered.", IRCP_USER_HOST(ClientS), IRC_RFC1459_ERR_NOTREGISTERED); }
273 
274     if ((ClientS->User != NULL) && (ClientS->Pass != NULL)) {
275 
276       assert(geteuid() != 0);
277       client_authcheck(ClientS->User, ClientS->Pass);
278       assert(geteuid() != 0);
279       if (aerrno != AESUCCESS) {
280         client_close(ClientS, "USER / PASS is incorrect.");
281         return;
282       }
283 
284       sysprint(BITMASK_MAIN, "Client %s (%s) sucessfully authenticated as \"%s\".", ClientS->HostName, ClientS->HostIPS, ClientS->User);
285       client_noticeallbutone(ClientS, "Client %s (%s) authenticated as \"%s\".", ClientS->HostName, ClientS->HostIPS, ClientS->User);
286 
287       Client_SetVerified(ClientS);
288       client_welcome(ClientS);
289 
290       return;
291     }
292     return;
293   }
294 
295   if (strcasecmp(CommandPT, "PCONNLIST") == FALSE) {
296 
297     struct Conn_Struct *ConnS = NULL;
298     struct ConnServer_Struct *ConnServer = NULL;
299     unsigned short int Count = 0;
300 
301     for (ConnS = Conn_Head ; ConnS != NULL ; ConnS = ConnS->Next) {
302 
303       if (strcmp(ConnS->User, ClientS->User) != FALSE) { continue; }
304       ++Count;
305 
306       client_notice(ClientS, "\2Connection: %s Host: %s\2", ConnS->Name, ConnS->Host);
307 
308       if (Host_IsResolving(ConnS->ResolveFlags)) { client_notice(ClientS, "--- Status: Resolving local hostname %s to IP-address.", ConnS->HostName); }
309       else if (Host_IsResolving(ConnS->ServerResolveFlags)) { client_notice(ClientS, "--- Status: Resolving server nostname %s to IP-address.", ConnS->ServerHostName); }
310       else if (Conn_IsConnecting(ConnS)) { client_notice(ClientS, "--- Status: Connecting to %s(%s):%ld.", ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); }
311       else if (Conn_IsWelcome(ConnS)) { client_notice(ClientS, "--- Status: Connected to %s(%s):%ld \"%s\".", ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, ConnS->ServerName); }
312       else if (Conn_IsConnected(ConnS)) { client_notice(ClientS, "--- Status: Socket connected to %s(%s):%ld.", ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); }
313       else { client_notice(ClientS, "--- Status: Disconnected."); }
314 
315       if (ConnS->Server_Head == NULL) { client_notice(ClientS, "--- No servers!"); }
316       else {
317         for (ConnServer = ConnS->Server_Head ; ConnServer != NULL ; ConnServer = ConnServer->Next) {
318           client_notice(ClientS, "--- Server: %s Port: %ld", ConnServer->Host, ConnServer->Port);
319         }
320       }
321 
322     }
323 
324     if (Count == 0) {
325       client_notice(ClientS, "You have no connections.");
326     }
327 
328     return;
329 
330   }
331   else if (strcasecmp(CommandPT, "PCONNECT") == FALSE) {
332 
333     struct Conn_Struct *ConnS = NULL;
334 
335     if (Params < 1) {
336       if (ClientS->ConnS == NULL) { client_notice(ClientS, "Usage: PCONNECT <Connection name> [Connection server]."); return; }
337       ConnS = ClientS->ConnS;
338     }
339     else { ConnS = conn_get(ParamsPT[0]); }
340 
341     if (ConnS == NULL) { client_notice(ClientS, "No such connection, use /PCONNLIST to list your connections."); return; }
342 
343     if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }
344 
345     if (Params >= 2) {
346       for (ConnS->ConnServerTry = ConnS->Server_Head ; ConnS->ConnServerTry != NULL ; ConnS->ConnServerTry = ConnS->ConnServerTry->Next) {
347         if (strwm(ParamsPT[1], ConnS->ConnServerTry->Host) == TRUE) { break; }
348       }
349       if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "No such server for that connection."); return; }
350     }
351     if (Host_IsResolving(ConnS->ResolveFlags)) {
352       if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "Connection %s: Resolving local hostname %s to IP-address.", ConnS->Name, ConnS->HostName); return; }
353 #if HAVE_ARES_CANCELQUERY
354       ares_cancelquery(Ares_Channel, ConnS);
355 #endif
356       return;
357     }
358     else if (Host_IsResolving(ConnS->ServerResolveFlags)) {
359       if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "Connection %s: Resolving server hostname %s to IP-address.", ConnS->Name, ConnS->ServerHostName); return; }
360 #if HAVE_ARES_CANCELQUERY
361       ares_cancelquery(Ares_Channel, ConnS);
362 #endif
363       return;
364     }
365     else if (Conn_IsConnecting(ConnS)) {
366       if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "%s: Connecting to %s(%s):%ld.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); return; }
367       conn_disconnect(ConnS, "Connection %s: Connection attempt to %s(%s):%ld cancelled by user %s(%s).", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, ClientS->HostName, ClientS->HostIPS);
368       return;
369     }
370     else if (Conn_IsWelcome(ConnS)) {
371       if ((ConnS->ConnServerTry == NULL) || (strcasecmp(ConnS->ServerHostName, ConnS->ConnServerTry->Host) == FALSE)) { client_notice(ClientS, "Connection %s: Already connected to %s, use PQUIT to quit.", ConnS->Name, ConnS->ServerHostName); return; }
372       conn_quit(ConnS, "Changing server to %s.", ConnS->ConnServerTry->Host);
373       return;
374     }
375     else if (Conn_IsConnected(ConnS)) {
376       if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "Connection %s: Socket connected to %s(%s):%ld.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); return; }
377       conn_disconnect(ConnS, "Connection %s attempt to %s(%s):%ld cancelled by user %s(%s).", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, ClientS->HostName, ClientS->HostIPS);
378       return;
379     }
380 
381 #if SSL_SUPPORT
382     conn_connect(ConnS);
383 #else
384     if (!ConnConf_IsSSL(ConnS)) { conn_connect(ConnS); }
385     else { client_notice(ClientS, "Connection %s: SSL is enabled for this connection, but %s is compiled without SSL support.", ConnS->Name, PACKAGE); }
386 #endif
387     return;
388 
389   }
390   else if (strcasecmp(CommandPT, "PQUIT") == FALSE) {
391 
392     struct Conn_Struct *ConnS = NULL;
393 
394     if (Params >= 1) {
395       ConnS = conn_get(ParamsPT[0]);
396       if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; }
397     }
398     else {
399       ConnS = ClientS->ConnS;
400       if (ConnS == NULL) { client_notice(ClientS, "Usage: PQUIT <Connection name>"); return; }
401     }
402 
403     if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }
404 
405     if (Conn_IsConnect(ConnS)) {
406       if (Conn_IsWelcome(ConnS)) {
407         if (Conn_IsSentQuit(ConnS)) { conn_disconnect(ConnS, "Connection %s to server %s aborted by user %s (%s[%s]).", ConnS->Name, ConnS->ServerHostName, ClientS->User, ClientS->HostName, ClientS->HostIPS); return; }
408         else { conn_quit(ConnS, "%s (%s[%s]) requested QUIT.", ClientS->User, ClientS->HostName, ClientS->HostIPS); }
409       }
410       else { conn_disconnect(ConnS, "Connection %s to server %s aborted by user %s (%s[%s]).", ConnS->Name, ConnS->ServerHostName, ClientS->User, ClientS->HostName, ClientS->HostIPS); return; }
411     }
412     else { client_notice(ClientS, "Connection %s is not connected.", ConnS->Name); return; }
413 
414     return;
415 
416   }
417   else if ((strcasecmp(CommandPT, "PATTACH") == FALSE) || (strcasecmp(CommandPT, "PRESUME") == FALSE)) {
418 
419     struct Conn_Struct *ConnS = NULL;
420 
421     if (ClientS->ConnS != NULL) { client_notice(ClientS, "You are already attached on connection %s.", ClientS->ConnS->Name); return; }
422     if (Params < 1) { client_notice(ClientS, "Usage: PATTACH <Connection name>"); return; }
423     ConnS = conn_get(ParamsPT[0]);
424     if (ConnS == NULL) { client_notice(ClientS, "No such connection."); return; }
425     if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }
426     client_attach(ClientS, ConnS);
427 
428   }
429   else if (strcasecmp(CommandPT, "PDETACH") == FALSE) {
430     if (ClientS->ConnS == NULL) { client_notice(ClientS, "You have no attached connection."); return; }
431     client_detach(ClientS);
432   }
433   else if (strcasecmp(CommandPT, "QUIT") == FALSE) {
434     client_close(ClientS, "Client sent QUIT.");
435   }
436   else if (strcasecmp(CommandPT, "PING") == FALSE) {
437     if (Params < 1) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; }
438     client_addsend(ClientS, ":%s PONG %s :%s", IRCP_USER_HOST(ClientS), IRCP_USER_HOST(ClientS), ParamsPT[0]);
439     /* client_addsend(ClientS, "PONG :%s", ParamsPT[0]); */
440   }
441   else if (strcasecmp(CommandPT, "PONG") == FALSE) {
442     Client_ClearSentPing(ClientS);
443   }
444   else if (strcasecmp(CommandPT, "QUOTE") == FALSE) {
445 
446     char *DumbPT = NULL;
447 
448     if (ClientS->ConnS == NULL) { client_notice(ClientS, "QUOTE: No attached connection."); return; }
449     if (!Conn_IsConnected(ClientS->ConnS)) { client_notice(ClientS, "Connection %s not established.", ClientS->ConnS->Name); return; }
450 
451     DumbPT = strchr(LinePT, ' ');
452     DumbPT++;
453     conn_addsendq(ClientS->ConnS, "%s", DumbPT);
454   }
455 
456 else if (strcasecmp(CommandPT, "LOGLIST") == FALSE) {
457 
458     struct Conn_Struct *ConnS = NULL;
459     struct ConnLog_Struct *ConnLog = NULL;
460     unsigned long int LogFiles = 0;
461     char *LogFilePT = NULL;
462 
463     if (Params < 2) {
464       ConnS = ClientS->ConnS;
465       if (Params < 1) LogFilePT = "*";
466       else LogFilePT = ParamsPT[0];
467     }
468     else {
469       ConnS = conn_get(ParamsPT[0]);
470       if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; }
471       LogFilePT = ParamsPT[1];
472     }
473 
474     if (ConnS == NULL) { client_notice(ClientS, "Usage: READLOG <Connection name> <Logfile>"); return; }
475 
476     if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }
477 
478     if (ConnS->NumLogs == 0) { client_notice(ClientS, "No logs for connection %s.", ConnS->Name); return; }
479 
480     for (ConnLog = ConnS->Log_Head ; ConnLog != NULL ; ConnLog = ConnLog->Next) {
481       if (((ConnLog->LogType != CONN_LOG_TYPE_PRIV) && (ConnLog->LogType != CONN_LOG_TYPE_CHAN)) || (strwm(LogFilePT, ConnLog->Name) == FALSE)) { continue; }
482       LogFiles++;
483       client_addsend(ClientS, ":%s PRIVMSG %s :%s", IRCP_USER_HOST(ClientS), IRCP_USER_NICK(ClientS), ConnLog->LogFile);
484     }
485 
486     if (LogFiles == 0) { client_notice(ClientS, "No private logs for connection %s matching your query.", ConnS->Name); return; }
487     else { client_notice(ClientS, "%ld logfiles.", LogFiles); return; }
488 
489   }
490 
491   else if (strcasecmp(CommandPT, "READLOG") == FALSE) {
492 
493     FILE *FilePT = NULL;
494     struct Conn_Struct *ConnS = NULL;
495     struct ConnLog_Struct *ConnLog = NULL;
496     unsigned long int LogFiles = 0;
497     unsigned long int LogLines = 0;
498     char *LogFilePT = NULL;
499 
500     if (Params < 2) {
501       ConnS = ClientS->ConnS;
502       if (Params < 1) LogFilePT = "*";
503       else LogFilePT = ParamsPT[0];
504     }
505     else {
506       ConnS = conn_get(ParamsPT[0]);
507       if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; }
508       LogFilePT = ParamsPT[1];
509     }
510 
511     if (ConnS == NULL) { client_notice(ClientS, "Usage: READLOG <Connection name> <Logfile>"); return; }
512 
513     if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }
514 
515     if (ConnS->NumLogs == 0) { client_notice(ClientS, "No logs for connection %s.", ConnS->Name); return; }
516 
517 #if !WIN32
518       if (Conn_IsLogHomeDir(ConnS)) { sysseteuidbyuser(ConnS->User); }
519 #endif
520 
521     for (ConnLog = ConnS->Log_Head ; ConnLog != NULL ; ConnLog = ConnLog->Next) {
522       if ((ConnLog->LogType != CONN_LOG_TYPE_PRIV) || (strwm(LogFilePT, ConnLog->Name) == FALSE)) { continue; }
523       LogFiles++;
524 
525       FilePT = fopen(ConnLog->LogFileFullPath, "r");
526       if (FilePT == NULL) {
527         sysprint(BITMASK_ERROR, "Unable to open %s for reading: [%d] %s", ConnLog->LogFileFullPath, errno, strerror(errno));
528         client_notice(ClientS, "Unable to open %s for reading: [%d] %s", ConnLog->LogFileFullPath, errno, strerror(errno));
529 #if !WIN32
530         if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
531 #endif
532         continue;
533       }
534 
535       do {
536         char Line[LINELEN+1] = "";
537         char *LinePT = NULL;
538         char *TempPT = NULL;
539         memset(&Line, 0, LINELEN+1);
540         LinePT = fgets(Line, LINELEN, FilePT);
541         if (LinePT == NULL) { break; }
542         while ((TempPT = strchr(LinePT, '\r')) != NULL) { *TempPT = '\0'; }
543         while ((TempPT = strchr(LinePT, '\n')) != NULL) { *TempPT = '\0'; }
544         LogLines++;
545         client_addsend(ClientS, ":%s PRIVMSG %s :%s: %s", IRCP_USER_HOST(ClientS), IRCP_USER_NICK(ClientS), ConnLog->LogFile, LinePT);
546       } while(1);
547       fclose(FilePT);
548     }
549 
550 #if !WIN32
551     if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
552 #endif
553 
554     if (LogFiles == 0) { client_notice(ClientS, "No private logs for connection %s matching your query.", ConnS->Name); return; }
555     else { client_notice(ClientS, "Dumped %ld lines of %ld log files using PRIVMSG.", LogLines, LogFiles); return; }
556 
557   }
558   else if (strcasecmp(CommandPT, "ERASELOG") == FALSE) {
559 
560     struct Conn_Struct *ConnS = NULL;
561     struct ConnLog_Struct *ConnLog = NULL;
562     unsigned long int LogFiles = 0;
563     char *LogFilePT = NULL;
564     signed long int Result = 0;
565 
566     if (Params < 2) {
567       ConnS = ClientS->ConnS;
568       if (Params < 1) LogFilePT = "*";
569       else LogFilePT = ParamsPT[0];
570     }
571     else {
572       ConnS = conn_get(ParamsPT[0]);
573       if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; }
574       LogFilePT = ParamsPT[1];
575     }
576 
577     if (ConnS == NULL) { client_notice(ClientS, "Usage: ERASELOG <Connection name> <Logfile>"); return; }
578 
579     if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }
580 
581     if (ConnS->NumLogs == 0) { client_notice(ClientS, "No logs for connection %s.", ConnS->Name); return; }
582 
583 #if !WIN32
584     if (Conn_IsLogHomeDir(ConnS)) { sysseteuidbyuser(ConnS->User); }
585 #endif
586 
587     for (ConnLog = ConnS->Log_Head ; ConnLog != NULL ;) {
588       char ResultStr[RECVBUFFERLEN+1] = "";
589       char RunCmd[LINELEN+1] = "";
590       char *TempPT = NULL;
591       if ((ConnLog->LogType != CONN_LOG_TYPE_PRIV) || (strwm(LogFilePT, ConnLog->LogFile) == FALSE)) {
592         ConnLog = ConnLog->Next;
593         continue;
594       }
595       LogFiles++;
596       if ((ConnS->PrivLogFile != NULL) && (strcasecmp(ConnLog->LogFile, ConnS->PrivLogFile) == FALSE)) {
597         FREE(ConnS->PrivLogFile);
598         fclose(ConnS->PrivLogFileFP);
599         ConnS->PrivLogFileFP = NULL;
600       }
601       snprintf(RunCmd, LINELEN+1, "rm %s", ConnLog->LogFileFullPath);
602       Result = sysrun(RunCmd, ResultStr, LINELEN);
603       while ((TempPT = strchr(ResultStr, '\n'))) { *TempPT = '\0'; }
604       if (Result <= ERROR) {
605         client_notice(ClientS, "Unable to remove logfile %s.: [%d] %s", ConnLog->LogFile, errno, strerror(errno));
606         ConnLog = ConnLog->Next;
607       }
608       else {
609         struct ConnLog_Struct *ConnLog_DEL = ConnLog;
610         ConnLog = ConnLog->Next;
611         client_notice(ClientS, "Removed logfile %s.", ConnLog_DEL->LogFile);
612         conn_remlog(ConnS, ConnLog_DEL);
613       }
614     }
615 
616 #if !WIN32
617     if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
618 #endif
619 
620     if (LogFiles == 0) { client_notice(ClientS, "No private logs for connection %s matching your query.", ConnS->Name); return; }
621 
622     return;
623 
624   }
625 
626 else if (strcasecmp(CommandPT, "READCHANLOG") == FALSE) {
627 
628     FILE *FilePT = NULL;
629     struct Conn_Struct *ConnS = NULL;
630     struct ConnLog_Struct *ConnLog = NULL;
631     unsigned long int LogFiles = 0;
632     unsigned long int LogLines = 0;
633     char *LogFilePT = NULL;
634 
635     if (Params < 2) {
636       ConnS = ClientS->ConnS;
637       if (Params < 1) LogFilePT = "*";
638       else LogFilePT = ParamsPT[0];
639     }
640     else {
641       ConnS = conn_get(ParamsPT[0]);
642       if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; }
643       LogFilePT = ParamsPT[1];
644     }
645 
646     if (ConnS == NULL) { client_notice(ClientS, "Usage: READCHANLOG <Connection name> <Logfile>"); return; }
647 
648     if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }
649 
650     if (ConnS->NumLogs == 0) { client_notice(ClientS, "No logs for connection %s.", ConnS->Name); return; }
651 
652 #if !WIN32
653       if (Conn_IsLogHomeDir(ConnS)) { sysseteuidbyuser(ConnS->User); }
654 #endif
655 
656     for (ConnLog = ConnS->Log_Head ; ConnLog != NULL ; ConnLog = ConnLog->Next) {
657       if ((ConnLog->LogType != CONN_LOG_TYPE_CHAN) || (strwm(LogFilePT, ConnLog->Name) == FALSE)) { continue; }
658       LogFiles++;
659 
660       FilePT = fopen(ConnLog->LogFileFullPath, "r");
661       if (FilePT == NULL) {
662         sysprint(BITMASK_ERROR, "Unable to open %s for reading: [%d] %s", ConnLog->LogFileFullPath, errno, strerror(errno));
663         client_notice(ClientS, "Unable to open %s for reading: [%d] %s", ConnLog->LogFileFullPath, errno, strerror(errno));
664 #if !WIN32
665         if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
666 #endif
667         continue;
668       }
669 
670       do {
671         char Line[LINELEN+1] = "";
672         char *LinePT = NULL;
673         char *TempPT = NULL;
674         memset(&Line, 0, LINELEN+1);
675         LinePT = fgets(Line, LINELEN, FilePT);
676         if (LinePT == NULL) { break; }
677         while ((TempPT = strchr(LinePT, '\r')) != NULL) { *TempPT = '\0'; }
678         while ((TempPT = strchr(LinePT, '\n')) != NULL) { *TempPT = '\0'; }
679         LogLines++;
680         client_addsend(ClientS, ":%s PRIVMSG %s :%s: %s", IRCP_USER_HOST(ClientS), IRCP_USER_NICK(ClientS), ConnLog->LogFile, LinePT);
681       } while(1);
682       fclose(FilePT);
683     }
684 
685 #if !WIN32
686     if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
687 #endif
688 
689     if (LogFiles == 0) { client_notice(ClientS, "No private logs for connection %s matching your query.", ConnS->Name); return; }
690     else { client_notice(ClientS, "Dumped %ld lines of %ld log files using PRIVMSG.", LogLines, LogFiles); return; }
691 
692   }
693   else if (strcasecmp(CommandPT, "ERASECHANLOG") == FALSE) {
694 
695     struct Conn_Struct *ConnS = NULL;
696     struct ConnLog_Struct *ConnLog = NULL;
697     unsigned long int LogFiles = 0;
698     char *LogFilePT = NULL;
699     signed long int Result = 0;
700     struct Chan_Struct *ChanS = NULL;
701 
702     if (Params < 2) {
703       ConnS = ClientS->ConnS;
704       if (Params < 1) LogFilePT = "*";
705       else LogFilePT = ParamsPT[0];
706     }
707     else {
708       ConnS = conn_get(ParamsPT[0]);
709       if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; }
710       LogFilePT = ParamsPT[1];
711     }
712 
713     if (ConnS == NULL) { client_notice(ClientS, "Usage: ERASECHANLOG <Connection name> <Logfile>"); return; }
714 
715     if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; }
716 
717     if (ConnS->NumLogs == 0) { client_notice(ClientS, "No logs for connection %s.", ConnS->Name); return; }
718 
719 #if !WIN32
720     if (Conn_IsLogHomeDir(ConnS)) { sysseteuidbyuser(ConnS->User); }
721 #endif
722 
723     for (ConnLog = ConnS->Log_Head ; ConnLog != NULL ;) {
724       char ResultStr[RECVBUFFERLEN+1] = "";
725       char RunCmd[LINELEN+1] = "";
726       char *TempPT = NULL;
727       if ((ConnLog->LogType != CONN_LOG_TYPE_CHAN) || (strwm(LogFilePT, ConnLog->LogFile) == FALSE)) {
728         ConnLog = ConnLog->Next;
729         continue;
730       }
731       LogFiles++;
732       for (ChanS = ConnS->Chan_Head ; ChanS != NULL ; ChanS = ChanS->Next) {
733         if ((ConnLog->LogFile != NULL) && (ChanS->LogFile != NULL) && (strcasecmp(ConnLog->LogFile, ChanS->LogFile) == FALSE)) {
734           FREE(ChanS->LogFile);
735           fclose(ChanS->LogFileFP);
736           ChanS->LogFileFP = NULL;
737         }
738       }
739       snprintf(RunCmd, LINELEN+1, "rm %s", ConnLog->LogFileFullPath);
740       Result = sysrun(RunCmd, ResultStr, LINELEN);
741       while ((TempPT = strchr(ResultStr, '\n'))) { *TempPT = '\0'; }
742       if (Result <= ERROR) {
743         client_notice(ClientS, "Unable to remove logfile %s.: [%d] %s", ConnLog->LogFile, errno, strerror(errno));
744         ConnLog = ConnLog->Next;
745       }
746       else {
747         struct ConnLog_Struct *ConnLog_DEL = ConnLog;
748         ConnLog = ConnLog->Next;
749         client_notice(ClientS, "Removed logfile %s.", ConnLog_DEL->LogFile);
750         conn_remlog(ConnS, ConnLog_DEL);
751       }
752     }
753 
754 #if !WIN32
755     if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
756 #endif
757 
758     if (LogFiles == 0) { client_notice(ClientS, "No channel logs for connection %s matching your query.", ConnS->Name); return; }
759 
760     return;
761 
762   }
763 
764 #if MEMDEBUG
765   else if (strcasecmp(CommandPT, "MEMLEAKS") == FALSE) {
766     client_notice(ClientS, "Calculating memory leaks...");
767     mem_leaks();
768     if (G_Leaks > 1 ) {
769       client_notice(ClientS, "Memory leaks: %ld size: %ld", G_Leaks, G_LeaksSize);
770       client_notice(ClientS, "!!! MEMORY LEAKS FOUND --- LOOK IN THE ERROR LOG !!!");
771     }
772     else { client_notice(ClientS, "No memory leaks found! IGNORE 1 FAKE MEMLEAK IN client_io.c!"); }
773   }
774 #endif
775   else {
776     if (ClientS->ConnS == NULL) { client_notice(ClientS, "No attached connection, use PATTACH to attach first."); }
777     else {
778       if (Conn_IsConnect(ClientS->ConnS)) {
779         if (Conn_IsWelcome(ClientS->ConnS)) {
780           if ((!strcasecmp(CommandPT, "WHO")) && (Params >= 1)) {
781             struct Chan_Struct *ChanS;
782             struct Who_Struct *WhoS;
783             ChanS = chan_get(ClientS->ConnS, ParamsPT[0]);
784             if (ChanS != NULL) {
785               WhoS = malloc(sizeof(struct Who_Struct));
786               if (WhoS != NULL) {
787                 memset(WhoS, 0, sizeof(struct Who_Struct));
788                 WhoS->PrevForClient = NULL;
789                 WhoS->NextForClient = ClientS->Who_Head;
790                 if (WhoS->NextForClient != NULL) { WhoS->NextForClient->PrevForClient = WhoS; }
791                 ClientS->Who_Head = WhoS;
792                 WhoS->NextForChan = ChanS->Who_Head;
793                 ChanS->Who_Head = WhoS;
794                 WhoS->ClientS = ClientS;
795               }
796             }
797           }
798         }
799         conn_addsendq(ClientS->ConnS, "%s", LinePT);
800       }
801       else { client_notice(ClientS, "Attached connection not established."); }
802     }
803   }
804 }
805