ftp.inc
Original include source with line numbers.
| 1 | |
| 2 | /* |
| 3 | FTP |
| 4 | v0.3 |
| 5 | by bugsy |
| 6 | |
| 7 | http://forums.alliedmods.net/showthread.php?t=142850 |
| 8 | */ |
| 9 | |
| 10 | //#define TESTING |
| 11 | |
| 12 | #if defined _ftp_included |
| 13 | #endinput |
| 14 | #endif |
| 15 | #define _ftp_included |
| 16 | |
| 17 | #if !defined _engine_included |
| 18 | #include <engine> |
| 19 | #endif |
| 20 | |
| 21 | #if !defined _socket_included |
| 22 | #include <sockets> |
| 23 | #endif |
| 24 | |
| 25 | const BUFFER_SIZE = 4096; |
| 26 | |
| 27 | enum FTPStatus |
| 28 | { |
| 29 | FTP_DISCONNECTED, |
| 30 | FTP_CONNECTING, |
| 31 | FTP_USER, |
| 32 | FTP_PASS, |
| 33 | FTP_READYFORDATA, |
| 34 | FTP_IDLE |
| 35 | } |
| 36 | |
| 37 | enum FTPTransType |
| 38 | { |
| 39 | FTP_OPEN, |
| 40 | FTP_STOR, |
| 41 | FTP_RETR, |
| 42 | FTP_LIST |
| 43 | } |
| 44 | |
| 45 | enum FTPInfo |
| 46 | { |
| 47 | UserName[ 64 ], |
| 48 | Password[ 64 ], |
| 49 | Server[ 128 ], |
| 50 | Port, |
| 51 | DataServer[ 16 ], |
| 52 | DataPort, |
| 53 | FileHandle, |
| 54 | LocalFile[ 64 ], |
| 55 | RemoteFile[ 64 ], |
| 56 | FileSize, |
| 57 | BytesTransferred, |
| 58 | Socket_Cmd, |
| 59 | Socket_Data, |
| 60 | FTPTransType:TransType, |
| 61 | FTPStatus:Status, |
| 62 | FWDHandles[ FTPTransType ], |
| 63 | bool:SentNoop |
| 64 | } |
| 65 | |
| 66 | new FTP[ FTPInfo ] , g_iFTPEntity , g_DataBuffer[ BUFFER_SIZE ] , g_szCmdBuffer[ 128 ] , g_szCmd[ 128 ]; |
| 67 | |
| 68 | stock FTP_Open( const szServer[] , const iPort=21 , const szUser[] , const szPassword[] , const szForward[]="" ) |
| 69 | { |
| 70 | if ( !FTP[ FWDHandles ][ FTP_OPEN ] ) |
| 71 | FTP[ FWDHandles ][ FTP_OPEN ] = CreateMultiForward( szForward , ET_IGNORE , FP_CELL ); |
| 72 | |
| 73 | copy( FTP[ Server ] , charsmax( FTP[ Server ] ) , szServer ); |
| 74 | FTP[ Port ] = iPort; |
| 75 | formatex( FTP[ UserName ] , charsmax( FTP[ UserName ] ) , "USER %s^r^n" , szUser ); |
| 76 | formatex( FTP[ Password ] , charsmax( FTP[ Password ] ) , "PASS %s^r^n" , szPassword ); |
| 77 | |
| 78 | FTP[ Status ] = _:FTP_CONNECTING; |
| 79 | FTP[ TransType ] = _:FTP_OPEN; |
| 80 | |
| 81 | new iError; |
| 82 | if ( ( FTP[ Socket_Cmd ] = socket_open( FTP[ Server ] , FTP[ Port ] , SOCKET_TCP , iError ) ) && !iError ) |
| 83 | { |
| 84 | if ( !g_iFTPEntity ) |
| 85 | CreateFTPEntity(); |
| 86 | |
| 87 | entity_set_float( g_iFTPEntity , EV_FL_nextthink , get_gametime() + 0.5 ); |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | stock FTP_Close() |
| 92 | { |
| 93 | if ( FTP[ Status ] != FTP_IDLE ) |
| 94 | return 0; |
| 95 | |
| 96 | socket_send( FTP[ Socket_Cmd ] , "QUIT^r^n", 7 ); |
| 97 | socket_close( FTP[ Socket_Cmd ] ); |
| 98 | socket_close( FTP[ Socket_Data ] ); |
| 99 | FTP[ Status ] = _:FTP_DISCONNECTED; |
| 100 | |
| 101 | return 1; |
| 102 | } |
| 103 | |
| 104 | stock FTP_SendFile( szLocalFile[] , szRemoteFile[] , szForward[]="" ) |
| 105 | { |
| 106 | return FTP_Command( FTP_STOR , szLocalFile , szRemoteFile , szForward ); |
| 107 | } |
| 108 | |
| 109 | stock FTP_GetFile( szLocalFile[] , szRemoteFile[] , szForward[]="" ) |
| 110 | { |
| 111 | return FTP_Command( FTP_RETR , szLocalFile , szRemoteFile , szForward ); |
| 112 | } |
| 113 | |
| 114 | stock FTP_GetList( szLocalFile[] , szRemoteFile[] , szForward[]="" ) |
| 115 | { |
| 116 | return FTP_Command( FTP_LIST , szLocalFile , szRemoteFile , szForward ); |
| 117 | } |
| 118 | |
| 119 | stock FTP_Ready() |
| 120 | { |
| 121 | return ( FTP[ Status ] == FTP_IDLE ); |
| 122 | } |
| 123 | |
| 124 | FTP_Command( FTPTransType:FTPTT , szLocalFile[] , szRemoteFile[] , szForward[] ) |
| 125 | { |
| 126 | if ( FTP[ Status ] != FTP_IDLE ) |
| 127 | return 0; |
| 128 | |
| 129 | if ( !FTP[ FWDHandles ][ FTPTT ] ) |
| 130 | { |
| 131 | switch ( FTPTT ) |
| 132 | { |
| 133 | case FTP_RETR , FTP_STOR: FTP[ FWDHandles ][ FTPTT ] = CreateMultiForward( szForward , ET_IGNORE , FP_STRING , FP_CELL , FP_CELL ); |
| 134 | case FTP_LIST: FTP[ FWDHandles ][ FTPTT ] = CreateMultiForward( szForward , ET_IGNORE , FP_STRING , FP_CELL ); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | copy( FTP[ LocalFile ] , charsmax( FTP[ LocalFile ] ) , szLocalFile ); |
| 139 | copy( FTP[ RemoteFile ] , charsmax( FTP[ RemoteFile ] ) , szRemoteFile ); |
| 140 | |
| 141 | FTP[ TransType ] = _:FTPTT; |
| 142 | |
| 143 | socket_send( FTP[ Socket_Cmd ] , "PASV^r^n" , 6 ); |
| 144 | |
| 145 | return 1; |
| 146 | } |
| 147 | |
| 148 | public _FTP_Think( iEntity ) |
| 149 | { |
| 150 | if ( FTP[ Status ] != FTP_READYFORDATA ) |
| 151 | { |
| 152 | if ( g_szCmdBuffer[ 0 ] || socket_change( FTP[ Socket_Cmd ] ) ) |
| 153 | { |
| 154 | new szCode[ 4 ] , iPos ; |
| 155 | |
| 156 | if ( !g_szCmdBuffer[ 0 ] ) |
| 157 | socket_recv( FTP[ Socket_Cmd ] , g_szCmdBuffer , charsmax( g_szCmdBuffer ) ); |
| 158 | |
| 159 | parse( g_szCmdBuffer , szCode , charsmax( szCode ) ); |
| 160 | |
| 161 | #if defined TESTING |
| 162 | server_print( g_szCmdBuffer ); |
| 163 | #endif |
| 164 | |
| 165 | switch ( str_to_num( szCode ) ) |
| 166 | { |
| 167 | case 150: // Opening BINARY mode data connection |
| 168 | { |
| 169 | if ( FTP[ TransType ] != FTP_LIST ) |
| 170 | FTP[ Status ] = _:FTP_READYFORDATA; |
| 171 | } |
| 172 | case 200: // Type set to I. & 200 MODE S ok. |
| 173 | { |
| 174 | switch ( FTP[ TransType ] ) |
| 175 | { |
| 176 | case FTP_STOR: |
| 177 | { |
| 178 | formatex( g_szCmd , charsmax( g_szCmd ) , "STOR %s^r^n" , FTP[ RemoteFile ] ); |
| 179 | socket_send( FTP[ Socket_Cmd ] , g_szCmd , sizeof( g_szCmd ) ); |
| 180 | |
| 181 | new iError; |
| 182 | FTP[ Socket_Data ] = socket_open( FTP[ DataServer ] , FTP[ DataPort ] , SOCKET_TCP , iError ); |
| 183 | FTP[ FileHandle ] = fopen( FTP[ LocalFile ] , "rb" ); |
| 184 | FTP[ FileSize ] = file_size( FTP[ LocalFile ] ); |
| 185 | |
| 186 | if ( !FTP[ FileHandle ] ) |
| 187 | set_fail_state( "FTP_STOR: File doesnt exist" ); |
| 188 | } |
| 189 | case FTP_LIST: |
| 190 | { |
| 191 | formatex( g_szCmd , charsmax( g_szCmd ) , "NLST %s^r^n" , FTP[ RemoteFile ] ); |
| 192 | socket_send( FTP[ Socket_Cmd ] , g_szCmd , sizeof( g_szCmd ) ); |
| 193 | FTP[ Status ] = _:FTP_READYFORDATA; |
| 194 | |
| 195 | new iError; |
| 196 | FTP[ Socket_Data ] = socket_open( FTP[ DataServer ] , FTP[ DataPort ] , SOCKET_TCP , iError ); |
| 197 | FTP[ FileHandle ] = fopen( FTP[ LocalFile ] , "wt" ); |
| 198 | |
| 199 | if ( !FTP[ FileHandle ] ) |
| 200 | set_fail_state( "FTP_LIST: Error creating local file" ); |
| 201 | } |
| 202 | case FTP_RETR: |
| 203 | { |
| 204 | formatex( g_szCmd , charsmax( g_szCmd ) , "SIZE %s^r^n" , FTP[ RemoteFile ] ); |
| 205 | socket_send( FTP[ Socket_Cmd ] , g_szCmd , sizeof( g_szCmd ) ); |
| 206 | } |
| 207 | } |
| 208 | } |
| 209 | case 213: //For GetFile: 213 9545 (file size) |
| 210 | { |
| 211 | FTP[ FileSize ] = str_to_num( g_szCmdBuffer[ 4 ] ); |
| 212 | formatex( g_szCmd , charsmax( g_szCmd ) , "RETR %s^r^n" , FTP[ RemoteFile ] ); |
| 213 | socket_send( _:FTP[ Socket_Cmd ] , g_szCmd , sizeof( g_szCmd ) ); |
| 214 | |
| 215 | new iError; |
| 216 | FTP[ Socket_Data ] = socket_open( FTP[ DataServer ] , FTP[ DataPort ] , SOCKET_TCP , iError ); |
| 217 | FTP[ FileHandle ] = fopen( FTP[ LocalFile ] , "wb" ); |
| 218 | |
| 219 | if ( !FTP[ FileHandle ] ) |
| 220 | set_fail_state( "FTP_RETR: Error creating local file" ); |
| 221 | } |
| 222 | case 220: // Welcome |
| 223 | { |
| 224 | if ( FTP[ Status ] == FTP_CONNECTING ) |
| 225 | { |
| 226 | socket_send( FTP[ Socket_Cmd ] , FTP[ UserName ] , sizeof( FTP[ UserName ] ) ); |
| 227 | FTP[ Status ] = _:FTP_USER; |
| 228 | } |
| 229 | } |
| 230 | case 226: // Transfer complete. |
| 231 | { |
| 232 | if ( FTP[ TransType ] == FTP_LIST ) |
| 233 | { |
| 234 | fclose( FTP[ FileHandle ] ); |
| 235 | socket_close( FTP[ Socket_Data ] ); |
| 236 | |
| 237 | FTP[ Status ] = _:FTP_IDLE; |
| 238 | } |
| 239 | } |
| 240 | case 227: // Entering Passive Mode (216,87,188,9,205,122) |
| 241 | { |
| 242 | new iStartPos = strfind( g_szCmdBuffer , "(" ) , iEndPos = strfind( g_szCmdBuffer , ")" ); |
| 243 | |
| 244 | if ( ( iStartPos == -1 ) || ( iEndPos == -1 ) ) |
| 245 | return; |
| 246 | |
| 247 | new szDataSvr[ 6 ][ 4 ]; |
| 248 | g_szCmdBuffer[ iEndPos ] = EOS; |
| 249 | ExplodeString( szDataSvr , 6 , 4 , g_szCmdBuffer[ iStartPos + 1 ] , ',' ); |
| 250 | |
| 251 | formatex( FTP[ DataServer ] , charsmax( FTP[ DataServer ] ) , "%s.%s.%s.%s" , szDataSvr[ 0 ] , szDataSvr[ 1 ] , szDataSvr[ 2 ] , szDataSvr[ 3 ] ); |
| 252 | FTP[ DataPort ] = ( ( str_to_num( szDataSvr[ 4 ] ) * 256 ) + str_to_num( szDataSvr[ 5 ] ) ); |
| 253 | |
| 254 | socket_send( FTP[ Socket_Cmd ] , "TYPE I^r^n" , 8 ); |
| 255 | |
| 256 | FTP[ Status ] = _:FTP_IDLE; |
| 257 | } |
| 258 | case 230: // Logged on |
| 259 | { |
| 260 | new iRet; |
| 261 | ExecuteForward( FTP[ FWDHandles ][ FTP[ TransType ] ] , iRet , 1 ); |
| 262 | FTP[ Status ] = _:FTP_IDLE; |
| 263 | } |
| 264 | case 331: // Password required for XX |
| 265 | { |
| 266 | if ( FTP[ Status ] < FTP_PASS ) |
| 267 | { |
| 268 | socket_send( FTP[ Socket_Cmd ] , FTP[ Password ] , sizeof( FTP[ Password ] ) ); |
| 269 | FTP[ Status ] = _:FTP_PASS; |
| 270 | } |
| 271 | } |
| 272 | case 530: // Login incorrect |
| 273 | { |
| 274 | new iRet; |
| 275 | ExecuteForward( FTP[ FWDHandles ][ FTP[ TransType ] ] , iRet , 0 ); |
| 276 | FTP[ Status ] = _:FTP_DISCONNECTED; |
| 277 | socket_close( FTP[ Socket_Cmd ] ); |
| 278 | } |
| 279 | case 550: // File not found / Permission denied |
| 280 | { |
| 281 | FTP[ Status ] = _:FTP_IDLE; |
| 282 | socket_close( FTP[ Socket_Data ] ); |
| 283 | fclose( FTP[ FileHandle ] ); |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | if ( ( ( iPos = strfind( g_szCmdBuffer , "^r^n" ) ) != -1 ) && ( ( iPos + 2 ) < sizeof( g_szCmdBuffer ) ) && strlen( g_szCmdBuffer[ iPos + 2 ] ) ) |
| 288 | { |
| 289 | copy( g_szCmdBuffer , charsmax( g_szCmdBuffer ) , g_szCmdBuffer[ iPos + 2 ] ); |
| 290 | } |
| 291 | else |
| 292 | { |
| 293 | g_szCmdBuffer[ 0 ] = EOS; |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | else |
| 298 | { |
| 299 | switch ( FTP[ TransType ] ) |
| 300 | { |
| 301 | case FTP_RETR , FTP_LIST: |
| 302 | { |
| 303 | if ( socket_change( FTP[ Socket_Data ] ) ) |
| 304 | { |
| 305 | static iDataRecv , iRet; |
| 306 | iDataRecv = socket_recv( FTP[ Socket_Data ] , g_DataBuffer , sizeof( g_DataBuffer ) ); |
| 307 | fwrite_blocks( FTP[ FileHandle ] , g_DataBuffer , iDataRecv , BLOCK_BYTE ); |
| 308 | |
| 309 | FTP[ BytesTransferred ] += iDataRecv; |
| 310 | |
| 311 | if ( FTP[ TransType ] == FTP_LIST ) |
| 312 | { |
| 313 | ExecuteForward( FTP[ FWDHandles ][ FTP_LIST ] , iRet , FTP[ LocalFile ] , FTP[ BytesTransferred ] ); |
| 314 | |
| 315 | fclose( FTP[ FileHandle ] ); |
| 316 | socket_close( FTP[ Socket_Data ] ); |
| 317 | |
| 318 | FTP[ BytesTransferred ] = 0; |
| 319 | FTP[ Status ] = _:FTP_IDLE; |
| 320 | } |
| 321 | else |
| 322 | { |
| 323 | ExecuteForward( FTP[ FWDHandles ][ FTP_RETR ] , iRet , FTP[ RemoteFile ] , FTP[ BytesTransferred ] , FTP[ FileSize ] ); |
| 324 | } |
| 325 | } |
| 326 | } |
| 327 | case FTP_STOR: |
| 328 | { |
| 329 | static iBlocksRead , iRet; |
| 330 | iBlocksRead = fread_blocks( FTP[ FileHandle ] , g_DataBuffer , BUFFER_SIZE , BLOCK_BYTE ); |
| 331 | socket_send2( FTP[ Socket_Data ] , g_DataBuffer , iBlocksRead ); |
| 332 | |
| 333 | FTP[ BytesTransferred ] += iBlocksRead; |
| 334 | ExecuteForward( FTP[ FWDHandles ][ FTP[ TransType ] ] , iRet , FTP[ ( FTP[ TransType ] == FTP_STOR ) ? LocalFile : RemoteFile ] , FTP[ BytesTransferred ] , FTP[ FileSize ] ); |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | if ( ( FTP[ TransType ] != FTP_LIST ) && ( FTP[ FileSize ] && ( FTP[ BytesTransferred ] == FTP[ FileSize ] ) ) ) |
| 339 | { |
| 340 | fclose( FTP[ FileHandle ] ); |
| 341 | socket_close( FTP[ Socket_Data ] ); |
| 342 | |
| 343 | FTP[ BytesTransferred ] = 0; |
| 344 | FTP[ Status ] = _:FTP_IDLE; |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | if ( FTP[ Status ] != FTP_DISCONNECTED ) |
| 349 | entity_set_float( iEntity , EV_FL_nextthink , get_gametime() + ( ( FTP[ Status ] == FTP_READYFORDATA ) ? 0.01 : 0.5 ) ); |
| 350 | } |
| 351 | |
| 352 | CreateFTPEntity() |
| 353 | { |
| 354 | g_iFTPEntity = create_entity( "info_target" ); |
| 355 | entity_set_string( g_iFTPEntity , EV_SZ_classname , "ftp_entity" ); |
| 356 | register_think( "ftp_entity" , "_FTP_Think" ); |
| 357 | } |
| 358 | |
| 359 | ExplodeString( szOutput[][] , iNumOutputStrings , iOutputCharsMax , const szInput[] , cDelimiter ) |
| 360 | { |
| 361 | new iIndex , iPos , iLen = strlen( szInput ); |
| 362 | |
| 363 | do |
| 364 | { |
| 365 | iPos += ( copyc( szOutput[ iIndex ] , iOutputCharsMax , szInput[ iPos ] , cDelimiter ) + 1 ); |
| 366 | } |
| 367 | while( ( iPos < iLen ) && ( ++iIndex < iNumOutputStrings ) ) |
| 368 | |
| 369 | return iIndex; |
| 370 | } |
| 371 | /* AMXX-Studio Notes - DO NOT MODIFY BELOW HERE |
| 372 | *{\\ rtf1\\ ansi\\ deff0{\\ fonttbl{\\ f0\\ fnil Tahoma;}}\n\\ viewkind4\\ uc1\\ pard\\ lang1033\\ f0\\ fs16 \n\\ par } |
| 373 | */ |
| 374 | |