summaryrefslogtreecommitdiff
path: root/protocols/JabberG/jabber_file.cpp
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
commit48540940b6c28bb4378abfeb500ec45a625b37b6 (patch)
tree2ef294c0763e802f91d868bdef4229b6868527de /protocols/JabberG/jabber_file.cpp
parent5c350913f011e119127baeb32a6aedeb4f0d33bc (diff)
initial commit
git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'protocols/JabberG/jabber_file.cpp')
-rw-r--r--protocols/JabberG/jabber_file.cpp569
1 files changed, 569 insertions, 0 deletions
diff --git a/protocols/JabberG/jabber_file.cpp b/protocols/JabberG/jabber_file.cpp
new file mode 100644
index 0000000000..9577c310e9
--- /dev/null
+++ b/protocols/JabberG/jabber_file.cpp
@@ -0,0 +1,569 @@
+/*
+
+Jabber Protocol Plugin for Miranda IM
+Copyright ( C ) 2002-04 Santithorn Bunchua
+Copyright ( C ) 2005-11 George Hazan
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or ( at your option ) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+Revision : $Revision: 13452 $
+Last change on : $Date: 2011-03-17 21:12:56 +0200 (Чт, 17 мар 2011) $
+Last change by : $Author: george.hazan $
+
+*/
+
+#include "jabber.h"
+#include <io.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "jabber_caps.h"
+
+#define JABBER_NETWORK_BUFFER_SIZE 2048
+
+void __cdecl CJabberProto::FileReceiveThread( filetransfer* ft )
+{
+ char* buffer;
+ int datalen;
+ ThreadData info( this, JABBER_SESSION_NORMAL );
+
+ Log( "Thread started: type=file_receive server='%s' port='%d'", ft->httpHostName, ft->httpPort );
+
+ ft->type = FT_OOB;
+
+ if (( buffer=( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE )) == NULL ) {
+ Log( "Cannot allocate network buffer, thread ended" );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 );
+ delete ft;
+ return;
+ }
+
+ NETLIBOPENCONNECTION nloc = { 0 };
+ nloc.cbSize = sizeof( nloc );
+ nloc.cbSize = sizeof( NETLIBOPENCONNECTION );
+ nloc.szHost = ft->httpHostName;
+ nloc.wPort = ft->httpPort;
+ info.s = ( HANDLE ) JCallService( MS_NETLIB_OPENCONNECTION, ( WPARAM ) m_hNetlibUser, ( LPARAM )&nloc );
+ if ( info.s == NULL ) {
+ Log( "Connection failed ( %d ), thread ended", WSAGetLastError());
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 );
+ mir_free( buffer );
+ delete ft;
+ return;
+ }
+
+ ft->s = info.s;
+
+ info.send( "GET /%s HTTP/1.1\r\nHost: %s\r\n\r\n", ft->httpPath, ft->httpHostName );
+ ft->state = FT_CONNECTING;
+
+ Log( "Entering file_receive recv loop" );
+ datalen = 0;
+
+ while ( ft->state != FT_DONE && ft->state != FT_ERROR ) {
+ int recvResult, bytesParsed;
+
+ Log( "Waiting for data..." );
+ recvResult = info.recv( buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen );
+ if ( recvResult <= 0 )
+ break;
+ datalen += recvResult;
+
+ bytesParsed = FileReceiveParse( ft, buffer, datalen );
+ if ( bytesParsed < datalen )
+ memmove( buffer, buffer+bytesParsed, datalen-bytesParsed );
+ datalen -= bytesParsed;
+ }
+
+ ft->s = NULL;
+
+ if ( ft->state==FT_DONE || ( ft->state==FT_RECEIVING && ft->std.currentFileSize < 0 ))
+ ft->complete();
+
+ Log( "Thread ended: type=file_receive server='%s'", ft->httpHostName );
+
+ mir_free( buffer );
+ delete ft;
+}
+
+int CJabberProto::FileReceiveParse( filetransfer* ft, char* buffer, int datalen )
+{
+ char* p, *q, *s, *eob;
+ char* str;
+ int num, code;
+
+ eob = buffer + datalen;
+ p = buffer;
+ num = 0;
+ while ( true ) {
+ if ( ft->state==FT_CONNECTING || ft->state==FT_INITIALIZING ) {
+ for ( q=p; q+1<eob && ( *q!='\r' || *( q+1 )!='\n' ); q++ );
+ if ( q+1 < eob ) {
+ if (( str=( char* )mir_alloc( q-p+1 )) != NULL ) {
+ strncpy( str, p, q-p );
+ str[q-p] = '\0';
+ Log( "FT Got: %s", str );
+ if ( ft->state == FT_CONNECTING ) {
+ // looking for "HTTP/1.1 200 OK"
+ if ( sscanf( str, "HTTP/%*d.%*d %d %*s", &code )==1 && code==200 ) {
+ ft->state = FT_INITIALIZING;
+ ft->std.currentFileSize = -1;
+ Log( "Change to FT_INITIALIZING" );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, ft, 0 );
+ }
+ }
+ else { // FT_INITIALIZING
+ if ( str[0] == '\0' ) {
+ TCHAR* s;
+ if (( s = _tcsrchr( ft->httpPath, '/' )) != NULL )
+ s++;
+ else
+ s = ft->httpPath;
+ ft->std.tszCurrentFile = mir_tstrdup( s );
+ JabberHttpUrlDecode( ft->std.tszCurrentFile );
+ if ( ft->create() == -1 ) {
+ ft->state = FT_ERROR;
+ break;
+ }
+ ft->state = FT_RECEIVING;
+ ft->std.currentFileProgress = 0;
+ Log( "Change to FT_RECEIVING" );
+ }
+ else if (( s=strchr( str, ':' )) != NULL ) {
+ *s = '\0';
+ if ( !strcmp( str, "Content-Length" ))
+ ft->std.totalBytes = ft->std.currentFileSize = _atoi64( s+1 );
+ } }
+
+ mir_free( str );
+ q += 2;
+ num += ( q-p );
+ p = q;
+ }
+ else {
+ ft->state = FT_ERROR;
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ }
+ else if ( ft->state == FT_RECEIVING ) {
+ int bufferSize, writeSize;
+ __int64 remainingBytes;
+
+ if ( ft->std.currentFileSize < 0 || ft->std.currentFileProgress < ft->std.currentFileSize ) {
+ bufferSize = eob - p;
+ remainingBytes = ft->std.currentFileSize - ft->std.currentFileProgress;
+ if ( remainingBytes < bufferSize )
+ writeSize = remainingBytes;
+ else
+ writeSize = bufferSize;
+ if ( _write( ft->fileId, p, writeSize ) != writeSize ) {
+ Log( "_write() error" );
+ ft->state = FT_ERROR;
+ }
+ else {
+ ft->std.currentFileProgress += writeSize;
+ ft->std.totalProgress += writeSize;
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, ( LPARAM )&ft->std );
+ if ( ft->std.currentFileProgress == ft->std.currentFileSize )
+ ft->state = FT_DONE;
+ }
+ }
+ num = datalen;
+ break;
+ }
+ else break;
+ }
+
+ return num;
+}
+
+void JabberFileServerConnection( JABBER_SOCKET hConnection, DWORD /*dwRemoteIP*/, void* extra )
+{
+ CJabberProto* ppro = ( CJabberProto* )extra;
+ WORD localPort = 0;
+ SOCKET s = JCallService( MS_NETLIB_GETSOCKET, ( WPARAM ) hConnection, 0 );
+ if ( s != INVALID_SOCKET ) {
+ SOCKADDR_IN saddr;
+ int len = sizeof( saddr );
+ if ( getsockname( s, ( SOCKADDR * ) &saddr, &len ) != SOCKET_ERROR ) {
+ localPort = ntohs( saddr.sin_port );
+ }
+ }
+ if ( localPort == 0 ) {
+ ppro->Log( "Unable to determine the local port, file server connection closed." );
+ Netlib_CloseHandle( hConnection );
+ return;
+ }
+
+ TCHAR szPort[20];
+ mir_sntprintf( szPort, SIZEOF( szPort ), _T("%d"), localPort );
+ ppro->Log( "File server incoming connection accepted: local_port=" TCHAR_STR_PARAM, szPort );
+
+ JABBER_LIST_ITEM *item = ppro->ListGetItemPtr( LIST_FILE, szPort );
+ if ( item == NULL ) {
+ ppro->Log( "No file is currently served, file server connection closed." );
+ Netlib_CloseHandle( hConnection );
+ return;
+ }
+
+ filetransfer* ft = item->ft;
+ JABBER_SOCKET slisten = ft->s;
+ ft->s = hConnection;
+ ppro->Log( "Set ft->s to %d ( saving %d )", hConnection, slisten );
+
+ char* buffer = ( char* )mir_alloc( JABBER_NETWORK_BUFFER_SIZE+1 );
+ if ( buffer == NULL ) {
+ ppro->Log( "Cannot allocate network buffer, file server connection closed." );
+ Netlib_CloseHandle( hConnection );
+ ft->state = FT_ERROR;
+ if ( ft->hFileEvent != NULL )
+ SetEvent( ft->hFileEvent );
+ return;
+ }
+
+ ppro->Log( "Entering recv loop for this file connection... ( ft->s is hConnection )" );
+ int datalen = 0;
+ while ( ft->state!=FT_DONE && ft->state!=FT_ERROR ) {
+ int recvResult, bytesParsed;
+
+ recvResult = Netlib_Recv( hConnection, buffer+datalen, JABBER_NETWORK_BUFFER_SIZE-datalen, 0 );
+ if ( recvResult <= 0 )
+ break;
+ datalen += recvResult;
+
+ buffer[datalen] = '\0';
+ ppro->Log( "RECV:%s", buffer );
+
+ bytesParsed = ppro->FileSendParse( hConnection, ft, buffer, datalen );
+ if ( bytesParsed < datalen )
+ memmove( buffer, buffer+bytesParsed, datalen-bytesParsed );
+ datalen -= bytesParsed;
+ }
+
+ ppro->Log( "Closing connection for this file transfer... ( ft->s is now hBind )" );
+ Netlib_CloseHandle( hConnection );
+ ft->s = slisten;
+ ppro->Log( "ft->s is restored to %d", ft->s );
+ if ( ft->hFileEvent != NULL )
+ SetEvent( ft->hFileEvent );
+ mir_free( buffer );
+}
+
+void __cdecl CJabberProto::FileServerThread( filetransfer* ft )
+{
+ Log( "Thread started: type=file_send" );
+
+ ThreadData info( this, JABBER_SESSION_NORMAL );
+ ft->type = FT_OOB;
+
+ NETLIBBIND nlb = {0};
+ nlb.cbSize = sizeof( NETLIBBIND );
+ nlb.pfnNewConnectionV2 = JabberFileServerConnection;
+ nlb.pExtra = this;
+ nlb.wPort = 0; // Use user-specified incoming port ranges, if available
+ info.s = ( HANDLE ) JCallService( MS_NETLIB_BINDPORT, ( WPARAM ) m_hNetlibUser, ( LPARAM )&nlb );
+ if ( info.s == NULL ) {
+ Log( "Cannot allocate port to bind for file server thread, thread ended." );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 );
+ delete ft;
+ return;
+ }
+
+ ft->s = info.s;
+ Log( "ft->s = %d", info.s );
+
+ HANDLE hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ ft->hFileEvent = hEvent;
+
+ TCHAR szPort[20];
+ mir_sntprintf( szPort, SIZEOF( szPort ), _T("%d"), nlb.wPort );
+ JABBER_LIST_ITEM *item = ListAdd( LIST_FILE, szPort );
+ item->ft = ft;
+
+ TCHAR* ptszResource = ListGetBestClientResourceNamePtr( ft->jid );
+ if ( ptszResource != NULL ) {
+ ft->state = FT_CONNECTING;
+ for ( int i=0; i < ft->std.totalFiles && ft->state != FT_ERROR && ft->state != FT_DENIED; i++ ) {
+ ft->std.currentFileNumber = i;
+ ft->state = FT_CONNECTING;
+ if ( ft->httpPath ) mir_free( ft->httpPath );
+ ft->httpPath = NULL;
+
+ TCHAR* p;
+ if (( p = _tcschr( ft->std.ptszFiles[i], '\\' )) != NULL )
+ p++;
+ else
+ p = ft->std.ptszFiles[i];
+
+ in_addr in;
+ in.S_un.S_addr = m_dwJabberLocalIP;
+
+ TCHAR* pFileName = JabberHttpUrlEncode( p );
+ if ( pFileName != NULL ) {
+ int id = SerialNext();
+ if ( ft->iqId ) mir_free( ft->iqId );
+ ft->iqId = ( TCHAR* )mir_alloc( sizeof(TCHAR)*( strlen( JABBER_IQID )+20 ));
+ wsprintf( ft->iqId, _T(JABBER_IQID)_T("%d"), id );
+
+ char *myAddr;
+ DBVARIANT dbv;
+ if (m_options.BsDirect && m_options.BsDirectManual) {
+ if ( !DBGetContactSettingString( NULL, m_szModuleName, "BsDirectAddr", &dbv )) {
+ myAddr = NEWSTR_ALLOCA( dbv.pszVal );
+ JFreeVariant( &dbv );
+ }
+ else myAddr = inet_ntoa( in );
+ }
+ else myAddr = inet_ntoa( in );
+
+ char szAddr[ 256 ];
+ mir_snprintf( szAddr, sizeof(szAddr), "http://%s:%d/%s", myAddr, nlb.wPort, pFileName );
+
+ int len = lstrlen(ptszResource) + lstrlen(ft->jid) + 2;
+ TCHAR* fulljid = ( TCHAR* )alloca( sizeof( TCHAR )*len );
+ wsprintf( fulljid, _T("%s/%s"), ft->jid, ptszResource );
+
+ XmlNodeIq iq( _T("set"), id, fulljid );
+ HXML query = iq << XQUERY( _T(JABBER_FEAT_OOB));
+ query << XCHILD( _T("url"), _A2T(szAddr));
+ query << XCHILD( _T("desc"), ft->szDescription);
+ m_ThreadInfo->send( iq );
+
+ Log( "Waiting for the file to be sent..." );
+ WaitForSingleObject( hEvent, INFINITE );
+ mir_free( pFileName );
+ }
+ Log( "File sent, advancing to the next file..." );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0 );
+ }
+ CloseHandle( hEvent );
+ ft->hFileEvent = NULL;
+ Log( "Finish all files" );
+ }
+
+ ft->s = NULL;
+ Log( "ft->s is NULL" );
+
+ ListRemove( LIST_FILE, szPort );
+
+ switch ( ft->state ) {
+ case FT_DONE:
+ Log( "Finish successfully" );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0 );
+ break;
+ case FT_DENIED:
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DENIED, ft, 0 );
+ break;
+ default: // FT_ERROR:
+ Log( "Finish with errors" );
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0 );
+ break;
+ }
+
+ Log( "Thread ended: type=file_send" );
+ delete ft;
+}
+
+int CJabberProto::FileSendParse( JABBER_SOCKET s, filetransfer* ft, char* buffer, int datalen )
+{
+ char* p, *q, *t, *eob;
+ char* str;
+ int num;
+ int currentFile;
+ int fileId;
+ int numRead;
+
+ eob = buffer + datalen;
+ p = buffer;
+ num = 0;
+ while ( ft->state==FT_CONNECTING || ft->state==FT_INITIALIZING ) {
+ for ( q=p; q+1<eob && ( *q!='\r' || *( q+1 )!='\n' ); q++ );
+ if ( q+1 >= eob )
+ break;
+ if (( str=( char* )mir_alloc( q-p+1 )) == NULL ) {
+ ft->state = FT_ERROR;
+ break;
+ }
+ strncpy( str, p, q-p );
+ str[q-p] = '\0';
+ Log( "FT Got: %s", str );
+ if ( ft->state == FT_CONNECTING ) {
+ // looking for "GET filename.ext HTTP/1.1"
+ if ( !strncmp( str, "GET ", 4 )) {
+ for ( t=str+4; *t!='\0' && *t!=' '; t++ );
+ *t = '\0';
+ for ( t=str+4; *t!='\0' && *t=='/'; t++ );
+ ft->httpPath = mir_a2t( t );
+ JabberHttpUrlDecode( ft->httpPath );
+ ft->state = FT_INITIALIZING;
+ Log( "Change to FT_INITIALIZING" );
+ }
+ }
+ else { // FT_INITIALIZING
+ if ( str[0] == '\0' ) {
+ struct _stati64 statbuf;
+
+ mir_free( str );
+ num += 2;
+
+ currentFile = ft->std.currentFileNumber;
+ TCHAR* t = _tcsrchr( ft->std.ptszFiles[ currentFile ], '\\' );
+ if ( t != NULL )
+ t++;
+ else
+ t = ft->std.ptszFiles[currentFile];
+
+ if ( ft->httpPath==NULL || lstrcmp( ft->httpPath, t )) {
+ if ( ft->httpPath == NULL )
+ Log( "Requested file name does not matched ( httpPath==NULL )" );
+ else
+ Log( "Requested file name does not matched ( '%s' vs. '%s' )", ft->httpPath, t );
+ ft->state = FT_ERROR;
+ break;
+ }
+ Log( "Sending [%s]", ft->std.ptszFiles[ currentFile ] );
+ _tstati64( ft->std.ptszFiles[ currentFile ], &statbuf ); // file size in statbuf.st_size
+ if (( fileId = _topen( ft->std.ptszFiles[currentFile], _O_BINARY|_O_RDONLY )) < 0 ) {
+ Log( "File cannot be opened" );
+ ft->state = FT_ERROR;
+ mir_free( ft->httpPath );
+ ft->httpPath = NULL;
+ break;
+ }
+
+ char fileBuffer[ 2048 ];
+ int bytes = mir_snprintf( fileBuffer, sizeof(fileBuffer), "HTTP/1.1 200 OK\r\nContent-Length: %I64u\r\n\r\n", statbuf.st_size );
+ WsSend( s, fileBuffer, bytes, MSG_DUMPASTEXT );
+
+ ft->std.flags |= PFTS_SENDING;
+ ft->std.currentFileProgress = 0;
+ Log( "Sending file data..." );
+
+ while (( numRead = _read( fileId, fileBuffer, 2048 )) > 0 ) {
+ if ( Netlib_Send( s, fileBuffer, numRead, 0 ) != numRead ) {
+ ft->state = FT_ERROR;
+ break;
+ }
+ ft->std.currentFileProgress += numRead;
+ ft->std.totalProgress += numRead;
+ JSendBroadcast( ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, ( LPARAM )&ft->std );
+ }
+ _close( fileId );
+ if ( ft->state != FT_ERROR )
+ ft->state = FT_DONE;
+ Log( "Finishing this file..." );
+ mir_free( ft->httpPath );
+ ft->httpPath = NULL;
+ break;
+ } }
+
+ mir_free( str );
+ q += 2;
+ num += ( q-p );
+ p = q;
+ }
+
+ return num;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// filetransfer class members
+
+filetransfer::filetransfer( CJabberProto* proto )
+{
+ memset( this, 0, sizeof( filetransfer ));
+ ppro = proto;
+ fileId = -1;
+ std.cbSize = sizeof( std );
+ std.flags = PFTS_TCHAR;
+}
+
+filetransfer::~filetransfer()
+{
+ ppro->Log( "Destroying file transfer session %08p", this );
+
+ if ( !bCompleted )
+ ppro->JSendBroadcast( std.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, this, 0 );
+
+ close();
+
+ if ( hWaitEvent != INVALID_HANDLE_VALUE )
+ CloseHandle( hWaitEvent );
+
+ if ( jid ) mir_free( jid );
+ if ( sid ) mir_free( sid );
+ if ( iqId ) mir_free( iqId );
+ if ( fileSize ) mir_free( fileSize );
+ if ( httpHostName ) mir_free( httpHostName );
+ if ( httpPath ) mir_free( httpPath );
+ if ( szDescription ) mir_free( szDescription );
+
+ if ( std.tszWorkingDir ) mir_free( std.tszWorkingDir );
+ if ( std.tszCurrentFile ) mir_free( std.tszCurrentFile );
+
+ if ( std.ptszFiles ) {
+ for ( int i=0; i < std.totalFiles; i++ )
+ if ( std.ptszFiles[i] ) mir_free( std.ptszFiles[i] );
+
+ mir_free( std.ptszFiles );
+} }
+
+void filetransfer::close()
+{
+ if ( fileId != -1 ) {
+ _close( fileId );
+ fileId = -1;
+} }
+
+void filetransfer::complete()
+{
+ close();
+
+ bCompleted = true;
+ ppro->JSendBroadcast( std.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, this, 0);
+}
+
+int filetransfer::create()
+{
+ if ( fileId != -1 )
+ return fileId;
+
+ TCHAR filefull[ MAX_PATH ];
+ mir_sntprintf( filefull, SIZEOF(filefull), _T("%s\\%s"), std.tszWorkingDir, std.tszCurrentFile );
+ replaceStr( std.tszCurrentFile, filefull );
+
+ if ( hWaitEvent != INVALID_HANDLE_VALUE )
+ CloseHandle( hWaitEvent );
+ hWaitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+
+ if ( ppro->JSendBroadcast( std.hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, this, ( LPARAM )&std ))
+ WaitForSingleObject( hWaitEvent, INFINITE );
+
+ if ( fileId == -1 ) {
+ ppro->Log( "Saving to [%s]", std.tszCurrentFile );
+ fileId = _topen( std.tszCurrentFile, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE );
+ }
+
+ if ( fileId == -1 )
+ ppro->Log( "Cannot create file '%s' during a file transfer", filefull );
+ else if ( std.currentFileSize != 0 )
+ _chsize( fileId, std.currentFileSize );
+
+ return fileId;
+}