2018-06-23 16:59:23 +00:00
# include "common/PythonManager.h"
2018-02-10 18:04:31 +00:00
# include "CutterApplication.h"
2018-06-23 16:59:23 +00:00
# ifdef CUTTER_ENABLE_JUPYTER
# include "common/JupyterConnection.h"
# endif
2019-02-09 13:05:06 +00:00
# include "plugins/PluginManager.h"
2018-02-10 18:04:31 +00:00
# include <QApplication>
# include <QFileOpenEvent>
# include <QEvent>
2019-02-05 07:46:39 +00:00
# include <QMenu>
2018-02-10 18:04:31 +00:00
# include <QMessageBox>
# include <QCommandLineParser>
# include <QTextCodec>
# include <QStringList>
# include <QProcess>
2018-06-26 07:40:08 +00:00
# include <QPluginLoader>
# include <QDir>
2018-10-31 16:07:53 +00:00
# include <QTranslator>
# include <QLibraryInfo>
2018-02-10 18:04:31 +00:00
2018-08-26 18:37:11 +00:00
# include "CutterConfig.h"
2018-10-21 16:23:35 +00:00
# include <cstdlib>
2019-03-09 13:11:39 +00:00
# ifdef Q_OS_WIN
# include <QtNetwork/QtNetwork>
# endif // Q_OS_WIN
2018-03-21 20:32:32 +00:00
CutterApplication : : CutterApplication ( int & argc , char * * argv ) : QApplication ( argc , argv )
{
2018-11-02 16:49:15 +00:00
// Setup application information
setApplicationVersion ( CUTTER_VERSION_FULL ) ;
setWindowIcon ( QIcon ( " :/img/cutter.svg " ) ) ;
setAttribute ( Qt : : AA_DontShowIconsInMenus ) ;
2019-01-12 19:44:23 +00:00
setLayoutDirection ( Qt : : LeftToRight ) ;
2018-11-02 16:49:15 +00:00
// WARN!!! Put initialization code below this line. Code above this line is mandatory to be run First
2019-03-09 13:11:39 +00:00
# ifdef Q_OS_WIN
// Hack to force Cutter load internet connection related DLL's
QSslSocket s ;
s . sslConfiguration ( ) ;
# endif // Q_OS_WIN
2018-11-01 22:23:01 +00:00
// Load translations
2019-01-20 17:00:23 +00:00
if ( ! loadTranslations ( ) ) {
qWarning ( ) < < " Cannot load translations " ;
2018-10-31 16:07:53 +00:00
}
2019-01-20 17:00:23 +00:00
2018-11-01 22:23:01 +00:00
// Load fonts
int ret = QFontDatabase : : addApplicationFont ( " :/fonts/Anonymous Pro.ttf " ) ;
if ( ret = = - 1 ) {
qWarning ( ) < < " Cannot load Anonymous Pro font. " ;
}
ret = QFontDatabase : : addApplicationFont ( " :/fonts/Inconsolata-Regular.ttf " ) ;
if ( ret = = - 1 ) {
qWarning ( ) < < " Cannot load Incosolata-Regular font. " ;
}
2018-02-10 18:04:31 +00:00
// Set QString codec to UTF-8
QTextCodec : : setCodecForLocale ( QTextCodec : : codecForName ( " UTF-8 " ) ) ;
# if QT_VERSION < QT_VERSION_CHECK(5,0,0)
QTextCodec : : setCodecForCStrings ( QTextCodec : : codecForName ( " UTF-8 " ) ) ;
QTextCodec : : setCodecForTr ( QTextCodec : : codecForName ( " UTF-8 " ) ) ;
# endif
QCommandLineParser cmd_parser ;
2018-03-21 20:32:32 +00:00
cmd_parser . setApplicationDescription (
QObject : : tr ( " A Qt and C++ GUI for radare2 reverse engineering framework " ) ) ;
2018-02-10 18:04:31 +00:00
cmd_parser . addHelpOption ( ) ;
cmd_parser . addVersionOption ( ) ;
cmd_parser . addPositionalArgument ( " filename " , QObject : : tr ( " Filename to open. " ) ) ;
QCommandLineOption analOption ( { " A " , " anal " } ,
2018-03-21 20:32:32 +00:00
QObject : : tr ( " Automatically open file and optionally start analysis. Needs filename to be specified. May be a value between 0 and 2: 0 = no analysis, 1 = aaa, 2 = aaaa (experimental) " ) ,
QObject : : tr ( " level " ) ) ;
2018-02-10 18:04:31 +00:00
cmd_parser . addOption ( analOption ) ;
2018-08-18 16:09:49 +00:00
QCommandLineOption scriptOption ( " i " ,
QObject : : tr ( " Run script file " ) ,
QObject : : tr ( " file " ) ) ;
cmd_parser . addOption ( scriptOption ) ;
2018-06-23 16:59:23 +00:00
QCommandLineOption pythonHomeOption ( " pythonhome " , QObject : : tr ( " PYTHONHOME to use for embeded python interpreter " ) ,
2018-03-21 20:32:32 +00:00
" PYTHONHOME " ) ;
2018-03-06 17:21:42 +00:00
cmd_parser . addOption ( pythonHomeOption ) ;
2018-02-10 18:04:31 +00:00
cmd_parser . process ( * this ) ;
QStringList args = cmd_parser . positionalArguments ( ) ;
// Check r2 version
QString r2version = r_core_version ( ) ;
QString localVersion = " " R2_GITTAP ;
2018-03-21 20:32:32 +00:00
if ( r2version ! = localVersion ) {
2018-02-10 18:04:31 +00:00
QMessageBox msg ;
msg . setIcon ( QMessageBox : : Critical ) ;
msg . setStandardButtons ( QMessageBox : : Yes | QMessageBox : : No ) ;
msg . setWindowTitle ( QObject : : tr ( " Version mismatch! " ) ) ;
2018-03-21 20:32:32 +00:00
msg . setText ( QString (
QObject : : tr ( " The version used to compile Cutter (%1) does not match the binary version of radare2 (%2). This could result in unexpected behaviour. Are you sure you want to continue? " ) ) . arg (
localVersion , r2version ) ) ;
2018-10-21 16:23:35 +00:00
if ( msg . exec ( ) = = QMessageBox : : No ) {
std : : exit ( 1 ) ;
}
2018-02-10 18:04:31 +00:00
}
2019-02-13 21:53:52 +00:00
# ifdef CUTTER_ENABLE_PYTHON
2018-06-23 16:59:23 +00:00
// Init python
2018-03-21 20:32:32 +00:00
if ( cmd_parser . isSet ( pythonHomeOption ) ) {
2018-06-23 16:59:23 +00:00
Python ( ) - > setPythonHome ( cmd_parser . value ( pythonHomeOption ) ) ;
2018-03-06 17:21:42 +00:00
}
2018-06-23 16:59:23 +00:00
Python ( ) - > initialize ( ) ;
2019-02-13 21:53:52 +00:00
# endif
2018-06-23 16:59:23 +00:00
2019-02-20 17:52:11 +00:00
Core ( ) - > initialize ( ) ;
Core ( ) - > setSettings ( ) ;
Config ( ) - > loadInitial ( ) ;
2018-03-06 17:21:42 +00:00
2018-02-10 18:04:31 +00:00
bool analLevelSpecified = false ;
2018-03-21 20:32:32 +00:00
int analLevel = 0 ;
2018-02-10 18:04:31 +00:00
2018-03-21 20:32:32 +00:00
if ( cmd_parser . isSet ( analOption ) ) {
2018-02-10 18:04:31 +00:00
analLevel = cmd_parser . value ( analOption ) . toInt ( & analLevelSpecified ) ;
2018-03-21 20:32:32 +00:00
if ( ! analLevelSpecified | | analLevel < 0 | | analLevel > 2 ) {
printf ( " %s \n " ,
QObject : : tr ( " Invalid Analysis Level. May be a value between 0 and 2. " ) . toLocal8Bit ( ) . constData ( ) ) ;
2018-10-21 16:23:35 +00:00
std : : exit ( 1 ) ;
2018-02-10 18:04:31 +00:00
}
}
2019-02-09 13:05:06 +00:00
Plugins ( ) - > loadPlugins ( ) ;
2018-07-06 18:14:39 +00:00
2018-02-27 13:06:04 +00:00
mainWindow = new MainWindow ( ) ;
2018-05-13 07:50:01 +00:00
installEventFilter ( mainWindow ) ;
2018-02-10 18:04:31 +00:00
2019-02-05 07:46:39 +00:00
// set up context menu shortcut display fix
# if QT_VERSION_CHECK(5, 10, 0) < QT_VERSION
setStyle ( new CutterProxyStyle ( ) ) ;
# endif // QT_VERSION_CHECK(5, 10, 0) < QT_VERSION
2018-03-21 20:32:32 +00:00
if ( args . empty ( ) ) {
if ( analLevelSpecified ) {
printf ( " %s \n " ,
QObject : : tr ( " Filename must be specified to start analysis automatically. " ) . toLocal8Bit ( ) . constData ( ) ) ;
2018-10-21 16:23:35 +00:00
std : : exit ( 1 ) ;
2018-02-10 18:04:31 +00:00
}
2019-01-19 20:54:02 +00:00
// check if this is the first execution of Cutter in this computer
// Note: the execution after the preferences benn reset, will be considered as first-execution
if ( Config ( ) - > isFirstExecution ( ) ) {
mainWindow - > displayWelcomeDialog ( ) ;
}
2018-02-27 13:06:04 +00:00
mainWindow - > displayNewFileDialog ( ) ;
2018-03-21 20:32:32 +00:00
} else { // filename specified as positional argument
2018-08-18 16:04:45 +00:00
InitialOptions options ;
options . filename = args [ 0 ] ;
if ( analLevelSpecified ) {
switch ( analLevel ) {
case 0 :
default :
options . analCmd = { } ;
break ;
case 1 :
options . analCmd = { " aaa " } ;
break ;
case 2 :
options . analCmd = { " aaaa " } ;
break ;
}
}
2018-08-18 16:09:49 +00:00
options . script = cmd_parser . value ( scriptOption ) ;
2018-08-18 16:04:45 +00:00
mainWindow - > openNewFile ( options , analLevelSpecified ) ;
2018-02-10 18:04:31 +00:00
}
2018-06-26 07:40:08 +00:00
2018-11-10 12:10:26 +00:00
# ifdef CUTTER_APPVEYOR_R2DEC
qputenv ( " R2DEC_HOME " , " radare2 \\ lib \\ plugins \\ r2dec-js " ) ;
# endif
2018-02-10 18:04:31 +00:00
}
2018-02-27 13:06:04 +00:00
CutterApplication : : ~ CutterApplication ( )
{
2019-02-13 21:53:52 +00:00
# ifdef CUTTER_ENABLE_PYTHON
2019-02-09 13:05:06 +00:00
Plugins ( ) - > destroyPlugins ( ) ;
2019-02-13 21:53:52 +00:00
# endif
2018-02-27 13:06:04 +00:00
delete mainWindow ;
2019-02-13 21:53:52 +00:00
# ifdef CUTTER_ENABLE_PYTHON
2019-02-03 13:00:40 +00:00
Python ( ) - > shutdown ( ) ;
2019-02-13 21:53:52 +00:00
# endif
2018-02-27 13:06:04 +00:00
}
bool CutterApplication : : event ( QEvent * e )
{
2018-03-21 20:32:32 +00:00
if ( e - > type ( ) = = QEvent : : FileOpen ) {
2018-02-10 18:04:31 +00:00
QFileOpenEvent * openEvent = static_cast < QFileOpenEvent * > ( e ) ;
2018-03-21 20:32:32 +00:00
if ( openEvent ) {
if ( m_FileAlreadyDropped ) {
2018-05-08 20:44:53 +00:00
// We already dropped a file in macOS, let's spawn another instance
2018-02-10 18:04:31 +00:00
// (Like the File -> Open)
QString fileName = openEvent - > file ( ) ;
QProcess process ( this ) ;
process . setEnvironment ( QProcess : : systemEnvironment ( ) ) ;
QStringList args = QStringList ( fileName ) ;
process . startDetached ( qApp - > applicationFilePath ( ) , args ) ;
2018-03-21 20:32:32 +00:00
} else {
2018-02-10 18:04:31 +00:00
QString fileName = openEvent - > file ( ) ;
m_FileAlreadyDropped = true ;
2018-02-27 13:06:04 +00:00
mainWindow - > closeNewFileDialog ( ) ;
2018-08-18 16:04:45 +00:00
InitialOptions options ;
options . filename = fileName ;
mainWindow - > openNewFile ( options ) ;
2018-02-10 18:04:31 +00:00
}
}
}
return QApplication : : event ( e ) ;
}
2018-06-26 07:40:08 +00:00
2019-01-20 17:00:23 +00:00
bool CutterApplication : : loadTranslations ( )
{
const QString & language = Config ( ) - > getCurrLocale ( ) . bcp47Name ( ) ;
if ( language = = QStringLiteral ( " en " ) | | language . startsWith ( QStringLiteral ( " en- " ) ) ) {
return true ;
}
const auto & allLocales = QLocale : : matchingLocales ( QLocale : : AnyLanguage , QLocale : : AnyScript ,
QLocale : : AnyCountry ) ;
bool cutterTrLoaded = false ;
for ( const QLocale & it : allLocales ) {
const QString & langPrefix = it . bcp47Name ( ) ;
if ( langPrefix = = language ) {
QApplication : : setLayoutDirection ( it . textDirection ( ) ) ;
QLocale : : setDefault ( it ) ;
QTranslator * trCutter = new QTranslator ;
QTranslator * trQtBase = new QTranslator ;
QTranslator * trQt = new QTranslator ;
const QStringList & cutterTrPaths = Config ( ) - > getTranslationsDirectories ( ) ;
for ( const auto & trPath : cutterTrPaths ) {
if ( trCutter & & trCutter - > load ( it , QLatin1String ( " cutter " ) , QLatin1String ( " _ " ) , trPath ) ) {
installTranslator ( trCutter ) ;
cutterTrLoaded = true ;
trCutter = nullptr ;
}
if ( trQt & & trQt - > load ( it , " qt " , " _ " , trPath ) ) {
installTranslator ( trQt ) ;
trQt = nullptr ;
}
if ( trQtBase & & trQtBase - > load ( it , " qtbase " , " _ " , trPath ) ) {
installTranslator ( trQtBase ) ;
trQtBase = nullptr ;
}
}
if ( trCutter ) {
delete trCutter ;
}
if ( trQt ) {
delete trQt ;
}
if ( trQtBase ) {
delete trQtBase ;
}
return true ;
}
}
if ( ! cutterTrLoaded ) {
qWarning ( ) < < " Cannot load Cutter's translation for " < < language ;
}
return false ;
2019-02-05 07:46:39 +00:00
}
void CutterProxyStyle : : polish ( QWidget * widget )
{
QProxyStyle : : polish ( widget ) ;
# if QT_VERSION_CHECK(5, 10, 0) < QT_VERSION
// HACK: This is the only way I've found to force Qt (5.10 and newer) to
// display shortcuts in context menus on all platforms. It's ugly,
// but it gets the job done.
if ( auto menu = qobject_cast < QMenu * > ( widget ) ) {
const auto & actions = menu - > actions ( ) ;
for ( auto action : actions ) {
action - > setShortcutVisibleInContextMenu ( true ) ;
}
}
# endif // QT_VERSION_CHECK(5, 10, 0) < QT_VERSION
}