/*
* 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
#endif
#ifdef HAVE_NETINET_IN_H
#endif
#ifdef HAVE_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
#endif
#ifdef HAVE_SYS_SELECT_H
#endif
#ifdef HAVE_SYS_SOCKET_H
#endif
#ifdef HAVE_SYS_TYPES_H
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <libxml/xmlmemory.h>
#include <libxml/xmlerror.h>
/* #define DEBUG_FTP 1 */
#ifdef STANDALONE
#ifndef DEBUG_FTP
#endif
#endif
#if defined(__MINGW32__) || defined(_WIN32_WCE)
#define _WINSOCKAPI_
#include <wsockcompat.h>
#include <winsock2.h>
#define XML_SOCKLEN_T unsigned int
#endif
/**
* A couple portability macros
*/
#ifndef _WINSOCKAPI_
#ifndef __BEOS__
#endif
#define SOCKET int
#endif
#ifdef __BEOS__
#ifndef PF_INET
#endif
#endif
#ifdef _AIX
#ifdef HAVE_BROKEN_SS_FAMILY
#endif
#endif
#ifndef XML_SOCKLEN_T
#define XML_SOCKLEN_T unsigned int
#endif
typedef struct xmlNanoFTPCtxt {
#ifdef SUPPORT_IP6
#else
#endif
/* buffer for data received from the control connection */
int controlBufIndex;
int controlBufUsed;
int controlBufAnswer;
static int initialized = 0;
#ifdef SUPPORT_IP6
static
int have_ipv6(void) {
int s;
if (s != -1) {
close (s);
return (1);
}
return (0);
}
#endif
/**
* xmlFTPErrMemory:
* @extra: extra informations
*
* Handle an out of memory condition
*/
static void
{
}
/**
* 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_
#endif
if (initialized)
return;
#ifdef _WINSOCKAPI_
return;
#endif
proxyPort = 21;
return;
} else {
}
}
}
}
initialized = 1;
}
/**
* xmlNanoFTPCleanup:
*
* Cleanup the FTP protocol layer. This cleanup proxy informations.
*/
void
xmlNanoFTPCleanup(void) {
}
}
if (proxyPasswd != NULL) {
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
}
}
if (proxyPasswd != NULL) {
proxyPasswd = NULL;
}
if (host)
if (user)
if (passwd)
}
/**
* 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
/*
* Clear any existing data from the context
*/
}
}
}
return;
return;
}
else
char *cptr;
else {
}
}
}
/**
* 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
return(-1);
return(-1);
return(-1);
return(-1);
return(-1);
return(-1);
}
return(-1);
}
}
else
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
}
proxyPort = 0;
#ifdef DEBUG_FTP
"Removing FTP proxy info\n");
else
"Using FTP proxy %s\n", URL);
#endif
return;
}
}
/**
* 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*
char *unescaped;
xmlFTPErrMemory("allocating FTP context");
return(NULL);
}
ret->returnValue = 0;
ret->controlBufIndex = 0;
ret->controlBufUsed = 0;
return(ret);
}
/**
* xmlNanoFTPFreeCtxt:
* @ctx: an FTP context
*
* Frees the context after closing the connection.
*/
void
}
/**
* 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
*/
static int
int val = 0;
else
return(0);
buf++;
else
return(0);
buf++;
else
return(0);
buf++;
if (*buf == '-')
return(-val);
return(val);
}
/**
* xmlNanoFTPGetMore:
* @ctx: an FTP context
*
* Read more information from the FTP control connection
* Returns the number of bytes read, < 0 indicates an error
*/
static int
int len;
int size;
#ifdef DEBUG_FTP
"xmlNanoFTPGetMore : controlBufIndex = %d\n",
#endif
return(-1);
}
#ifdef DEBUG_FTP
"xmlNanoFTPGetMore : controlBufUsed = %d\n",
#endif
return(-1);
}
#ifdef DEBUG_FTP
"xmlNanoFTPGetMore : controlBufIndex > controlBufUsed %d > %d\n",
#endif
return(-1);
}
/*
* First pack the control buffer
*/
if (ctxt->controlBufIndex > 0) {
ctxt->controlBufIndex = 0;
}
if (size == 0) {
#ifdef DEBUG_FTP
#endif
return(0);
}
/*
* Read the amount left on the control connection
*/
size, 0)) < 0) {
return(-1);
}
#ifdef DEBUG_FTP
"xmlNanoFTPGetMore : read %d [%d - %d]\n", len,
#endif
return(len);
}
/**
* xmlNanoFTPReadResponse:
* @ctx: an FTP context
*
* Read the response from the FTP server after a command.
* Returns the code number
*/
static int
int len;
/*
* Assumes everything up to controlBuf[controlBufIndex] has been read
* and analyzed.
*/
if (len < 0) {
return(-1);
}
return(-1);
}
#ifdef DEBUG_FTP
"\n<<<\n%s\n--\n", ptr);
#endif
if (cur > 0) {
/*
* Successfully scanned the control code, scratch
* till the end of the line, but keep the index to be
* able to analyze the result if needed.
*/
ptr += 3;
break;
}
goto get_more;
}
}
#ifdef DEBUG_FTP
#endif
#ifdef DEBUG_FTP
#endif
return(res / 100);
}
/**
* xmlNanoFTPGetResponse:
* @ctx: an FTP context
*
* Get the response from the FTP server after a command.
* Returns the code number
*/
int
int res;
return(res);
}
/**
* xmlNanoFTPCheckResponse:
* @ctx: an FTP context
*
* Check if there is a response from the FTP server after a command.
* Returns the code number, or 0
*/
int
case 0:
return(0);
case -1:
return(-1);
}
return(xmlNanoFTPReadResponse(ctx));
}
/**
* Send the user authentication
*/
static int
int len;
int res;
else
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
return(0);
}
/**
* Send the password authentication
*/
static int
int len;
int res;
else
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
return(0);
}
/**
* xmlNanoFTPQuit:
* @ctx: an FTP context
*
* Send a QUIT command to the server
*
* Returns -1 in case of error, 0 otherwise
*/
int
#ifdef DEBUG_FTP
xmlGenericError(xmlGenericErrorContext, "%s", buf); /* Just to be consistent, even though we know it can't have a % in it */
#endif
if (res < 0) {
return(res);
}
return(0);
}
/**
* xmlNanoFTPConnect:
* @ctx: an FTP context
*
* Tries to open a control connection
*
* Returns -1 in case of error, 0 otherwise
*/
int
int port;
int res;
return(-1);
return(-1);
/*
* do the blocking DNS query.
*/
if (proxy) {
} else {
}
if (port == 0)
port = 21;
#ifdef SUPPORT_IP6
if (have_ipv6 ()) {
if (proxy) {
return (-1);
}
}
else
return (-1);
}
break;
if (!tmp) {
if (result)
return (-1);
}
return (-1);
}
}
else {
}
}
else
#endif
{
if (proxy)
else
return (-1);
}
return (-1);
}
/*
* Prepare the socket
*/
addrlen = sizeof (struct sockaddr_in);
}
return(-1);
}
/*
* Do the connect.
*/
addrlen) < 0) {
return(-1);
}
/*
* Wait for the HELLO from the server.
*/
if (res != 2) {
return(-1);
}
/*
* State diagram for the login operation on the FTP server
*
* Reference: RFC 959
*
* 1
* +---+ USER +---+------------->+---+
* | B |---------->| W | 2 ---->| E |
* +---+ +---+------ | -->+---+
* | | | | |
* 3 | | 4,5 | | |
* -------------- ----- | | |
* | | | | |
* | | | | |
* | --------- |
* | 1| | | |
* V | | | |
* +---+ PASS +---+ 2 | ------>+---+
* | |---------->| W |------------->| S |
* +---+ +---+ ---------->+---+
* | | | | |
* 3 | |4,5| | |
* -------------- -------- |
* | | | | |
* | | | | |
* | -----------
* | 1,3| | | |
* V | 2| | |
* +---+ ACCT +---+-- | ----->+---+
* | |---------->| W | 4,5 -------->| F |
* +---+ +---+------------->+---+
*
* Of course in case of using a proxy this get really nasty and is not
* standardized at all :-(
*/
if (proxy) {
int len;
/*
* We need proxy auth
*/
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
switch (res) {
case 2:
if (proxyPasswd == NULL)
break;
case 3:
if (proxyPasswd != NULL)
else
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
if (res > 3) {
return(-1);
}
break;
case 1:
break;
case 4:
case 5:
case -1:
default:
return(-1);
}
}
/*
* We assume we don't need more authentication to the proxy
* and that it succeeded :-\
*/
switch (proxyType) {
case 0:
/* we will try in sequence */
case 1:
/* Using SITE command */
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
if (res == 2) {
/* we assume it worked :-\ 1 is error for SITE command */
proxyType = 1;
break;
}
if (proxyType == 1) {
return(-1);
}
case 2:
/* USER user@host command */
else
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
/* we assume it worked :-\ */
proxyType = 2;
return(0);
}
else
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
/* we assume it worked :-\ */
proxyType = 2;
return(0);
}
if (proxyType == 2) {
return(-1);
}
case 3:
/*
* If you need support for other Proxy authentication scheme
* send the code or at least the sequence in use.
*/
default:
return(-1);
}
}
/*
* Non-proxy handling.
*/
if (res < 0) {
return(-1);
}
switch (res) {
case 2:
return(0);
case 3:
break;
case 1:
case 4:
case 5:
case -1:
default:
return(-1);
}
if (res < 0) {
return(-1);
}
switch (res) {
case 2:
break;
case 3:
"FTP server asking for ACCNT on anonymous\n");
case 1:
case 4:
case 5:
case -1:
default:
return(-1);
}
return(0);
}
/**
* xmlNanoFTPConnectTo:
* @server: an FTP server name
* @port: the port (use 21 if 0)
*
*
* Returns an fTP context or NULL if it failed
*/
void*
int res;
return(NULL);
if (port <= 0)
return(NULL);
if (port != 0)
if (res < 0) {
return(NULL);
}
return(ctxt);
}
/**
* xmlNanoFTPCwd:
* @ctx: an FTP context
* @directory: a directory on the server
*
* Tries to change the remote directory
*
* Returns -1 incase of error, 1 if CWD worked, 0 if it failed
*/
int
int len;
int res;
/*
* Expected response code for CWD:
*
* CWD
* 250
* 500, 501, 502, 421, 530, 550
*/
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
if (res == 4) {
return(-1);
}
if (res == 5) {
return(0);
}
return(0);
}
/**
* xmlNanoFTPDele:
* @ctx: an FTP context
* @file: a file or directory on the server
*
* Tries to delete an item (file or directory) from server
*
* Returns -1 incase of error, 1 if DELE worked, 0 if it failed
*/
int
int len;
int res;
/*
* Expected response code for DELE:
*
* DELE
* 250
* 450, 550
* 500, 501, 502, 421, 530
*/
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
if (res == 4) {
return(-1);
}
if (res == 5) {
return(0);
}
return(0);
}
/**
* xmlNanoFTPGetConnection:
* @ctx: an FTP context
*
* Try to open a data connection to the server. Currently only
* passive mode is supported.
*
* Returns -1 incase of error, 0 otherwise
*/
int
int len, i;
int res;
#ifdef SUPPORT_IP6
#else
#endif
#ifdef SUPPORT_IP6
dataAddrLen = sizeof(struct sockaddr_in6);
} else
#endif
{
dataAddrLen = sizeof (struct sockaddr_in);
}
return (-1);
}
#ifdef SUPPORT_IP6
else
#endif
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
if (res != 2) {
if (res == 5) {
return(-1);
} else {
/*
* retry with an active connection
*/
}
}
#ifdef SUPPORT_IP6
"Invalid answer to EPSV\n");
}
return (-1);
}
memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
}
else
#endif
{
"Invalid answer to PASV\n");
}
return (-1);
}
}
return (-1);
}
} else {
#ifdef SUPPORT_IP6
else
#endif
return (-1);
}
return (-1);
}
#ifdef SUPPORT_IP6
} else
#endif
{
}
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
if (res != 2) {
return(-1);
}
}
}
/**
* xmlNanoFTPCloseConnection:
* @ctx: an FTP context
*
* Close the data connection from the server
*
* Returns -1 incase of error, 0 otherwise
*/
int
int res;
if (res < 0) {
#ifdef DEBUG_FTP
perror("select");
#endif
return(-1);
}
if (res == 0) {
#ifdef DEBUG_FTP
"xmlNanoFTPCloseConnection: timeout\n");
#endif
} else {
if (res != 2) {
return(-1);
}
}
return(0);
}
/**
* xmlNanoFTPParseList:
* @list: some data listing received from the server
* @callback: the user callback
* @userData: the user callback data
*
* Parse at most one entry from the listing.
*
* Returns -1 incase of error, the length of data parsed otherwise
*/
static int
int year = 0;
int minute = 0;
int hour = 0;
int day = 0;
unsigned long size = 0;
int links = 0;
int i;
cur += 5;
cur++;
} else if (*list == '+') {
return(0);
} else {
cur++;
if (*cur == 0) return(0);
i = 0;
while (*cur != ' ') {
if (i < 10)
cur++;
if (*cur == 0) return(0);
}
attrib[10] = 0;
if (*cur == 0) return(0);
if (*cur == 0) return(0);
i = 0;
while (*cur != ' ') {
if (i < 10)
cur++;
if (*cur == 0) return(0);
}
owner[i] = 0;
if (*cur == 0) return(0);
i = 0;
while (*cur != ' ') {
if (i < 10)
cur++;
if (*cur == 0) return(0);
}
group[i] = 0;
if (*cur == 0) return(0);
if (*cur == 0) return(0);
i = 0;
while (*cur != ' ') {
if (i < 3)
cur++;
if (*cur == 0) return(0);
}
month[i] = 0;
if (*cur == 0) return(0);
if (*cur == 0) return(0);
} else {
}
if (*cur == 0) return(0);
i = 0;
if (i < 150)
cur++;
if (*cur == 0) return(0);
}
filename[i] = 0;
return(0);
cur++;
}
}
}
/**
* xmlNanoFTPList:
* @ctx: an FTP context
* @callback: the user callback
* @userData: the user callback data
* @filename: optional files to list
*
* Do a listing on the server. All files info are passed back
* in the callbacks.
*
* Returns -1 incase of error, 0 otherwise
*/
int
const char *filename) {
return(-1);
return(-1);
} else {
if (filename[0] != '/') {
return(-1);
}
return(-1);
}
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
if (res != 1) {
return(-res);
}
do {
if (res < 0) {
#ifdef DEBUG_FTP
perror("select");
#endif
return(-1);
}
if (res == 0) {
if (res < 0) {
return(-1);
}
if (res == 2) {
return(0);
}
continue;
}
return(-1);
}
#ifdef DEBUG_FTP
#endif
base = 0;
do {
} while (res > 0);
} while (len != 0);
return(0);
}
/**
* xmlNanoFTPGetSocket:
* @ctx: an FTP context
* @filename: the file to retrieve (or NULL if path is in context).
*
* Initiate fetch of the given file from the server.
*
* Returns the socket for the data connection, or <0 in case of error
*/
int
return(-1);
return(-1);
return(-1);
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
if (res != 2) {
return(-res);
}
else
#ifdef DEBUG_FTP
#endif
if (res < 0) {
return(res);
}
if (res != 1) {
return(-res);
}
}
/**
* xmlNanoFTPGet:
* @ctx: an FTP context
* @callback: the user callback
* @userData: the user callback data
* @filename: the file to retrieve
*
* Fetch the given file from the server. All data are passed back
* in the callbacks. The last callback has a size of 0 block.
*
* Returns -1 incase of error, 0 otherwise
*/
int
const char *filename) {
return(-1);
return(-1);
return(-1);
do {
if (res < 0) {
#ifdef DEBUG_FTP
perror("select");
#endif
return(-1);
}
if (res == 0) {
if (res < 0) {
return(-1);
}
if (res == 2) {
return(0);
}
continue;
}
return(-1);
}
} while (len != 0);
return(xmlNanoFTPCloseConnection(ctxt));
}
/**
* xmlNanoFTPRead:
* @ctx: the FTP context
* @dest: a buffer
* @len: the buffer length
*
* This function tries to read @len bytes from the existing FTP connection
* and saves them in @dest. This is a blocking call.
*
* Returns the number of byte read. 0 is an indication of an end of connection.
* -1 indicates a parameter error.
*/
int
if (len <= 0) return(0);
if (len <= 0) {
if (len < 0)
}
#ifdef DEBUG_FTP
#endif
return(len);
}
/**
* xmlNanoFTPOpen:
* @URL: the URL to the resource
*
* Start to fetch the given ftp:// resource
*
* Returns an FTP context, or NULL
*/
void*
int sock;
if (xmlNanoFTPConnect(ctxt) < 0) {
return(NULL);
}
if (sock < 0) {
return(NULL);
}
return(ctxt);
}
/**
* xmlNanoFTPClose:
* @ctx: an FTP context
*
* Close the connection and both control and transport
*
* Returns -1 incase of error, 0 otherwise
*/
int
return(-1);
}
}
return(0);
}
#ifdef STANDALONE
/************************************************************************
* *
* Basic test in Standalone mode *
* *
************************************************************************/
static
}
static
if (len <= 0) {
return;
}
}
void *ctxt;
if (argc > 1) {
if (xmlNanoFTPConnect(ctxt) < 0) {
exit(1);
}
if (argc > 2)
} else
"Couldn't connect to localhost\n");
exit(1);
}
"Failed to get file\n");
}
exit(0);
}
#endif /* STANDALONE */
#else /* !LIBXML_FTP_ENABLED */
#ifdef STANDALONE
#include <stdio.h>
"%s : FTP support not compiled in\n", argv[0]);
return(0);
}
#endif /* STANDALONE */
#endif /* LIBXML_FTP_ENABLED */
#define bottom_nanoftp
#include "elfgcchack.h"