/* * ---------------------------------------------------------------- * Night Light IRC Proxy - Client Parser Functions * ---------------------------------------------------------------- * Copyright (C) 1997-2007 Jonas Kvinge * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Last modified by: * Jonas Kvinge (25.11.2007) * */ #define CLIENT_PARSER_C #define NEED_SYS_TYPES_H 1 /* Extra types */ #define NEED_SYS_PARAM_H 1 /* Some systems need this */ #define NEED_LIMITS_H 0 /* Kernel limits */ #define NEED_STDARG_H 1 /* va_list, etc */ #define NEED_ERRNO_H 1 /* errno */ #define NEED_CTYPE_H 1 /* isdigit(), etc */ #define NEED_NETINET_IN_H 1 /* in_addr, sockaddr_in, etc */ #define NEED_ARPA_INET_H 0 /* inet_ntoa(), inet_aton(), etc */ #define NEED_STDIO_H 1 /* Standard C UNIX functions */ #define NEED_STDLIB_H 1 /* malloc(), exit(), atoi(), etc */ #define NEED_TIME_H 1 /* time(), etc */ #define NEED_SYSCTL_H 0 /* sysctl(), etc */ #define NEED_SYS_STAT_H 0 /* chmod(), mkdir(), etc */ #define NEED_SYS_UIO_H 0 /* iovec, etc */ #define NEED_FCNTL_H 1 /* open(), creat(), fcntl(), etc */ #define NEED_SYS_IOCTL_H 0 /* ioctl(), etc */ #define NEED_SYS_FILIO_H 0 /* Solaris need this for ioctl(), etc */ #define NEED_UNISTD_H 1 /* Unix standard functions */ #define NEED_STRING_H 1 /* C string functions */ #define NEED_SIGNAL_H 0 /* Signal functions */ #define NEED_SYS_SOCKET_H 0 /* Socket functions */ #define NEED_NETDB_H 0 /* Network database functions */ #define NEED_ARPA_NAMESER_H 0 /* Nameserver definitions */ #define NEED_GETUSERPW_HEADERS 0 /* Functions to retrive system passwords */ #define NEED_ARES 1 /* Functions needed for ares */ #define NEED_SSL 1 /* Needed for SSL support */ #include "includes.h" #include "irc.h" #include "conf.h" #if MEMDEBUG #include "memlist.h" #endif #include "access_conf.h" #include "conn_conf.h" #include "listen.h" #include "client.h" #include "client_connection.h" #include "client_parser.h" #include "client_auth.h" #include "client_handle.h" #include "client_notice.h" #include "conn.h" #include "conn_connection.h" #include "conn_sendq.h" #include "chan.h" #include "chan_user.h" /* VARIABLES - JONAS (31.07.2001) */ #if MEMDEBUG extern unsigned long int Leaks; extern unsigned long int LeaksSize; #endif #if ARES extern ares_channel Ares_Channel; #endif extern struct Conf_Struct ConfS; extern struct Conn_Struct *Conn_Head; /* CLIENT_PARSE FUNCTION - JONAS (18.07.2001) */ void client_parse(struct Client_Struct *ClientS) { char *BufferPT = NULL; char *LinePT = NULL; unsigned long int Count = 0; unsigned long int Len = 0; char *TempPT = NULL; BufferPT = ClientS->RecvBuffer; for (Count = 1 ; BufferPT != NULL ; Count++) { TempPT = strchr(BufferPT, '\n'); if (TempPT == NULL) { if (Count == 1) { return; } Len = strlen(BufferPT) + 1; memmove(ClientS->RecvBuffer, BufferPT, Len); TempPT = realloc(ClientS->RecvBuffer, Len); if (TempPT == NULL) { client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno)); return; } ClientS->RecvBuffer = TempPT; return; } LinePT = strtok(BufferPT, "\n"); BufferPT = strtok(NULL, "\0"); if (LinePT == NULL) { continue; } Len = strlen(LinePT); if (LinePT[Len-1] == '\r') { LinePT[Len-1] = '\0'; } client_parse_message(ClientS, LinePT); if (ClientS->RecvBuffer == NULL) { return; } } FREE(ClientS->RecvBuffer); } /* CLIENT_PARSE_MESSAGE FUNCTION - JONAS (17.07.2001) */ void client_parse_message(struct Client_Struct *ClientS, char *MessagePT) { char *PrefixPT = NULL; char *LinePT = NULL; char *CommandPT = NULL; char **ParamsPT = NULL; char **TempPT = NULL; unsigned short int Index = 0; unsigned short int Params = 0; time_t Duration = 0; Duration = (NOW - ClientS->LineTime); if (Duration >= ConfS.ClientLineFloodTTL) { ClientS->Lines = 0; } ++ClientS->Lines; if (ClientS->Lines >= ConfS.ClientMaxLines) { client_close(ClientS, ACCESS_CONF_USERFLOOD); access_conf_adddeny(ClientS->HostName, ACCESS_CONF_USERFLOOD); return; } ClientS->LineTime = NOW; while (MessagePT[0] == ' ') { MessagePT++; } if (MessagePT[0] == '\0') { return; } if (MessagePT[0] == ':') { MessagePT++; PrefixPT = MessagePT; MessagePT = strchr(MessagePT, ' '); if (MessagePT == NULL) { return; } MessagePT[0] = '\0'; MessagePT++; while (MessagePT[0] == ' ') { MessagePT++; } if (MessagePT[0] == '\0') { return; } } else { if (ClientS->User == NULL) { PrefixPT = "*"; } else { PrefixPT = ClientS->User; } } LinePT = strdup(MessagePT); if (LinePT == NULL) { client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno)); return; } CommandPT = MessagePT; MessagePT = strchr(MessagePT, ' '); if (MessagePT != NULL) { MessagePT[0] = '\0'; MessagePT++; while (MessagePT[0] == ' ') { MessagePT++; } if (MessagePT[0] == '\0') { free(LinePT); return; } FOREVERLOOP { if (MessagePT[0] == ':') { MessagePT++; ++Params; TempPT = realloc(ParamsPT, (sizeof(char *) * Params)); if (TempPT == NULL) { client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno)); free(ParamsPT); free(LinePT); return; } ParamsPT = TempPT; ParamsPT[Index] = MessagePT; break; } else { ++Params; TempPT = realloc(ParamsPT, (sizeof(char *) * Params)); if (TempPT == NULL) { client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno)); free(ParamsPT); free(LinePT); return; } ParamsPT = TempPT; ParamsPT[Index] = MessagePT; ++Index; MessagePT = strchr(MessagePT, ' '); if (MessagePT == NULL) { break; } MessagePT[0] = '\0'; MessagePT++; while (MessagePT[0] == ' ') { MessagePT++; } if (MessagePT[0] == '\0') { break; } } } } strupper(CommandPT); client_parse_command(ClientS, LinePT, CommandPT, ParamsPT, Params); free(LinePT); free(ParamsPT); } /* CLIENT_PARSE_COMMAND FUNCTION - JONAS (17.07.2001) */ void client_parse_command(struct Client_Struct *ClientS, char *LinePT, char *CommandPT, char **ParamsPT, const unsigned short int Params) { if (!Client_IsVerified(ClientS)) { if (strcasecmp(CommandPT, "PASS") == FALSE) { if (Params < 1) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; } ClientS->Pass = strrealloc(ClientS->Pass, ParamsPT[0]); #if 0 client_notice(ClientS, "Got password."); #endif } else if (strcasecmp(CommandPT, "NICK") == FALSE) { if (Params < 1) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; } ClientS->Nick = strrealloc(ClientS->Nick, ParamsPT[0]); #if 0 client_notice(ClientS, "Got nickname."); #endif if (ClientS->Pass == NULL) { client_notice(ClientS, "You need to send the PASS command!"); } } else if (strcasecmp(CommandPT, "USER") == FALSE) { if (Params < 4) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; } ClientS->User = strrealloc(ClientS->User, ParamsPT[0]); #if 0 client_notice(ClientS, "Got username."); #endif if (ClientS->Pass == NULL) { client_notice(ClientS, "You need to send the PASS command!"); } } else { client_addsend(ClientS, ":%s 451 * :You have not registered.", IRCP_USER_HOST(ClientS)); } if ((ClientS->User != NULL) && (ClientS->Pass != NULL)) { struct Conn_Struct *ConnS = NULL; assert(geteuid() != 0); client_authcheck(ClientS->User, ClientS->Pass); assert(geteuid() != 0); if (aerrno != AESUCCESS) { client_close(ClientS, "USER / PASS is incorrect."); return; } sysprint(BITMASK_MAIN, "Client %s (%s) sucessfully authenticated as \"%s\".", ClientS->HostName, ClientS->HostIPS, ClientS->User); client_noticeallbutone(ClientS, "Client %s (%s) authenticated as \"%s\".", ClientS->HostName, ClientS->HostIPS, ClientS->User); Client_SetVerified(ClientS); client_welcome(ClientS); for (ConnS = Conn_Head ; ConnS != NULL ; ConnS = ConnS->Next) { if ((strcmp(ClientS->User, ConnS->User) == FALSE) && (Conn_IsAttached(ConnS))) { client_attach(ClientS, ConnS); break; } } return; } return; } if (strcasecmp(CommandPT, "PCONNLIST") == FALSE) { struct Conn_Struct *ConnS = NULL; struct ConnServer_Struct *ConnServer = NULL; unsigned short int Count = 0; for (ConnS = Conn_Head ; ConnS != NULL ; ConnS = ConnS->Next) { if (strcmp(ConnS->User, ClientS->User) != FALSE) { continue; } ++Count; client_notice(ClientS, "\2Connection: %s Host: %s\2", ConnS->Name, ConnS->Host); if (Host_IsResolving(ConnS->ResolveFlags)) { client_notice(ClientS, "--- Status: Resolving local hostname %s to IP-address.", ConnS->HostName); } else if (Host_IsResolving(ConnS->ServerResolveFlags)) { client_notice(ClientS, "--- Status: Resolving server nostname %s to IP-address.", ConnS->ServerHostName); } else if (Conn_IsConnecting(ConnS)) { client_notice(ClientS, "--- Status: Connecting to %s(%s):%ld.", ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); } else if (Conn_IsWelcome(ConnS)) { client_notice(ClientS, "--- Status: Connected to %s(%s):%ld \"%s\".", ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, ConnS->ServerName); } else if (Conn_IsConnected(ConnS)) { client_notice(ClientS, "--- Status: Socket connected to %s(%s):%ld.", ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); } else { client_notice(ClientS, "--- Status: Disconnected."); } if (ConnS->Server_Head == NULL) { client_notice(ClientS, "--- No servers!"); } else { for (ConnServer = ConnS->Server_Head ; ConnServer != NULL ; ConnServer = ConnServer->Next) { client_notice(ClientS, "--- Server: %s Port: %ld", ConnServer->Host, ConnServer->Port); } } } if (Count == 0) { client_notice(ClientS, "You have no connections."); } return; } else if (strcasecmp(CommandPT, "PCONNECT") == FALSE) { struct Conn_Struct *ConnS = NULL; if (Params < 1) { if (ClientS->ConnS == NULL) { client_notice(ClientS, "Usage: PCONNECT [Connection server]."); return; } ConnS = ClientS->ConnS; } else { ConnS = conn_get(ParamsPT[0]); } if (ConnS == NULL) { client_notice(ClientS, "No such connection, use /PCONNLIST to list your connections."); return; } if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; } if (Params >= 2) { for (ConnS->ConnServerTry = ConnS->Server_Head ; ConnS->ConnServerTry != NULL ; ConnS->ConnServerTry = ConnS->ConnServerTry->Next) { if (strwm(ParamsPT[1], ConnS->ConnServerTry->Host) == TRUE) { break; } } if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "No such server for that connection."); return; } } if (Host_IsResolving(ConnS->ResolveFlags)) { if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "Connection %s: Resolving local hostname %s to IP-address.", ConnS->Name, ConnS->HostName); return; } #if HAVE_ARES_CANCELQUERY ares_cancelquery(Ares_Channel, ConnS); #endif return; } else if (Host_IsResolving(ConnS->ServerResolveFlags)) { if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "Connection %s: Resolving server hostname %s to IP-address.", ConnS->Name, ConnS->ServerHostName); return; } #if HAVE_ARES_CANCELQUERY ares_cancelquery(Ares_Channel, ConnS); #endif return; } else if (Conn_IsConnecting(ConnS)) { if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "%s: Connecting to %s(%s):%ld.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); return; } 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); return; } else if (Conn_IsWelcome(ConnS)) { 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; } conn_quit(ConnS, "Changing server to %s.", ConnS->ConnServerTry->Host); return; } else if (Conn_IsConnected(ConnS)) { if (ConnS->ConnServerTry == NULL) { client_notice(ClientS, "Connection %s: Socket connected to %s(%s):%ld.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); return; } 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); return; } #if SSL_SUPPORT conn_connect(ConnS); #else if (!ConnConf_IsSSL(ConnS)) { conn_connect(ConnS); } else { client_notice(ClientS, "Connection %s: SSL is enabled for this connection, but %s is compiled without SSL support.", ConnS->Name, PACKAGE); } #endif return; } else if (strcasecmp(CommandPT, "PQUIT") == FALSE) { struct Conn_Struct *ConnS = NULL; if (Params >= 1) { ConnS = conn_get(ParamsPT[0]); if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; } } else { ConnS = ClientS->ConnS; if (ConnS == NULL) { client_notice(ClientS, "Usage: PQUIT "); return; } } if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; } if (Conn_IsConnect(ConnS)) { if (Conn_IsWelcome(ConnS)) { 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; } else { conn_quit(ConnS, "%s (%s[%s]) requested QUIT.", ClientS->User, ClientS->HostName, ClientS->HostIPS); } } 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; } } else { client_notice(ClientS, "Connection %s is not connected.", ConnS->Name); return; } return; } else if ((strcasecmp(CommandPT, "PATTACH") == FALSE) || (strcasecmp(CommandPT, "PRESUME") == FALSE)) { struct Conn_Struct *ConnS = NULL; if (ClientS->ConnS != NULL) { client_notice(ClientS, "You are already attached on connection %s.", ClientS->ConnS->Name); return; } if (Params < 1) { client_notice(ClientS, "Usage: PATTACH "); return; } ConnS = conn_get(ParamsPT[0]); if (ConnS == NULL) { client_notice(ClientS, "No such connection."); return; } if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; } client_attach(ClientS, ConnS); } else if (strcasecmp(CommandPT, "PDETACH") == FALSE) { if (ClientS->ConnS == NULL) { client_notice(ClientS, "You have no attached connection."); return; } Conn_ClearAttached(ClientS->ConnS); client_detach(ClientS); } else if (strcasecmp(CommandPT, "QUIT") == FALSE) { client_close(ClientS, "Client sent QUIT."); } else if (strcasecmp(CommandPT, "PING") == FALSE) { if (Params < 1) { client_notice(ClientS, "%s :Not enough parameters.", CommandPT); return; } client_addsend(ClientS, ":%s PONG %s :%s", HOSTNAME, IRCP_USER_HOST(ClientS), ParamsPT[0]); /* client_addsend(ClientS, "PONG :%s", ParamsPT[0]); */ } else if (strcasecmp(CommandPT, "PONG") == FALSE) { Client_ClearSentPing(ClientS); } else if (strcasecmp(CommandPT, "QUOTE") == FALSE) { char *DumbPT = NULL; if (ClientS->ConnS == NULL) { client_notice(ClientS, "QUOTE: No attached connection."); return; } if (!Conn_IsConnected(ClientS->ConnS)) { client_notice(ClientS, "Connection %s not established.", ClientS->ConnS->Name); return; } DumbPT = strchr(LinePT, ' '); DumbPT++; conn_addsendq(ClientS->ConnS, "%s", DumbPT); } else if (strcasecmp(CommandPT, "READLOG") == FALSE) { FILE *FilePT = NULL; struct Conn_Struct *ConnS = NULL; unsigned long int Count = 0; if (Params >= 1) { ConnS = conn_get(ParamsPT[0]); if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; } } else { ConnS = ClientS->ConnS; if (ConnS == NULL) { client_notice(ClientS, "Usage: READLOG "); return; } } if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; } if (ConnS->FileName == NULL) { client_notice(ClientS, "Logfile missing for connection %s.", ConnS->Name); return; } #if !WIN32 if (Conn_IsLogHomeDir(ConnS)) { sysseteuidbyuser(ConnS->User); } #endif FilePT = fopen(ConnS->FileName, "r"); if (FilePT == NULL) { sysprint(BITMASK_ERROR, "Unable to open %s for reading: [%d] %s", ConnS->FileName, errno, strerror(errno)); client_notice(ClientS, "Unable to open %s for reading: [%d] %s", ConnS->FileName, errno, strerror(errno)); #if !WIN32 if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); } #endif return; } FOREVERLOOP { char Line[LINELEN+1] = ""; char *DumbPT = NULL; memset(&Line, 0, LINELEN+1); DumbPT = fgets(Line, LINELEN, FilePT); if (DumbPT == NULL) { break; } Count++; client_addsend(ClientS, ":%s PRIVMSG %s :%s", IRCP_USER_HOST(ClientS), IRCP_USER_NICK(ClientS), Line); } if (Count == 0) { client_notice(ClientS, "Logfile for %s is empty.", ConnS->Name); } fclose(FilePT); #if !WIN32 if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); } #endif } else if (strcasecmp(CommandPT, "ERASELOG") == FALSE) { FILE *FilePT = NULL; struct Conn_Struct *ConnS = NULL; unsigned long int Count = 0; if (Params >= 1) { ConnS = conn_get(ParamsPT[0]); if (ConnS == NULL) { client_notice(ClientS, "No such connection, eventually use /PCONNLIST to list your connections."); return; } } else { ConnS = ClientS->ConnS; if (ConnS == NULL) { client_notice(ClientS, "Usage: ERASELOG "); return; } } if (strcmp(ConnS->User, ClientS->User) != FALSE) { client_notice(ClientS, "Permission denied, You're not the owner of connection %s.", ConnS->Name); return; } if (ConnS->FileName == NULL) { client_notice(ClientS, "Logfile missing for connection %s.", ConnS->Name); return; } #if !WIN32 if (Conn_IsLogHomeDir(ConnS)) { sysseteuidbyuser(ConnS->User); } #endif FilePT = fopen(ConnS->FileName, "r"); if (FilePT == NULL) { sysprint(BITMASK_ERROR, "Unable to open %s for reading: [%d] %s", ConnS->FileName, errno, strerror(errno)); client_notice(ClientS, "Unable to open %s for reading: [%d] %s", ConnS->FileName, errno, strerror(errno)); #if !WIN32 if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); } #endif return; } FOREVERLOOP { char Line[LINELEN+1] = ""; char *DumbPT = NULL; memset(&Line, 0, LINELEN+1); DumbPT = fgets(Line, LINELEN, FilePT); if (DumbPT == NULL) { break; } Count++; } fclose(FilePT); if (Count == 0) { client_notice(ClientS, "Logfile for %s is empty.", ConnS->Name); #if !WIN32 if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); } #endif return; } FilePT = fopen(ConnS->FileName, "w+"); if (FilePT == NULL) { sysprint(BITMASK_ERROR, "Unable to open %s for writing: [%d] %s", ConnS->FileName, errno, strerror(errno)); client_notice(ClientS, "Unable to open %s for writing: [%d] %s", ConnS->FileName, errno, strerror(errno)); #if !WIN32 if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); } #endif return; } fclose(FilePT); #if !WIN32 if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); } #endif client_notice(ClientS, "Logfile for %s erased.", ConnS->Name); } #if MEMDEBUG else if (strcasecmp(CommandPT, "MEMLEAKS") == FALSE) { client_notice(ClientS, "Calculating memory leaks..."); mem_leaks(); if (Leaks > 1 ) { client_notice(ClientS, "Memory leaks: %ld size: %ld", Leaks, LeaksSize); client_notice(ClientS, "!!! MEMORY LEAKS FOUND --- LOOK IN THE ERROR LOG !!!"); } else { client_notice(ClientS, "No memory leaks found! IGNORE 1 FAKE MEMLEAK IN client_io.c!"); } } #endif else { if (ClientS->ConnS == NULL) { client_notice(ClientS, "No attached connection, use PATTACH to attach first."); } else { if (Conn_IsConnect(ClientS->ConnS)) { if (Conn_IsWelcome(ClientS->ConnS)) { if ((!strcasecmp(CommandPT, "WHO")) && (Params >= 1)) { struct Chan_Struct *ChanS; struct Who_Struct *WhoS; ChanS = chan_get(ClientS->ConnS, ParamsPT[0]); if (ChanS != NULL) { WhoS = malloc(sizeof(struct Who_Struct)); if (WhoS != NULL) { memset(WhoS, 0, sizeof(struct Who_Struct)); WhoS->PrevForClient = NULL; WhoS->NextForClient = ClientS->Who_Head; if (WhoS->NextForClient != NULL) { WhoS->NextForClient->PrevForClient = WhoS; } ClientS->Who_Head = WhoS; WhoS->NextForChan = ChanS->Who_Head; ChanS->Who_Head = WhoS; WhoS->ClientS = ClientS; } } } } conn_addsendq(ClientS->ConnS, "%s", LinePT); } else { client_notice(ClientS, "Attached connection not established."); } } } }