Files
Mozilla/mozilla/xpinstall/wizard/libxpnet/src/nsFTPConn.cpp
syd%netscape.com d5d69c4e06 Fix for 78282 r=ssu, sr=mscott
git-svn-id: svn://10.0.0.236/trunk@94799 18797224-902f-48f8-a5cc-f745e15eee43
2001-05-13 00:45:45 +00:00

582 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code,
* released March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Samir Gehani <sgehani@netscape.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#if defined(__unix) || defined(__unix__)
#include <sys/param.h>
#elif defined(_WINDOWS)
#include <windows.h>
#define MAXPATHLEN MAX_PATH
#elif defined(macintosh)
#define MAXPATHLEN 512
#endif
#include "nsSocket.h"
#include "nsFTPConn.h"
const int kCntlPort = 21;
const int kDataPort = 20;
const int kCmdBufSize = 64 + MAXPATHLEN;
const int kRespBufSize = 1024;
const int kKilobyte = 1024;
const int kUsecsPerSec = 1000000;
const int kDlBufSize = 1024;
nsFTPConn::nsFTPConn(char *aHost, int (*aEventPumpCB)(void)) :
mEventPumpCB(aEventPumpCB),
mHost(aHost),
mState(CLOSED),
mPassive(FALSE),
mCntlSock(NULL),
mDataSock(NULL)
{
}
nsFTPConn::nsFTPConn(char *aHost) :
mEventPumpCB(NULL),
mHost(aHost),
mState(CLOSED),
mPassive(FALSE),
mCntlSock(NULL),
mDataSock(NULL)
{
}
nsFTPConn::~nsFTPConn()
{
// don't release mHost cause we don't own it
}
int
nsFTPConn::Open()
{
int err = OK;
char cmd[kCmdBufSize], resp[kRespBufSize];
int respBufSize = kRespBufSize;
if (!mHost)
return E_PARAM;
if (mState != CLOSED)
return E_ALREADY_OPEN;
/* open control connection on port 21 */
mCntlSock = new nsSocket(mHost, kCntlPort);
if (!mCntlSock)
return E_MEM;
ERR_CHECK(mCntlSock->Open());
ERR_CHECK(mCntlSock->Recv((unsigned char *)resp, &respBufSize));
DUMP(resp);
/* issue USER command on control connection */
sprintf(cmd, "USER anonymous\r\n");
err = IssueCmd(cmd, resp, kRespBufSize, mCntlSock);
/* issue PASS command on control connection */
sprintf(cmd, "PASS -linux@installer.sbg\r\n");
ERR_CHECK(IssueCmd(cmd, resp, kRespBufSize, mCntlSock));
mState = OPEN;
return err;
BAIL:
if (mCntlSock)
{
mCntlSock->Close();
delete mCntlSock;
mCntlSock = NULL;
}
if (mDataSock)
{
mDataSock->Close();
delete mDataSock;
mDataSock = NULL;
}
return err;
}
int
nsFTPConn::Open(char *aHost)
{
if (!aHost)
return E_PARAM;
mHost = aHost;
return Open();
}
int
nsFTPConn::ResumeOrGet(char *aSrvPath, char *aLoclPath, int aType,
int aOvWrite, FTPGetCB aCBFunc)
{
struct stat stbuf;
int err = OK;
int resPos = 0;
if (!aLoclPath)
return E_PARAM;
/* stat local file */
err = stat(aLoclPath, &stbuf);
if (err == 0)
resPos = stbuf.st_size;
return Get(aSrvPath, aLoclPath, aType, resPos, aOvWrite, aCBFunc);
}
int
nsFTPConn::Get(char *aSrvPath, char *aLoclPath, int aType,
int aOvWrite, FTPGetCB aCBFunc)
{
// deprecated API; wrapper for backwards compatibility
return ResumeOrGet(aSrvPath, aLoclPath, aType, aOvWrite, aCBFunc);
}
int
nsFTPConn::Get(char *aSrvPath, char *aLoclPath, int aType, int aResumePos,
int aOvWrite, FTPGetCB aCBFunc)
{
struct stat dummy;
int err = OK, wrote = 0, totBytesRd = 0;
char cmd[kCmdBufSize], resp[kRespBufSize];
int fileSize = 0, respBufSize = kRespBufSize;
FILE *loclfd = NULL;
if (!aSrvPath || !aLoclPath)
return E_PARAM;
if (mState != OPEN || !mCntlSock)
return E_NOT_OPEN;
/* stat local path and verify aOvWrite is set if file already exists */
err = stat(aLoclPath, &dummy);
if (err != -1 && aOvWrite == FALSE)
return E_CANT_OVWRITE;
mState = GETTING;
/* initialize data connection */
ERR_CHECK(DataInit(mHost, kDataPort, &mDataSock));
/* issue TYPE command on control connection */
sprintf(cmd, "TYPE %s\r\n", aType==BINARY ? "I" : "A");
ERR_CHECK(IssueCmd(cmd, resp, kRespBufSize, mCntlSock));
/* issue SIZE command on control connection */
sprintf(cmd, "SIZE %s\r\n", aSrvPath);
err = IssueCmd(cmd, resp, kRespBufSize, mCntlSock); /* non-fatal */
if (err == OK && (resp[0] == '2'))
fileSize = atoi(&resp[4]);
if (aResumePos > 0)
{
/* issue restart command */
sprintf(cmd, "REST %d\r\n", aResumePos);
ERR_CHECK(IssueCmd(cmd, resp, kRespBufSize, mCntlSock));
}
/* issue RETR command on control connection */
sprintf(cmd, "RETR %s\r\n", aSrvPath);
ERR_CHECK(IssueCmd(cmd, resp, kRespBufSize, mCntlSock));
/* get file contents on data connection */
if (!mPassive)
ERR_CHECK(mDataSock->SrvAccept());
/* initialize locl file */
if (aResumePos <= 0)
{
if (!(loclfd = fopen(aLoclPath, aType==BINARY ? "w+b" : "w+")))
{
err = E_LOCL_INIT;
goto BAIL;
}
}
else
{
if (!(loclfd = fopen(aLoclPath, aType==BINARY ? "r+b" : "r+")) ||
(fseek(loclfd, aResumePos, SEEK_SET) != 0))
{
err = E_LOCL_INIT;
goto BAIL;
}
}
totBytesRd = 0;
do
{
respBufSize = kDlBufSize;
err = mDataSock->Recv((unsigned char *)resp, &respBufSize);
if (err != nsSocket::E_READ_MORE &&
err != nsSocket::E_EOF_FOUND &&
err != nsSocket::OK)
goto BAIL;
totBytesRd += respBufSize;
if (err == nsSocket::E_READ_MORE && aCBFunc)
if ((err = aCBFunc(totBytesRd, fileSize)) == E_USER_CANCEL)
goto BAIL;
/* append to local file */
wrote = fwrite((void *)resp, 1, respBufSize, loclfd);
if (wrote != respBufSize)
{
err = E_WRITE;
goto BAIL;
}
if ( mEventPumpCB )
mEventPumpCB();
}
while (err == nsSocket::E_READ_MORE || err == nsSocket::OK);
if (err == nsSocket::E_EOF_FOUND)
err = OK;
BAIL:
/* close locl file if open */
if (loclfd)
fclose(loclfd);
/* kill data connection if it exists */
if (mDataSock)
{
mDataSock->Close();
delete mDataSock;
mDataSock = NULL;
}
mState = OPEN;
mPassive = FALSE;
return err;
}
int
nsFTPConn::Close()
{
int err = OK;
if (mState != OPEN)
return E_NOT_OPEN;
/* close sockets */
if (mCntlSock)
{
ERR_CHECK(mCntlSock->Close());
delete mCntlSock;
mCntlSock = NULL;
}
if (mDataSock)
{
ERR_CHECK(mDataSock->Close());
delete mDataSock;
mDataSock = NULL;
}
BAIL:
return err;
}
int
nsFTPConn::IssueCmd(char *aCmd, char *aResp, int aRespSize, nsSocket *aSock)
{
int err = OK;
int len;
/* param check */
if (!aSock || !aCmd || !aResp || aRespSize <= 0)
return E_PARAM;
/* send command */
len = strlen(aCmd);
ERR_CHECK(aSock->Send((unsigned char *)aCmd, &len));
DUMP(aCmd);
/* receive response */
do
{
err = aSock->Recv((unsigned char *)aResp, &aRespSize);
if (err != nsSocket::OK &&
err != nsSocket::E_READ_MORE &&
err != nsSocket::E_EOF_FOUND)
goto BAIL;
DUMP(aResp);
if ( mEventPumpCB )
mEventPumpCB();
}
while (err == nsSocket::E_READ_MORE);
/* alternate interpretation of err codes */
if ( (strncmp(aCmd, "APPE", 4) == 0) ||
(strncmp(aCmd, "LIST", 4) == 0) ||
(strncmp(aCmd, "NLST", 4) == 0) ||
(strncmp(aCmd, "REIN", 4) == 0) ||
(strncmp(aCmd, "RETR", 4) == 0) ||
(strncmp(aCmd, "STOR", 4) == 0) ||
(strncmp(aCmd, "STOU", 4) == 0) )
{
switch (*aResp)
{
case '1': /* exception: 100 series is OK */
case '2':
break;
case '3':
err = E_CMD_ERR;
break;
case '4':
case '5':
err = E_CMD_FAIL;
break;
default:
err = E_CMD_UNEXPECTED;
break;
}
}
/* restart command case */
else if (strncmp(aCmd, "REST", 4) == 0)
{
switch (*aResp)
{
case '1':
case '2':
err = E_CMD_ERR;
break;
case '3':
break;
case '4':
case '5':
err = E_CMD_FAIL;
break;
default:
err = E_CMD_UNEXPECTED;
break;
}
}
/* regular interpretation of err codes */
else
{
switch (*aResp)
{
case '2':
break;
case '1':
case '3':
err = E_CMD_ERR;
break;
case '4':
case '5':
err = E_CMD_FAIL;
break;
default:
err = E_CMD_UNEXPECTED;
break;
}
}
BAIL:
return err;
}
int
nsFTPConn::ParseAddr(char *aBuf, char **aHost, int *aPort)
{
int err = OK;
char *c;
int addr[6];
/* param check */
if (!aBuf || !aHost || !aPort)
return E_PARAM;
c = aBuf + strlen("227 "); /* pass by return code */
while (!isdigit((int)(*c)))
{
if (*c == '\0')
return E_INVALID_ADDR;
c++;
}
if (sscanf(c, "%d,%d,%d,%d,%d,%d",
&addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]) != 6)
return E_INVALID_ADDR;
*aHost = (char *)malloc(strlen("XXX.XXX.XXX.XXX") + 1);
sprintf(*aHost, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
*aPort = ((addr[4] & 0xFF) << 8) | (addr[5] & 0xFF);
#ifdef DEBUG
printf("%s %d: PASV response: %d,%d,%d,%d,%d,%d\n", __FILE__, __LINE__,
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
printf("%s %d: Host = %s\tPort = %d\n", __FILE__, __LINE__, *aHost, *aPort);
#endif
return err;
}
int
nsFTPConn::DataInit(char *aHost, int aPort, nsSocket **aSock)
{
int err = OK;
char cmd[kCmdBufSize], resp[kRespBufSize];
char *srvHost = NULL;
int srvPort = 0;
char *hostPort = NULL;
/* param check */
if (!aHost || !aSock)
return E_PARAM;
/* issue PASV command */
sprintf(cmd, "PASV\r\n");
err = IssueCmd(cmd, resp, kRespBufSize, mCntlSock);
if (err != OK)
{
err = OK;
goto ACTIVE; /* failover to active mode */
}
mPassive = TRUE;
ERR_CHECK(ParseAddr(resp, &srvHost, &srvPort));
*aSock = new nsSocket(srvHost, srvPort);
if (!*aSock)
{
err = E_MEM;
goto BAIL;
}
ERR_CHECK((*aSock)->Open());
if (srvHost)
{
free(srvHost);
srvHost = NULL;
}
return err;
ACTIVE:
*aSock = new nsSocket(aHost, aPort);
if (!*aSock)
{
err = E_MEM;
goto BAIL;
}
/* init data socket making it listen */
ERR_CHECK((*aSock)->SrvOpen());
ERR_CHECK((*aSock)->GetHostPortString(&hostPort)); // allocates
if (!hostPort)
{
err = E_MEM;
goto BAIL;
}
sprintf(cmd, "PORT %s\r\n", hostPort);
ERR_CHECK(IssueCmd(cmd, resp, kRespBufSize, mCntlSock));
BAIL:
if (mPassive && err != OK)
mPassive = FALSE;
if (err != OK && (*aSock))
{
delete *aSock;
*aSock = NULL;
}
if (srvHost)
{
free(srvHost);
srvHost = NULL;
}
if (hostPort)
{
free(hostPort);
hostPort = NULL;
}
return err;
}
#ifdef TEST_NSFTPCONN
static struct timeval init;
int
TestFTPGetCB(int aBytesRd, int aTotal)
{
struct timeval now;
float rate;
gettimeofday(&now, NULL);
rate = nsSocket::CalcRate(&init, &now, aBytesRd);
printf("br=%d\ttot=%d\trt=%f\tirt=%d\n",aBytesRd, aTotal, rate, (int)rate);
return 0;
}
int
main(int argc, char **argv)
{
int err = nsFTPConn::OK;
nsFTPConn *conn = 0;
char *leaf = NULL;
if (argc < 2)
{
printf("usage: %s <host> <path/on/server>\n", argv[0]);
exit(0);
}
if ((leaf = strrchr(argv[2], '/'))) leaf++;
else leaf = argv[2];
conn = new nsFTPConn(argv[1]);
printf("Opening connection to %s...\n", argv[1]);
err = conn->Open();
if (err != nsFTPConn::OK) { printf("error: %d\n", err); exit(err); }
printf("Getting binary file %s...\n", argv[2]);
gettimeofday(&init, NULL);
err = conn->Get(argv[2], leaf, nsFTPConn::BINARY, TRUE, TestFTPGetCB);
if (err != nsFTPConn::OK) { printf("error: %d\n", err); exit(err); }
printf("Closing connection to %s...\n", argv[1]);
err = conn->Close();
if (err != nsFTPConn::OK) { printf("error: %d\n", err); exit(err); }
printf("Test successful!\n");
exit(err);
}
#endif /* TEST_NSFTPCONN */