📄 nanoftp.c
字号:
/*
* nanoftp.c: basic FTP client support
*
* Reference: RFC 959
*/
#ifdef TESTING
#define STANDALONE
#define HAVE_STDLIB_H
#define HAVE_UNISTD_H
#define HAVE_SYS_SOCKET_H
#define HAVE_NETINET_IN_H
#define HAVE_NETDB_H
#define HAVE_SYS_TIME_H
#else /* TESTING */
#define NEED_SOCKETS
#endif /* TESTING */
#define IN_LIBXML
#include "libxml.h"
#ifdef LIBXML_FTP_ENABLED
#include <string.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/xmlerror.h>
#include <libxml/uri.h>
#include <libxml/nanoftp.h>
#include <libxml/globals.h>
/* #define DEBUG_FTP 1 */
#ifdef STANDALONE
#ifndef DEBUG_FTP
#define DEBUG_FTP 1
#endif
#endif
#ifdef __MINGW32__
#define _WINSOCKAPI_
#include <wsockcompat.h>
#include <winsock2.h>
#undef XML_SOCKLEN_T
#define XML_SOCKLEN_T unsigned int
#endif
/**
* A couple portability macros
*/
#ifndef _WINSOCKAPI_
#ifndef __BEOS__
#define closesocket(s) close(s)
#endif
#define SOCKET int
#endif
#ifdef __BEOS__
#ifndef PF_INET
#define PF_INET AF_INET
#endif
#endif
#ifdef _AIX
#define ss_family __ss_family
#endif
#ifndef XML_SOCKLEN_T
#define XML_SOCKLEN_T unsigned int
#endif
#define FTP_COMMAND_OK 200
#define FTP_SYNTAX_ERROR 500
#define FTP_GET_PASSWD 331
#define FTP_BUF_SIZE 1024
#define XML_NANO_MAX_URLBUF 4096
typedef struct xmlNanoFTPCtxt {
char *protocol; /* the protocol name */
char *hostname; /* the host name */
int port; /* the port */
char *path; /* the path within the URL */
char *user; /* user string */
char *passwd; /* passwd string */
#ifdef SUPPORT_IP6
struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
#else
struct sockaddr_in ftpAddr; /* the socket address struct */
#endif
int passive; /* currently we support only passive !!! */
SOCKET controlFd; /* the file descriptor for the control socket */
SOCKET dataFd; /* the file descriptor for the data socket */
int state; /* WRITE / READ / CLOSED */
int returnValue; /* the protocol return value */
/* buffer for data received from the control connection */
char controlBuf[FTP_BUF_SIZE + 1];
int controlBufIndex;
int controlBufUsed;
int controlBufAnswer;
} xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
static int initialized = 0;
static char *proxy = NULL; /* the proxy name if any */
static int proxyPort = 0; /* the proxy port if any */
static char *proxyUser = NULL; /* user for proxy authentication */
static char *proxyPasswd = NULL;/* passwd for proxy authentication */
static int proxyType = 0; /* uses TYPE or a@b ? */
#ifdef SUPPORT_IP6
static
int have_ipv6(void) {
int s;
s = socket (AF_INET6, SOCK_STREAM, 0);
if (s != -1) {
close (s);
return (1);
}
return (0);
}
#endif
/**
* xmlFTPErrMemory:
* @extra: extra informations
*
* Handle an out of memory condition
*/
static void
xmlFTPErrMemory(const char *extra)
{
__xmlSimpleError(XML_FROM_FTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
}
/**
* xmlNanoFTPInit:
*
* Initialize the FTP protocol layer.
* Currently it just checks for proxy informations,
* and get the hostname
*/
void
xmlNanoFTPInit(void) {
const char *env;
#ifdef _WINSOCKAPI_
WSADATA wsaData;
#endif
if (initialized)
return;
#ifdef _WINSOCKAPI_
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
return;
#endif
proxyPort = 21;
env = getenv("no_proxy");
if (env && ((env[0] == '*' ) && (env[1] == 0)))
return;
env = getenv("ftp_proxy");
if (env != NULL) {
xmlNanoFTPScanProxy(env);
} else {
env = getenv("FTP_PROXY");
if (env != NULL) {
xmlNanoFTPScanProxy(env);
}
}
env = getenv("ftp_proxy_user");
if (env != NULL) {
proxyUser = xmlMemStrdup(env);
}
env = getenv("ftp_proxy_password");
if (env != NULL) {
proxyPasswd = xmlMemStrdup(env);
}
initialized = 1;
}
/**
* xmlNanoFTPCleanup:
*
* Cleanup the FTP protocol layer. This cleanup proxy informations.
*/
void
xmlNanoFTPCleanup(void) {
if (proxy != NULL) {
xmlFree(proxy);
proxy = NULL;
}
if (proxyUser != NULL) {
xmlFree(proxyUser);
proxyUser = NULL;
}
if (proxyPasswd != NULL) {
xmlFree(proxyPasswd);
proxyPasswd = NULL;
}
#ifdef _WINSOCKAPI_
if (initialized)
WSACleanup();
#endif
initialized = 0;
}
/**
* xmlNanoFTPProxy:
* @host: the proxy host name
* @port: the proxy port
* @user: the proxy user name
* @passwd: the proxy password
* @type: the type of proxy 1 for using SITE, 2 for USER a@b
*
* Setup the FTP proxy informations.
* This can also be done by using ftp_proxy ftp_proxy_user and
* ftp_proxy_password environment variables.
*/
void
xmlNanoFTPProxy(const char *host, int port, const char *user,
const char *passwd, int type) {
if (proxy != NULL) {
xmlFree(proxy);
proxy = NULL;
}
if (proxyUser != NULL) {
xmlFree(proxyUser);
proxyUser = NULL;
}
if (proxyPasswd != NULL) {
xmlFree(proxyPasswd);
proxyPasswd = NULL;
}
if (host)
proxy = xmlMemStrdup(host);
if (user)
proxyUser = xmlMemStrdup(user);
if (passwd)
proxyPasswd = xmlMemStrdup(passwd);
proxyPort = port;
proxyType = type;
}
/**
* xmlNanoFTPScanURL:
* @ctx: an FTP context
* @URL: The URL used to initialize the context
*
* (Re)Initialize an FTP context by parsing the URL and finding
* the protocol host port and path it indicates.
*/
static void
xmlNanoFTPScanURL(void *ctx, const char *URL) {
xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
xmlURIPtr uri;
/*
* Clear any existing data from the context
*/
if (ctxt->protocol != NULL) {
xmlFree(ctxt->protocol);
ctxt->protocol = NULL;
}
if (ctxt->hostname != NULL) {
xmlFree(ctxt->hostname);
ctxt->hostname = NULL;
}
if (ctxt->path != NULL) {
xmlFree(ctxt->path);
ctxt->path = NULL;
}
if (URL == NULL) return;
uri = xmlParseURI(URL);
if (uri == NULL)
return;
if ((uri->scheme == NULL) || (uri->server == NULL)) {
xmlFreeURI(uri);
return;
}
ctxt->protocol = xmlMemStrdup(uri->scheme);
ctxt->hostname = xmlMemStrdup(uri->server);
if (uri->path != NULL)
ctxt->path = xmlMemStrdup(uri->path);
else
ctxt->path = xmlMemStrdup("/");
if (uri->port != 0)
ctxt->port = uri->port;
if (uri->user != NULL) {
char *cptr;
if ((cptr=strchr(uri->user, ':')) == NULL)
ctxt->user = xmlMemStrdup(uri->user);
else {
ctxt->user = (char *)xmlStrndup((xmlChar *)uri->user,
(cptr - uri->user));
ctxt->passwd = xmlMemStrdup(cptr+1);
}
}
xmlFreeURI(uri);
}
/**
* xmlNanoFTPUpdateURL:
* @ctx: an FTP context
* @URL: The URL used to update the context
*
* Update an FTP context by parsing the URL and finding
* new path it indicates. If there is an error in the
* protocol, hostname, port or other information, the
* error is raised. It indicates a new connection has to
* be established.
*
* Returns 0 if Ok, -1 in case of error (other host).
*/
int
xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
xmlURIPtr uri;
if (URL == NULL)
return(-1);
if (ctxt == NULL)
return(-1);
if (ctxt->protocol == NULL)
return(-1);
if (ctxt->hostname == NULL)
return(-1);
uri = xmlParseURI(URL);
if (uri == NULL)
return(-1);
if ((uri->scheme == NULL) || (uri->server == NULL)) {
xmlFreeURI(uri);
return(-1);
}
if ((strcmp(ctxt->protocol, uri->scheme)) ||
(strcmp(ctxt->hostname, uri->server)) ||
((uri->port != 0) && (ctxt->port != uri->port))) {
xmlFreeURI(uri);
return(-1);
}
if (uri->port != 0)
ctxt->port = uri->port;
if (ctxt->path != NULL) {
xmlFree(ctxt->path);
ctxt->path = NULL;
}
if (uri->path == NULL)
ctxt->path = xmlMemStrdup("/");
else
ctxt->path = xmlMemStrdup(uri->path);
xmlFreeURI(uri);
return(0);
}
/**
* xmlNanoFTPScanProxy:
* @URL: The proxy URL used to initialize the proxy context
*
* (Re)Initialize the FTP Proxy context by parsing the URL and finding
* the protocol host port it indicates.
* Should be like ftp://myproxy/ or ftp://myproxy:3128/
* A NULL URL cleans up proxy informations.
*/
void
xmlNanoFTPScanProxy(const char *URL) {
xmlURIPtr uri;
if (proxy != NULL) {
xmlFree(proxy);
proxy = NULL;
}
proxyPort = 0;
#ifdef DEBUG_FTP
if (URL == NULL)
xmlGenericError(xmlGenericErrorContext,
"Removing FTP proxy info\n");
else
xmlGenericError(xmlGenericErrorContext,
"Using FTP proxy %s\n", URL);
#endif
if (URL == NULL) return;
uri = xmlParseURI(URL);
if ((uri == NULL) || (uri->scheme == NULL) ||
(strcmp(uri->scheme, "ftp")) || (uri->server == NULL)) {
__xmlIOErr(XML_FROM_FTP, XML_FTP_URL_SYNTAX, "Syntax Error\n");
if (uri != NULL)
xmlFreeURI(uri);
return;
}
proxy = xmlMemStrdup(uri->server);
if (uri->port != 0)
proxyPort = uri->port;
xmlFreeURI(uri);
}
/**
* xmlNanoFTPNewCtxt:
* @URL: The URL used to initialize the context
*
* Allocate and initialize a new FTP context.
*
* Returns an FTP context or NULL in case of error.
*/
void*
xmlNanoFTPNewCtxt(const char *URL) {
xmlNanoFTPCtxtPtr ret;
char *unescaped;
ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
if (ret == NULL) {
xmlFTPErrMemory("allocating FTP context");
return(NULL);
}
memset(ret, 0, sizeof(xmlNanoFTPCtxt));
ret->port = 21;
ret->passive = 1;
ret->returnValue = 0;
ret->controlBufIndex = 0;
ret->controlBufUsed = 0;
ret->controlFd = -1;
unescaped = xmlURIUnescapeString(URL, 0, NULL);
if (unescaped != NULL) {
xmlNanoFTPScanURL(ret, unescaped);
xmlFree(unescaped);
} else if (URL != NULL)
xmlNanoFTPScanURL(ret, URL);
return(ret);
}
/**
* xmlNanoFTPFreeCtxt:
* @ctx: an FTP context
*
* Frees the context after closing the connection.
*/
void
xmlNanoFTPFreeCtxt(void * ctx) {
xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
if (ctxt == NULL) return;
if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
if (ctxt->path != NULL) xmlFree(ctxt->path);
ctxt->passive = 1;
if (ctxt->controlFd >= 0) closesocket(ctxt->controlFd);
ctxt->controlFd = -1;
ctxt->controlBufIndex = -1;
ctxt->controlBufUsed = -1;
xmlFree(ctxt);
}
/**
* xmlNanoFTPParseResponse:
* @buf: the buffer containing the response
* @len: the buffer length
*
* Parsing of the server answer, we just extract the code.
*
* returns 0 for errors
* +XXX for last line of response
* -XXX for response to be continued
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -