📄 nut-transfer.c
字号:
/* * nut-transfer.c - gnutella file transfer implementation * * Copyright (C) 2000, 2001 Stefan Jahn <stefan@lkcc.org> * * This 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, or (at your option) * any later version. * * This software 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 package; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * $Id: nut-transfer.c,v 1.38 2001/08/01 10:16:23 ela Exp $ * */#if HAVE_CONFIG_H# include <config.h>#endif#if ENABLE_GNUTELLA#define _GNU_SOURCE#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <time.h>#include <sys/stat.h>#if HAVE_UNISTD_H# include <unistd.h>#endif#include <sys/types.h>#include <fcntl.h>#include <errno.h>#ifndef __MINGW32__# include <sys/types.h># include <sys/socket.h># include <netinet/in.h># include <arpa/inet.h># if HAVE_DIRENT_H# include <dirent.h># define NAMLEN(dirent) strlen((dirent)->d_name)# else# define dirent direct# define NAMLEN(dirent) (dirent)->d_namlen# if HAVE_SYS_NDIR_H# include <sys/ndir.h># endif# if HAVE_SYS_DIR_H# include <sys/dir.h># endif# if HAVE_NDIR_H# include <ndir.h># endif# endif#endif /* not __MINGW32__ */#ifdef __MINGW32__# include <windows.h># include <winsock2.h># include <io.h>#endif#if HAVE_SYS_DIRENT_H# include <sys/dirent.h>#endif#ifndef __MINGW32__# define FILENAME de->d_name#else # define FILENAME de.cFileName# define closedir(dir) FindClose (dir)#endif#include "libserveez.h"#include "gnutella.h"#include "nut-core.h"#include "nut-transfer.h"/* * Check if a given search pattern matches a filename. Return non-zero * on success and zero otherwise. */static intnut_string_regex (char *text, char *regex){ char *p, *token, *str, *reg; /* first check if text tokens are in text */ if (!strchr (regex, '*') && !strchr (regex, '?')) { str = svz_strdup (text); reg = svz_strdup (regex); svz_tolower (str); svz_tolower (reg); /* all tokens must be in the text */ for (token = strtok (reg, " "); token; token = strtok (NULL, " ")) { if (!strstr (str, token)) break; } svz_free (str); svz_free (reg); if (!token) return -1; return 0; } /* parse until end of both strings */ else while (*regex && *text) { /* find end of strings or '?' or '*' */ while (*regex != '*' && *regex != '?' && *regex && *text) { /* return no Match if so */ if (tolower (*text) != tolower (*regex)) return 0; text++; regex++; } /* one free character */ if (*regex == '?') { if (!(*text)) return 0; text++; regex++; } /* free characters */ else if (*regex == '*') { regex++; /* skip useless '?'s after '*'s */ while (*regex == '?') regex++; /* skip all characters until next character in pattern found */ while (*text && tolower (*regex) != tolower (*text)) text++; /* next character in pattern found */ if (*text) { /* find the last occurrence of this character in the text */ p = text + strlen (text); while (tolower (*p) != tolower (*text)) p--; /* continue parsing at this character */ text = p; } } } /* is the text longer than the regex ? */ if (!*text && !*regex) return -1; return 0;}/* * Within this callback the actual file transfer is done. */static intnut_save_transfer (svz_socket_t *sock){ int fill = sock->recv_buffer_fill; nut_transfer_t *transfer = sock->data; int num_written; /* do we have something to write in the receive buffer ? */ if (fill > 0) { /* write as much data as possible */ num_written = write (sock->file_desc, sock->recv_buffer, fill); /* seems like an error occurred */ if (num_written < 0) { svz_log (LOG_ERROR, "nut: write: %s\n", SYS_ERROR); return -1; } /* crop written data from receive buffer */ svz_sock_reduce_recv (sock, num_written); /* did we get all data */ if ((transfer->size -= num_written) <= 0) {#if ENABLE_DEBUG svz_log (LOG_DEBUG, "nut: file successfully received\n");#endif /* yes, shutdown the connection */ return -1; } } return 0;}/* * This is the sock->check_request callback for gnutella file transfers. * Whenever there is data within the receive queue it will be called. */static intnut_check_transfer (svz_socket_t *sock){ int fill = sock->recv_buffer_fill; int len = strlen (NUT_GET_OK); char *p = sock->recv_buffer, *length; nut_transfer_t *transfer = sock->data; /* check if got at least the first part of the HTTP header */ if (fill >= len && !memcmp (sock->recv_buffer, NUT_GET_OK, len)) { /* find the end of the HTTP header (twice a CR/LF) */ while (p < sock->recv_buffer + (fill - 3) && memcmp (p, NUT_SEPERATOR, 4)) p++; /* did we get all the header information ? */ if (p < sock->recv_buffer + (fill - 3) && !memcmp (p, NUT_SEPERATOR, 4)) {#if ENABLE_DEBUG svz_log (LOG_DEBUG, "nut: download header received\n");#endif len = p - sock->recv_buffer + 1; length = nut_parse_property (sock->recv_buffer, len, NUT_LENGTH); if (length == NULL) {#if ENABLE_DEBUG svz_log (LOG_DEBUG, "nut: no content length given\n");#endif return -1; } /* * check if the announced file length in the search reply * corresponds to the content length of this HTTP header */ sock->userflags |= NUT_FLAG_HDR; transfer->size = svz_atoi (length); svz_free (length); if (transfer->original_size != transfer->size) { svz_log (LOG_WARNING, "nut: transfer sizes differ (%u!=%u)\n", transfer->original_size, transfer->size); } /* assign the appropriate gnutella transfer callbacks */ sock->check_request = nut_save_transfer; sock->write_socket = NULL; sock->idle_func = NULL; /* crop header from receive buffer */ len = (p - sock->recv_buffer) + 4; svz_sock_reduce_recv (sock, len); } } return 0;}/* * This callback is executed whenever a gnutella file transfer aborted * or successfully exited. */static intnut_disconnect_transfer (svz_socket_t *sock){ nut_config_t *cfg = sock->cfg; nut_transfer_t *transfer = sock->data; /* decrement amount of concurrent downloads */ cfg->dnloads--; /* finally close the received file */ if (close (sock->file_desc) == -1) svz_log (LOG_ERROR, "nut: close: %s\n", SYS_ERROR); /* free the transfer data */ if (transfer) { /* if the transfer was really aborted we remove the downloaded file */ if (transfer->size > 0 || !(sock->userflags & NUT_FLAG_HDR)) {#if ENABLE_DEBUG svz_log (LOG_DEBUG, "nut: downloading `%s' aborted\n", transfer->file);#endif if (unlink (transfer->file) == -1) svz_log (LOG_ERROR, "nut: unlink: %s\n", SYS_ERROR); } /* * send a push request if the connection process itself has been * aborted (refused or no route) */ if (sock->userflags & NUT_FLAG_DNLOAD && !(sock->userflags & NUT_FLAG_HDR)) { nut_send_push (sock->cfg, sock->data); } svz_free (transfer->file); svz_free (transfer); sock->data = NULL; } return 0;}/* * This routine tries to connect to a foreign gnutella host in order to * get a certain file. */intnut_init_transfer (svz_socket_t *sock, nut_reply_t *reply, nut_record_t *record, char *savefile){ nut_config_t *cfg = sock->cfg; svz_socket_t *xsock; char *ext, *file, *pattern; struct stat buf; int fd; nut_transfer_t *transfer; int n = 0, pos; /* has the requested file the right file extension ? */ if (cfg->extensions) { /* go through all file extensions */ svz_array_foreach (cfg->extensions, ext, n) { if (strlen (savefile) > strlen (ext)) { pos = strlen (savefile) - strlen (ext); if (pos < 0 || !svz_strcasecmp (&savefile[pos], ext)) break; } } /* did the above code "break" ? */ if ((unsigned long) n >= svz_array_size (cfg->extensions)) { svz_log (LOG_WARNING, "nut: not a valid extension: %s\n", savefile); return -1; } } /* first check if the requested file is not already created */ file = svz_malloc (strlen (cfg->save_path) + strlen (savefile) + 2); sprintf (file, "%s/%s", cfg->save_path, savefile); if (stat (file, &buf) != -1) { svz_log (LOG_NOTICE, "nut: %s already exists\n", savefile); svz_free (file); return -1; } /* second check if the file matches the original search patterns */ svz_array_foreach (cfg->search, pattern, n) { if (nut_string_regex (savefile, pattern)) break; } if ((unsigned long) n >= svz_array_size (cfg->search)) { svz_log (LOG_NOTICE, "nut: no search pattern for %s\n", savefile); svz_free (file); return -1; } /* try creating local file */ if ((fd = open (file, O_RDWR | O_CREAT | O_BINARY, 0644)) == -1) { svz_log (LOG_ERROR, "nut: open: %s\n", SYS_ERROR); svz_free (file); return -1; } /* try to connect to the host */ if ((xsock = svz_tcp_connect (reply->ip, reply->port)) != NULL) { svz_log (LOG_NOTICE, "nut: connecting %s:%u\n", svz_inet_ntoa (reply->ip), ntohs (reply->port)); cfg->dnloads++; xsock->cfg = cfg; xsock->flags |= SOCK_FLAG_NOFLOOD; svz_sock_setparent (xsock, svz_sock_getparent (sock)); xsock->disconnected_socket = nut_disconnect_transfer; xsock->check_request = nut_check_transfer; xsock->userflags = NUT_FLAG_DNLOAD; xsock->file_desc = fd; xsock->idle_func = nut_connect_timeout; xsock->idle_counter = NUT_CONNECT_TIMEOUT; /* initialize transfer data */ transfer = svz_malloc (sizeof (nut_transfer_t)); memset (transfer, 0, sizeof (nut_transfer_t)); transfer->original_size = record->size; transfer->file = svz_strdup (file); transfer->start = time (NULL); xsock->data = transfer; /* save all data for sending a push request some time later */ memcpy (transfer->guid, reply->id, NUT_GUID_SIZE); transfer->index = record->index; transfer->id = sock->id; transfer->version = sock->version; /* send HTTP request to the listening gnutella host */ svz_sock_printf (xsock, NUT_GET "%d/%s " NUT_HTTP "1.0\r\n", record->index, savefile); svz_sock_printf (xsock, NUT_AGENT); svz_sock_printf (xsock, NUT_RANGE ": bytes=0-\r\n"); svz_sock_printf (xsock, "\r\n"); svz_free (file); return 0; } close (fd); svz_free (file); return 0;}/* * This is the check_request callback for given files. */intnut_check_given (svz_socket_t *sock){ nut_config_t *cfg = sock->cfg; int fill = sock->recv_buffer_fill; char *p = sock->recv_buffer; nut_transfer_t *transfer; char *pushkey, *file; struct stat buf; int len, fd; /* check if we got the whole "GIV " line */ while (p < sock->recv_buffer + (fill - 1) && memcmp (p, "\n\n", 2)) p++; if (p < sock->recv_buffer + (fill - 1) && !memcmp (p, "\n\n", 2)) { len = p + 2 - sock->recv_buffer; /* find start of file name */ pushkey = p = sock->recv_buffer + strlen (NUT_GIVE); while (p < sock->recv_buffer + fill && *p != '/') p++; if (p >= sock->recv_buffer + fill || *p != '/') { svz_log (LOG_ERROR, "nut: invalid GIV line\n"); return -1; } /* get original push request */ *p = '\0'; transfer = (nut_transfer_t *) svz_hash_get (cfg->push, pushkey); if (transfer == NULL) { svz_log (LOG_ERROR, "nut: no such push request sent\n"); return -1; } /* delete key and data from push request hash */ svz_hash_delete (cfg->push, pushkey); svz_sock_reduce_recv (sock, len); /* assign all necessary callbacks for downloading the file */ sock->data = transfer; cfg->dnloads++; sock->flags |= SOCK_FLAG_NOFLOOD; sock->disconnected_socket = nut_disconnect_transfer; sock->check_request = nut_check_transfer; sock->userflags |= NUT_FLAG_DNLOAD; sock->idle_func = NULL; transfer->start = time (NULL); /* test the file to download once again */ file = transfer->file; if (stat (file, &buf) != -1) { svz_log (LOG_NOTICE, "nut: %s already exists\n", file); return -1; } /* try creating local file */ if ((fd = open (file, O_RDWR | O_CREAT | O_BINARY, 0644)) == -1) { svz_log (LOG_ERROR, "nut: open: %s\n", SYS_ERROR); return -1; } /* assign file descriptor and find original file name */ sock->file_desc = fd; file = file + strlen (file); while (*file != '/' && *file != '\\' && file > transfer->file) file--; if (*file == '/' || *file == '\\') file++; /* send HTTP request to the listening gnutella host */ svz_sock_printf (sock, NUT_GET "%d/%s " NUT_HTTP "1.0\r\n", transfer->index, file); svz_sock_printf (sock, NUT_AGENT); svz_sock_printf (sock, NUT_RANGE ": bytes=0-\r\n"); svz_sock_printf (sock, "\r\n"); } return 0;}/* * The routine is called when a connection to another host timed out. * When trying to get a remote file from a host behind a masquerading * gateway or firewall you are able to request this host to connect to * ourselves and thus "push" the download connection. There is NO way * if both of the hosts are behind such a gateway. */intnut_send_push (nut_config_t *cfg, nut_transfer_t *transfer){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -