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