openmohaa/code/gamespy/GP/gpiTransfer.c
2023-02-04 21:00:01 +01:00

2000 lines
47 KiB
C

/*
gpiTransfer.c
GameSpy Presence SDK
Dan "Mr. Pants" Schoenblum
Copyright 1999-2007 GameSpy Industries, Inc
devsupport@gamespy.com
***********************************************************************
Please see the GameSpy Presence SDK documentation for more information
**********************************************************************/
//INCLUDES
//////////
#include <stdlib.h>
#ifdef _WIN32
#include <sys/stat.h>
#endif
#include <string.h>
#include "gpi.h"
#define GPI_TRANSFER_VERSION 1
#define GPI_PEER_TIMEOUT_TIME (1 * 60000)
#define GPI_KEEPALIVE_TIME (4 * 60000)
//#define GPI_ACKNOWLEDGED_WINDOW (100000 * 1024)
#define GPI_DATA_SIZE (1 * 1024)
//#define GPI_CONFIRM_FILES
//FUNCTIONS
///////////
#ifndef NOFILE
static void gpiTransferFree(void * elem)
{
GPITransfer * transfer = (GPITransfer *)elem;
if(transfer->message)
freeclear(transfer->message);
if(transfer->baseDirectory)
gsifree(transfer->baseDirectory);
if(transfer->files)
{
ArrayFree(transfer->files);
transfer->files = NULL;
}
}
static int gpiTransferCompare(const void * elem1, const void * elem2)
{
GPITransfer * transfer1 = (GPITransfer *)elem1;
GPITransfer * transfer2 = (GPITransfer *)elem2;
return (transfer1->localID - transfer2->localID);
}
static void gpiFreeFile(void * elem)
{
GPIFile * file = (GPIFile *)elem;
gsifree(file->path);
gsifree(file->name);
#ifdef GSI_UNICODE
gsifree(file->path_W);
gsifree(file->name_W);
#endif
}
static GPResult
gpiFinishTransferMessage(
GPConnection * connection,
GPITransfer * transfer,
const char * message,
int len
)
{
GPResult result = gpiPeerFinishTransferMessage(connection, transfer->peer, message, len);
transfer->lastSend = current_time();
return result;
}
GPResult gpiInitTransfers(
GPConnection * connection
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
iconnection->transfers = ArrayNew(sizeof(GPITransfer), 0, gpiTransferFree);
if(!iconnection->transfers)
Error(connection, GP_MEMORY_ERROR, "Out of memory.");
return GP_NO_ERROR;
}
void gpiCleanupTransfers(
GPConnection * connection
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
// Free the transfers.
//////////////////////
if(iconnection->transfers)
{
ArrayFree(iconnection->transfers);
iconnection->transfers = NULL;
}
}
static GPResult gpiNewTransfer
(
GPConnection * connection,
GPITransfer ** transfer,
GPProfile profile,
GPIBool sender
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPITransfer transferTemp;
// Fill in the object.
//////////////////////
memset(&transferTemp, 0, sizeof(GPITransfer));
transferTemp.files = ArrayNew(sizeof(GPIFile), 0, gpiFreeFile);
if(!transferTemp.files)
Error(connection, GP_MEMORY_ERROR, "Out of memory.");
transferTemp.localID = iconnection->nextTransferID++;
transferTemp.sender = sender;
transferTemp.profile = profile;
transferTemp.throttle = -1;
transferTemp.currentFile = -1;
// Add it.
//////////
ArrayAppend(iconnection->transfers, &transferTemp);
// Get it.
//////////
*transfer = (GPITransfer *)ArrayNth(iconnection->transfers, ArrayLength(iconnection->transfers) - 1);
return GP_NO_ERROR;
}
GPResult gpiNewSenderTransfer
(
GPConnection * connection,
GPITransfer ** transfer,
GPProfile profile
)
{
GPITransfer * pTransfer;
GPIConnection * iconnection = (GPIConnection*)*connection;
unsigned long now;
CHECK_RESULT(gpiNewTransfer(connection, transfer, profile, GPITrue));
now = current_time();
pTransfer = *transfer;
pTransfer->state = GPITransferPinging;
pTransfer->transferID.profileid = iconnection->profileid;
pTransfer->transferID.count = pTransfer->localID;
pTransfer->transferID.time = (unsigned int)now;
pTransfer->lastSend = now;
return GP_NO_ERROR;
}
static GPResult gpiNewReceiverTransfer
(
GPConnection * connection,
GPITransfer ** transfer,
GPProfile profile,
GPITransferID * transferID
)
{
#ifdef WIN32
char buffer[FILENAME_MAX];
#endif
GPITransfer * pTransfer;
CHECK_RESULT(gpiNewTransfer(connection, transfer, profile, GPIFalse));
pTransfer = *transfer;
pTransfer->state = GPITransferWaiting;
memcpy(&pTransfer->transferID, transferID, sizeof(GPITransferID));
#ifdef WIN32
if(GetTempPathA(sizeof(buffer), buffer) != 0)
pTransfer->baseDirectory = goastrdup(buffer);
#endif
return GP_NO_ERROR;
}
void gpiFreeTransfer
(
GPConnection * connection,
GPITransfer * transfer
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
int pos;
// Find the transfer.
/////////////////////
pos = ArraySearch(iconnection->transfers, transfer, gpiTransferCompare, 0, 0);
assert(pos != NOT_FOUND);
if(pos == NOT_FOUND)
return;
// Remove it.
/////////////
ArrayDeleteAt(iconnection->transfers, pos);
}
void gpiCancelTransfer
(
GPConnection * connection,
GPITransfer * transfer
)
{
// Send the cancel message.
///////////////////////////
if(transfer->peer)
{
// Start the message.
/////////////////////
if(gpiPeerStartTransferMessage(connection, transfer->peer, GPI_BM_FILE_TRANSFER_CANCEL, (GPITransferID_st)&transfer->transferID) == GP_NO_ERROR)
{
// Finish the message.
//////////////////////
gpiFinishTransferMessage(connection, transfer, NULL, 0);
}
}
}
void gpiTransferError
(
GPConnection * connection,
const GPITransfer * transfer
)
{
GPTransferCallbackArg * arg;
GPIConnection * iconnection = (GPIConnection*)*connection;
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->type = GP_TRANSFER_ERROR;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
}
GPIFile * gpiAddFileToTransfer
(
GPITransfer * transfer,
const char * path,
const char * name
)
{
GPIFile file;
char * str;
assert(name && name[0]);
memset(&file, 0, sizeof(GPIFile));
// Copy the path.
/////////////////
if(path)
{
file.path = goastrdup(path);
if(!file.path)
return NULL;
}
// Copy the name.
/////////////////
file.name = goastrdup(name);
if(!file.name)
{
gsifree(file.path);
return NULL;
}
// Change all slashes to backslashes.
/////////////////////////////////////
while((str = strchr(file.name, '\\')) != NULL)
*str = '/';
// Check for a directory.
/////////////////////////
if(name[strlen(name) - 1] == '/')
file.flags = GPI_FILE_DIRECTORY;
// No size yet.
///////////////
file.size = -1;
// Add it to the list of files.
///////////////////////////////
ArrayAppend(transfer->files, &file);
#ifdef GSI_UNICODE
// Copy the unicode versions
file.name_W = UTF8ToUCS2StringAlloc(file.name);
file.path_W = UTF8ToUCS2StringAlloc(file.path);
#endif
// Return the file.
///////////////////
return (GPIFile *)ArrayNth(transfer->files, ArrayLength(transfer->files) - 1);
}
static GPResult gpiSendTransferRequest
(
GPConnection * connection,
GPITransfer * transfer
)
{
char buffer[32];
GPIFile * file;
int i;
int num;
// Get the number of files.
///////////////////////////
num = ArrayLength(transfer->files);
// Start the message.
/////////////////////
CHECK_RESULT(gpiPeerStartTransferMessage(connection, transfer->peer, GPI_BM_FILE_SEND_REQUEST, (GPITransferID_st)&transfer->transferID));
// Add the rest of the headers.
///////////////////////////////
sprintf(buffer, "\\version\\%d\\num\\%d", GPI_TRANSFER_VERSION, num);
CHECK_RESULT(gpiSendOrBufferString(connection, transfer->peer, buffer));
for(i = 0 ; i < num ; i++)
{
file = (GPIFile *)ArrayNth(transfer->files, i);
sprintf(buffer, "\\name%d\\", i);
CHECK_RESULT(gpiSendOrBufferString(connection, transfer->peer, buffer));
CHECK_RESULT(gpiSendOrBufferString(connection, transfer->peer, file->name));
sprintf(buffer, "\\size%d\\", i);
CHECK_RESULT(gpiSendOrBufferString(connection, transfer->peer, buffer));
CHECK_RESULT(gpiSendOrBufferInt(connection, transfer->peer, file->size));
sprintf(buffer, "\\mtime%d\\", i);
CHECK_RESULT(gpiSendOrBufferString(connection, transfer->peer, buffer));
CHECK_RESULT(gpiSendOrBufferUInt(connection, transfer->peer, file->modTime));
}
// Finish the message.
//////////////////////
CHECK_RESULT(gpiFinishTransferMessage(connection, transfer, transfer->message, -1));
return GP_NO_ERROR;
}
static GPITransfer * gpiFindTransferByTransferID
(
GPConnection * connection,
GPITransferID * transferID
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
int i;
int num;
GPITransfer * transfer;
num = ArrayLength(iconnection->transfers);
for(i = 0 ; i < num ; i++)
{
transfer = (GPITransfer *)ArrayNth(iconnection->transfers, i);
if(memcmp(&transfer->transferID, transferID, sizeof(GPITransferID)) == 0)
return transfer;
}
return NULL;
}
GPITransfer * gpiFindTransferByLocalID
(
GPConnection * connection,
int localID
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
int i;
int num;
GPITransfer * transfer;
num = ArrayLength(iconnection->transfers);
for(i = 0 ; i < num ; i++)
{
transfer = (GPITransfer *)ArrayNth(iconnection->transfers, i);
if(transfer->localID == localID)
return transfer;
}
return NULL;
}
int gpiGetTransferLocalIDByIndex
(
GPConnection * connection,
int index
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPITransfer * transfer;
int num;
num = ArrayLength(iconnection->transfers);
assert(index >= 0);
assert(index < num);
if((index < 0) || (index >= num))
return -1;
transfer = (GPITransfer *)ArrayNth(iconnection->transfers, index);
assert(transfer);
if(!transfer)
return -1;
return transfer->localID;
}
void gpiSkipFile
(
GPConnection * connection,
GPITransfer * transfer,
int file,
int reason
)
{
char buffer[32];
if(gpiPeerStartTransferMessage(connection, transfer->peer, GP_FILE_SKIP, (GPITransferID_st)&transfer->transferID) != GP_NO_ERROR)
return;
sprintf(buffer, "\\file\\%d\\reason\\%d", file, reason);
gpiSendOrBufferString(connection, transfer->peer, buffer);
gpiFinishTransferMessage(connection, transfer, NULL, 0);
}
void gpiSkipCurrentFile
(
GPConnection * connection,
GPITransfer * transfer,
int reason
)
{
gpiSkipFile(connection, transfer, transfer->currentFile, reason);
transfer->currentFile++;
}
static GPIBool gpiHandleSendRequest
(
GPConnection * connection,
GPIPeer * peer,
GPITransferID * transferID,
const char * headers,
const char * buffer,
int bufferLen
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPITransfer * transfer;
GPIFile * file;
GPTransferCallbackArg * arg;
char key[16];
char intValue[16];
int version;
int numFiles;
char name[FILENAME_MAX];
int size;
unsigned int mtime;
int i;
size_t len;
int totalSize = 0;
// If we don't have a callback, we're not accepting requests.
/////////////////////////////////////////////////////////////
if(!iconnection->callbacks[GPI_TRANSFER_CALLBACK].callback)
return GPIFalse;
// Check the version.
/////////////////////
if(!gpiValueForKey(headers, "\\version\\", intValue, sizeof(intValue)))
return GPIFalse;
version = atoi(intValue);
if(version < 1)
return GPIFalse;
// Get the number of files.
///////////////////////////
if(!gpiValueForKey(headers, "\\num\\", intValue, sizeof(intValue)))
return GPIFalse;
numFiles = atoi(intValue);
if(numFiles < 1)
return GPIFalse;
// Create the transfer object.
//////////////////////////////
if(gpiNewReceiverTransfer(connection, &transfer, peer->profile, transferID) != GP_NO_ERROR)
return GPIFalse;
// Set the peer.
////////////////
transfer->peer = peer;
// Parse the file list.
///////////////////////
for(i = 0 ; i < numFiles ; i++)
{
sprintf(key, "\\name%d\\", i);
if(!gpiValueForKey(headers, key, name, sizeof(name)))
{
gpiFreeTransfer(connection, transfer);
return GPIFalse;
}
len = strlen(name);
if(strstr(name, "//") || strstr(name, "./") || (name[len - 1] == '.') || (name[0] == '/') || (strcspn(name, ":*?\"<>|\n") != len))
{
gpiFreeTransfer(connection, transfer);
return GPIFalse;
}
sprintf(key, "\\size%d\\", i);
if(!gpiValueForKey(headers, key, intValue, sizeof(intValue)))
{
gpiFreeTransfer(connection, transfer);
return GPIFalse;
}
size = atoi(intValue);
if(size < 0)
{
gpiFreeTransfer(connection, transfer);
return GPIFalse;
}
totalSize += size;
sprintf(key, "\\mtime%d\\", i);
if(!gpiValueForKey(headers, key, intValue, sizeof(intValue)))
{
gpiFreeTransfer(connection, transfer);
return GPIFalse;
}
mtime = (unsigned int)strtoul(intValue, NULL, 10);
file = gpiAddFileToTransfer(transfer, NULL, name);
if(!file)
{
gpiFreeTransfer(connection, transfer);
return GPIFalse;
}
file->size = size;
file->modTime = mtime;
}
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(!arg)
{
gpiFreeTransfer(connection, transfer);
return GPIFalse;
}
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->type = GP_TRANSFER_SEND_REQUEST;
arg->num = numFiles;
#ifndef GSI_UNICODE
arg->message = goastrdup(buffer);
#else
arg->message = UTF8ToUCS2StringAlloc(buffer);
#endif
{
GPResult aResult = gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
if (aResult != GP_NO_ERROR)
return GPIFalse;
}
// Store the total size.
////////////////////////
transfer->totalSize = totalSize;
return GPITrue;
GSI_UNUSED(bufferLen);
}
static GPIBool gpiHandleSendReply
(
GPConnection * connection,
GPITransfer * transfer,
const char * headers,
const char * buffer,
int bufferLen
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPTransferCallbackArg * arg;
char intValue[16];
int version;
int result;
if(!transfer->sender)
return GPIFalse;
// Check the version.
////////////////////
if(!gpiValueForKey(headers, "\\version\\", intValue, sizeof(intValue)))
return GPIFalse;
version = atoi(intValue);
if(version < 1)
return GPIFalse;
// Get the result.
//////////////////
if(!gpiValueForKey(headers, "\\result\\", intValue, sizeof(intValue)))
return GPIFalse;
result = atoi(intValue);
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
if(result == GPI_ACCEPTED)
arg->type = GP_TRANSFER_ACCEPTED;
else if(result == GPI_REJECTED)
arg->type = GP_TRANSFER_REJECTED;
else
arg->type = GP_TRANSFER_NOT_ACCEPTING;
#ifndef GSI_UNICODE
arg->message = goastrdup(buffer);
#else
arg->message = UTF8ToUCS2StringAlloc(buffer);
#endif
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
// Update transfer state if accepted.
/////////////////////////////////////
if(result == GPI_ACCEPTED)
{
transfer->state = GPITransferTransferring;
transfer->currentFile = 0;
}
return GPITrue;
GSI_UNUSED(bufferLen);
}
static GPIBool gpiHandleBegin
(
GPConnection * connection,
GPITransfer * transfer,
const char * headers
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPTransferCallbackArg * arg;
GPIFile * file;
char intValue[16];
int fileIndex;
int size;
unsigned int mtime;
char buffer[FILENAME_MAX];
int count;
if(transfer->sender)
return GPIFalse;
// Get the file.
////////////////
if(!gpiValueForKey(headers, "\\file\\", intValue, sizeof(intValue)))
return GPIFalse;
fileIndex = atoi(intValue);
if((fileIndex < 0) || (fileIndex >= ArrayLength(transfer->files)))
return GPIFalse;
if(fileIndex != transfer->currentFile)
return GPIFalse;
file = (GPIFile *)ArrayNth(transfer->files, fileIndex);
// Is this a directory?
///////////////////////
if(file->flags & GPI_FILE_DIRECTORY)
return GPIFalse;
// Get the size.
////////////////
if(!gpiValueForKey(headers, "\\size\\", intValue, sizeof(intValue)))
return GPIFalse;
size = atoi(intValue);
if(size < 0)
return GPIFalse;
// Update the total size.
/////////////////////////
transfer->totalSize -= file->size;
transfer->totalSize += size;
// Get the mod time.
////////////////////
if(!gpiValueForKey(headers, "\\mtime\\", intValue, sizeof(intValue)))
return GPIFalse;
mtime = (unsigned int)strtoul(intValue, NULL, 10);
// Set file stuff.
//////////////////
MD5Init(&file->md5);
file->modTime = mtime;
file->size = size;
// Setup the temp path.
///////////////////////
count = 0;
do
{
sprintf(buffer, "%sgpt_%d_%d_%d.gpt", transfer->baseDirectory, transfer->localID, fileIndex, rand());
file->file = fopen(buffer, "wb");
count++;
}
while(!file->file && (count < 5));
// Copy off the path.
/////////////////////
if(file->file)
{
file->path = goastrdup(buffer);
if(!file->path)
return GPIFalse;
#ifdef GSI_UNICODE
file->path_W = UTF8ToUCS2StringAlloc(file->path);
#endif
}
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->index = fileIndex;
if(file->file)
{
arg->type = GP_FILE_BEGIN;
}
else
{
arg->type = GP_FILE_FAILED;
arg->num = GP_FILE_WRITE_ERROR;
}
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
// Did it fail?
///////////////
if(!file->file)
{
gpiSkipCurrentFile(connection, transfer, GPI_SKIP_WRITE_ERROR);
file->flags |= GPI_FILE_FAILED;
file->reason = GP_FILE_WRITE_ERROR;
}
return GPITrue;
}
static GPIBool gpiHandleEnd
(
GPConnection * connection,
GPITransfer * transfer,
const char * headers
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPTransferCallbackArg * arg;
GPIFile * file;
GPIBool md5Failed = GPITrue;
unsigned char rawMD5[16];
char localMD5[33];
char remoteMD5[33];
char intValue[16];
int fileIndex;
if(transfer->currentFile == -1)
return GPIFalse;
// Check the file index.
////////////////////////
if(!gpiValueForKey(headers, "\\file\\", intValue, sizeof(intValue)))
return GPIFalse;
fileIndex = atoi(intValue);
if((fileIndex < 0) || (fileIndex >= ArrayLength(transfer->files)))
return GPIFalse;
if(fileIndex != transfer->currentFile)
return GPITrue;
// Get the current file.
////////////////////////
file = (GPIFile *)ArrayNth(transfer->files, transfer->currentFile);
// Sender?
//////////
if(transfer->sender)
{
#ifdef GPI_CONFIRM_FILES
// We should be waiting for confirmation.
/////////////////////////////////////////
assert(file->flags & GPI_FILE_CONFIRMING);
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->index = transfer->currentFile;
arg->type = GP_FILE_END;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
// Done with the file.
//////////////////////
file->flags &= ~GPI_FILE_CONFIRMING;
file->flags |= GPI_FILE_COMPLETED;
transfer->currentFile++;
#endif
return GPITrue;
}
// Is this a directory?
///////////////////////
if(file->flags & GPI_FILE_DIRECTORY)
{
// Directory completed.
///////////////////////
file->flags |= GPI_FILE_COMPLETED;
}
else
{
// Check the file.
//////////////////
assert(file->file);
if(!file->file)
return GPIFalse;
// Get the remote md5.
//////////////////////
if(!gpiValueForKey(headers, "\\md5\\", remoteMD5, sizeof(remoteMD5)))
return GPIFalse;
// Get the local md5.
/////////////////////
MD5Final(rawMD5, &file->md5);
MD5Print(rawMD5, localMD5);
// Check the md5.
/////////////////
md5Failed = (memcmp(localMD5, remoteMD5, 32) != 0) ? GPITrue:GPIFalse;
// Set the state.
/////////////////
if(md5Failed)
{
file->flags |= GPI_FILE_FAILED;
file->reason = GP_FILE_DATA_ERROR;
}
else
file->flags |= GPI_FILE_COMPLETED;
// Close the file.
//////////////////
fclose(file->file);
file->file = NULL;
// If the md5 failed, remove the file.
//////////////////////////////////////
if(md5Failed)
remove(file->path);
#ifdef GPI_CONFIRM_FILES
// Send a confirmation.
///////////////////////
if(gpiPeerStartTransferMessage(connection, transfer->peer, GPI_BM_FILE_END, (GPITransferID_st)&transfer->transferID) != GP_NO_ERROR)
return GPIFalse;
gpiFinishTransferMessage(connection, transfer, NULL, 0);
#endif
}
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->index = transfer->currentFile;
if(file->flags & GPI_FILE_DIRECTORY)
{
arg->type = GP_FILE_DIRECTORY;
}
else if(md5Failed)
{
arg->type = GP_FILE_FAILED;
arg->num = GP_FILE_DATA_ERROR;
}
else
{
arg->type = GP_FILE_END;
}
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
// Next file.
/////////////
transfer->currentFile++;
// Done?
////////
if(transfer->currentFile == ArrayLength(transfer->files))
{
// The transfer is complete.
////////////////////////////
transfer->state = GPITransferComplete;
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->type = GP_TRANSFER_DONE;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
}
return GPITrue;
}
static GPIBool gpiHandleData
(
GPConnection * connection,
GPITransfer * transfer,
const char * headers,
const char * buffer,
int bufferLen
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPTransferCallbackArg * arg;
GPIFile * file;
GPIBool writeFailed;
char intValue[16];
int fileIndex;
if(transfer->currentFile == -1)
return GPIFalse;
// Check the file index.
////////////////////////
if(!gpiValueForKey(headers, "\\file\\", intValue, sizeof(intValue)))
return GPIFalse;
fileIndex = atoi(intValue);
if((fileIndex < 0) || (fileIndex >= ArrayLength(transfer->files)))
return GPIFalse;
if(fileIndex != transfer->currentFile)
return GPITrue;
// Get the current file.
////////////////////////
file = (GPIFile *)ArrayNth(transfer->files, transfer->currentFile);
// Is this a directory?
///////////////////////
if(file->flags & GPI_FILE_DIRECTORY)
return GPIFalse;
#ifdef GPI_ACKNOWLEDGED_WINDOW
// Sender?
//////////
if(transfer->sender)
{
char intValue[16];
// Get the progress.
////////////////////
if(!gpiValueForKey(headers, "\\pro\\", intValue, sizeof(intValue)))
return GPIFalse;
file->acknowledged = atoi(intValue);
return GPITrue;
}
#endif
// Check the file.
//////////////////
assert(file->file);
if(!file->file)
return GPIFalse;
gsDebugFormat(GSIDebugCat_GP, GSIDebugType_File, GSIDebugLevel_RawDump,
"HNDLDATA(PT): %d\n", bufferLen);
// Write the data.
//////////////////
writeFailed = (GPIBool)(fwrite(buffer, 1, bufferLen, file->file) != (size_t)bufferLen);
if(writeFailed)
{
// Flag the errors.
///////////////////
file->flags |= GPI_FILE_FAILED;
file->reason = GP_FILE_WRITE_ERROR;
// Remove the file.
///////////////////
fclose(file->file);
file->file = NULL;
remove(file->path);
}
else
{
// Update the MD5.
///////////////////
MD5Update(&file->md5, (unsigned char *)buffer, bufferLen);
// Update the progress.
///////////////////////
file->progress += bufferLen;
transfer->progress += bufferLen;
#ifdef GPI_ACKNOWLEDGED_WINDOW
// Send an acknowledgment.
//////////////////////////
if(gpiPeerStartTransferMessage(connection, transfer->peer, GPI_BM_FILE_DATA, (GPITransferID_st)&transfer->transferID) != GP_NO_ERROR)
return GPIFalse;
gpiSendOrBufferString(connection, transfer->peer, "\\pro\\");
gpiSendOrBufferInt(connection, transfer->peer, file->progress);
gpiFinishTransferMessage(connection, transfer, NULL, 0);
#endif
}
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->index = transfer->currentFile;
if(!writeFailed)
{
arg->type = GP_FILE_PROGRESS;
arg->num = file->progress;
}
else
{
arg->type = GP_FILE_FAILED;
arg->num = GP_FILE_WRITE_ERROR;
}
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
// Did it fail?
///////////////
if(writeFailed)
{
// Skip the file.
/////////////////
gpiSkipCurrentFile(connection, transfer, GPI_SKIP_WRITE_ERROR);
}
return GPITrue;
}
static GPIBool gpiHandleSkip
(
GPConnection * connection,
GPITransfer * transfer,
const char * headers
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPTransferCallbackArg * arg;
GPIFile * file;
char intValue[16];
int fileIndex;
int reason;
// Get the file.
////////////////
if(!gpiValueForKey(headers, "\\file\\", intValue, sizeof(intValue)))
return GPIFalse;
fileIndex = atoi(intValue);
if((fileIndex < 0) || (fileIndex >= ArrayLength(transfer->files)))
return GPIFalse;
file = (GPIFile *)ArrayNth(transfer->files, fileIndex);
// Get the reason.
//////////////////
if(!gpiValueForKey(headers, "\\reason\\", intValue, sizeof(intValue)))
return GPIFalse;
reason = atoi(intValue);
// Is it not the current file?
//////////////////////////////
if(fileIndex != transfer->currentFile)
{
// Check if we already finished this file.
//////////////////////////////////////////
if(fileIndex < transfer->currentFile)
return GPIFalse;
// Mark it for skipping later.
//////////////////////////////
if(reason == GPI_SKIP_USER_SKIP)
{
file->flags |= GPI_FILE_SKIP;
}
else
{
file->flags |= GPI_FILE_FAILED;
if(reason == GPI_SKIP_READ_ERROR)
file->reason = GP_FILE_READ_ERROR;
else
file->reason = GP_FILE_WRITE_ERROR;
}
return GPITrue;
}
// Delete the file if its already opened.
/////////////////////////////////////////
if(!transfer->sender && file->file)
{
fclose(file->file);
file->file = NULL;
remove(file->path);
}
// Next file.
/////////////
transfer->currentFile++;
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->index = fileIndex;
if(reason == GPI_SKIP_USER_SKIP)
{
arg->type = GP_FILE_SKIP;
}
else
{
arg->type = GP_FILE_FAILED;
if(reason == GPI_SKIP_READ_ERROR)
arg->num = GP_FILE_READ_ERROR;
else
arg->num = GP_FILE_WRITE_ERROR;
}
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
return GPITrue;
}
static GPIBool gpiHandleTransferThrottle
(
GPConnection * connection,
GPITransfer * transfer,
const char * headers
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
int throttle;
char intValue[16];
GPTransferCallbackArg * arg;
// Get the throttle.
////////////////////
if(!gpiValueForKey(headers, "\\rate\\", intValue, sizeof(intValue)))
return GPIFalse;
throttle = atoi(intValue);
// Store the throttle.
//////////////////////
transfer->throttle = throttle;
// If we're the sender, send this back.
///////////////////////////////////////
if(transfer->sender)
{
if(gpiPeerStartTransferMessage(connection, transfer->peer, GPI_BM_FILE_TRANSFER_THROTTLE, (GPITransferID_st)&transfer->transferID) != GP_NO_ERROR)
return GPIFalse;
gpiSendOrBufferString(connection, transfer->peer, "\\rate\\");
gpiSendOrBufferInt(connection, transfer->peer, throttle);
gpiFinishTransferMessage(connection, transfer, NULL, 0);
}
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->type = GP_TRANSFER_THROTTLE;
arg->num = throttle;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
return GPITrue;
}
static GPIBool gpiHandleTransferCancel
(
GPConnection * connection,
GPITransfer * transfer
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPTransferCallbackArg * arg;
// if(transfer->sender)
// return GPIFalse;
// Mark the transfer cancelled.
///////////////////////////////
transfer->state = GPITransferCancelled;
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->type = GP_TRANSFER_CANCELLED;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
return GPITrue;
}
static GPIBool gpiHandleTransferKeepalive
(
GPConnection * connection,
GPITransfer * transfer
)
{
GSI_UNUSED(connection);
GSI_UNUSED(transfer);
// Ignore keep-alive.
/////////////////////
return GPITrue;
}
static GPResult gpiSendFileEnd
(
GPConnection * connection,
GPITransfer * transfer,
GPIFile * file
)
{
CHECK_RESULT(gpiPeerStartTransferMessage(connection, transfer->peer, GPI_BM_FILE_END, (GPITransferID_st)&transfer->transferID));
// Add the file index.
//////////////////////
gpiSendOrBufferStringLenToPeer(connection, transfer->peer, "\\file\\", 6);
gpiSendOrBufferInt(connection, transfer->peer, transfer->currentFile);
// Only add the MD5 for files.
//////////////////////////////
if(!(file->flags & GPI_FILE_DIRECTORY))
{
unsigned char md5Raw[16];
char md5[33];
// Get the MD5.
///////////////
MD5Final(md5Raw, &file->md5);
MD5Print(md5Raw, md5);
// Add it.
//////////
gpiSendOrBufferString(connection, transfer->peer, "\\md5\\");
gpiSendOrBufferString(connection, transfer->peer, md5);
}
gpiFinishTransferMessage(connection, transfer, NULL, 0);
return GP_NO_ERROR;
}
static GPResult gpiSendFileBegin
(
GPConnection * connection,
GPITransfer * transfer,
GPIFile * file
)
{
char buffer[64];
// Get the file info.
/////////////////////
if(!gpiGetTransferFileInfo(file->file, &file->size, &file->modTime))
Error(connection, GP_PARAMETER_ERROR, "Can't get info on file.");
CHECK_RESULT(gpiPeerStartTransferMessage(connection, transfer->peer, GPI_BM_FILE_BEGIN, (GPITransferID_st)&transfer->transferID));
sprintf(buffer, "\\file\\%d\\size\\%d\\mtime\\%u", transfer->currentFile, file->size, (unsigned int)file->modTime);
gpiSendOrBufferString(connection, transfer->peer, buffer);
gpiFinishTransferMessage(connection, transfer, NULL, 0);
return GP_NO_ERROR;
}
static GPResult gpiSendFileData
(
GPConnection * connection,
GPITransfer * transfer,
unsigned char * data,
size_t len
)
{
CHECK_RESULT(gpiPeerStartTransferMessage(connection, transfer->peer, GPI_BM_FILE_DATA, (GPITransferID_st)&transfer->transferID));
// Add the file index.
//////////////////////
gpiSendOrBufferStringLenToPeer(connection, transfer->peer, "\\file\\", 6);
gpiSendOrBufferInt(connection, transfer->peer, transfer->currentFile);
gpiFinishTransferMessage(connection, transfer, (char *)data, len);
return GP_NO_ERROR;
}
GPResult gpiProcessCurrentFile
(
GPConnection * connection,
GPITransfer * transfer
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPIFile * file;
GPTransferCallbackArg * arg;
size_t num;
int i;
int total;
assert(transfer->currentFile >= 0);
assert(transfer->currentFile < ArrayLength(transfer->files));
// Get the current file.
////////////////////////
file = (GPIFile *)ArrayNth(transfer->files, transfer->currentFile);
assert(!(file->flags & GPI_FILE_FAILED));
#ifdef GPI_CONFIRM_FILES
// If it's being confirmed, just wait.
//////////////////////////////////////
if(file->flags & GPI_FILE_CONFIRMING)
return GP_NO_ERROR;
#endif
// Check if its been marked for skipping.
/////////////////////////////////////////
if(file->flags & GPI_FILE_SKIP)
{
// Skip it.
///////////
gpiSkipCurrentFile(connection, transfer, GPI_SKIP_USER_SKIP);
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->index = transfer->currentFile;
arg->type = GP_FILE_SKIP;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
}
else
{
// Is this a directory?
///////////////////////
if(file->flags & GPI_FILE_DIRECTORY)
{
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->index = transfer->currentFile;
arg->type = GP_FILE_DIRECTORY;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
// Send the end.
////////////////
gpiSendFileEnd(connection, transfer, file);
file->flags |= GPI_FILE_COMPLETED;
transfer->currentFile++;
}
else
{
static char buffer[GPI_DATA_SIZE];
// Open the file if we need to.
///////////////////////////////
if(!file->file)
{
// Open it.
///////////
file->file = fopen(file->path, "rb");
if(file->file)
{
// Send the begin.
//////////////////
CHECK_RESULT(gpiSendFileBegin(connection, transfer, file));
// Init the md5.
////////////////
MD5Init(&file->md5);
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->index = transfer->currentFile;
arg->type = GP_FILE_BEGIN;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
}
else
{
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->index = transfer->currentFile;
arg->type = GP_FILE_FAILED;
arg->num = GP_FILE_READ_ERROR;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
// Failed to open.
//////////////////
gpiSkipCurrentFile(connection, transfer, GPI_SKIP_READ_ERROR);
file->flags |= GPI_FILE_FAILED;
file->reason = GP_FILE_READ_ERROR;
return GP_NO_ERROR;
}
}
// TODO: THROTTLING
// Send until done, and while messages are actually being sent.
///////////////////////////////////////////////////////////////
total = 0;
for(i = 0 ; (file->progress < file->size) && !transfer->peer->outputBuffer.len /*&& (i < 20)*/ ; i++)
{
#ifdef GPI_ACKNOWLEDGED_WINDOW
// Don't get too far ahead.
///////////////////////////
if((file->acknowledged + GPI_ACKNOWLEDGED_WINDOW) < file->progress)
break;
#endif
// Read data.
/////////////
num = fread(buffer, 1, sizeof(buffer), file->file);
if(num)
{
// Update the md5.
//////////////////
MD5Update(&file->md5, (unsigned char*)buffer, num);
// Send the data.
/////////////////
CHECK_RESULT(gpiSendFileData(connection, transfer, (unsigned char*)buffer, num));
// Update progress.
///////////////////
transfer->progress += num;
file->progress += num;
total += num;
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->index = transfer->currentFile;
arg->type = GP_FILE_PROGRESS;
arg->num = file->progress;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
}
// Did we not get to the end?
/////////////////////////////
if((num < sizeof(buffer)) && (file->progress != file->size))
{
// Failed reading.
//////////////////
gpiSkipCurrentFile(connection, transfer, GPI_SKIP_READ_ERROR);
file->flags |= GPI_FILE_FAILED;
file->reason = GP_FILE_READ_ERROR;
return GP_NO_ERROR;
}
}
if(total)
{
gsDebugFormat(GSIDebugCat_GP, GSIDebugType_File, GSIDebugLevel_RawDump,
"SENTTOTL(PT): %d\n", total);
}
// Did we finish the file?
//////////////////////////
if(file->progress == file->size)
{
// Close the file.
//////////////////
fclose(file->file);
file->file = NULL;
// Send the end.
////////////////
gpiSendFileEnd(connection, transfer, file);
#ifdef GPI_CONFIRM_FILES
// Wait for the confirmation.
/////////////////////////////
file->flags |= GPI_FILE_CONFIRMING;
#else
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->index = transfer->currentFile;
arg->type = GP_FILE_END;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
// Done with the file.
//////////////////////
file->flags |= GPI_FILE_COMPLETED;
transfer->currentFile++;
#endif
}
}
}
return GP_NO_ERROR;
}
GPResult gpiProcessTransfer
(
GPConnection * connection,
GPITransfer * transfer
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
int currentFile;
int len;
GPTransferCallbackArg * arg;
unsigned long now;
// We only process sending transfers.
/////////////////////////////////////
if(!transfer->sender)
return GP_NO_ERROR;
// Is the transfer finished?
////////////////////////////
if(transfer->state >= GPITransferComplete)
return GP_NO_ERROR;
// Get the time.
////////////////
now = current_time();
// Check for no peer connection established.
////////////////////////////////////////////
if(!transfer->peer)
{
// If its been too long, the person probably isn't really online.
/////////////////////////////////////////////////////////////////
if((now - transfer->lastSend) > GPI_PEER_TIMEOUT_TIME)
{
GPTransferCallbackArg * arg;
// We couldn't connect.
///////////////////////
transfer->state = GPITransferNoConnection;
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->type = GP_TRANSFER_NO_CONNECTION;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
return GP_NO_ERROR;
}
}
else
{
// Check for inactivity.
////////////////////////
if((now - transfer->lastSend) > GPI_KEEPALIVE_TIME)
{
// Send a keepalive.
////////////////////
CHECK_RESULT(gpiPeerStartTransferMessage(connection, transfer->peer, GPI_BM_FILE_TRANSFER_KEEPALIVE, (GPITransferID_st)&transfer->transferID));
gpiFinishTransferMessage(connection, transfer, NULL, 0);
}
}
// If we're paused, there's nothing else to do.
///////////////////////////////////////////////
if(transfer->throttle == 0)
return GP_NO_ERROR;
// Don't send files if we're not transfering yet.
//////////////////////////////////////////////////
if(transfer->state < GPITransferTransferring)
return GP_NO_ERROR;
// Don't send files if we have regular messages pending.
////////////////////////////////////////////////////////
if(ArrayLength(transfer->peer->messages))
return GP_NO_ERROR;
// Process the current file.
////////////////////////////
len = ArrayLength(transfer->files);
while(transfer->currentFile < len)
{
currentFile = transfer->currentFile;
CHECK_RESULT(gpiProcessCurrentFile(connection, transfer));
if(currentFile == transfer->currentFile)
break;
}
// Did we finish?
/////////////////
if(transfer->currentFile == len)
{
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->type = GP_TRANSFER_DONE;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
// Mark it as complete.
///////////////////////
transfer->state = GPITransferComplete;
}
return GP_NO_ERROR;
}
GPResult gpiProcessTransfers
(
GPConnection * connection
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
int len;
int i;
GPITransfer * transfer;
// Go through each transfer.
////////////////////////////
len = ArrayLength(iconnection->transfers);
for(i = 0 ; i < len ; i++)
{
// Get the transfer.
////////////////////
transfer = (GPITransfer *)ArrayNth(iconnection->transfers, i);
// Process it.
//////////////
gpiProcessTransfer(connection, transfer);
}
return GP_NO_ERROR;
}
GPIBool gpiGetTransferFileInfo
(
FILE * file,
int * size,
gsi_time * modTime
)
{
#ifdef _WIN32
struct _stat stats;
if(_fstat(_fileno(file), &stats) != 0)
return GPIFalse;
*size = (int)stats.st_size;
*modTime = (gsi_time)stats.st_mtime;
#else
if(fseek(file, 0, SEEK_END) != 0)
return GPIFalse;
*size = (int)ftell(file);
if(*size == -1)
return GPIFalse;
*modTime = 0;
fseek(file, 0, SEEK_SET);
#endif
return GPITrue;
}
void gpiTransferPeerDestroyed
(
GPConnection * connection,
GPIPeer * peer
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPTransferCallbackArg * arg;
GPITransfer * transfer;
int i;
int len;
// Search for transfers that use this peer.
///////////////////////////////////////////
len = ArrayLength(iconnection->transfers);
for(i = 0 ; i < len ; i++)
{
transfer = (GPITransfer *)ArrayNth(iconnection->transfers, i);
if (transfer->peer == peer)
{
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->type = GP_TRANSFER_LOST_CONNECTION;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
// So long tranfer.
///////////////////
transfer->state = GPITransferNoConnection;
}
}
}
void gpiTransfersHandlePong
(
GPConnection * connection,
GPProfile profile,
GPIPeer * peer
)
{
GPIConnection * iconnection = (GPIConnection*)*connection;
GPITransfer * transfer;
int i;
int len;
// Go through all the transfers.
////////////////////////////////
len = ArrayLength(iconnection->transfers);
for(i = 0 ; i < len ; i++)
{
// Get this transfer.
/////////////////////
transfer = (GPITransfer *)ArrayNth(iconnection->transfers, i);
assert(transfer);
// Is it waiting on a pong from this profile?
/////////////////////////////////////////////
if((transfer->state == GPITransferPinging) && (transfer->profile == profile))
{
// Did we not get a connection?
///////////////////////////////
if(!peer)
{
GPTransferCallbackArg * arg;
// We couldn't connect.
///////////////////////
transfer->state = GPITransferNoConnection;
// Call the callback.
/////////////////////
arg = (GPTransferCallbackArg *)gsimalloc(sizeof(GPTransferCallbackArg));
if(arg)
{
memset(arg, 0, sizeof(GPTransferCallbackArg));
arg->transfer = transfer->localID;
arg->type = GP_TRANSFER_NO_CONNECTION;
gpiAddCallback(connection, iconnection->callbacks[GPI_TRANSFER_CALLBACK], arg, NULL, GPI_ADD_TRANSFER_CALLBACK);
}
}
else
{
// Store the peer we're connected on.
/////////////////////////////////////
transfer->peer = peer;
// We're connected, so send our request.
////////////////////////////////////////
gpiSendTransferRequest(connection, transfer);
// Waiting for a response.
//////////////////////////
transfer->state = GPITransferWaiting;
}
}
}
}
#endif
GPResult gpiSendTransferReply
(
GPConnection * connection,
const GPITransferID * transferID,
GPIPeer * peer,
int result,
const char * msg
)
{
char buffer[32];
if(!msg)
msg = "";
// Start the message.
/////////////////////
CHECK_RESULT(gpiPeerStartTransferMessage(connection, peer, GPI_BM_FILE_SEND_REPLY, transferID));
// Add the rest of the headers.
///////////////////////////////
sprintf(buffer, "\\version\\%d\\result\\%d", GPI_TRANSFER_VERSION, result);
CHECK_RESULT(gpiSendOrBufferString(connection, peer, buffer));
// Finish the message.
//////////////////////
CHECK_RESULT(gpiPeerFinishTransferMessage(connection, peer, msg, -1));
return GP_NO_ERROR;
}
void gpiHandleTransferMessage
(
GPConnection * connection,
GPIPeer * peer,
int type,
const char * headers,
const char * buffer,
int len
)
{
char value[64];
GPITransferID transferID;
#ifndef NOFILE
GPITransfer * transfer;
GPIBool success;
#endif
// Get the transfer ID.
///////////////////////
if(!gpiValueForKey(headers, "\\xfer\\", value, sizeof(value)))
return;
if(sscanf(value, "%d %u %u", &transferID.profileid, &transferID.count, &transferID.time) != 3)
return;
#ifdef NOFILE
gpiSendTransferReply(connection, &transferID, peer, GPI_NOT_ACCEPTING, NULL);
#else
// Send request messages don't yet have a transfer object.
//////////////////////////////////////////////////////////
if(type == GPI_BM_FILE_SEND_REQUEST)
{
// Check for not accepting connections.
///////////////////////////////////////
if(!gpiHandleSendRequest(connection, peer, &transferID, headers, buffer, len))
gpiSendTransferReply(connection, &transferID, peer, GPI_NOT_ACCEPTING, NULL);
return;
}
// Find the transfer based on the ID.
/////////////////////////////////////
transfer = gpiFindTransferByTransferID(connection, &transferID);
if(!transfer || (transfer->peer != peer))
return;
// Handle it based on the type.
///////////////////////////////
switch(type)
{
case GPI_BM_FILE_SEND_REPLY:
success = gpiHandleSendReply(connection, transfer, headers, buffer, len);
break;
case GPI_BM_FILE_BEGIN:
success = gpiHandleBegin(connection, transfer, headers);
break;
case GPI_BM_FILE_END:
success = gpiHandleEnd(connection, transfer, headers);
break;
case GPI_BM_FILE_DATA:
success = gpiHandleData(connection, transfer, headers, buffer, len);
break;
case GPI_BM_FILE_SKIP:
success = gpiHandleSkip(connection, transfer, headers);
break;
case GPI_BM_FILE_TRANSFER_THROTTLE:
success = gpiHandleTransferThrottle(connection, transfer, headers);
break;
case GPI_BM_FILE_TRANSFER_CANCEL:
success = gpiHandleTransferCancel(connection, transfer);
break;
case GPI_BM_FILE_TRANSFER_KEEPALIVE:
success = gpiHandleTransferKeepalive(connection, transfer);
break;
default:
success = GPITrue;
}
// Check if there was a transfer error.
///////////////////////////////////////
if(!success)
gpiTransferError(connection, transfer);
#endif
GSI_UNUSED(type);
GSI_UNUSED(buffer);
GSI_UNUSED(len);
}