mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
1033 lines
20 KiB
C
1033 lines
20 KiB
C
![]() |
/*
|
||
|
GameSpy Chat SDK
|
||
|
Dan "Mr. Pants" Schoenblum
|
||
|
dan@gamespy.com
|
||
|
|
||
|
Copyright 1999-2007 GameSpy Industries, Inc
|
||
|
|
||
|
devsupport@gamespy.com
|
||
|
*/
|
||
|
|
||
|
/*************
|
||
|
** INCLUDES **
|
||
|
*************/
|
||
|
#include <stdio.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <string.h>
|
||
|
#include <limits.h>
|
||
|
#include <stdlib.h>
|
||
|
#include "chatMain.h"
|
||
|
#include "chatSocket.h"
|
||
|
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
// Silence the "conditional expression is constant" on the FD_SET macros
|
||
|
#pragma warning(disable:4127)
|
||
|
#endif
|
||
|
|
||
|
/************
|
||
|
** DEFINES **
|
||
|
************/
|
||
|
#define BUFFER_INC 8192
|
||
|
#define RECV_LEN 4096
|
||
|
|
||
|
/***********
|
||
|
** MACROS **
|
||
|
***********/
|
||
|
#define ASSERT_SOCK(sock) {\
|
||
|
assert((sock) != NULL);\
|
||
|
assert(((sock)->connectState == ciNotConnected) ||\
|
||
|
((sock)->connectState == ciConnected) ||\
|
||
|
((sock)->connectState == ciDisconnected));\
|
||
|
ASSERT_BUFFER(&(sock)->inputQueue);\
|
||
|
ASSERT_BUFFER(&(sock)->outputQueue);\
|
||
|
}
|
||
|
|
||
|
#define ASSERT_CONNECTED(sock) assert((sock)->connectState == ciConnected)
|
||
|
|
||
|
#define ASSERT_BUFFER(buffer) {\
|
||
|
assert((buffer) != NULL);\
|
||
|
assert((buffer)->size >= 0);\
|
||
|
assert(((buffer)->size % BUFFER_INC) == 0);\
|
||
|
assert((buffer)->length >= 0);\
|
||
|
assert((buffer)->length <= (buffer)->size);\
|
||
|
}
|
||
|
#define RESET(ptr) {if (ptr) {gsifree(ptr); ptr = NULL;} }
|
||
|
|
||
|
/*********
|
||
|
** TIME **
|
||
|
*********/
|
||
|
#ifdef IRC_LOG
|
||
|
static const char * ciGetTime(void)
|
||
|
{
|
||
|
#if defined(UNDER_CE) || defined(_PS2) || defined(_NITRO)
|
||
|
return "";
|
||
|
#else
|
||
|
static char buffer[256];
|
||
|
time_t timer;
|
||
|
struct tm * now;
|
||
|
|
||
|
timer = time(NULL);
|
||
|
now = localtime(&timer);
|
||
|
if(now) // fixes the date > 2060 crash (23apr03/bgw)
|
||
|
{
|
||
|
if(now->tm_year > 99)
|
||
|
now->tm_year -= 100;
|
||
|
sprintf(buffer, "%02d.%02d.%02d %02d:%02d.%02d", now->tm_mon + 1, now->tm_mday, now->tm_year, now->tm_hour, now->tm_min, now->tm_sec);
|
||
|
}
|
||
|
else
|
||
|
strcpy(buffer, "00.00.00 00:00.00");
|
||
|
|
||
|
return buffer;
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/***********
|
||
|
** BUFFER **
|
||
|
***********/
|
||
|
static CHATBool ciBufferInit(ciBuffer * buffer)
|
||
|
{
|
||
|
assert(buffer != NULL);
|
||
|
|
||
|
buffer->length = 0;
|
||
|
buffer->size = BUFFER_INC;
|
||
|
buffer->buffer = (char *)gsimalloc((unsigned int)buffer->size + 1);
|
||
|
if(buffer->buffer == NULL)
|
||
|
return CHATFalse;
|
||
|
|
||
|
// Just for fun.
|
||
|
////////////////
|
||
|
buffer->buffer[0] = '\0';
|
||
|
|
||
|
return CHATTrue;
|
||
|
}
|
||
|
|
||
|
static void ciBufferFree(ciBuffer * buffer)
|
||
|
{
|
||
|
gsifree(buffer->buffer);
|
||
|
}
|
||
|
|
||
|
static CHATBool ciBufferPreAppend(ciBuffer * buffer, int len)
|
||
|
{
|
||
|
int total;
|
||
|
char * tempPtr;
|
||
|
|
||
|
ASSERT_BUFFER(buffer);
|
||
|
assert(len >= 0);
|
||
|
assert(len <= SHRT_MAX); // sanity check
|
||
|
|
||
|
// Check if the buffer is big enough.
|
||
|
/////////////////////////////////////
|
||
|
total = (buffer->length + len);
|
||
|
if(total <= buffer->size)
|
||
|
return CHATTrue;
|
||
|
|
||
|
// Figure out the new size.
|
||
|
///////////////////////////
|
||
|
total += BUFFER_INC;
|
||
|
total -= (total % BUFFER_INC);
|
||
|
|
||
|
// Allocate the memory.
|
||
|
///////////////////////
|
||
|
tempPtr = (char *)gsirealloc(buffer->buffer, (unsigned int)total + 1);
|
||
|
if(tempPtr == NULL)
|
||
|
return CHATFalse;
|
||
|
|
||
|
// Update the buffer.
|
||
|
/////////////////////
|
||
|
buffer->buffer = tempPtr;
|
||
|
buffer->size = total;
|
||
|
|
||
|
return CHATTrue;
|
||
|
}
|
||
|
|
||
|
static void ciBufferClipFront(ciBuffer * buffer, int len)
|
||
|
{
|
||
|
ASSERT_BUFFER(buffer);
|
||
|
assert(len >= 0);
|
||
|
assert(len <= buffer->length);
|
||
|
|
||
|
buffer->length -= len;
|
||
|
memmove(buffer->buffer, &buffer->buffer[len], (unsigned int)buffer->length);
|
||
|
buffer->buffer[buffer->length] = '\0';
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef __MWERKS__ // CodeWarrior will warn if not prototyped
|
||
|
/***************
|
||
|
** PROTOTYPES **
|
||
|
****************/
|
||
|
CHATBool ciParseParam(const char *pText, ciServerMessage * message);
|
||
|
#endif
|
||
|
|
||
|
/**************
|
||
|
** FUNCTIONS **
|
||
|
**************/
|
||
|
CHATBool ciSocketInit(ciSocket * sock, const char * nick)
|
||
|
{
|
||
|
#ifdef IRC_LOG
|
||
|
FILE * log;
|
||
|
#endif
|
||
|
|
||
|
assert(sock != NULL);
|
||
|
|
||
|
memset(sock, 0, sizeof(ciSocket));
|
||
|
|
||
|
sock->sock = INVALID_SOCKET;
|
||
|
if(!ciBufferInit(&sock->inputQueue))
|
||
|
return CHATFalse;
|
||
|
if(!ciBufferInit(&sock->outputQueue))
|
||
|
{
|
||
|
ciBufferFree(&sock->inputQueue);
|
||
|
return CHATFalse;
|
||
|
}
|
||
|
#ifdef IRC_LOG
|
||
|
sprintf(sock->filename, "%s_irc.log", nick);
|
||
|
log = fopen(sock->filename, "at");
|
||
|
if(log != NULL)
|
||
|
{
|
||
|
fprintf(log, "\n\n\n\n\nCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n");
|
||
|
fclose(log);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
GSI_UNUSED(nick);
|
||
|
return CHATTrue;
|
||
|
}
|
||
|
|
||
|
CHATBool ciSocketConnect(ciSocket * sock,
|
||
|
const char * serverAddress,
|
||
|
int port)
|
||
|
{
|
||
|
unsigned int ip;
|
||
|
HOSTENT * host;
|
||
|
SOCKADDR_IN address;
|
||
|
int rcode;
|
||
|
|
||
|
#if !defined(INSOCK) && !defined(_NITRO) && !defined(_REVOLUTION)
|
||
|
int keepalive;
|
||
|
#endif
|
||
|
|
||
|
ASSERT_SOCK(sock);
|
||
|
assert(serverAddress != NULL);
|
||
|
assert(port >= 0);
|
||
|
assert(port <= USHRT_MAX);
|
||
|
assert(sock->connectState == ciNotConnected);
|
||
|
|
||
|
// Copy off the address.
|
||
|
////////////////////////
|
||
|
strzcpy(sock->serverAddress, serverAddress, 255);
|
||
|
|
||
|
// Try resolving the string as an IP a.b.c.d number.
|
||
|
////////////////////////////////////////////////////
|
||
|
ip = inet_addr(serverAddress);
|
||
|
if(ip == INADDR_NONE)
|
||
|
{
|
||
|
// Try resolving with DNS.
|
||
|
//////////////////////////
|
||
|
host = gethostbyname((char *)serverAddress);
|
||
|
if(host == NULL)
|
||
|
return CHATFalse;
|
||
|
|
||
|
// Get the ip.
|
||
|
//////////////
|
||
|
ip = *(unsigned int *)host->h_addr_list[0];
|
||
|
}
|
||
|
|
||
|
// Setup the address.
|
||
|
/////////////////////
|
||
|
memset(&address, 0, sizeof(SOCKADDR_IN));
|
||
|
address.sin_family = AF_INET;
|
||
|
address.sin_addr.s_addr = ip;
|
||
|
address.sin_port = htons((unsigned short)port);
|
||
|
|
||
|
// Create the socket.
|
||
|
/////////////////////
|
||
|
sock->sock = socket(AF_INET, SOCK_STREAM, 0);
|
||
|
if(sock->sock == INVALID_SOCKET)
|
||
|
return CHATFalse;
|
||
|
|
||
|
// Enable keep-alive.
|
||
|
/////////////////////
|
||
|
#if !defined(INSOCK) && !defined(_NITRO) && !defined(_REVOLUTION)
|
||
|
keepalive = 1;
|
||
|
rcode = setsockopt(sock->sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof(int));
|
||
|
//assert(gsiSocketIsNotError(rcode));
|
||
|
#endif
|
||
|
|
||
|
// Try and connect.
|
||
|
///////////////////
|
||
|
rcode = connect(sock->sock, (SOCKADDR *)&address, sizeof(SOCKADDR_IN));
|
||
|
if(gsiSocketIsError(rcode))
|
||
|
{
|
||
|
closesocket(sock->sock);
|
||
|
return CHATFalse;
|
||
|
}
|
||
|
|
||
|
// We're connected.
|
||
|
///////////////////
|
||
|
sock->connectState = ciConnected;
|
||
|
|
||
|
return CHATTrue;
|
||
|
}
|
||
|
|
||
|
void ciSocketDisconnect(ciSocket * sock)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
ASSERT_SOCK(sock);
|
||
|
|
||
|
// Shutdown the socket.
|
||
|
///////////////////////
|
||
|
if(sock->sock != INVALID_SOCKET)
|
||
|
{
|
||
|
shutdown(sock->sock, 2);
|
||
|
closesocket(sock->sock);
|
||
|
}
|
||
|
|
||
|
// We're disconnected.
|
||
|
//////////////////////
|
||
|
sock->connectState = ciDisconnected;
|
||
|
|
||
|
// gsifree the buffers.
|
||
|
////////////////////
|
||
|
ciBufferFree(&sock->inputQueue);
|
||
|
ciBufferFree(&sock->outputQueue);
|
||
|
|
||
|
// gsifree the last-message pointers.
|
||
|
//////////////////////////////////
|
||
|
gsifree(sock->lastMessage.message);
|
||
|
gsifree(sock->lastMessage.server);
|
||
|
gsifree(sock->lastMessage.nick);
|
||
|
gsifree(sock->lastMessage.user);
|
||
|
gsifree(sock->lastMessage.host);
|
||
|
gsifree(sock->lastMessage.command);
|
||
|
gsifree(sock->lastMessage.middle);
|
||
|
gsifree(sock->lastMessage.param);
|
||
|
for(i = 0 ; i < sock->lastMessage.numParams ; i++)
|
||
|
gsifree(sock->lastMessage.params[i]);
|
||
|
gsifree(sock->lastMessage.params);
|
||
|
}
|
||
|
|
||
|
static void ciSocketSelect(SOCKET sock, CHATBool * readFlag, CHATBool * writeFlag, CHATBool * exceptFlag)
|
||
|
{
|
||
|
int aReadFlag = 0;
|
||
|
int aWriteFlag = 0;
|
||
|
int aExceptFlag = 0;
|
||
|
|
||
|
// Call generic select GSISocketSelect
|
||
|
GSISocketSelect(sock, &aReadFlag, &aWriteFlag, &aExceptFlag);
|
||
|
|
||
|
// Translate the int flags to ChatBool flags
|
||
|
if (readFlag)
|
||
|
*readFlag = (CHATBool)aReadFlag;
|
||
|
if (writeFlag)
|
||
|
*writeFlag = (CHATBool)aWriteFlag;
|
||
|
if (exceptFlag)
|
||
|
*exceptFlag = (CHATBool)aExceptFlag;
|
||
|
}
|
||
|
|
||
|
static void ciSocketThinkSend(ciSocket * sock)
|
||
|
{
|
||
|
int rcode;
|
||
|
int len;
|
||
|
|
||
|
ASSERT_SOCK(sock);
|
||
|
ASSERT_CONNECTED(sock);
|
||
|
|
||
|
// While there's data to send...
|
||
|
////////////////////////////////
|
||
|
while(sock->outputQueue.length > 0)
|
||
|
{
|
||
|
CHATBool writeFlag;
|
||
|
|
||
|
// Can we send?
|
||
|
///////////////
|
||
|
ciSocketSelect(sock->sock, NULL, &writeFlag, NULL);
|
||
|
if(!writeFlag)
|
||
|
return;
|
||
|
|
||
|
// Try and send some.
|
||
|
/////////////////////
|
||
|
len = min(sock->outputQueue.length, 1024);
|
||
|
rcode = send(sock->sock, sock->outputQueue.buffer, len, 0);
|
||
|
|
||
|
if(rcode == 0)
|
||
|
return;
|
||
|
if(gsiSocketIsError(rcode))
|
||
|
return; //ERRCON
|
||
|
|
||
|
// Update the output queue.
|
||
|
///////////////////////////
|
||
|
ciBufferClipFront(&sock->outputQueue, rcode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void ciSocketThinkRecv(ciSocket * sock)
|
||
|
{
|
||
|
CHATBool readFlag;
|
||
|
int len;
|
||
|
int rcode;
|
||
|
char * pos;
|
||
|
|
||
|
ASSERT_SOCK(sock);
|
||
|
ASSERT_CONNECTED(sock);
|
||
|
|
||
|
while(CHATTrue)
|
||
|
{
|
||
|
// Check the read flag.
|
||
|
///////////////////////
|
||
|
ciSocketSelect(sock->sock, &readFlag, NULL, NULL);
|
||
|
|
||
|
// Nothing to read?
|
||
|
///////////////////
|
||
|
if(!readFlag)
|
||
|
return;
|
||
|
|
||
|
// Make sure the buffer is ready.
|
||
|
/////////////////////////////////
|
||
|
if(!ciBufferPreAppend(&sock->inputQueue, RECV_LEN))
|
||
|
return; //ERRCON
|
||
|
|
||
|
// Recv into the buffer.
|
||
|
////////////////////////
|
||
|
pos = &sock->inputQueue.buffer[sock->inputQueue.length];
|
||
|
rcode = recv(sock->sock, pos, RECV_LEN, 0);
|
||
|
|
||
|
// Connection closed?
|
||
|
/////////////////////
|
||
|
if(rcode <= 0) //crt -- handle remote disconnections
|
||
|
{
|
||
|
sock->connectState = ciDisconnected;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Set the number of bytes read.
|
||
|
////////////////////////////////
|
||
|
len = rcode;
|
||
|
|
||
|
#if 0
|
||
|
{
|
||
|
char * buffer;
|
||
|
int i;
|
||
|
buffer = (char *)gsimalloc(len + 2);
|
||
|
assert(buffer != NULL);
|
||
|
memcpy(buffer, &sock->inputQueue.buffer[sock->inputQueue.length], len);
|
||
|
buffer[len] = '\0';
|
||
|
OutputDebugString("--->>>XXXRECV\n");
|
||
|
for(i = 0 ; i < len ; i += 1023)
|
||
|
OutputDebugString(&buffer[i]);
|
||
|
OutputDebugString("<<<---XXXRECV\n");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Decrypt data if the socket is secure.
|
||
|
////////////////////////////////////////
|
||
|
if(sock->secure)
|
||
|
gs_crypt((unsigned char *)pos, len, &sock->inKey);
|
||
|
|
||
|
// Update the buffer length.
|
||
|
////////////////////////////
|
||
|
sock->inputQueue.length += len;
|
||
|
|
||
|
// NUL terminate the buffer.
|
||
|
////////////////////////////
|
||
|
sock->inputQueue.buffer[sock->inputQueue.length] = '\0';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ciSocketThink(ciSocket * sock)
|
||
|
{
|
||
|
ASSERT_SOCK(sock);
|
||
|
ASSERT_CONNECTED(sock);
|
||
|
|
||
|
// Disconnected?
|
||
|
////////////////
|
||
|
if(sock->connectState == ciDisconnected)
|
||
|
return;
|
||
|
|
||
|
// Send any waiting data.
|
||
|
/////////////////////////
|
||
|
ciSocketThinkSend(sock);
|
||
|
|
||
|
// Recv any waiting data.
|
||
|
/////////////////////////
|
||
|
ciSocketThinkRecv(sock);
|
||
|
}
|
||
|
|
||
|
CHATBool ciSocketSend(ciSocket * sock,
|
||
|
const char * buffer)
|
||
|
{
|
||
|
#ifdef IRC_LOG
|
||
|
FILE * log;
|
||
|
#endif
|
||
|
int len;
|
||
|
char * pos;
|
||
|
|
||
|
ASSERT_SOCK(sock);
|
||
|
ASSERT_CONNECTED(sock);
|
||
|
assert(buffer != NULL);
|
||
|
|
||
|
// Disconnected?
|
||
|
////////////////
|
||
|
if(sock->connectState == ciDisconnected)
|
||
|
return CHATTrue;
|
||
|
|
||
|
// Get the buffer length.
|
||
|
/////////////////////////
|
||
|
len = (int)strlen(buffer);
|
||
|
|
||
|
// Make sure the buffer is big enough.
|
||
|
//////////////////////////////////////
|
||
|
if(!ciBufferPreAppend(&sock->outputQueue, len + 2))
|
||
|
return CHATFalse;
|
||
|
|
||
|
// Append to the output buffer.
|
||
|
///////////////////////////////
|
||
|
pos = &sock->outputQueue.buffer[sock->outputQueue.length];
|
||
|
memcpy(pos, buffer, (unsigned int)len);
|
||
|
|
||
|
// Update the buffer length.
|
||
|
////////////////////////////
|
||
|
sock->outputQueue.length += len;
|
||
|
|
||
|
// Add the CRLF.
|
||
|
////////////////
|
||
|
sock->outputQueue.buffer[sock->outputQueue.length++] = 0x0D;
|
||
|
sock->outputQueue.buffer[sock->outputQueue.length++] = 0x0A;
|
||
|
|
||
|
// Encrypt data if the socket is secure.
|
||
|
////////////////////////////////////////
|
||
|
if(sock->secure)
|
||
|
gs_crypt((unsigned char *)pos, len + 2, &sock->outKey);
|
||
|
|
||
|
#ifdef IRC_LOG
|
||
|
// Write it to the log.
|
||
|
///////////////////////
|
||
|
log = fopen(sock->filename, "at");
|
||
|
if(log != NULL)
|
||
|
{
|
||
|
fprintf(log, "%s | OUT | %s\n", ciGetTime(), buffer);
|
||
|
fclose(log);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return CHATTrue;
|
||
|
}
|
||
|
|
||
|
CHATBool ciSocketSendf(ciSocket * sock,
|
||
|
const char * format,
|
||
|
...)
|
||
|
{
|
||
|
static char buffer[4096];
|
||
|
int num;
|
||
|
va_list args;
|
||
|
|
||
|
ASSERT_SOCK(sock);
|
||
|
ASSERT_CONNECTED(sock);
|
||
|
assert(buffer != NULL);
|
||
|
|
||
|
// Disconnected?
|
||
|
////////////////
|
||
|
if(sock->connectState == ciDisconnected)
|
||
|
return CHATTrue;
|
||
|
|
||
|
// Do the formatting.
|
||
|
/////////////////////
|
||
|
va_start(args, format);
|
||
|
num = vsprintf(buffer, format, args);
|
||
|
if(num != -1)
|
||
|
buffer[num] = '\0';
|
||
|
else
|
||
|
buffer[sizeof(buffer) - 1] = '\0'; //ERRCON
|
||
|
|
||
|
// Send.
|
||
|
////////
|
||
|
return ciSocketSend(sock, buffer);
|
||
|
}
|
||
|
|
||
|
static CHATBool ciParseUser(const char *pText, ciServerMessage * message)
|
||
|
{
|
||
|
char *pTmpNick = NULL, *pTmpUsername = NULL, *pTmpHost = NULL;
|
||
|
int nNick = 0, nUsername = 0, nHost = 0;
|
||
|
char *p;
|
||
|
|
||
|
if(pText == NULL || pText[0] == '\0')
|
||
|
{
|
||
|
assert(0);
|
||
|
return CHATFalse; //ERRCON
|
||
|
}
|
||
|
|
||
|
p = pTmpNick = (char *)pText;
|
||
|
|
||
|
while(*p != '\0')
|
||
|
{
|
||
|
if(*p != '!')
|
||
|
{
|
||
|
++p;
|
||
|
++nNick;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pTmpUsername = ++p;
|
||
|
|
||
|
while(*p != '\0')
|
||
|
{
|
||
|
if(*p != '@')
|
||
|
{
|
||
|
++p;
|
||
|
++nUsername;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pTmpHost = ++p;
|
||
|
|
||
|
while(*p != '\0')
|
||
|
{
|
||
|
++p;
|
||
|
++nHost;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(nNick)
|
||
|
{
|
||
|
message->nick = (char *)gsimalloc((unsigned int)nNick + 1);
|
||
|
|
||
|
if(message->nick)
|
||
|
{
|
||
|
memcpy(message->nick, pTmpNick, (unsigned int)nNick);
|
||
|
message->nick[nNick] = '\0';
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
message->nick = NULL;
|
||
|
|
||
|
if(nUsername)
|
||
|
{
|
||
|
message->user = (char *)gsimalloc((unsigned int)nUsername + 1);
|
||
|
|
||
|
if(message->user)
|
||
|
{
|
||
|
memcpy(message->user, pTmpUsername, (unsigned int)nUsername);
|
||
|
message->user[nUsername] = '\0';
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
message->user = NULL;
|
||
|
|
||
|
if(nHost)
|
||
|
{
|
||
|
message->host = (char *)gsimalloc((unsigned int)nHost + 1);
|
||
|
|
||
|
if(message->host)
|
||
|
{
|
||
|
memcpy(message->host, pTmpHost, (unsigned int)nHost);
|
||
|
message->host[nHost] = '\0';
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
message->host = NULL;
|
||
|
|
||
|
return CHATTrue;
|
||
|
}
|
||
|
|
||
|
static CHATBool ciAddParam(const char *param, ciServerMessage * message)
|
||
|
{
|
||
|
void * tempPtr;
|
||
|
|
||
|
// Reallocate the parameter array.
|
||
|
//////////////////////////////////
|
||
|
tempPtr = gsirealloc(message->params, sizeof(char *) * (message->numParams + 1));
|
||
|
if(tempPtr == NULL)
|
||
|
return CHATFalse; //ERRCON
|
||
|
message->params = (char **)tempPtr;
|
||
|
|
||
|
// Allocate mem for the param.
|
||
|
//////////////////////////////
|
||
|
tempPtr = gsimalloc(strlen(param) + 1);
|
||
|
if(tempPtr == NULL)
|
||
|
return CHATFalse; //ERRCON
|
||
|
|
||
|
// Copy the param.
|
||
|
//////////////////
|
||
|
strcpy((char *)tempPtr, param);
|
||
|
message->params[message->numParams++] = (char *)tempPtr;
|
||
|
|
||
|
return CHATTrue;
|
||
|
}
|
||
|
|
||
|
CHATBool ciParseParam(const char *pText, ciServerMessage * message)
|
||
|
{
|
||
|
char * colon;
|
||
|
char * str;
|
||
|
char * p;
|
||
|
|
||
|
assert(pText != NULL);
|
||
|
assert(message != NULL);
|
||
|
|
||
|
// Copy off the text.
|
||
|
/////////////////////
|
||
|
p = (char *)gsimalloc(strlen(pText) + 1);
|
||
|
if(p == NULL)
|
||
|
return CHATFalse; //ERRCON
|
||
|
strcpy(p, pText);
|
||
|
|
||
|
// Find the colon.
|
||
|
//////////////////
|
||
|
if(p[0] == ':')
|
||
|
{
|
||
|
p[0] = '\0';
|
||
|
colon = &p[1];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
colon = strstr(p, " :");
|
||
|
if(colon != NULL)
|
||
|
{
|
||
|
*colon = '\0';
|
||
|
colon += 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
str = strtok(p, " ");
|
||
|
while(str != NULL)
|
||
|
{
|
||
|
// Add the param.
|
||
|
/////////////////
|
||
|
if(!ciAddParam(str, message))
|
||
|
{
|
||
|
gsifree(p);
|
||
|
return CHATFalse; //ERRCON
|
||
|
}
|
||
|
|
||
|
// Get the next one.
|
||
|
////////////////////
|
||
|
str = strtok(NULL, " ");
|
||
|
}
|
||
|
|
||
|
if(colon != NULL)
|
||
|
{
|
||
|
// Add the last param.
|
||
|
//////////////////////
|
||
|
if(!ciAddParam(colon, message))
|
||
|
{
|
||
|
gsifree(p);
|
||
|
return CHATFalse; //ERRCON
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gsifree(p);
|
||
|
return CHATTrue;
|
||
|
}
|
||
|
|
||
|
static CHATBool ciParseMessage(ciSocket * sock, const char *sText)
|
||
|
{
|
||
|
// TRACE("ServM: Parse Data=%s\n", sText);
|
||
|
int nMessage = 0, nServer = 0, nCommand = 0, nMiddle = 0, nParam = 0;
|
||
|
char *p, *temp;
|
||
|
ciServerMessage * message = &sock->lastMessage;
|
||
|
|
||
|
if(sText == NULL || sText[0] == '\0')
|
||
|
{
|
||
|
assert(0);
|
||
|
return CHATFalse; //ERRCON
|
||
|
}
|
||
|
|
||
|
nMessage = (int)strlen(sText);
|
||
|
message->message = (char *)gsimalloc((unsigned int)nMessage + 1);
|
||
|
if(message->message == NULL)
|
||
|
return CHATFalse; //ERRCON
|
||
|
memcpy(message->message, sText, (unsigned int)nMessage);
|
||
|
message->message[nMessage] = '\0';
|
||
|
|
||
|
p = (char *)sText;
|
||
|
|
||
|
while(*p == '\n' || *p == '\r')
|
||
|
++p;
|
||
|
|
||
|
if(*p == ':')
|
||
|
{ // server
|
||
|
message->server = ++p; // ?? BUGBUG
|
||
|
|
||
|
if(*p != '\0')
|
||
|
{
|
||
|
while(*p != ' ' && *p != '\0')
|
||
|
{
|
||
|
++nServer;
|
||
|
++p;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while(*p == ' ')
|
||
|
// skip spaces
|
||
|
++p;
|
||
|
|
||
|
if(*p != '\0')
|
||
|
{ // command
|
||
|
message->command = p;
|
||
|
|
||
|
while(*p != ' ' && *p != '\0')
|
||
|
{
|
||
|
++nCommand;
|
||
|
++p;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while(*p == ' ')
|
||
|
// skip spaces
|
||
|
++p;
|
||
|
|
||
|
if(*p != ':' && *p != '\0')
|
||
|
{ // middle
|
||
|
message->middle = p;
|
||
|
|
||
|
while(*p != ' ' && *p != '\0')
|
||
|
{
|
||
|
++nMiddle;
|
||
|
++p;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while(*p == ' ')
|
||
|
// skip spaces
|
||
|
++p;
|
||
|
|
||
|
//if(*p == ':')
|
||
|
// params delimiter
|
||
|
// ++p;
|
||
|
|
||
|
if(*p != '\0')
|
||
|
{ // params
|
||
|
message->param = p;
|
||
|
|
||
|
while(*p != '\0')
|
||
|
{
|
||
|
++nParam;
|
||
|
++p;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(nServer)
|
||
|
{
|
||
|
temp = message->server;
|
||
|
message->server = (char *)gsimalloc((unsigned int)nServer + 1);
|
||
|
|
||
|
if(message->server)
|
||
|
{
|
||
|
memcpy(message->server, temp, (unsigned int)nServer);
|
||
|
message->server[nServer] = '\0';
|
||
|
}
|
||
|
|
||
|
if(!ciParseUser(message->server, message))
|
||
|
{
|
||
|
RESET(message->message);
|
||
|
RESET(message->server);
|
||
|
return CHATFalse; //ERRCON
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
message->server = NULL;
|
||
|
message->nick = NULL;
|
||
|
message->user = NULL;
|
||
|
message->host = NULL;
|
||
|
}
|
||
|
|
||
|
if(nMiddle)
|
||
|
{
|
||
|
if(!ciParseParam(message->middle, message))
|
||
|
{
|
||
|
RESET(message->message);
|
||
|
RESET(message->server);
|
||
|
RESET(message->nick);
|
||
|
RESET(message->user);
|
||
|
RESET(message->host);
|
||
|
return CHATFalse; //ERRCON
|
||
|
}
|
||
|
}
|
||
|
else if(nParam)
|
||
|
{
|
||
|
if(!ciParseParam(message->param, message))
|
||
|
{
|
||
|
RESET(message->message);
|
||
|
RESET(message->server);
|
||
|
RESET(message->nick);
|
||
|
RESET(message->user);
|
||
|
RESET(message->host);
|
||
|
return CHATFalse; //ERRCON
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
message->params = NULL;
|
||
|
message->numParams = 0;
|
||
|
}
|
||
|
|
||
|
if(nParam)
|
||
|
{
|
||
|
temp = message->param;
|
||
|
message->param = (char *)gsimalloc((unsigned int)nParam + 1);
|
||
|
|
||
|
if(message->param)
|
||
|
{
|
||
|
memcpy(message->param, temp, (unsigned int)nParam);
|
||
|
message->param[nParam] = '\0';
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
message->param = NULL;
|
||
|
}
|
||
|
|
||
|
if(nCommand)
|
||
|
{
|
||
|
temp = message->command;
|
||
|
message->command = (char *)gsimalloc((unsigned int)nCommand + 1);
|
||
|
|
||
|
if(message->command)
|
||
|
{
|
||
|
memcpy(message->command, temp, (unsigned int)nCommand);
|
||
|
message->command[nCommand] = '\0';
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
message->command = NULL;
|
||
|
|
||
|
if(nMiddle)
|
||
|
{
|
||
|
temp = message->middle;
|
||
|
message->middle = (char *)gsimalloc((unsigned int)nMiddle + 1);
|
||
|
|
||
|
if(message->middle)
|
||
|
{
|
||
|
memcpy(message->middle, temp, (unsigned int)nMiddle);
|
||
|
message->middle[nMiddle] = '\0';
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
message->middle = NULL;
|
||
|
|
||
|
return CHATTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
static CHATBool ciParseInput(ciSocket * sock)
|
||
|
{
|
||
|
char *p, *q, *r;
|
||
|
char temp;
|
||
|
int i;
|
||
|
|
||
|
p = sock->inputQueue.buffer; // start
|
||
|
|
||
|
if(*p != '\0')
|
||
|
{
|
||
|
// eat all CRs & LFs
|
||
|
while(*p == 13 || *p == 10)
|
||
|
++p;
|
||
|
|
||
|
// end of string ?
|
||
|
if(*p != '\0')
|
||
|
{
|
||
|
r = q = p;
|
||
|
|
||
|
// everything between last-nonspace char and CRLF should be discarded
|
||
|
while(*q != 10 && *q != 13 && *q != '\0')
|
||
|
{
|
||
|
if(*q != ' ')
|
||
|
r = q;
|
||
|
++q;
|
||
|
}
|
||
|
|
||
|
if(*q != '\0')
|
||
|
{
|
||
|
++r;
|
||
|
temp = *r;
|
||
|
*r = '\0';
|
||
|
|
||
|
// gsifree the old message stuff.
|
||
|
//////////////////////////////
|
||
|
RESET(sock->lastMessage.message);
|
||
|
RESET(sock->lastMessage.server);
|
||
|
RESET(sock->lastMessage.nick);
|
||
|
RESET(sock->lastMessage.user);
|
||
|
RESET(sock->lastMessage.host);
|
||
|
RESET(sock->lastMessage.command);
|
||
|
RESET(sock->lastMessage.middle);
|
||
|
RESET(sock->lastMessage.param);
|
||
|
for(i = 0 ; i < sock->lastMessage.numParams ; i++)
|
||
|
RESET(sock->lastMessage.params[i]);
|
||
|
RESET(sock->lastMessage.params);
|
||
|
sock->lastMessage.numParams = 0;
|
||
|
|
||
|
// Parse the message.
|
||
|
/////////////////////
|
||
|
memset(&sock->lastMessage,0,sizeof(sock->lastMessage));
|
||
|
if(!ciParseMessage(sock, p))
|
||
|
{
|
||
|
memset(&sock->lastMessage,0,sizeof(sock->lastMessage));
|
||
|
return CHATFalse; //ERRCON
|
||
|
}
|
||
|
|
||
|
// Restore the temp character.
|
||
|
//////////////////////////////
|
||
|
*r = temp;
|
||
|
|
||
|
// Take the message out of the buffer.
|
||
|
//////////////////////////////////////
|
||
|
ciBufferClipFront(&sock->inputQueue, (q - sock->inputQueue.buffer));
|
||
|
|
||
|
return CHATTrue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return CHATFalse;
|
||
|
}
|
||
|
|
||
|
ciServerMessage * ciSocketRecv(ciSocket * sock)
|
||
|
{
|
||
|
#ifdef IRC_LOG
|
||
|
FILE * log;
|
||
|
#endif
|
||
|
ASSERT_SOCK(sock);
|
||
|
|
||
|
// Bill: 08-10-04
|
||
|
// There may be unprocessed messages in the queue
|
||
|
//if(sock->connectState == ciDisconnected)
|
||
|
// return NULL;
|
||
|
|
||
|
// Check for an empty buffer.
|
||
|
/////////////////////////////
|
||
|
if(sock->inputQueue.length == 0)
|
||
|
return NULL;
|
||
|
|
||
|
// Check for a message.
|
||
|
///////////////////////
|
||
|
if(!ciParseInput(sock))
|
||
|
{
|
||
|
// No message.
|
||
|
//////////////
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
#ifdef IRC_LOG
|
||
|
// Write it to the log.
|
||
|
///////////////////////
|
||
|
log = fopen(sock->filename, "at");
|
||
|
if(log != NULL)
|
||
|
{
|
||
|
fprintf(log, "%s | IN | %s\n", ciGetTime(), sock->lastMessage.message);
|
||
|
fclose(log);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Got a message.
|
||
|
/////////////////
|
||
|
return &sock->lastMessage;
|
||
|
}
|