#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include <errno.h>
#include <wslib.h>
#include "connap.h"
#include "message.h"
#include "connect.h"
#include "search.h"
#include "transfer.h"
#include "shared.h"
#include "util.h"
DOWNLOAD *downloads;
UPLOAD *uploads;
int dest_ip, dest_port;
int data_sock = 0;
static int fw_addr = -1, fw_fd;
static char *cur_dl_dir = NULL;
static void download2(DOWNLOAD *new_dl, int offset);
#if 0
static String MapUlName(String name)
{
static String mappedName = NULL;
String p;
if (mappedName)
XtFree(mappedName);
mappedName = XtNewString(name);
for (p = (mappedName + 2); *p; p++) {
if ((*p) == '\\')
(*p) = '/';
}
return mappedName + 2;
}
static void UpdateDlWin(XtPointer clientData, XtInputId *id)
{
DOWNLOAD *rec = (DOWNLOAD*)clientData;
int percent, sec;
char tmp[128];
percent = (rec->count * 100) / rec->tot;
XmScaleSetValue(XtNameToWidget(rec->w, "*dlProgBar"), percent);
sec = time(NULL) - rec->start;
sprintf(tmp, "%d%% %.2f kb/sec", percent,
sec ? ((float)(rec->count - rec->offset) / 1024.0) /
(float)sec : 0.00);
XmTextFieldSetString(XtNameToWidget(rec->w, "*dlStatusLabel"), tmp);
rec->timerId = XtAppAddTimeOut(appCon, 1000,
(XtTimerCallbackProc)UpdateDlWin, clientData);
}
static void ReadMP3Data(XtPointer clientData, int *sock, XtInputId *id)
{
DOWNLOAD *rec = (DOWNLOAD*)clientData;
DOWNLOAD *newDl;
int n, r;
String msg;
n = read(*sock, Buf, sizeof(Buf));
if (n <= 0) {
newDl = XtNew(DOWNLOAD);
newDl->fileName = XtNewString(rec->fileName);
newDl->dlName = XtNewString(rec->dlName);
newDl->nick = XtNewString(rec->nick);
newDl->tot = rec->tot;
newDl->count = rec->count;
newDl->w = NULL;
newDl->inputId = newDl->fd = newDl->mp3Fd = 0;
EndDl(rec, 1, 0);
if (newDl->count < newDl->tot) {
msg = XtMalloc(1024);
sprintf(msg, "%s\n\nPremature EOF. Resume?",
newDl->dlName);
r = YesNoMsg(msg, "OK");
XtFree(msg);
if (r) {
Download2(newDl, newDl->count);
} else
remove(newDl->dlName);
} else {
XtFree(newDl->fileName);
XtFree(newDl->dlName);
XtFree(newDl->nick);
XtFree((char*)newDl);
}
return;
}
if (write(rec->mp3Fd, Buf, n) == -1)
AbortDl(rec, 1, strerror(errno));
rec->count += n;
}
static void WriteMP3Data(XtPointer clientData, int *sock, XtInputId *id)
{
int n, err, optLen = sizeof(int);
UPLOAD *rec = (UPLOAD*)clientData;
if((n = read(rec->mp3Fd, Buf, 1024)) <= 0) {
EndUl(rec, 1);
return;
}
if (WriteChars(*sock, Buf, n) == -1) {
EndUl(rec, 1);
return;
}
if (getsockopt(*sock, SOL_SOCKET, SO_ERROR,
(char*)&err, &optLen) == -1) {
EndUl(rec, 1);
return;
}
if (err != 0) {
EndUl(rec, 1);
return;
}
rec->count += n;
}
void EndDl(DOWNLOAD *rec, int dlStat, int aborted) {
if (rec->inputId)
XtRemoveInput(rec->inputId);
if (rec->fd > 0)
close(rec->fd);
if (rec->mp3Fd > 0)
close(rec->mp3Fd);
if (aborted && rec->dlName) {
remove(rec->dlName);
XtFree(rec->dlName);
}
if (dlStat) {
if (rec->w) {
XtRemoveTimeOut(rec->timerId);
XtDestroyWidget(rec->w);
curFocus = NULL;
}
if (SendMsg(MSG_CLIENT_DOWNLOAD_END, ""))
Disconnect(strerror(errno));
}
XtFree(rec->nick);
XtFree(rec->fileName);
XtFree((char*)rec);
}
void AbortDl(DOWNLOAD *rec, int dlStat, String err) {
SimpleMsgRemove();
EndDl(rec, dlStat, 1);
if (strlen(err))
ErrMsg(err);
}
static void Unreachable(String nick)
{
if (SendMsg(MSG_CLIENT_DATA_PORT_ERROR, nick))
Disconnect(strerror(errno));
}
static void Download2(DOWNLOAD *newDl, int offset)
{
String tmp = XtMalloc(8192);
int i, r, fwDl, openFlags;
char byte, offsetStr[20];
struct in_addr sin_addr;
sprintf(offsetStr, "%d", offset);
SimpleMsg("Preparing for download...");
destIp = destPort = -1;
sprintf(tmp, "%s \"%s\"", newDl->nick, newDl->fileName);
if (SendMsg(MSG_CLIENT_DOWNLOAD, tmp)) {
Disconnect(strerror(errno));
AbortDl(newDl, 0, strerror(errno));
goto error;
}
WaitVar(&destPort, timeOut);
if (destPort == -1) {
AbortDl(newDl, 0, "Timeout");
goto error;
}
fwDl = 0;
if (destPort == 0) {
/* Firewalled download */
fwDl = 1;
if (SendMsg(MSG_CLIENT_DOWNLOAD_FIREWALL, tmp)) {
AbortDl(newDl, 0, strerror(errno));
Disconnect(strerror(errno));
goto error;
}
fwAddr = destIp;
fwFd = -1;
WaitVar(&fwFd, timeOut);
if (fwFd == -1) {
AbortDl(newDl, 0, "Timeout");
goto error;
}
newDl->fd = fwFd;
if ((r = WriteChars(newDl->fd, "1", 1)) == -1) {
AbortDl(newDl, 0, strerror(errno));
goto error;
}
tmp[4] = 0;
if ((r = ReadChars(newDl->fd, tmp, 4)) < 4) {
AbortDl(newDl, 0, ReadErr(r));
goto error;
}
if (strcmp(tmp, "SEND")) {
AbortDl(newDl, 0, "EOF from peer or invalid data");
goto error;
}
memset(tmp, 0, 8192);
do {
r = read(newDl->fd, tmp, 8192);
} while ((r == -1) && (errno == EAGAIN));
if (r <= 0) {
AbortDl(newDl, 0, "EOF from peer of invalid data");
goto error;
}
if ((r = WriteChars(newDl->fd, offsetStr,
strlen(offsetStr))) == -1) {
AbortDl(newDl, 0, strerror(errno));
goto error;
}
} else {
sin_addr.s_addr = destIp;
if (! ConnSock(inet_ntoa(sin_addr), destPort, &newDl->fd)) {
Unreachable(newDl->nick);
AbortDl(newDl, 0, "");
goto error;
}
tmp[1] = 0;
if ((r = ReadChars(newDl->fd, tmp, 1)) < 1) {
AbortDl(newDl, 0, ReadErr(r));
goto error;
}
if (strcmp(tmp, "1")) {
AbortDl(newDl, 0, "EOF from peer or invalid data");
goto error;
}
if ((r = WriteChars(newDl->fd, "GET", 3)) == -1) {
AbortDl(newDl, 0, strerror(errno));
goto error;
}
sprintf(tmp, "%s \"%s\" %s", userInfo.userName,
newDl->fileName, offsetStr);
if ((r = WriteChars(newDl->fd, tmp, strlen(tmp))) == -1) {
AbortDl(newDl, 0, strerror(errno));
goto error;
}
memset(tmp, 0, 8192);
i = 0;
while (1) {
if ((r = ReadChars(newDl->fd, &byte, 1)) < 1) {
AbortDl(newDl, 0, ReadErr(r));
goto error;
}
if (! isdigit(byte))
break;
tmp[i++] = byte;
}
if (strtoul(tmp, NULL, 10) != newDl->tot) {
AbortDl(newDl, 0,
"EOF from peer, file not shared or invalid data");
goto error;
}
}
if (SendMsg(MSG_CLIENT_DOWNLOAD_START, "")) {
AbortDl(newDl, 0, strerror(errno));
Disconnect("");
goto error;
}
openFlags = O_CREAT | O_WRONLY;
if (! offset)
openFlags |= O_TRUNC;
newDl->mp3Fd = open(newDl->dlName, openFlags);
if (newDl->mp3Fd == -1) {
AbortDl(newDl, 1, strerror(errno));
goto error;
}
fchmod(newDl->mp3Fd, 0644);
lseek(newDl->mp3Fd, offset, SEEK_SET);
if (! fwDl) {
if (write(newDl->mp3Fd, &byte, 1) == -1) {
AbortDl(newDl, 1, strerror(errno));
goto error;
}
}
SimpleMsgRemove();
newDl->w = DlWin(newDl);
newDl->count = fwDl ? offset : offset + 1;
newDl->offset = offset;
newDl->start = time(NULL);
ForceWindow(newDl->w);
newDl->inputId = XtAppAddInput(appCon, newDl->fd,
(XtPointer)XtInputReadMask,
(XtInputCallbackProc)ReadMP3Data,
(XtPointer)newDl);
newDl->timerId = XtAppAddTimeOut(appCon, 100,
(XtTimerCallbackProc)UpdateDlWin, (XtPointer)newDl);
error:
XtFree(tmp);
}
void Download(int n, int mode)
{
SRESULT *res;
DOWNLOAD *newDl;
String tmp = XtMalloc(8192), p, tail, dflt;
int i, nr, offset;
struct stat statBuf;
for (nr = 0, res = resData; res; nr++, res = res->next);
if (n > (nr - 1))
goto error;
for (i = 0, res = resData; i < n; i++)
res = res->next;
newDl = XtNew(DOWNLOAD);
newDl->inputId = newDl->fd = newDl->mp3Fd = 0;
newDl->dlName = NULL;
newDl->w = NULL;
strcpy(tmp, res->data);
if (mode) {
p = strtok(tmp, " ");
newDl->nick = XtNewString(p);
p = strtok(NULL, "\"");
newDl->fileName = XtNewString(p);
/* skip over md5 */
(void)strtok(NULL, " ");
p = strtok(NULL, " ");
newDl->tot = atoi(p);
} else {
p = strtok(tmp, "\"");
newDl->fileName = XtNewString(p);
/* skip over md5 */
(void)strtok(NULL, " ");
p = strtok(NULL, " ");
newDl->tot = atoi(p);
/* skip over bitrate, frequency and length */
(void)strtok(NULL, " ");
(void)strtok(NULL, " ");
(void)strtok(NULL, " ");
p = strtok(NULL, " ");
newDl->nick = XtNewString(p);
}
if (newDl->tot == 0) {
AbortDl(newDl, 0, "file size is 0 bytes");
goto error;
}
if (! curDlDir)
curDlDir = XtNewString(initDlDir);
if (! (dflt = strrchr(newDl->fileName, '\\') + 1))
dflt = strrchr(newDl->fileName, '/') + 1;
p = SaveFile(curDlDir, dflt);
if (! *p) {
AbortDl(newDl, 0, "");
goto error;
}
XtFree(curDlDir);
curDlDir = GetDir(p);
tail = strrchr(p, '/') + 1;
offset = 0;
if (! strcmp(tail, dflt)) {
if (stat(p, &statBuf) != -1) {
if (statBuf.st_size == newDl->tot) {
InfoMsg("File is complete");
AbortDl(newDl, 0, "");
goto error;
}
offset = statBuf.st_size;
}
}
newDl->dlName = XtNewString(p);
Download2(newDl, offset);
error:
XtFree(tmp);
}
void EndUl(UPLOAD *rec, int ulStat)
{
UPLOAD *upload, *prevUpload = NULL;
if (! uploads)
return;
if (rec->inputId)
XtRemoveInput(rec->inputId);
close(rec->fd);
if (rec->mp3Fd > 0)
close(rec->mp3Fd);
for (upload = uploads; upload; upload = upload->next) {
if ((! strcmp(upload->nick, rec->nick)) &&
(! strcmp(upload->fileName, rec->fileName)))
break;
prevUpload = upload;
}
if (! upload) {
ErrMsg("Upload not in list");
return;
}
if (prevUpload) {
prevUpload->next = upload->next;
} else {
uploads = uploads->next;
}
XtFree(upload->nick);
XtFree(upload->fileName);
XtFree((char*)upload);
if (ulStat) {
if (SendMsg(MSG_CLIENT_UPLOAD_END, ""))
Disconnect(strerror(errno));
}
}
static int AddUl(UPLOAD *newUl)
{
UPLOAD *ulPtr, *prevPtr = NULL;
String newFileName, fileName;
for (ulPtr = uploads; ulPtr; ulPtr = ulPtr->next) {
if ((! strcmp(ulPtr->fileName, newUl->fileName)) &&
(! strcmp(ulPtr->nick, newUl->nick)))
return 0;
}
newFileName = GetFileTail(newUl->fileName);
for (ulPtr = uploads; ulPtr; ulPtr = ulPtr->next) {
fileName = GetFileTail(ulPtr->fileName);
if (strcasecmp(fileName, newFileName) > 0) {
XtFree(fileName);
break;
}
XtFree(fileName);
prevPtr = ulPtr;
}
if (prevPtr) {
newUl->next = prevPtr->next;
prevPtr->next = newUl;
} else {
newUl->next = uploads;
uploads = newUl;
}
XtFree(newFileName);
return 1;
}
void Upload(int fd)
{
UPLOAD *newUl;
SHARED *shared;
String realName;
String tmp = XtMalloc(8192);
String nick, fileName, offset;
int r;
if ((r = WriteChars(fd, "1", 1)) == -1) {
close(fd);
goto error;
}
tmp[3] = 0;
if ((r = ReadChars(fd, tmp, 3)) < 3) {
close(fd);
goto error;
}
if (strcmp(tmp, "GET")) {
close(fd);
goto error;
}
/* Get nick, filename and offset */
memset(tmp, 0, 8192);
do {
r = read(fd, tmp, 8192);
} while ((r == -1) && (errno == EAGAIN));
if (r <= 0) {
close(fd);
goto error;
}
nick = strtok(tmp, " ");
fileName = strtok(NULL, "\"");
offset = strtok(NULL, " ");
newUl = XtNew(UPLOAD);
newUl->nick = XtNewString(nick);
realName = MapUlName(fileName);
newUl->fileName = XtNewString(realName);
newUl->fd = fd;
newUl->mp3Fd = 0;
newUl->count = strtoul(offset, NULL, 10);
newUl->inputId = 0;
if (! AddUl(newUl)) {
XtFree(newUl->nick);
XtFree(newUl->fileName);
XtFree((char*)newUl);
close(newUl->fd);
goto error;
}
for (shared = sharedFiles; shared; shared = shared->next) {
if (! strcmp(shared->fileName, newUl->fileName))
break;
}
if ((! shared) || (atoi(offset) > shared->size)) {
WriteChars(newUl->fd, "FILE NOT SHARED",
sizeof("FILE NOT SHARED"));
EndUl(newUl, 0);
goto error;
}
newUl->tot = shared->size;
newUl->mp3Fd = open(newUl->fileName, O_RDONLY);
if (newUl->mp3Fd == -1) {
EndUl(newUl, 0);
goto error;
}
lseek(newUl->mp3Fd, newUl->count, SEEK_SET);
sprintf(tmp, "%d", shared->size);
if ((r = WriteChars(fd, tmp, strlen(tmp))) == -1) {
EndUl(newUl, 0);
goto error;
}
if ((r = SendMsg(MSG_CLIENT_UPLOAD_START, ""))) {
EndUl(newUl, 0);
Disconnect(strerror(errno));
goto error;
}
newUl->inputId = XtAppAddInput(appCon, newUl->fd,
(XtPointer)XtInputWriteMask,
(XtInputCallbackProc)WriteMP3Data,
(XtPointer)newUl);
error:
XtFree(tmp);
}
void FwUpload(String data)
{
String nick, fileName, realName, md5;
unsigned long ip;
int fd, port, link, offset;
struct in_addr sin_addr;
String tmp = XtMalloc(8192), p;
UPLOAD *newUl;
SHARED *shared;
int r;
nick = strtok(data, " ");
ip = atol(strtok(NULL, " "));
port = atoi(strtok(NULL, " "));
fileName = strtok(NULL, "\"");
md5 = strtok(NULL, " ");
link = atoi(strtok(NULL, " "));
sin_addr.s_addr = destIp;
if (! ConnSock(inet_ntoa(sin_addr), port, &fd)) {
Unreachable(nick);
goto error;
}
newUl = XtNew(UPLOAD);
newUl->nick = XtNewString(nick);
realName = MapUlName(fileName);
newUl->fileName = XtNewString(realName);
newUl->fd = fd;
newUl->mp3Fd = 0;
newUl->inputId = 0;
if (! AddUl(newUl)) {
XtFree(newUl->nick);
XtFree(newUl->fileName);
XtFree((char*)newUl);
close(newUl->fd);
goto error;
}
for (shared = sharedFiles; shared; shared = shared->next) {
if (! strcmp(shared->fileName, newUl->fileName))
break;
}
if (! shared) {
EndUl(newUl, 0);
goto error;
}
newUl->tot = shared->size;
newUl->mp3Fd = open(newUl->fileName, O_RDONLY);
if (newUl->mp3Fd == -1) {
EndUl(newUl, 0);
goto error;
}
tmp[1] = 0;
if ((r = ReadChars(newUl->fd, tmp, 1)) < 1) {
EndUl(newUl, 0);
goto error;
}
if (strcmp(tmp, "1")) {
EndUl(newUl, 0);
goto error;
}
if ((r = WriteChars(newUl->fd, "SEND", 4)) == -1) {
EndUl(newUl, 0);
goto error;
}
sprintf(tmp, "%s \"%s\" %u", userInfo.userName, fileName,
shared->size);
if ((r = WriteChars(newUl->fd, tmp, strlen(tmp))) == -1) {
EndUl(newUl, 0);
goto error;
}
/* Get offset */
memset(tmp, 0, 8192);
do {
r = read(newUl->fd, tmp, 8192);
} while ((r == -1) && (errno == EAGAIN));
if (r <= 0) {
EndUl(newUl, 0);
goto error;
}
for (p = tmp; *p; p++) {
if (! isdigit(*p)) {
EndUl(newUl, 0);
goto error;
}
}
if ((offset = atoi(tmp)) > shared->size) {
EndUl(newUl, 0);
goto error;
}
newUl->count = offset;
lseek(newUl->mp3Fd, newUl->count, SEEK_SET);
if ((r = SendMsg(MSG_CLIENT_UPLOAD_START, ""))) {
EndUl(newUl, 0);
Disconnect(strerror(errno));
goto error;
}
newUl->inputId = XtAppAddInput(appCon, newUl->fd,
(XtPointer)XtInputWriteMask,
(XtInputCallbackProc)WriteMP3Data,
(XtPointer)newUl);
error:
XtFree(tmp);
}
void AcceptConn(XtPointer closure, int *sock, XtInputId *id)
{
int new, size;
struct sockaddr_in clientName;
size = sizeof(clientName);
new = accept(*sock, (struct sockaddr*)&clientName, &size);
if (new < 0)
return;
if (SetBlocked(new, 0)) {
close(new);
return;
}
/*
printf("Connect from host %s, port %hd\n",
inet_ntoa(clientName.sin_addr),
ntohs(clientName.sin_port));
*/
if ((clientName.sin_addr.s_addr == fwAddr) ||
((ntohl(clientName.sin_addr.s_addr) == 0x7f000001) &&
(fwAddr != -1))) {
fwFd = new;
fwAddr = -1;
return;
}
Upload(new);
return;
}
static int InitDataPort(int port, int manual)
{
char tmp[256];
if (MakeSocket(port, &dataSock) < 0) {
if (manual)
ErrMsg(strerror(errno));
return 0;
}
if (listen(dataSock, 1) < 0) {
close(dataSock);
if (manual) {
sprintf(tmp, "Listening on data port %d failed.\n"
"You are probably running a previous\n"
"instance of XmNap with the same port\n"
"number as this one", port);
ErrMsg(tmp);
}
return 0;
}
return 1;
}
void SetDataPort(int port, int manual)
{
char tmp[256];
if (dataSockId) {
XtRemoveInput(dataSockId);
close(dataSock);
dataSockId = 0;
}
if (port != 0) {
if (! InitDataPort(port, manual)) {
userInfo.dataPort = 0;
if (srvConn) {
if (SendMsg(MSG_CLIENT_CHANGE_DATA_PORT, "0"))
Disconnect(strerror(errno));
}
return;
}
dataSockId = XtAppAddInput(appCon, dataSock,
(XtPointer)XtInputReadMask,
(XtInputCallbackProc)AcceptConn, NULL);
}
userInfo.dataPort = port;
if (manual && srvConn) {
sprintf(tmp, "%d", userInfo.dataPort);
if (SendMsg(MSG_CLIENT_CHANGE_DATA_PORT, tmp))
Disconnect(strerror(errno));
}
}
#endif