1 //////////////////////////////////////////////////////////////////////
2 //
3 // BeeBEEP Copyright (C) 2010-2021 Marco Mastroddi
4 //
5 // BeeBEEP is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published
7 // by the Free Software Foundation, either version 3 of the License,
8 // or (at your option) any later version.
9 //
10 // BeeBEEP is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with BeeBEEP. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // Author: Marco Mastroddi (marco.mastroddi(AT)gmail.com)
19 //
20 // $Id: FileTransferUpload.cpp 1455 2020-12-23 10:17:53Z mastroddi $
21 //
22 //////////////////////////////////////////////////////////////////////
23 
24 #include "BeeUtils.h"
25 #include "FileTransferPeer.h"
26 #include "Protocol.h"
27 #include "Settings.h"
28 
29 
checkUploadData(const QByteArray & byte_array)30 void FileTransferPeer::checkUploadData( const QByteArray& byte_array )
31 {
32   switch( m_state )
33   {
34   case FileTransferPeer::Request:
35     checkUploadRequest( byte_array );
36     break;
37   case FileTransferPeer::Transferring:
38     checkUploading( byte_array );
39     break;
40   case FileTransferPeer::Pausing:
41     checkUploading( byte_array );
42     break;
43   default:
44     // discard data
45     qWarning() << qPrintable( name() ) << "discards data:" << byte_array;
46   }
47 }
48 
checkUploadRequest(const QByteArray & byte_array)49 void FileTransferPeer::checkUploadRequest( const QByteArray& byte_array )
50 {
51   Message m = Protocol::instance().toMessage( byte_array, mp_socket->protocolVersion() );
52   if( !m.isValid() )
53   {
54     qWarning() << qPrintable( name() ) << "receives an invalid file request:" << byte_array;
55     cancelTransfer();
56     return;
57   }
58 
59   FileInfo file_info = Protocol::instance().fileInfoFromMessage( m, mp_socket->protocolVersion() );
60   if( !file_info.isValid() )
61   {
62     qWarning() << qPrintable( name() ) << "receives an invalid file info:" << byte_array;
63     cancelTransfer();
64     return;
65   }
66 #ifdef BEEBEEP_DEBUG
67   qDebug() << qPrintable( name() ) << "receives a file request:" << file_info.name() << file_info.id() << file_info.password();
68 #else
69   qDebug() << qPrintable( name() ) << "receives a file request:" << file_info.name();
70 #endif
71   if( !file_info.name().endsWith( file_info.suffix(), Qt::CaseInsensitive ) )
72   {
73     qWarning() << qPrintable( name() ) << "receives an invalid file info with name" << qPrintable( file_info.name() ) << "and extension" << file_info.suffix();
74     cancelTransfer();
75     return;
76   }
77 
78   emit fileUploadRequest( file_info );
79 }
80 
startUpload(const FileInfo & fi)81 void FileTransferPeer::startUpload( const FileInfo& fi )
82 {
83   setTransferType( FileInfo::Upload );
84   setFileInfo( FileInfo::Upload, fi );
85   qDebug() << qPrintable( name() ) << "starts uploading" << qPrintable( fi.path() ) << "from" << fi.startingPosition() << "to" << fi.size() << "bytes";
86   if( mp_socket->protocolVersion() < FILE_TRANSFER_2_PROTO_VERSION )
87   {
88     qWarning() << qPrintable( name() ) << "using an old file upload protocol version" << mp_socket->protocolVersion();
89     setTransferringState();
90     sendUploadData();
91   }
92   else
93     sendFileHeader();
94 }
95 
sendFileHeader()96 void FileTransferPeer::sendFileHeader()
97 {
98   if( m_state == FileTransferPeer::FileHeader )
99     return;
100   m_state = FileTransferPeer::FileHeader;
101 #ifdef BEEBEEP_DEBUG
102   qDebug() << qPrintable( name() ) << "sends File Header for" << m_fileInfo.path();
103 #endif
104   m_bytesTransferred = 0;
105   m_totalBytesTransferred = 0;
106 
107   QFileInfo file_info_now_in_system( m_fileInfo.path() );
108   if( file_info_now_in_system.exists() )
109   {
110     m_fileInfo.setSize( file_info_now_in_system.size() );
111     m_fileInfo.setLastModified( file_info_now_in_system.lastModified() );
112     if( m_fileInfo.startingPosition() > 0 )
113     {
114       if( m_fileInfo.startingPosition() > m_fileInfo.size() )
115         m_fileInfo.setStartingPosition( 0 );
116       m_bytesTransferred = m_fileInfo.startingPosition();
117       m_isSkipped = m_bytesTransferred == m_fileInfo.size();
118     }
119   }
120   else
121   {
122     setError( tr( "file no longer exists" ) );
123     return;
124   }
125 
126   Message file_header_message = Protocol::instance().fileInfoToMessage( m_fileInfo, mp_socket->protocolVersion() );
127   QByteArray file_header = Protocol::instance().fromMessage( file_header_message, mp_socket->protocolVersion() );
128 
129   if( !mp_socket->sendData( file_header ) )
130     setError( tr( "unable to send file header" ) );
131   else
132     setTransferringState();
133 }
134 
checkUploading(const QByteArray & byte_array)135 void FileTransferPeer::checkUploading( const QByteArray& byte_array )
136 {
137   FileSizeType bytes_arrived = 0;
138   FileSizeType total_bytes = 0;
139   bool pause_transfer = false;
140   if( !Protocol::instance().parseFileTransferBytesArrivedConfirmation( mp_socket->protocolVersion(), byte_array, &bytes_arrived, &total_bytes, &pause_transfer ) )
141   {
142     setError( tr( "remote host sent invalid data" ) );
143     return;
144   }
145 
146   if( bytes_arrived <= 0 && pause_transfer )
147   {
148     setTransferPaused();
149   }
150   else if( bytes_arrived == m_bytesTransferred )
151   {
152 #ifdef BEEBEEP_DEBUG
153     qDebug() << qPrintable( name() ) << "receives corfirmation for" << m_bytesTransferred << "bytes";
154 #endif
155     m_totalBytesTransferred += m_bytesTransferred;
156 
157     showProgress();
158 
159     if( total_bytes > 0 && m_totalBytesTransferred != total_bytes )
160       setError( tr( "%1 bytes uploaded but the remote file size is %2 bytes" ).arg( m_totalBytesTransferred ).arg( total_bytes ) );
161     else if( m_totalBytesTransferred > m_fileInfo.size() )
162       setError( tr( "%1 bytes uploaded but the file size is only %2 bytes" ).arg( m_totalBytesTransferred ).arg( m_fileInfo.size() ) );
163     else if( m_totalBytesTransferred == m_fileInfo.size() )
164       setTransferCompleted();
165     else if( pause_transfer )
166       setTransferPaused();
167     else
168       sendTransferData();
169   }
170   else
171     setError( tr( "%1 bytes sent not confirmed (%2 bytes confirmed)").arg( m_bytesTransferred ).arg( bytes_arrived ) );
172 }
173 
sendUploadData()174 void FileTransferPeer::sendUploadData()
175 {
176   if( m_state == FileTransferPeer::Paused || m_state == FileTransferPeer::Pausing )
177     return;
178 
179   if( m_state != FileTransferPeer::Transferring )
180   {
181     qWarning() << qPrintable( name() ) << "tries to send data, but it was in state" << m_state << "... skipped";
182     return;
183   }
184 
185   if( !m_file.isOpen() )
186   {
187     if( !m_file.open( QIODevice::ReadOnly ) )
188     {
189       setError( tr( "Unable to open file %1" ).arg( m_file.fileName() ) );
190       return;
191     }
192 
193     if( m_totalBytesTransferred >= m_file.size() || !m_file.seek( m_totalBytesTransferred ) )
194     {
195       setError( tr( "Unable to seek %1 bytes in file %2" ).arg( m_totalBytesTransferred ).arg( m_file.fileName() ) );
196       return;
197     }
198   }
199 
200   if( m_file.atEnd() )
201     return;
202 
203   QByteArray byte_array = m_file.read( mp_socket->fileTransferBufferSize() );
204 
205   if( mp_socket->sendData( byte_array ) )
206   {
207     m_bytesTransferred = byte_array.size();
208   }
209   else
210   {
211     setError( tr( "Unable to upload data" ) );
212     return;
213   }
214 }
215