Descent3/netcon/mtclient/chat_api.cpp

1191 lines
35 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifdef WIN32
#include "windows.h"
#endif
#include <cstdio>
#include <cstring>
#if defined(POSIX)
#include <sys/time.h>
#include <unistd.h>
#endif
#include "chat_api.h"
#include "crossplat.h"
#include "grdefs.h"
#include "mtstrings.h"
#include "networking.h"
extern const char *GetString(int d);
typedef void *(*mem_malloc_fp)(int size);
extern mem_malloc_fp DLLmem_malloc;
typedef void (*mem_free_fp)(void *memblock);
extern mem_free_fp DLLmem_free;
typedef int (*nw_Asyncgethostbyname_fp)(uint32_t *ip, int command, const char *hostname);
extern nw_Asyncgethostbyname_fp DLLnw_Asyncgethostbyname;
typedef int (*PollUI_fp)();
extern PollUI_fp DLLPollUI;
#ifndef RELEASE
#define DLLmprintf(...) DLLDebug_ConsolePrintf(__VA_ARGS__)
#else
#define DLLmprintf(...)
#endif
typedef void (*Debug_ConsolePrintf_fp)(int n, const char *format, ...);
extern Debug_ConsolePrintf_fp DLLDebug_ConsolePrintf;
#define MAXCHATBUFFER 500
SOCKET Chatsock;
SOCKADDR_IN Chataddr;
int Socket_connecting = 0;
char Nick_name[33];
char Orignial_nick_name[33];
int Nick_variety = 0;
char szChat_channel[33] = "";
char Input_chat_buffer[MAXCHATBUFFER] = "";
char Chat_tracker_id[33];
char Getting_user_channel_info_for[33] = "";
char Getting_user_tracker_info_for[33] = "";
int Getting_user_channel_error = 0;
int Getting_user_tracker_error = 0;
char User_req_tracker_id[100] = ""; // These are oversized for saftey
char User_req_channel[100] = "";
char *User_list = nullptr;
char *Chan_list = nullptr;
int Socket_connected = 0;
int Chat_server_connected = 0;
int Joining_channel = 0;
int Joined_channel = 0;
int GettingChannelList = 0;
int GettingUserTID = 0;
int GettingUserChannel = 0;
Chat_user *Firstuser, *Curruser;
Chat_command *Firstcommand, *Currcommand;
Chat_channel *Firstchannel, *Currchannel;
void ChatInit() {
Socket_connecting = 0;
Nick_name[0] = 0;
Orignial_nick_name[0] = 0;
Nick_variety = 0;
szChat_channel[0] = '\0';
Input_chat_buffer[0] = '\0';
Chat_tracker_id[0] = 0;
Getting_user_channel_info_for[0] = '\0';
Getting_user_tracker_info_for[0] = '\0';
Getting_user_channel_error = 0;
Getting_user_tracker_error = 0;
User_req_tracker_id[0] = '\0';
User_req_channel[0] = '\0';
User_list = nullptr;
Chan_list = nullptr;
Socket_connected = 0;
Chat_server_connected = 0;
Joining_channel = 0;
Joined_channel = 0;
GettingChannelList = 0;
GettingUserTID = 0;
GettingUserChannel = 0;
}
// Return codes:
//-2 Already connected
//-1 Failed to connect
// 0 Connecting
// 1 Connected
// Call it once with the server IP address, and it will return immediately
// with 0. Keep calling it until it returns something other than 0
// note: the nickname may be changed if someone with that name already
// exists (Scourge1 for instance)
int ConnectToChatServer(const char *serveraddr, int16_t chat_port, char *nickname, char *trackerid) {
char signon_str[100];
// if(Socket_connected && ) return -2;
if (!Socket_connecting) {
strcpy(Nick_name, nickname);
for (uint32_t l = 0; l < strlen(Nick_name); l++)
if (Nick_name[l] == ' ')
Nick_name[l] = '_';
strcpy(Orignial_nick_name, nickname);
strcpy(Chat_tracker_id, trackerid);
Firstuser = nullptr;
Firstcommand = nullptr;
Chat_server_connected = 0;
FlushChatCommandQueue();
if (chat_port == 0) {
// AfxMessageBox("Invalid chat port, must be host.com:port (ie. irc.dal.net:6667)");
return -1;
}
Chatsock = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == Chatsock) {
// AfxMessageBox("Unable to open socket!");
return -1;
}
memset(&Chataddr, 0, sizeof(SOCKADDR_IN));
Chataddr.sin_family = AF_INET;
Chataddr.sin_addr.s_addr = INADDR_ANY;
Chataddr.sin_port = 0;
if (SOCKET_ERROR == bind(Chatsock, (SOCKADDR *)&Chataddr, sizeof(sockaddr))) {
// AfxMessageBox("Unable to bind socket!");
return -1;
}
#ifdef WIN32
unsigned long arg = 1;
ioctlsocket(Chatsock, FIONBIO, &arg);
#else // WIN32
fcntl(Chatsock, F_SETFL, fcntl(Chatsock, F_GETFL, 0) | O_NONBLOCK);
#endif
/*
HOSTENT *he;
he = gethostbyname(chat_server);
if(!he)
{
//AfxMessageBox("Unable to gethostbyname.\n");
return -1;
}
memcpy(&Chataddr.sin_addr.s_addr, he->h_addr_list[0],4);//&iaddr, 4);
*/
int rcode;
uint32_t ip;
DLLnw_Asyncgethostbyname(&ip, NW_AGHBN_LOOKUP, serveraddr);
do {
rcode = DLLnw_Asyncgethostbyname(&ip, NW_AGHBN_READ, serveraddr);
if (99 == DLLPollUI()) {
return 0;
}
} while (rcode == 0);
if (rcode != 1) {
DLLmprintf(0, "Unable to gethostbyname(\"%s\").\n", serveraddr);
DLLmprintf(0, "WSAGetLastError() returned %d.\n", WSAGetLastError());
DLLnw_Asyncgethostbyname(nullptr, NW_AGHBN_CANCEL, nullptr);
return 0;
}
memcpy(&Chataddr.sin_addr.s_addr, &ip, 4);
Chataddr.sin_port = htons(chat_port);
if (SOCKET_ERROR == connect(Chatsock, (SOCKADDR *)&Chataddr, sizeof(SOCKADDR_IN))) {
int ret = WSAGetLastError();
#if !defined(POSIX)
if (WSAEWOULDBLOCK == WSAGetLastError())
#else
if (EINPROGRESS == ret || 0 == ret)
#endif
{
DLLmprintf(0, "Beginning socket connect\n");
Socket_connecting = 1;
return 0;
}
} else {
// This should never happen, connect should always return WSAEWOULDBLOCK
DLLmprintf(0, "connect returned too soon!\n");
Socket_connecting = 1;
Socket_connected = 1;
DLLmprintf(0, "Socket connected, sending user and nickname request\n");
snprintf(signon_str, sizeof(signon_str), "/USER %s %s %s :%s", "user", "user", "user", Chat_tracker_id);
SendChatString(signon_str, 1);
snprintf(signon_str, sizeof(signon_str), "/NICK %s", Nick_name);
SendChatString(signon_str, 1);
return 0;
}
} else {
if (Chat_server_connected) {
return 1;
}
if (!Socket_connected) {
// Do a few select to check for an error, or to see if we are writeable (connected)
fd_set write_fds, error_fds;
#ifdef WIN32
TIMEVAL timeout;
#else
struct timeval timeout;
#endif
timeout.tv_sec = 0;
timeout.tv_usec = 0;
FD_ZERO(&write_fds);
FD_SET(Chatsock, &write_fds);
// Writable -- that means it's connected
if (select(Chatsock + 1, nullptr, &write_fds, nullptr, &timeout)) {
Socket_connected = 1;
DLLmprintf(0, "Socket connected, sending user and nickname request\n");
snprintf(signon_str, sizeof(signon_str), "/USER %s %s %s :%s", "user", "user", "user", Chat_tracker_id);
SendChatString(signon_str, 1);
snprintf(signon_str, sizeof(signon_str), "/NICK %s", Nick_name);
SendChatString(signon_str, 1);
return 0;
// Now we are waiting for Chat_server_connected
}
FD_ZERO(&error_fds);
FD_SET(Chatsock, &error_fds);
// error -- that means it's not going to connect
if (select(Chatsock + 1, nullptr, nullptr, &error_fds, &timeout)) {
DLLmprintf(0, "Select returned an error!\n");
return -1;
}
return 0;
}
}
return 0;
}
// Call it to close the connection. It returns immediately
void DisconnectFromChatServer() {
if (!Socket_connected)
return;
SendChatString("/QUIT", 1);
shutdown(Chatsock, 2);
#ifdef WIN32
closesocket(Chatsock);
#else
close(Chatsock);
#endif
Socket_connecting = 0;
Socket_connected = 0;
Input_chat_buffer[0] = '\0';
if (User_list) {
DLLmem_free(User_list);
User_list = nullptr;
}
if (Chan_list) {
DLLmem_free(Chan_list);
Chan_list = nullptr;
}
Chat_server_connected = 0;
Joining_channel = 0;
Joined_channel = 0;
RemoveAllChatUsers();
FlushChatCommandQueue();
FlushChannelList();
}
// returns NULL if no line is there to print, otherwise returns a string to
// print (all preformatted of course)
const char *GetChatText() {
if (!Socket_connected)
return nullptr;
// ChatGetString will do the formatting
return ChatGetString();
}
// Send a string to be sent as chat, or scanned for messages (/msg <user>
// string)
const char *SendChatString(const char *line, int raw) {
char szCmd[200];
char szTarget[50];
if (!Socket_connected)
return nullptr;
if (line[0] == '/') {
// Start off by getting the command
strcpy(szCmd, GetWordNum(0, line + 1));
if (stricmp(szCmd, "msg") == 0) {
strcpy(szTarget, GetWordNum(1, line + 1));
snprintf(szCmd, sizeof(szCmd), "PRIVMSG %s :%s\n\r", szTarget, line + strlen("/msg ") + strlen(szTarget) + 1);
send(Chatsock, szCmd, strlen(szCmd), 0);
szCmd[strlen(szCmd) - 2] = '\0';
return ParseIRCMessage(szCmd, MSG_LOCAL);
}
if (stricmp(szCmd, "me") == 0) {
snprintf(szCmd, sizeof(szCmd), "PRIVMSG %s :\001ACTION %s\001\n\r", szChat_channel, line + strlen("/me "));
send(Chatsock, szCmd, strlen(szCmd), 0);
szCmd[strlen(szCmd) - 2] = '\0';
return ParseIRCMessage(szCmd, MSG_LOCAL);
}
if (stricmp(szCmd, "xyz") == 0) {
// Special command to send raw irc commands
snprintf(szCmd, sizeof(szCmd), "%s\n\r", line + strlen("/xyz "));
send(Chatsock, szCmd, strlen(szCmd), 0);
return nullptr;
}
if (stricmp(szCmd, "list") == 0) {
snprintf(szCmd, sizeof(szCmd), "%s\n\r", line + 1);
send(Chatsock, szCmd, strlen(szCmd), 0);
return nullptr;
}
if (raw) {
snprintf(szCmd, sizeof(szCmd), "%s\n\r", line + 1);
send(Chatsock, szCmd, strlen(szCmd), 0);
return nullptr;
}
return "Unrecognized command";
} else {
if (szChat_channel[0]) {
snprintf(szCmd, sizeof(szCmd), "PRIVMSG %s :%s\n\r", szChat_channel, line);
send(Chatsock, szCmd, strlen(szCmd), 0);
szCmd[strlen(szCmd) - 2] = '\0';
return ParseIRCMessage(szCmd, MSG_LOCAL);
}
}
return nullptr;
}
// Returns a structure which contains a command and possible some data (like
// a user joining or leaving) if one is waiting
// This tells you if you need to add a user from the userlist, remove a user,
// etc. Also for status messages, like if you get knocked
// off the server for some reason.
Chat_command *GetChatCommand() {
if (!Socket_connected)
return nullptr;
return GetChatCommandFromQueue();
}
// This function returns a list of users in the current channel, in one
// string, separated by spaces, terminated by a null
// (Spaces aren't allowed as part of a nickname)
char *GetChatUserList() {
int iuser_list_length = 0;
;
if (User_list) {
DLLmem_free(User_list);
User_list = nullptr;
}
if (!Socket_connected)
return nullptr;
Curruser = Firstuser;
while (Curruser) {
iuser_list_length += strlen(Curruser->nick_name) + 1;
Curruser = Curruser->next;
}
Curruser = Firstuser;
User_list = (char *)DLLmem_malloc(iuser_list_length + 1);
User_list[0] = '\0';
while (Curruser) {
strcat(User_list, Curruser->nick_name);
strcat(User_list, " ");
Curruser = Curruser->next;
}
return User_list;
}
// Call this to set/join a channel. Since we can't be sure that we will be
// able to join that channel, check it for completion
// You can't be in more than one channel at a time with this API, so you
// leave the current channel before trying to join
// a new one. Because of this if the join fails, make sure you try to join
// another channel, or the user wont be able to chat
//-1 Failed to join
// 0 joining
// 1 successfully joined
int SetNewChatChannel(const char *channel) {
char partstr[100];
if (!Socket_connected)
return -1;
if (Joining_channel == 1) {
if (Joined_channel == 1) {
// We made it in!
Joining_channel = 0;
return 1;
} else if (Joined_channel == -1) {
// Error -- we got a message that the channel was invite only, or we were banned or something
Joining_channel = 0;
strcpy(szChat_channel, "");
return -1;
}
} else {
if (szChat_channel[0]) {
snprintf(partstr, sizeof(partstr), "/PART %s", szChat_channel);
SendChatString(partstr, 1);
}
strcpy(szChat_channel, channel);
snprintf(partstr, sizeof(partstr), "/JOIN %s", szChat_channel);
SendChatString(partstr, 1);
Joining_channel = 1;
Joined_channel = 0;
}
return 0;
}
const char *ChatGetString() {
char ch[2];
char *p;
int bytesread = 1;
static char return_string[MAXCHATBUFFER];
while ((bytesread != 0) && (bytesread != SOCKET_ERROR)) {
bytesread = recv(Chatsock, ch, 1, 0);
if (bytesread == SOCKET_ERROR) {
uint32_t lerror = WSAGetLastError();
#if !defined(POSIX)
if (WSAEWOULDBLOCK != lerror)
#else
if (WSAEWOULDBLOCK != lerror && 0 != lerror)
#endif
{
DLLmprintf(0, "recv caused an error: %d\n", lerror);
}
return nullptr;
}
if (bytesread) {
ch[1] = '\0';
// DLLmprintf(0,ch);
if ((ch[0] == 0x0a) || (ch[0] == 0x0d)) {
if (Input_chat_buffer[0] == '\0') {
// Blank line, ignore it
return nullptr;
}
strcpy(return_string, Input_chat_buffer);
Input_chat_buffer[0] = '\0';
// DLLmprintf(0,"->|%s\n",return_string);
p = ParseIRCMessage(return_string, MSG_REMOTE);
return p;
}
strcat(Input_chat_buffer, ch);
} else {
// Select said we had read data, but 0 bytes read means disconnected
DLLmprintf(0, "Disconnected! Doh!");
AddChatCommandToQueue(CC_DISCONNECTED, nullptr, 0);
return nullptr;
}
}
return nullptr;
}
const char *GetWordNum(int num, const char *l_String) {
static char strreturn[600];
static char ptokstr[600];
char seps[10] = " \n\r\t";
char *token, *strstart;
strstart = ptokstr;
strcpy(ptokstr, l_String);
token = strtok(ptokstr, seps);
for (int i = 0; i != num; i++) {
token = strtok(nullptr, seps);
}
if (token) {
strcpy(strreturn, token);
} else {
return "";
}
// check for the ':' char....
if (token[0] == ':') {
// Its not pretty, but it works, return the rest of the string
strcpy(strreturn, l_String + ((token - strstart) + 1));
}
// return the appropriate response.
return strreturn;
}
int AddChatUser(const char *nickname) {
Curruser = Firstuser;
while (Curruser) {
if (stricmp(nickname, Curruser->nick_name) == 0)
return 0;
Curruser = Curruser->next;
}
Curruser = Firstuser;
if (Firstuser == nullptr) {
Firstuser = (Chat_user *)DLLmem_malloc(sizeof(Chat_user));
// ASSERT(Firstuser);
strcpy(Firstuser->nick_name, nickname);
Firstuser->next = nullptr;
AddChatCommandToQueue(CC_USER_JOINING, nickname, strlen(nickname) + 1);
return 1;
} else {
while (Curruser->next) {
Curruser = Curruser->next;
}
Curruser->next = (Chat_user *)DLLmem_malloc(sizeof(Chat_user));
Curruser = Curruser->next;
// ASSERT(Curruser);
strcpy(Curruser->nick_name, nickname);
Curruser->next = nullptr;
AddChatCommandToQueue(CC_USER_JOINING, nickname, strlen(nickname) + 1);
return 1;
}
}
int RemoveChatUser(char *nickname) {
Chat_user *prv_user = nullptr;
Curruser = Firstuser;
while (Curruser) {
if (stricmp(nickname, Curruser->nick_name) == 0) {
if (prv_user) {
prv_user->next = Curruser->next;
} else {
Firstuser = Curruser->next;
}
AddChatCommandToQueue(CC_USER_LEAVING, Curruser->nick_name, strlen(Curruser->nick_name) + 1);
DLLmem_free(Curruser);
return 1;
}
prv_user = Curruser;
Curruser = Curruser->next;
}
return 0;
}
void RemoveAllChatUsers() {
Chat_user *tmp_user = nullptr;
Curruser = Firstuser;
while (Curruser) {
tmp_user = Curruser->next;
AddChatCommandToQueue(CC_USER_LEAVING, Curruser->nick_name, strlen(Curruser->nick_name) + 1);
DLLmem_free(Curruser);
Curruser = tmp_user;
}
Firstuser = nullptr;
}
char *ParseIRCMessage(char *Line, int iMode) {
char szRemLine[MAXLOCALSTRING];
const char *pszTempStr;
char szPrefix[MAXLOCALSTRING];
char szHackPrefix[MAXLOCALSTRING];
char szTarget[MAXLOCALSTRING];
char szNick[MAXLOCALSTRING];
char szCmd[MAXLOCALSTRING];
char szCTCPCmd[MAXLOCALSTRING];
static char szResponse[MAXLOCALSTRING];
int iNickLen;
int iPrefixLen = 0;
szPrefix[0] = '\0';
szNick[0] = '\0';
if (strlen(Line) >= MAXLOCALSTRING) {
return nullptr;
}
// Nick included....
if (iMode == MSG_REMOTE) {
strcpy(szRemLine, Line);
// Start by getting the prefix
szHackPrefix[0] = '\0';
if (Line[0] == ':') {
//
pszTempStr = GetWordNum(0, Line + 1);
strcpy(szPrefix, pszTempStr);
strcpy(szHackPrefix, pszTempStr);
strcpy(szRemLine, Line + 1 + strlen(szPrefix));
}
// Next, get the Nick
pszTempStr = strtok(szHackPrefix, "!");
if (pszTempStr) {
strcpy(szNick, pszTempStr);
} else {
strncpy(szNick, szPrefix, 31);
szNick[31] = 0;
}
// strcpy(NewMsg.Nickname,szNick);
iNickLen = strlen(szNick);
iPrefixLen = strlen(szPrefix);
} else if (iMode == MSG_LOCAL) {
strcpy(szRemLine, Line);
strcpy(szNick, Nick_name);
strcpy(szPrefix, Nick_name);
// strcpy(NewMsg.Nickname,szNick);
iNickLen = -2;
iPrefixLen = -2;
}
// Next is the command
pszTempStr = GetWordNum(0, szRemLine);
if (pszTempStr[0]) {
strcpy(szCmd, pszTempStr);
} else {
// Shouldn't ever happen, but we can't be sure of what the host will send us.
return nullptr;
}
// Move the szRemLine string up
strcpy(szRemLine, Line + iPrefixLen + strlen(szCmd) + 2);
// Now parse the commands!
// printf("%s",szCmd);
if (stricmp(szCmd, "PRIVMSG") == 0) {
pszTempStr = GetWordNum(0, szRemLine);
strcpy(szTarget, pszTempStr);
strcpy(szRemLine, Line + iPrefixLen + strlen(szCmd) + strlen(szTarget) + 4);
if (szRemLine[0] == ':') {
strcpy(szCTCPCmd, GetWordNum(0, szRemLine + 1));
if (szCTCPCmd[strlen(szCTCPCmd) - 1] == 0x01)
szCTCPCmd[strlen(szCTCPCmd) - 1] = 0x00;
} else {
strcpy(szCTCPCmd, GetWordNum(0, szRemLine));
if (szCTCPCmd[strlen(szCTCPCmd) - 1] == 0x01)
szCTCPCmd[strlen(szCTCPCmd) - 1] = 0x00;
}
if (szCTCPCmd[0] == 0x01) {
// Handle ctcp message
strcpy(szRemLine, Line + iPrefixLen + strlen(szCmd) + strlen(szTarget) + strlen(szCTCPCmd) + 6);
szRemLine[strlen(szRemLine) - 1] = '\0'; // null out the ending 0x01
if (stricmp(szCTCPCmd + 1, "ACTION") == 0) {
// Posture
snprintf(szResponse, sizeof(szResponse), "\1\xff\x28\x28* %s %s", szNick, szRemLine);
return szResponse;
}
if (iMode == MSG_LOCAL) {
strcpy(szHackPrefix, Line + iPrefixLen + strlen(szCmd) + strlen(szTarget) + 4);
szRemLine[strlen(szRemLine) - 1] = '\0';
snprintf(szResponse, sizeof(szResponse), "** CTCP %s %s %s", szTarget, szCTCPCmd + 1, szRemLine);
return szResponse;
}
if (stricmp(szCTCPCmd + 1, "PING") == 0) {
snprintf(szResponse, sizeof(szResponse), "/NOTICE %s :\001PING %s\001", szNick,
szRemLine); // Don't need the trailing \001 because szremline has it.
SendChatString(szResponse, 1);
return nullptr;
}
if (stricmp(szCTCPCmd + 1, "VERSION") == 0) {
// reply with a notice version & copyright
// sprintf(szTempLine,"NOTICE %s :\001VERSION Copyright(c)\001\n",szNick);
return nullptr;
}
strcpy(szRemLine, 1 + GetWordNum(0, Line + iPrefixLen + strlen(szCmd) + strlen(szTarget) + 4));
szRemLine[strlen(szRemLine) - 1] = '\0';
snprintf(szResponse, sizeof(szResponse), "** CTCP Message from %s (%s)", szNick, szRemLine);
return nullptr; // szResponse;
}
// differentiate between channel and private
if ((szTarget[0] == '#') || (szTarget[0] == '+')) {
pszTempStr = GetWordNum(0, szRemLine);
snprintf(szResponse, sizeof(szResponse), "\1\xff\xff\xff[%s] %s", szNick, pszTempStr);
return szResponse;
} else {
if (iMode == MSG_LOCAL) {
pszTempStr = GetWordNum(0, szRemLine);
snprintf(szResponse, sizeof(szResponse), TXT_PXO_PRIVMSGTO, szTarget, pszTempStr);
} else {
pszTempStr = GetWordNum(0, szRemLine);
snprintf(szResponse, sizeof(szResponse), TXT_PXO_PRIVMSGFROM, szNick, pszTempStr);
}
return szResponse;
}
}
// don't handle any other messages locally.
if (iMode == MSG_LOCAL) {
return nullptr;
}
if (stricmp(szCmd, "NOTICE") == 0) {
pszTempStr = GetWordNum(0, szRemLine);
strcpy(szTarget, pszTempStr);
strcpy(szRemLine, Line + iPrefixLen + strlen(szCmd) + strlen(szTarget) + 4);
if (szRemLine[0] == ':') {
strcpy(szCTCPCmd, GetWordNum(0, szRemLine + 1));
if (szCTCPCmd[strlen(szCTCPCmd) - 1] == 0x01)
szCTCPCmd[strlen(szCTCPCmd) - 1] = 0x00;
} else {
strcpy(szCTCPCmd, GetWordNum(0, szRemLine));
if (szCTCPCmd[strlen(szCTCPCmd) - 1] == 0x01)
szCTCPCmd[strlen(szCTCPCmd) - 1] = 0x00;
}
if (szCTCPCmd[0] == 0x01) {
// Handle ctcp message
strcpy(szRemLine, Line + iPrefixLen + strlen(szCmd) + strlen(szTarget) + strlen(szCTCPCmd) + 6);
szRemLine[strlen(szRemLine) - 1] = '\0'; // null out the ending 0x01
if (stricmp(szCTCPCmd + 1, "PING") == 0) {
// This is a ping response, figure out time and print
// sprintf(NewMsg.Message,"** Ping Response from %s: %ums",szNick,ulping);
return nullptr;
}
// Default message
strcpy(szRemLine, 1 + GetWordNum(0, Line + iPrefixLen + strlen(szCmd) + strlen(szTarget) + 4));
szRemLine[strlen(szRemLine) - 1] = '\0';
snprintf(szResponse, sizeof(szResponse), "** CTCP Message from %s (%s)", szNick, szRemLine);
return nullptr; // szResponse;
}
strncpy(szResponse, szRemLine, sizeof(szResponse));
return nullptr;
}
if (stricmp(szCmd, "JOIN") == 0) {
// see if it is me!
if (stricmp(Nick_name, szNick) == 0) {
// Yup, it's me!
// if(stricmp(szChat_channel,GetWordNum(0,szRemLine))==0)
//{
Joined_channel = 1;
// if(stricmp(szChat_channel,"#autoselect")==0)
//{
strcpy(szChat_channel, GetWordNum(0, szRemLine));
AddChatCommandToQueue(CC_YOURCHANNEL, szChat_channel, strlen(szChat_channel) + 1);
//}
// CC_YOURCHANNEL
//}
}
AddChatUser(szNick);
pszTempStr = GetWordNum(0, szRemLine);
strcpy(szTarget, pszTempStr);
// strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+3);
// strcpy(NewMsg.Channel,szTarget);
AddChatUser(szNick);
snprintf(szResponse, sizeof(szResponse), "** %s has joined %s", szNick, szTarget);
return nullptr; // szResponse;
// Add them to the userlist too!
}
if (stricmp(szCmd, "PART") == 0) {
pszTempStr = GetWordNum(0, szRemLine);
strcpy(szTarget, pszTempStr);
strcpy(szRemLine, Line + iPrefixLen + strlen(szCmd) + strlen(szTarget) + 3);
// see if it is me!
if (stricmp(Nick_name, szNick) == 0) {
// Yup, it's me!
// szChat_channel[0]=NULL;
RemoveAllChatUsers();
}
RemoveChatUser(szNick);
return nullptr;
// Remove them to the userlist too!
}
if (stricmp(szCmd, "KICK") == 0) {
pszTempStr = GetWordNum(0, szRemLine);
strcpy(szTarget, pszTempStr);
pszTempStr = GetWordNum(1, szRemLine);
strcpy(szHackPrefix, pszTempStr);
pszTempStr = GetWordNum(2, szRemLine);
// see if it is me!
if (stricmp(Nick_name, GetWordNum(1, szRemLine)) == 0) {
// Yup, it's me!
szChat_channel[0] = '\0';
// bNewStatus=1;
AddChatCommandToQueue(CC_KICKED, nullptr, 0);
RemoveAllChatUsers();
}
// sprintf(szResponse,"*** %s has kicked %s from channel %s (%s)",szNick,szHackPrefix,szTarget,pszTempStr);
// Remove them to the userlist too!
RemoveChatUser(szNick);
return nullptr; // szResponse;
}
if (stricmp(szCmd, "NICK") == 0) {
// see if it is me!
if (stricmp(Nick_name, szNick) == 0) {
// Yup, it's me!
strcpy(Nick_name, GetWordNum(0, szRemLine));
}
char nicks[70];
snprintf(nicks, sizeof(nicks), "%s %s", szNick, GetWordNum(0, szRemLine));
AddChatCommandToQueue(CC_NICKCHANGED, nicks, strlen(nicks) + 1);
RemoveChatUser(szNick);
AddChatUser(GetWordNum(0, szRemLine));
// sprintf(szResponse,"*** %s is now known as %s",szNick,GetWordNum(0,szRemLine));
return nullptr; // szResponse;
}
if (stricmp(szCmd, "PING") == 0) {
// respond with pong (GetWordNum(0,szRemLine))
snprintf(szResponse, sizeof(szResponse), "/PONG :%s", GetWordNum(0, szRemLine));
SendChatString(szResponse, 1);
return nullptr;
}
if (stricmp(szCmd, "MODE") == 0) {
// Channel Mode info
return nullptr;
}
if (stricmp(szCmd, "401") == 0) {
// This is whois user info, we can get their tracker info from here. -5
char szWhoisUser[33];
strcpy(szWhoisUser, GetWordNum(1, szRemLine));
Getting_user_tracker_error = 1;
Getting_user_channel_error = 1;
snprintf(szResponse, sizeof(szResponse), TXT_PXO_ERRORNOTONLINE, szWhoisUser);
return szResponse;
}
if (stricmp(szCmd, "311") == 0) {
char szWhoisUser[33];
strcpy(szWhoisUser, GetWordNum(1, szRemLine));
// This is whois user info, we can get their tracker info from here. -5
// if(stricmp(Getting_user_tracker_info_for,szWhoisUser)==0)
//{
strcpy(User_req_tracker_id, GetWordNum(5, szRemLine));
//}
return nullptr;
}
if (stricmp(szCmd, "319") == 0) {
char szWhoisUser[33];
strcpy(szWhoisUser, GetWordNum(1, szRemLine));
// This is whois channel info -- what channel they are on -2
// if(stricmp(Getting_user_channel_info_for,szWhoisUser)==0)
//{
strcpy(User_req_channel, GetWordNum(2, szRemLine));
//}
return nullptr;
}
// End of whois and we didn't get a channel means they aren't in a channel.
if (stricmp(szCmd, "318") == 0) {
if (!*User_req_channel) {
User_req_channel[0] = '*';
}
}
if (stricmp(szCmd, "321") == 0) {
// start of channel list
FlushChannelList();
GettingChannelList = 1;
return nullptr;
}
if (stricmp(szCmd, "322") == 0) {
// channel list data
if (GettingChannelList == 1) {
char channel_list_name[33];
char sztopic[200];
strcpy(sztopic, GetWordNum(3, szRemLine));
strcpy(channel_list_name, GetWordNum(1, szRemLine));
AddChannel(channel_list_name, atoi(GetWordNum(2, szRemLine)), sztopic);
}
return nullptr;
}
if (stricmp(szCmd, "323") == 0) {
// end of channel list
GettingChannelList = 2;
return nullptr;
}
if (stricmp(szCmd, "324") == 0) {
// Channel Mode info
return nullptr;
}
if (stricmp(szCmd, "332") == 0) {
// Channel Topic, update status bar.
if (stricmp(szChat_channel, szTarget) == 0) {
// strncpy(szChanTopic,GetWordNum(2,szRemLine),70);
}
// sprintf(NewMsg.Message,"*** %s has changed the topic to: %s",szNick,GetWordNum(2,szRemLine));
return nullptr;
}
if (stricmp(szCmd, "TOPIC") == 0) {
// Channel Topic, update status bar.
if (stricmp(szChat_channel, szTarget) == 0) {
// strncpy(szChanTopic,GetWordNum(1,szRemLine),70);
}
// sprintf(NewMsg.Message,"*** %s has changed the topic to: %s",szNick,GetWordNum(1,szRemLine));
return nullptr;
}
if (stricmp(szCmd, "QUIT") == 0) {
// Remove the user!
RemoveChatUser(szNick);
return nullptr;
}
if (stricmp(szCmd, "376") == 0) // end of motd, trigger autojoin...
{
if (!Chat_server_connected) {
DLLmprintf(0, "Connected to chat server!\n");
Chat_server_connected = 1;
// We want to make sure we know our nick. This is somewhat of a hack
strcpy(Nick_name, GetWordNum(0, szRemLine + 1));
}
return nullptr;
}
if ((stricmp(szCmd, "377") == 0) || (stricmp(szCmd, "372") == 0) || (stricmp(szCmd, "372") == 0)
) {
// Stip the message, and display it.
pszTempStr = GetWordNum(3, Line);
snprintf(szResponse, sizeof(szResponse), "\1\xcf\xf8\xb9%s", pszTempStr);
return szResponse;
}
// Ignore these messages
if (((stricmp(szCmd, "366") == 0)) || (stricmp(szCmd, "333") == 0) || // Who set the topic
(stricmp(szCmd, "329") == 0)) // Time Channel created
/*
(stricmp(szCmd,"305")==0) ||
(stricmp(szCmd,"306")==0) ||
(stricmp(szCmd,"311")==0) || //WHOIS stuff
(stricmp(szCmd,"312")==0) ||
(stricmp(szCmd,"313")==0) ||
(stricmp(szCmd,"317")==0) ||
(stricmp(szCmd,"318")==0) ||
(stricmp(szCmd,"319")==0) ||
*/
{
return nullptr;
}
if (stricmp(szCmd, "353") == 0) {
// Names in the channel.
pszTempStr = GetWordNum(3, Line + iPrefixLen + strlen(szCmd) + 2);
strcpy(szRemLine, pszTempStr);
pszTempStr = strtok(szRemLine, " ");
while (pszTempStr) {
if (pszTempStr[0] == '@') {
AddChatUser(pszTempStr + 1);
} else if (pszTempStr[0] == '+') {
AddChatUser(pszTempStr + 1);
} else {
AddChatUser(pszTempStr);
}
pszTempStr = strtok(nullptr, " ");
}
return nullptr;
}
// MOTD Codes
if ((stricmp(szCmd, "001") == 0) || (stricmp(szCmd, "002") == 0) || (stricmp(szCmd, "003") == 0) ||
(stricmp(szCmd, "004") == 0) || (stricmp(szCmd, "251") == 0) || (stricmp(szCmd, "254") == 0) ||
(stricmp(szCmd, "255") == 0) || (stricmp(szCmd, "265") == 0) || (stricmp(szCmd, "375") == 0) ||
(stricmp(szCmd, "372") == 0) || (stricmp(szCmd, "375") == 0)) {
// Stip the message, and display it.
pszTempStr = GetWordNum(3, Line);
strcpy(szResponse, pszTempStr);
return nullptr;
// return szResponse;
}
if (stricmp(szCmd, "432") == 0) {
// Channel Mode info
snprintf(szResponse, sizeof(szResponse), "%s", TXT_PXO_BADNICK);
AddChatCommandToQueue(CC_DISCONNECTED, nullptr, 0);
return szResponse;
}
if (stricmp(szCmd, "433") == 0) {
// Channel Mode info
char new_nick[33];
snprintf(new_nick, sizeof(new_nick), "%s%d", Orignial_nick_name, Nick_variety);
strcpy(Nick_name, new_nick);
Nick_variety++;
snprintf(szResponse, sizeof(szResponse), "/NICK %s", new_nick);
SendChatString(szResponse, 1);
return nullptr;
}
// Default print
strcpy(szResponse, Line);
// return szResponse;
return nullptr;
}
void AddChatCommandToQueue(int command, const void *data, int len) {
Currcommand = Firstcommand;
if (Firstcommand == nullptr) {
Firstcommand = (Chat_command *)DLLmem_malloc(sizeof(Chat_command));
// ASSERT(Firstcommand);
Firstcommand->next = nullptr;
Currcommand = Firstcommand;
} else {
while (Currcommand->next) {
Currcommand = Currcommand->next;
}
Currcommand->next = (Chat_command *)DLLmem_malloc(sizeof(Chat_command));
// ASSERT(Currcommand->next);
Currcommand = Currcommand->next;
}
Currcommand->command = command;
if (len && data)
memcpy(&Currcommand->data, data, len);
Currcommand->next = nullptr;
}
Chat_command *GetChatCommandFromQueue() {
static Chat_command response_cmd;
Chat_command *tmp_cmd;
if (!Firstcommand)
return nullptr;
Currcommand = Firstcommand;
memcpy(&response_cmd, Currcommand, sizeof(Chat_command));
tmp_cmd = Currcommand->next;
DLLmem_free(Firstcommand);
Firstcommand = tmp_cmd;
return &response_cmd;
}
void FlushChatCommandQueue() {
Chat_command *tmp_cmd;
Currcommand = Firstcommand;
while (Currcommand) {
tmp_cmd = Currcommand->next;
DLLmem_free(Currcommand);
Currcommand = tmp_cmd;
}
Firstcommand = nullptr;
}
void FlushChannelList() {
Chat_channel *tmp_chan;
Currchannel = Firstchannel;
while (Currchannel) {
tmp_chan = Currchannel->next;
DLLmem_free(Currchannel);
Currchannel = tmp_chan;
}
Firstchannel = nullptr;
}
char *GetChannelList() {
int ichan_list_length = 0;
char sznumusers[10];
if (GettingChannelList != 2)
return nullptr;
if (!Socket_connected)
return nullptr;
if (Chan_list) {
DLLmem_free(Chan_list);
Chan_list = nullptr;
}
Currchannel = Firstchannel;
while (Currchannel) {
ichan_list_length += strlen(Currchannel->topic) + 1 + strlen(Currchannel->channel_name) + 1 +
5; // 1 for the space, and 4 for the number of users 0000-9999 + space
Currchannel = Currchannel->next;
}
Currchannel = Firstchannel;
Chan_list = (char *)DLLmem_malloc(ichan_list_length + 1);
Chan_list[0] = '\0';
while (Currchannel) {
strcat(Chan_list, "$");
strcat(Chan_list, Currchannel->channel_name);
strcat(Chan_list, " ");
snprintf(sznumusers, sizeof(sznumusers), "%d ", Currchannel->users);
strcat(Chan_list, sznumusers);
strcat(Chan_list, Currchannel->topic); // fgets
strcat(Chan_list, " ");
Currchannel = Currchannel->next;
}
FlushChannelList();
GettingChannelList = 0;
return Chan_list;
}
void AddChannel(char *channel, uint16_t numusers, char *topic) {
Currchannel = Firstchannel;
if (Firstchannel == nullptr) {
Firstchannel = (Chat_channel *)DLLmem_malloc(sizeof(Chat_channel));
// ASSERT(Firstchannel);
strcpy(Firstchannel->channel_name, channel);
strcpy(Firstchannel->topic, topic);
Firstchannel->users = numusers;
Firstchannel->next = nullptr;
Currchannel = Firstchannel;
} else {
while (Currchannel->next) {
Currchannel = Currchannel->next;
}
Currchannel->next = (Chat_channel *)DLLmem_malloc(sizeof(Chat_channel));
// ASSERT(Currchannel->next);
Currchannel = Currchannel->next;
strcpy(Currchannel->channel_name, channel);
strcpy(Currchannel->topic, topic);
Currchannel->users = numusers;
}
Currchannel->next = nullptr;
}
char *GetTrackerIdByUser(const char *nickname) {
char szWhoisCmd[100];
if (nickname == (char *)-1) {
GettingUserTID = 0;
Getting_user_tracker_error = 0;
return nullptr;
} else if (GettingUserTID) {
if (Getting_user_tracker_error) {
Getting_user_tracker_error = 0;
GettingUserTID = 0;
return (char *)-1;
}
if (*User_req_tracker_id) {
GettingUserTID = 0;
return User_req_tracker_id;
}
} else {
strcpy(Getting_user_tracker_info_for, nickname);
snprintf(szWhoisCmd, sizeof(szWhoisCmd), "/WHOIS %s", nickname);
User_req_tracker_id[0] = '\0';
SendChatString(szWhoisCmd, 1);
GettingUserTID = 1;
}
return nullptr;
}
char *GetChannelByUser(const char *nickname) {
char szWhoisCmd[100];
if (nickname == (char *)-1) {
GettingUserChannel = 0;
Getting_user_channel_error = 0;
return nullptr;
} else if (GettingUserChannel) {
if (Getting_user_channel_error) {
Getting_user_channel_error = 0;
GettingUserChannel = 0;
return (char *)-1;
}
if (*User_req_channel) {
GettingUserChannel = 0;
return User_req_channel;
}
} else {
strcpy(Getting_user_channel_info_for, nickname);
User_req_channel[0] = '\0';
snprintf(szWhoisCmd, sizeof(szWhoisCmd), "/WHOIS %s", nickname);
SendChatString(szWhoisCmd, 1);
GettingUserChannel = 1;
}
return nullptr;
}