/* * XmNap A Motif napster client * * Copyright (C) 2000 Mats Peterson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Please send any comments/bug reports to * mats_peterson@swipnet.se (Mats Peterson) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "message.h" #include "connect.h" #include "search.h" #include "transfer.h" #include "shared.h" #include "dlwin.h" #include "netutil.h" #include "util.h" #include "fsbox.h" #include "msgbox.h" DOWNLOAD *downloads; UPLOAD *uploads; int destIp, destPort; int dataSock = 0; XtInputId dataSockId = 0; static int fwAddr = -1, fwFd; static String curDlDir = NULL; static void Download2(DOWNLOAD *newDl, int offset); 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)); } } .