2019-11-02 05:10:41 +07:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
2021-12-26 18:47:58 +01:00
* 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 3 of the License , or
* ( at your option ) any later version .
2019-11-02 05:10:41 +07:00
*
* 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
2021-12-26 18:47:58 +01:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2019-11-02 05:10:41 +07:00
*
*/
# define FORBIDDEN_SYMBOL_ALLOW_ALL
# include <curl/curl.h>
# include "backends/networking/curl/connectionmanager.h"
# include "backends/networking/curl/networkreadstream.h"
# include "backends/networking/curl/sessionrequest.h"
# include "common/debug.h"
2021-11-18 21:19:18 +01:00
# include "common/file.h"
2022-12-01 14:17:17 +00:00
# include "common/formats/json.h"
2019-11-02 05:10:41 +07:00
namespace Networking {
2021-11-18 21:19:18 +01:00
SessionRequest : : SessionRequest ( Common : : String url , Common : : String localFile , DataCallback cb , ErrorCallback ecb , bool binary ) :
2019-11-02 05:10:41 +07:00
CurlRequest ( cb , ecb , url ) , _contentsStream ( DisposeAfterUse : : YES ) ,
2021-11-18 21:19:18 +01:00
_buffer ( new byte [ CURL_SESSION_REQUEST_BUFFER_SIZE ] ) , _text ( nullptr ) , _localFile ( nullptr ) ,
2021-11-16 21:41:09 +01:00
_started ( false ) , _complete ( false ) , _success ( false ) , _binary ( binary ) {
2019-11-03 02:16:00 +07:00
2021-11-18 21:19:18 +01:00
openLocalFile ( localFile ) ;
2019-11-03 02:16:00 +07:00
// automatically go under ConnMan control so nobody would be able to leak the memory
// but, we don't need it to be working just yet
_state = PAUSED ;
ConnMan . addRequest ( this ) ;
}
2019-11-02 05:10:41 +07:00
SessionRequest : : ~ SessionRequest ( ) {
delete [ ] _buffer ;
}
2021-11-18 21:19:18 +01:00
void SessionRequest : : openLocalFile ( Common : : String localFile ) {
if ( localFile . empty ( ) )
return ;
_localFile = new Common : : DumpFile ( ) ;
if ( ! _localFile - > open ( localFile , true ) ) {
warning ( " SessionRequestFile: unable to open file to download into " ) ;
ErrorResponse error ( this , false , true , " SessionRequestFile: unable to open file to download into " , - 1 ) ;
finishError ( error ) ;
delete _localFile ;
2023-02-26 10:36:53 +01:00
_localFile = nullptr ;
2021-11-18 21:19:18 +01:00
return ;
}
2021-11-18 22:37:24 +01:00
debug ( 5 , " SessionRequest: opened localfile %s " , localFile . c_str ( ) ) ;
2021-11-18 21:19:18 +01:00
_binary = true ; // Enforce binary
}
2019-11-03 02:16:00 +07:00
bool SessionRequest : : reuseStream ( ) {
if ( ! _stream ) {
return false ;
}
if ( _bytesBuffer )
return _stream - > reuse ( _url . c_str ( ) , _headersList , _bytesBuffer , _bytesBufferSize , _uploading , _usingPatch , true ) ;
if ( ! _formFields . empty ( ) | | ! _formFiles . empty ( ) )
return _stream - > reuse ( _url . c_str ( ) , _headersList , _formFields , _formFiles ) ;
return _stream - > reuse ( _url . c_str ( ) , _headersList , _postFields , _uploading , _usingPatch ) ;
}
2019-11-02 05:10:41 +07:00
char * SessionRequest : : getPreparedContents ( ) {
//write one more byte in the end
byte zero [ 1 ] = { 0 } ;
_contentsStream . write ( zero , 1 ) ;
//replace all "bad" bytes with '.' character
byte * result = _contentsStream . getData ( ) ;
uint32 size = _contentsStream . size ( ) ;
//make it zero-terminated string
result [ size - 1 ] = ' \0 ' ;
return ( char * ) result ;
}
2021-11-16 22:53:32 +01:00
void SessionRequest : : finishError ( ErrorResponse error , RequestState state ) {
2019-11-02 05:10:41 +07:00
_complete = true ;
_success = false ;
2021-11-16 22:53:32 +01:00
CurlRequest : : finishError ( error , PAUSED ) ;
2019-11-02 05:10:41 +07:00
}
void SessionRequest : : finishSuccess ( ) {
_state = PAUSED ;
_complete = true ;
_success = true ;
2021-11-18 22:37:24 +01:00
if ( _localFile ) {
_localFile - > close ( ) ;
2021-11-20 00:15:45 +01:00
delete _localFile ;
2021-11-18 22:37:24 +01:00
_localFile = nullptr ;
}
2021-11-18 21:45:58 +01:00
if ( _callback ) { // If localfile is present, contentStream is empty, so it is fine
2021-11-18 21:19:18 +01:00
_response . buffer = _contentsStream . getData ( ) ;
_response . len = _contentsStream . size ( ) ;
_response . eos = true ;
( * _callback ) ( DataResponse ( this , & _response ) ) ;
}
2019-11-02 05:10:41 +07:00
}
void SessionRequest : : start ( ) {
2019-11-03 02:16:00 +07:00
if ( _state ! = PAUSED | | _started ) {
2019-11-02 05:10:41 +07:00
warning ( " Can't start() SessionRequest as it is already started " ) ;
2019-11-03 02:16:00 +07:00
return ;
2019-11-02 05:10:41 +07:00
}
2019-11-03 02:16:00 +07:00
_state = PROCESSING ;
_started = true ;
}
void SessionRequest : : startAndWait ( ) {
start ( ) ;
wait ( ) ;
}
2021-11-18 21:19:18 +01:00
void SessionRequest : : reuse ( Common : : String url , Common : : String localFile , DataCallback cb , ErrorCallback ecb , bool binary ) {
2019-11-03 02:16:00 +07:00
_url = url ;
delete _callback ;
delete _errorCallback ;
_callback = cb ;
_errorCallback = ecb ;
2021-11-18 21:19:18 +01:00
_binary = binary ;
openLocalFile ( localFile ) ;
2019-11-03 02:16:00 +07:00
restart ( ) ;
2019-11-02 05:10:41 +07:00
}
void SessionRequest : : handle ( ) {
if ( ! _stream ) _stream = makeStream ( ) ;
if ( _stream ) {
2021-11-16 22:53:32 +01:00
if ( _stream - > httpResponseCode ( ) ! = 200 & & _stream - > httpResponseCode ( ) ! = 0 ) {
warning ( " SessionRequest: HTTP response code is not 200 OK (it's %ld) " , _stream - > httpResponseCode ( ) ) ;
ErrorResponse error ( this , false , true , " HTTP response code is not 200 OK " , _stream - > httpResponseCode ( ) ) ;
finishError ( error ) ;
return ;
}
2019-11-02 05:10:41 +07:00
uint32 readBytes = _stream - > read ( _buffer , CURL_SESSION_REQUEST_BUFFER_SIZE ) ;
2021-11-18 21:19:18 +01:00
if ( readBytes ! = 0 ) {
if ( ! _localFile ) {
if ( _contentsStream . write ( _buffer , readBytes ) ! = readBytes )
warning ( " SessionRequest: unable to write all the bytes into MemoryWriteStreamDynamic " ) ;
} else {
_response . buffer = _buffer ;
_response . len = readBytes ;
_response . eos = _stream - > eos ( ) ;
if ( _localFile - > write ( _buffer , readBytes ) ! = readBytes ) {
warning ( " DownloadRequest: unable to write all received bytes into output file " ) ;
finishError ( Networking : : ErrorResponse ( this , false , true , " DownloadRequest::handle: failed to write all bytes into a file " , - 1 ) ) ;
return ;
}
if ( _callback )
( * _callback ) ( DataResponse ( this , & _response ) ) ;
}
}
2019-11-02 05:10:41 +07:00
2021-05-04 11:45:03 +03:00
if ( _stream - > eos ( ) ) {
2022-10-10 22:36:40 +02:00
if ( _stream - > hasError ( ) ) {
ErrorResponse error ( this , false , true , Common : : String : : format ( " TLS stream response code is not CURLE_OK OK: %s " , _stream - > getError ( ) ) , _stream - > getErrorCode ( ) ) ;
finishError ( error ) ;
return ;
}
2019-11-02 05:10:41 +07:00
finishSuccess ( ) ;
}
}
}
void SessionRequest : : restart ( ) {
2019-11-03 02:16:00 +07:00
if ( _stream ) {
bool deleteStream = true ;
if ( _keepAlive & & reuseStream ( ) ) {
deleteStream = false ;
}
if ( deleteStream ) {
delete _stream ;
_stream = nullptr ;
}
}
2019-11-02 05:10:41 +07:00
_contentsStream = Common : : MemoryWriteStreamDynamic ( DisposeAfterUse : : YES ) ;
_text = nullptr ;
_complete = false ;
_success = false ;
2019-11-03 02:16:00 +07:00
_started = false ;
2019-11-02 05:10:41 +07:00
//with no stream available next handle() will create another one
}
void SessionRequest : : close ( ) {
_state = FINISHED ;
}
2021-11-20 00:15:45 +01:00
void SessionRequest : : abortRequest ( ) {
if ( _localFile ) {
_localFile - > close ( ) ;
delete _localFile ;
_localFile = nullptr ;
// TODO we need to remove file, but there is no API
}
_state = FINISHED ;
}
2019-11-02 05:10:41 +07:00
bool SessionRequest : : complete ( ) {
return _complete ;
}
bool SessionRequest : : success ( ) {
return _success ;
}
char * SessionRequest : : text ( ) {
2021-11-18 21:19:18 +01:00
if ( _binary | | _localFile )
2021-11-16 21:41:09 +01:00
return nullptr ;
2019-11-02 05:10:41 +07:00
if ( _text = = nullptr )
_text = getPreparedContents ( ) ;
return _text ;
}
Common : : JSONValue * SessionRequest : : json ( ) {
2021-11-18 21:19:18 +01:00
if ( _binary )
error ( " SessionRequest::json() is called for binary stream " ) ;
if ( _localFile )
error ( " SessionRequest::json() is called for localFile stream " ) ;
2019-11-02 05:10:41 +07:00
return Common : : JSON : : parse ( text ( ) ) ;
}
} // End of namespace Networking