Files
fs2open.github.com/code/inetfile/cftp.cpp
T
asarium 7e77b5a67c Merge branch 'master' into antipodes
Conflicts:
	.gitignore
	code/Makefile.am
	code/ai/ai.h
	code/ai/ai_profiles.cpp
	code/ai/ai_profiles.h
	code/ai/aibig.cpp
	code/ai/aicode.cpp
	code/ai/aiturret.cpp
	code/asteroid/asteroid.cpp
	code/asteroid/asteroid.h
	code/bmpman/bm_internal.h
	code/bmpman/bmpman.cpp
	code/bmpman/bmpman.h
	code/camera/camera.cpp
	code/camera/camera.h
	code/cfile/cfile.cpp
	code/cmdline/cmdline.cpp
	code/cmdline/cmdline.h
	code/controlconfig/controlsconfig.h
	code/controlconfig/controlsconfigcommon.cpp
	code/cutscene/movie.cpp
	code/cutscene/oggplayer.cpp
	code/debugconsole/consoleparse.cpp
	code/fred2/campaigneditordlg.cpp
	code/fred2/fred.cpp
	code/fred2/fred.rc
	code/fred2/missionsave.cpp
	code/fred2/resource.h
	code/fred2/sexp_tree.cpp
	code/fred2/sexp_tree.h
	code/fred2/shipflagsdlg.cpp
	code/fred2/shipflagsdlg.h
	code/fred2/shiptexturesdlg.cpp
	code/fred2/voiceactingmanager.cpp
	code/freespace2/freespace.cpp
	code/freespace2/freespace.rc
	code/gamehelp/contexthelp.cpp
	code/globalincs/alphacolors.cpp
	code/globalincs/alphacolors.h
	code/globalincs/version.h
	code/globalincs/vmallocator.h
	code/globalincs/windebug.cpp
	code/graphics/2d.cpp
	code/graphics/font.cpp
	code/graphics/generic.cpp
	code/graphics/generic.h
	code/graphics/grinternal.h
	code/graphics/gropengl.cpp
	code/graphics/gropengldraw.cpp
	code/graphics/gropenglextension.h
	code/graphics/gropenglpostprocessing.cpp
	code/graphics/gropenglstate.cpp
	code/graphics/gropenglstate.h
	code/graphics/gropengltexture.cpp
	code/graphics/gropengltnl.cpp
	code/hud/hud.cpp
	code/hud/hud.h
	code/hud/hudconfig.cpp
	code/hud/hudlock.cpp
	code/hud/hudmessage.cpp
	code/hud/hudparse.cpp
	code/hud/hudreticle.cpp
	code/hud/hudreticle.h
	code/hud/hudshield.cpp
	code/hud/hudsquadmsg.cpp
	code/hud/hudtarget.cpp
	code/hud/hudtarget.h
	code/hud/hudtargetbox.cpp
	code/inetfile/cftp.cpp
	code/inetfile/chttpget.cpp
	code/io/joy.cpp
	code/io/joy_ff.cpp
	code/io/key.cpp
	code/io/keycontrol.cpp
	code/io/mouse.cpp
	code/io/timer.cpp
	code/localization/localize.cpp
	code/localization/localize.h
	code/math/fvi.cpp
	code/math/vecmat.cpp
	code/menuui/credits.cpp
	code/menuui/mainhallmenu.cpp
	code/menuui/mainhallmenu.h
	code/menuui/optionsmenumulti.cpp
	code/menuui/playermenu.cpp
	code/mission/missionbriefcommon.cpp
	code/mission/missioncampaign.cpp
	code/mission/missiongoals.h
	code/mission/missionmessage.cpp
	code/mission/missionparse.cpp
	code/mission/missionparse.h
	code/missionui/missionbrief.cpp
	code/missionui/missioncmdbrief.cpp
	code/missionui/missiondebrief.cpp
	code/missionui/missionscreencommon.cpp
	code/missionui/missionshipchoice.cpp
	code/missionui/missionweaponchoice.cpp
	code/missionui/redalert.cpp
	code/missionui/redalert.h
	code/mod_table/mod_table.cpp
	code/mod_table/mod_table.h
	code/model/model.h
	code/model/modelcollide.cpp
	code/model/modelinterp.cpp
	code/model/modelread.cpp
	code/network/multi.cpp
	code/network/multi_dogfight.cpp
	code/network/multi_ingame.cpp
	code/network/multi_log.cpp
	code/network/multi_rate.cpp
	code/network/multiteamselect.cpp
	code/network/multiui.cpp
	code/network/multiutil.cpp
	code/network/stand_gui-unix.cpp
	code/object/collideshipship.cpp
	code/object/collideshipweapon.cpp
	code/object/object.cpp
	code/object/object.h
	code/object/objectsnd.cpp
	code/object/objectsort.cpp
	code/osapi/osapi.cpp
	code/osapi/osapi_unix.cpp
	code/osapi/outwnd.h
	code/parse/generic_log.cpp
	code/parse/lua.cpp
	code/parse/parselo.cpp
	code/parse/parselo.h
	code/parse/scripting.cpp
	code/parse/scripting.h
	code/parse/sexp.cpp
	code/parse/sexp.h
	code/physics/physics.cpp
	code/pilotfile/csg.cpp
	code/pilotfile/csg_convert.cpp
	code/pilotfile/pilotfile.cpp
	code/pilotfile/pilotfile_convert.h
	code/playerman/playercontrol.cpp
	code/radar/radardradis.cpp
	code/radar/radardradis.h
	code/radar/radarsetup.cpp
	code/radar/radarsetup.h
	code/ship/ship.cpp
	code/ship/ship.h
	code/ship/shipfx.cpp
	code/ship/shiphit.cpp
	code/sound/audiostr.cpp
	code/sound/ds.cpp
	code/sound/openal.cpp
	code/sound/rtvoice.cpp
	code/sound/sound.cpp
	code/sound/voicerec.cpp
	code/stats/medals.cpp
	code/stats/medals.h
	code/stats/scoring.cpp
	code/ui/ui.h
	code/weapon/beam.cpp
	code/weapon/weapon.h
	code/weapon/weapons.cpp
	code/wxfred2/aboutbox.cpp
	code/wxfred2/campaigneditor.cpp
	code/wxfred2/campaigneditor.h
	code/wxfred2/commandbriefingeditor.cpp
	code/wxfred2/debriefingeditor.cpp
	code/wxfred2/fredframe.cpp
	code/wxfred2/fredframe.h
	code/wxfred2/missionspecseditor.cpp
	code/wxfred2/missionspecseditor.h
	code/wxfred2/voiceactingmanagereditor.cpp
	code/wxfred2/wxfred.xrc
	code/wxfred2/wxfred2.cpp
	code/wxfred2/wxfred2.h
	configure.ac
	fs2open.Doxyfile
	jansson/LICENSE
	jansson/dump.c
	jansson/hashtable.c
	jansson/hashtable.h
	jansson/jansson.h
	jansson/jansson_config.h
	jansson/jansson_private.h
	jansson/load.c
	jansson/memory.c
	jansson/pack_unpack.c
	jansson/strbuffer.c
	jansson/strbuffer.h
	jansson/strconv.c
	jansson/utf.c
	jansson/utf.h
	jansson/value.c
	libpng/png.c
	libpng/png.h
	libpng/pngconf.h
	libpng/pngdebug.h
	libpng/pngerror.c
	libpng/pngget.c
	libpng/pnginfo.h
	libpng/pnglibconf.h
	libpng/pngmem.c
	libpng/pngpread.c
	libpng/pngpriv.h
	libpng/pngread.c
	libpng/pngrio.c
	libpng/pngrtran.c
	libpng/pngrutil.c
	libpng/pngset.c
	libpng/pngstruct.h
	libpng/pngtrans.c
	libpng/pngwio.c
	libpng/pngwrite.c
	libpng/pngwtran.c
	libpng/pngwutil.c
	projects/Eclipse/Freespace2/.project
	projects/MSVC_2005/Fred2.vcproj
	projects/MSVC_2005/Freespace2.sln
	projects/MSVC_2005/Freespace2.vcproj
	projects/MSVC_2005/code.vcproj
	projects/MSVC_2005/wxFRED2.vcproj
	projects/MSVC_2008/Fred2.vcproj
	projects/MSVC_2008/Freespace2.doxproj
	projects/MSVC_2008/Freespace2.vcproj
	projects/MSVC_2008/code.vcproj
	projects/MSVC_2010/Fred2.vcxproj
	projects/MSVC_2010/Freespace2.vcxproj
	projects/MSVC_2010/code.vcxproj
	projects/MSVC_2010/code.vcxproj.filters
	projects/MSVC_2010/wxFRED2.vcxproj
	projects/MSVC_2011/Fred2.vcxproj
	projects/MSVC_2011/Freespace2.vcxproj
	projects/MSVC_2011/code.vcxproj
	projects/MSVC_2011/code.vcxproj.filters
	projects/MSVC_2011/wxFRED2.vcxproj
	projects/MSVC_2013/Fred2.vcxproj
	projects/MSVC_2013/Freespace2.vcxproj
	projects/MSVC_2013/code.vcxproj
	projects/MSVC_2013/code.vcxproj.filters
	projects/MSVC_2013/libjpeg.vcxproj
	projects/MSVC_2013/liblua.vcxproj
	projects/MSVC_2013/libpng.vcxproj
	projects/MSVC_2013/wxFRED2.vcxproj
	projects/MSVC_2013/zlib.vcxproj
	projects/MSVC_6/Fred2.dsp
	projects/MSVC_6/Freespace2.dsp
	projects/MSVC_6/code.dsp
	projects/MSVC_6/wxFRED2.dsp
	projects/Xcode/English.lproj/InfoPlist.strings
	projects/Xcode/FS2_Open.xcodeproj/project.pbxproj
	projects/Xcode4/English.lproj/InfoPlist.strings
	projects/Xcode4/FS2_Open.xcodeproj/project.pbxproj
	projects/Xcode4/Frameworks.tgz
	projects/codeblocks/Freespace2/Freespace2.cbp
	zlib/adler32.c
	zlib/compress.c
	zlib/crc32.c
	zlib/crc32.h
	zlib/deflate.c
	zlib/deflate.h
	zlib/infback.c
	zlib/inffast.c
	zlib/inffixed.h
	zlib/inflate.c
	zlib/inftrees.c
	zlib/trees.c
	zlib/uncompr.c
	zlib/zconf.h
	zlib/zlib.h
	zlib/zutil.c
	zlib/zutil.h
2015-04-25 16:48:43 +02:00

583 lines
11 KiB
C++

/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include <stdio.h>
#include "globalincs/pstypes.h"
#ifdef WIN32
#include <windows.h>
#include <process.h>
#else
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netdb.h>
#define WSAGetLastError() (errno)
#endif
#include "inetfile/cftp.h"
int FTPObjThread( void *obj )
{
((CFtpGet *)obj)->WorkerThread();
return 0;
}
void CFtpGet::AbortGet()
{
m_Aborting = true;
while(!m_Aborted) ; //Wait for the thread to end
fclose(LOCALFILE);
}
CFtpGet::CFtpGet(char *URL, char *localfile, char *Username, char *Password)
{
SOCKADDR_IN listensockaddr;
m_State = FTP_STATE_STARTUP;
m_ListenSock = INVALID_SOCKET;
m_DataSock = INVALID_SOCKET;
m_ControlSock = INVALID_SOCKET;
m_iBytesIn = 0;
m_iBytesTotal = 0;
m_Aborting = false;
m_Aborted = false;
thread_id = NULL;
LOCALFILE = fopen(localfile, "wb");
if (NULL == LOCALFILE) {
m_State = FTP_STATE_CANT_WRITE_FILE;
return;
}
if(Username)
{
strcpy_s(m_szUserName,Username);
}
else
{
strcpy_s(m_szUserName,"anonymous");
}
if(Password)
{
strcpy_s(m_szPassword,Password);
}
else
{
strcpy_s(m_szPassword,"[email protected]");
}
m_ListenSock = socket(AF_INET, SOCK_STREAM, 0);
if(INVALID_SOCKET == m_ListenSock)
{
m_State = FTP_STATE_SOCKET_ERROR;
return;
}
else
{
memset(&listensockaddr, 0, sizeof(SOCKADDR_IN));
listensockaddr.sin_family = AF_INET;
listensockaddr.sin_port = 0;
listensockaddr.sin_addr.s_addr = INADDR_ANY;
// Bind the listen socket
if (bind(m_ListenSock, (SOCKADDR *)&listensockaddr, sizeof(SOCKADDR)))
{
//Couldn't bind the socket
m_State = FTP_STATE_SOCKET_ERROR;
return;
}
// Listen for the server connection
if (listen(m_ListenSock, 1))
{
//Couldn't listen on the socket
m_State = FTP_STATE_SOCKET_ERROR;
return;
}
}
m_ControlSock = socket(AF_INET, SOCK_STREAM, 0);
if(INVALID_SOCKET == m_ControlSock)
{
m_State = FTP_STATE_SOCKET_ERROR;
return;
}
//Parse the URL
//Get rid of any extra ftp:// stuff
char *pURL = URL;
if(_strnicmp(URL,"ftp:",4)==0)
{
pURL +=4;
while(*pURL == '/')
{
pURL++;
}
}
//There shouldn't be any : in this string
if(strchr(pURL,':'))
{
m_State = FTP_STATE_URL_PARSING_ERROR;
return;
}
//read the filename by searching backwards for a /
//then keep reading until you find the first /
//when you found it, you have the host and dir
char *filestart = NULL;
char *dirstart = NULL;
for(int i = strlen(pURL);i>=0;i--)
{
if(pURL[i]== '/')
{
if(!filestart)
{
filestart = pURL+i+1;
dirstart = pURL+i+1;
strcpy_s(m_szFilename,filestart);
}
else
{
dirstart = pURL+i+1;
}
}
}
if((dirstart==NULL) || (filestart==NULL))
{
m_State = FTP_STATE_URL_PARSING_ERROR;
return;
}
else
{
strncpy(m_szDir,dirstart,(filestart-dirstart));
m_szDir[(filestart-dirstart)] = '\0';
strncpy(m_szHost,pURL,(dirstart-pURL));
m_szHost[(dirstart-pURL)-1] = '\0';
}
//At this point we should have a nice host,dir and filename
if ( (thread_id = SDL_CreateThread(FTPObjThread, "FTP", this)) == NULL )
{
m_State = FTP_STATE_INTERNAL_ERROR;
return;
}
m_State = FTP_STATE_CONNECTING;
}
CFtpGet::~CFtpGet()
{
if (thread_id)
SDL_WaitThread(thread_id, NULL);
fclose(LOCALFILE);
fclose(LOCALFILE);
if(m_ListenSock != INVALID_SOCKET)
{
shutdown(m_ListenSock,2);
closesocket(m_ListenSock);
}
if(m_DataSock != INVALID_SOCKET)
{
shutdown(m_DataSock,2);
closesocket(m_DataSock);
}
if(m_ControlSock != INVALID_SOCKET)
{
shutdown(m_ControlSock,2);
closesocket(m_ControlSock);
}
}
//Returns a value to specify the status (ie. connecting/connected/transferring/done)
int CFtpGet::GetStatus()
{
return m_State;
}
uint CFtpGet::GetBytesIn()
{
return m_iBytesIn;
}
uint CFtpGet::GetTotalBytes()
{
return m_iBytesTotal;
}
//This function does all the work -- connects on a blocking socket
//then sends the appropriate user and password commands
//and then the cwd command, the port command then get and finally the quit
void CFtpGet::WorkerThread()
{
ConnectControlSocket();
if(m_State != FTP_STATE_LOGGING_IN)
{
return;
}
LoginHost();
if(m_State != FTP_STATE_LOGGED_IN)
{
return;
}
GetFile();
//We are all done now, and state has the current state.
m_Aborted = true;
}
uint CFtpGet::GetFile()
{
//Start off by changing into the proper dir.
char szCommandString[200];
int rcode;
sprintf(szCommandString,"TYPE I\r\n");
rcode = SendFTPCommand(szCommandString);
if(rcode >=400)
{
m_State = FTP_STATE_UNKNOWN_ERROR;
return 0;
}
if(m_Aborting)
return 0;
if(m_szDir[0])
{
sprintf(szCommandString,"CWD %s\r\n",m_szDir);
rcode = SendFTPCommand(szCommandString);
if(rcode >=400)
{
m_State = FTP_STATE_DIRECTORY_INVALID;
return 0;
}
}
if(m_Aborting)
return 0;
if(!IssuePort())
{
m_State = FTP_STATE_UNKNOWN_ERROR;
return 0;
}
if(m_Aborting)
return 0;
sprintf(szCommandString,"RETR %s\r\n",m_szFilename);
rcode = SendFTPCommand(szCommandString);
if(rcode >=400)
{
m_State = FTP_STATE_FILE_NOT_FOUND;
return 0;
}
if(m_Aborting)
return 0;
//Now we will try to determine the file size...
char *p,*s;
p = strchr(recv_buffer,'(');
p++;
if(p)
{
s = strchr(p,' ');
*s = '\0';
m_iBytesTotal = atoi(p);
}
if(m_Aborting)
return 0;
m_DataSock = accept(m_ListenSock, NULL,NULL);
// Close the listen socket
closesocket(m_ListenSock);
if (m_DataSock == INVALID_SOCKET)
{
m_State = FTP_STATE_SOCKET_ERROR;
return 0;
}
if(m_Aborting)
return 0;
ReadDataChannel();
m_State = FTP_STATE_FILE_RECEIVED;
return 1;
}
uint CFtpGet::IssuePort()
{
char szCommandString[200];
SOCKADDR_IN listenaddr; // Socket address structure
#ifdef SCP_UNIX
socklen_t iLength; // Length of the address structure
#else
int iLength; // Length of the address structure
#endif
UINT nLocalPort; // Local port for listening
UINT nReplyCode; // FTP server reply code
// Get the address for the hListenSocket
iLength = sizeof(listenaddr);
if (getsockname(m_ListenSock, (LPSOCKADDR)&listenaddr,&iLength) == SOCKET_ERROR)
{
m_State = FTP_STATE_SOCKET_ERROR;
return 0;
}
// Extract the local port from the hListenSocket
nLocalPort = listenaddr.sin_port;
// Now, reuse the socket address structure to
// get the IP address from the control socket.
if (getsockname(m_ControlSock, (LPSOCKADDR)&listenaddr,&iLength) == SOCKET_ERROR)
{
m_State = FTP_STATE_SOCKET_ERROR;
return 0;
}
// Format the PORT command with the correct numbers.
#ifdef WINDOWS
sprintf(szCommandString, "PORT %d,%d,%d,%d,%d,%d\r\n",
listenaddr.sin_addr.S_un.S_un_b.s_b1,
listenaddr.sin_addr.S_un.S_un_b.s_b2,
listenaddr.sin_addr.S_un.S_un_b.s_b3,
listenaddr.sin_addr.S_un.S_un_b.s_b4,
nLocalPort & 0xFF,
nLocalPort >> 8);
#else
sprintf(szCommandString, "PORT %d,%d,%d,%d,%d,%d\r\n",
(listenaddr.sin_addr.s_addr & 0xff000000) >> 24,
(listenaddr.sin_addr.s_addr & 0x00ff0000) >> 16,
(listenaddr.sin_addr.s_addr & 0x0000ff00) >> 8,
(listenaddr.sin_addr.s_addr & 0x000000ff),
nLocalPort & 0xFF,
nLocalPort >> 8);
#endif
// Tell the server which port to use for data.
nReplyCode = SendFTPCommand(szCommandString);
if (nReplyCode != 200)
{
m_State = FTP_STATE_SOCKET_ERROR;
return 0;
}
return 1;
}
int CFtpGet::ConnectControlSocket()
{
HOSTENT *he;
SERVENT *se;
SOCKADDR_IN hostaddr;
he = gethostbyname(m_szHost);
if(he == NULL)
{
m_State = FTP_STATE_HOST_NOT_FOUND;
return 0;
}
//m_ControlSock
if(m_Aborting)
return 0;
memset(&hostaddr, 0, sizeof(SOCKADDR_IN));
se = getservbyname("ftp", NULL);
if(se == NULL)
{
hostaddr.sin_port = htons(21);
}
else
{
hostaddr.sin_port = se->s_port;
}
hostaddr.sin_family = AF_INET;
memcpy(&hostaddr.sin_addr,he->h_addr_list[0],4);
if(m_Aborting)
return 0;
//Now we will connect to the host
if(connect(m_ControlSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR)))
{
m_State = FTP_STATE_CANT_CONNECT;
return 0;
}
m_State = FTP_STATE_LOGGING_IN;
return 1;
}
int CFtpGet::LoginHost()
{
char szLoginString[200];
int rcode;
sprintf(szLoginString,"USER %s\r\n",m_szUserName);
rcode = SendFTPCommand(szLoginString);
if(rcode >=400)
{
m_State = FTP_STATE_LOGIN_ERROR;
return 0;
}
sprintf(szLoginString,"PASS %s\r\n",m_szPassword);
rcode = SendFTPCommand(szLoginString);
if(rcode >=400)
{
m_State = FTP_STATE_LOGIN_ERROR;
return 0;
}
m_State = FTP_STATE_LOGGED_IN;
return 1;
}
uint CFtpGet::SendFTPCommand(char *command)
{
FlushControlChannel();
// Send the FTP command
if (SOCKET_ERROR ==(send(m_ControlSock,command,strlen(command), 0)))
{
// Return 999 to indicate an error has occurred
return(999);
}
// Read the server's reply and return the reply code as an integer
return(ReadFTPServerReply());
}
uint CFtpGet::ReadFTPServerReply()
{
uint rcode;
int iBytesRead;
char chunk[2];
char szcode[4];
uint igotcrlf = 0;
memset(recv_buffer,0,1000);
do
{
chunk[0] = '\0';
iBytesRead = recv(m_ControlSock,chunk,1,0);
if (iBytesRead == SOCKET_ERROR)
{
// Return 999 to indicate an error has occurred
return(999);
}
if((chunk[0]==0x0a) || (chunk[0]==0x0d))
{
if(recv_buffer[0] != '\0')
{
igotcrlf = 1;
}
}
else
{ chunk[1] = '\0';
strcat_s(recv_buffer,chunk);
}
Sleep(1);
}while(igotcrlf==0);
if(recv_buffer[3] == '-')
{
//Hack -- must be a MOTD
return ReadFTPServerReply();
}
if(recv_buffer[3] != ' ')
{
//We should have 3 numbers then a space
return 999;
}
memcpy(szcode,recv_buffer,3);
szcode[3] = '\0';
rcode = atoi(szcode);
// Extract the reply code from the server reply and return as an integer
return(rcode);
}
uint CFtpGet::ReadDataChannel()
{
char sDataBuffer[4096]; // Data-storage buffer for the data channel
int nBytesRecv; // Bytes received from the data channel
m_State = FTP_STATE_RECEIVING;
if(m_Aborting)
return 0;
do
{
if(m_Aborting)
return 0;
nBytesRecv = recv(m_DataSock, (LPSTR)&sDataBuffer,sizeof(sDataBuffer), 0);
m_iBytesIn += nBytesRecv;
if (nBytesRecv > 0 )
{
fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE);
}
Sleep(1);
}while (nBytesRecv > 0);
fclose(LOCALFILE);
// Close the file and check for error returns.
if (nBytesRecv == SOCKET_ERROR)
{
//Ok, we got a socket error -- xfer aborted?
m_State = FTP_STATE_RECV_FAILED;
return 0;
}
else
{
//done!
m_State = FTP_STATE_FILE_RECEIVED;
return 1;
}
}
void CFtpGet::FlushControlChannel()
{
fd_set read_fds;
TIMEVAL timeout;
char flushbuff[3];
timeout.tv_sec = 0;
timeout.tv_usec = 0;
FD_ZERO(&read_fds);
FD_SET(m_ControlSock, &read_fds);
#ifdef WIN32
while ( select(0, &read_fds, NULL, NULL, &timeout) )
#else
while ( select(m_ControlSock+1, &read_fds, NULL, NULL, &timeout) )
#endif
{
recv(m_ControlSock,flushbuff,1,0);
Sleep(1);
}
}