2018-08-16 22:48:31 -04:00
# include <QDir>
# include <QApplication>
# include <QProgressDialog>
# include "../ui_qt.h"
extern " C " {
# include <string/stdstring.h>
# include <streams/file_stream.h>
# include <file/archive_file.h>
# include "../../../tasks/tasks_internal.h"
# include "../../../verbosity.h"
# include "../../../config.def.h"
}
# define USER_AGENT "RetroArch-WIMP / 1.0"
# define PARTIAL_EXTENSION ".partial"
# define TEMP_EXTENSION ".update_tmp"
# define RETROARCH_NIGHTLY_UPDATE_PATH ".. / RetroArch_update.zip"
static void extractCB ( void * task_data , void * user_data , const char * err )
{
decompress_task_data_t * dec = ( decompress_task_data_t * ) task_data ;
MainWindow * mainwindow = ( MainWindow * ) user_data ;
if ( err )
RARCH_ERR ( " %s " , err ) ;
if ( dec )
{
if ( filestream_exists ( dec - > source_file ) )
filestream_delete ( dec - > source_file ) ;
free ( dec - > source_file ) ;
free ( dec ) ;
}
mainwindow - > onUpdateRetroArchFinished ( string_is_empty ( err ) ) ;
}
void MainWindow : : removeUpdateTempFiles ( )
{
/* a QDir with no path means the current working directory */
QDir dir ;
QStringList dirList = dir . entryList ( QStringList ( ) , QDir : : Dirs | QDir : : Files | QDir : : NoDotAndDotDot | QDir : : Hidden | QDir : : System , QDir : : Name ) ;
int i ;
for ( i = 0 ; i < dirList . count ( ) ; i + + )
{
QString path ( dir . path ( ) + " / " + dirList . at ( i ) ) ;
QFile file ( path ) ;
if ( path . endsWith ( TEMP_EXTENSION ) )
{
QByteArray pathArray = path . toUtf8 ( ) ;
const char * pathData = pathArray . constData ( ) ;
if ( file . remove ( ) )
RARCH_LOG ( " [Qt]: removed temporary update file %s \n " , pathData ) ;
else
RARCH_LOG ( " [Qt]: could not remove temporary update file %s \n " , pathData ) ;
}
}
}
void MainWindow : : onUpdateNetworkError ( QNetworkReply : : NetworkError code )
{
QNetworkReply * reply = m_updateReply . data ( ) ;
QByteArray errorStringArray ;
const char * errorStringData = NULL ;
m_updateProgressDialog - > cancel ( ) ;
if ( ! reply )
return ;
errorStringArray = reply - > errorString ( ) . toUtf8 ( ) ;
errorStringData = errorStringArray . constData ( ) ;
RARCH_ERR ( " [Qt]: Network error code %d received: %s \n " , code , errorStringData ) ;
/* Deleting the reply here seems to cause a strange heap-use-after-free crash. */
/*
reply - > disconnect ( ) ;
reply - > abort ( ) ;
reply - > deleteLater ( ) ;
*/
}
void MainWindow : : onUpdateNetworkSslErrors ( const QList < QSslError > & errors )
{
QNetworkReply * reply = m_updateReply . data ( ) ;
int i ;
if ( ! reply )
return ;
for ( i = 0 ; i < errors . count ( ) ; i + + )
{
const QSslError & error = errors . at ( i ) ;
QString string = QString ( " Ignoring SSL error code " ) + QString : : number ( error . error ( ) ) + " : " + error . errorString ( ) ;
QByteArray stringArray = string . toUtf8 ( ) ;
const char * stringData = stringArray . constData ( ) ;
RARCH_ERR ( " [Qt]: %s \n " , stringData ) ;
}
/* ignore all SSL errors for now, like self-signed, expired etc. */
reply - > ignoreSslErrors ( ) ;
}
void MainWindow : : onUpdateDownloadCanceled ( )
{
m_updateProgressDialog - > cancel ( ) ;
}
void MainWindow : : onRetroArchUpdateDownloadFinished ( )
{
QNetworkReply * reply = m_updateReply . data ( ) ;
QNetworkReply : : NetworkError error ;
int code ;
m_updateProgressDialog - > cancel ( ) ;
/* At least on Linux, the progress dialog will refuse to hide itself and will stay on screen in a corrupted way if we happen to show an error message in this function. processEvents() will sometimes fix it, other times not... seems random. */
qApp - > processEvents ( ) ;
if ( ! reply )
return ;
error = reply - > error ( ) ;
code = reply - > attribute ( QNetworkRequest : : HttpStatusCodeAttribute ) . toInt ( ) ;
if ( m_updateFile . isOpen ( ) )
m_updateFile . close ( ) ;
if ( code ! = 200 )
{
emit showErrorMessageDeferred ( QString ( msg_hash_to_str ( MENU_ENUM_LABEL_VALUE_QT_NETWORK_ERROR ) ) + " : HTTP Code " + QString : : number ( code ) ) ;
RARCH_ERR ( " [Qt]: RetroArch update failed with HTTP status code: %d \n " , code ) ;
reply - > disconnect ( ) ;
reply - > abort ( ) ;
reply - > deleteLater ( ) ;
return ;
}
if ( error = = QNetworkReply : : NoError )
{
int index = m_updateFile . fileName ( ) . lastIndexOf ( PARTIAL_EXTENSION ) ;
QString newFileName = m_updateFile . fileName ( ) . left ( index ) ;
QFile newFile ( newFileName ) ;
/* rename() requires the old file to be deleted first if it exists */
if ( newFile . exists ( ) & & ! newFile . remove ( ) )
RARCH_ERR ( " [Qt]: RetroArch update finished, but old file could not be deleted. \n " ) ;
else
{
if ( m_updateFile . rename ( newFileName ) )
{
RARCH_LOG ( " [Qt]: RetroArch update finished downloading successfully. \n " ) ;
emit extractArchiveDeferred ( newFileName ) ;
}
else
{
RARCH_ERR ( " [Qt]: RetroArch update finished, but temp file could not be renamed. \n " ) ;
emit showErrorMessageDeferred ( msg_hash_to_str ( MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_RENAME_FILE ) ) ;
}
}
}
else
{
QByteArray errorArray = reply - > errorString ( ) . toUtf8 ( ) ;
const char * errorData = errorArray . constData ( ) ;
RARCH_ERR ( " [Qt]: RetroArch update ended prematurely: %s \n " , errorData ) ;
emit showErrorMessageDeferred ( QString ( msg_hash_to_str ( MENU_ENUM_LABEL_VALUE_QT_NETWORK_ERROR ) ) + " : Code " + QString : : number ( code ) + " : " + errorData ) ;
}
reply - > disconnect ( ) ;
reply - > close ( ) ;
reply - > deleteLater ( ) ;
}
void MainWindow : : onUpdateRetroArchFinished ( bool success )
{
m_updateProgressDialog - > cancel ( ) ;
if ( ! success )
{
RARCH_ERR ( " [Qt]: RetroArch update failed. \n " ) ;
emit showErrorMessageDeferred ( msg_hash_to_str ( MENU_ENUM_LABEL_VALUE_QT_UPDATE_RETROARCH_FAILED ) ) ;
return ;
}
RARCH_LOG ( " [Qt]: RetroArch update finished successfully. \n " ) ;
emit showInfoMessageDeferred ( msg_hash_to_str ( MENU_ENUM_LABEL_VALUE_QT_UPDATE_RETROARCH_FINISHED ) ) ;
}
int MainWindow : : onExtractArchive ( QString path )
{
QByteArray pathArray = path . toUtf8 ( ) ;
const char * file = pathArray . constData ( ) ;
file_archive_transfer_t state ;
struct archive_extract_userdata userdata ;
struct string_list * file_list = file_archive_get_file_list ( file , NULL ) ;
bool returnerr = true ;
unsigned i ;
if ( ! file_list | | file_list - > size = = 0 )
{
showMessageBox ( " Error: Archive is empty. " , MainWindow : : MSGBOX_TYPE_ERROR , Qt : : ApplicationModal , false ) ;
RARCH_ERR ( " [Qt]: Downloaded archive is empty? \n " ) ;
return - 1 ;
}
for ( i = 0 ; i < file_list - > size ; i + + )
{
QFile fileObj ( file_list - > elems [ i ] . data ) ;
if ( fileObj . exists ( ) )
{
if ( ! fileObj . remove ( ) )
{
/* if we cannot delete the existing file to update it, rename it for now and delete later */
QFile fileTemp ( fileObj . fileName ( ) + TEMP_EXTENSION ) ;
if ( fileTemp . exists ( ) )
{
if ( ! fileTemp . remove ( ) )
{
showMessageBox ( msg_hash_to_str ( MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_DELETE_FILE ) , MainWindow : : MSGBOX_TYPE_ERROR , Qt : : ApplicationModal , false ) ;
RARCH_ERR ( " [Qt]: Could not delete file: %s \n " , file_list - > elems [ i ] . data ) ;
return - 1 ;
}
}
if ( ! fileObj . rename ( fileTemp . fileName ( ) ) )
{
showMessageBox ( msg_hash_to_str ( MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_RENAME_FILE ) , MainWindow : : MSGBOX_TYPE_ERROR , Qt : : ApplicationModal , false ) ;
RARCH_ERR ( " [Qt]: Could not rename file: %s \n " , file_list - > elems [ i ] . data ) ;
return - 1 ;
}
}
}
}
string_list_free ( file_list ) ;
memset ( & state , 0 , sizeof ( state ) ) ;
memset ( & userdata , 0 , sizeof ( userdata ) ) ;
state . type = ARCHIVE_TRANSFER_INIT ;
m_updateProgressDialog - > setWindowModality ( Qt : : NonModal ) ;
m_updateProgressDialog - > setMinimumDuration ( 0 ) ;
m_updateProgressDialog - > setRange ( 0 , 0 ) ;
m_updateProgressDialog - > setAutoClose ( true ) ;
m_updateProgressDialog - > setAutoReset ( true ) ;
m_updateProgressDialog - > setValue ( 0 ) ;
m_updateProgressDialog - > setLabelText ( QString ( msg_hash_to_str ( MSG_EXTRACTING ) ) + " ... " ) ;
m_updateProgressDialog - > setCancelButtonText ( QString ( ) ) ;
m_updateProgressDialog - > show ( ) ;
if ( ! task_push_decompress ( file , " . " ,
NULL , NULL , NULL ,
extractCB , this ) )
{
m_updateProgressDialog - > cancel ( ) ;
return - 1 ;
}
return returnerr ;
}
void MainWindow : : onUpdateDownloadProgress ( qint64 bytesReceived , qint64 bytesTotal )
{
QNetworkReply * reply = m_updateReply . data ( ) ;
int progress = ( bytesReceived / ( float ) bytesTotal ) * 100.0f ;
if ( ! reply )
return ;
m_updateProgressDialog - > setValue ( progress ) ;
}
void MainWindow : : onUpdateDownloadReadyRead ( )
{
QNetworkReply * reply = m_updateReply . data ( ) ;
if ( ! reply )
return ;
m_updateFile . write ( reply - > readAll ( ) ) ;
}
void MainWindow : : updateRetroArchNightly ( )
{
QUrl url ( QUrl ( buildbot_server_url ) . resolved ( QUrl ( RETROARCH_NIGHTLY_UPDATE_PATH ) ) ) ;
QNetworkRequest request ( url ) ;
QNetworkReply * reply = NULL ;
QByteArray urlArray = url . toString ( ) . toUtf8 ( ) ;
const char * urlData = urlArray . constData ( ) ;
if ( m_updateFile . isOpen ( ) )
{
RARCH_ERR ( " [Qt]: File is already open. \n " ) ;
return ;
}
else
{
QString fileName = QFileInfo ( url . toString ( ) ) . fileName ( ) + PARTIAL_EXTENSION ;
QByteArray fileNameArray = fileName . toUtf8 ( ) ;
const char * fileNameData = fileNameArray . constData ( ) ;
m_updateFile . setFileName ( fileName ) ;
if ( ! m_updateFile . open ( QIODevice : : WriteOnly ) )
{
showMessageBox ( msg_hash_to_str ( MENU_ENUM_LABEL_VALUE_QT_FILE_WRITE_OPEN_FAILED ) , MainWindow : : MSGBOX_TYPE_ERROR , Qt : : ApplicationModal , false ) ;
RARCH_ERR ( " [Qt]: Could not open file for writing: %s \n " , fileNameData ) ;
return ;
}
}
RARCH_LOG ( " [Qt]: Starting update of RetroArch... \n " ) ;
RARCH_LOG ( " [Qt]: Downloading URL %s \n " , urlData ) ;
request . setHeader ( QNetworkRequest : : UserAgentHeader , USER_AGENT ) ;
m_updateProgressDialog - > setWindowModality ( Qt : : NonModal ) ;
m_updateProgressDialog - > setMinimumDuration ( 0 ) ;
m_updateProgressDialog - > setRange ( 0 , 100 ) ;
m_updateProgressDialog - > setAutoClose ( true ) ;
m_updateProgressDialog - > setAutoReset ( true ) ;
m_updateProgressDialog - > setValue ( 0 ) ;
m_updateProgressDialog - > setLabelText ( QString ( msg_hash_to_str ( MSG_DOWNLOADING ) ) + " ... " ) ;
m_updateProgressDialog - > setCancelButtonText ( tr ( " Cancel " ) ) ;
m_updateProgressDialog - > show ( ) ;
m_updateReply = m_networkManager - > get ( request ) ;
2018-08-25 01:00:18 -04:00
2018-08-16 22:48:31 -04:00
reply = m_updateReply . data ( ) ;
/* make sure any previous connection is removed first */
disconnect ( m_updateProgressDialog , SIGNAL ( canceled ( ) ) , reply , SLOT ( abort ( ) ) ) ;
disconnect ( m_updateProgressDialog , SIGNAL ( canceled ( ) ) , m_updateProgressDialog , SLOT ( cancel ( ) ) ) ;
connect ( m_updateProgressDialog , SIGNAL ( canceled ( ) ) , reply , SLOT ( abort ( ) ) ) ;
connect ( m_updateProgressDialog , SIGNAL ( canceled ( ) ) , m_updateProgressDialog , SLOT ( cancel ( ) ) ) ;
connect ( reply , SIGNAL ( error ( QNetworkReply : : NetworkError ) ) , this , SLOT ( onUpdateNetworkError ( QNetworkReply : : NetworkError ) ) ) ;
connect ( reply , SIGNAL ( sslErrors ( const QList < QSslError > & ) ) , this , SLOT ( onUpdateNetworkSslErrors ( const QList < QSslError > & ) ) ) ;
connect ( reply , SIGNAL ( finished ( ) ) , this , SLOT ( onRetroArchUpdateDownloadFinished ( ) ) ) ;
connect ( reply , SIGNAL ( readyRead ( ) ) , this , SLOT ( onUpdateDownloadReadyRead ( ) ) ) ;
connect ( reply , SIGNAL ( downloadProgress ( qint64 , qint64 ) ) , this , SLOT ( onUpdateDownloadProgress ( qint64 , qint64 ) ) ) ;
}