⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nut-transfer.c

📁 Serveez是一个服务器框架
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -