/******************************************************************************* * retroshare-gui/src/: main.cpp * * * * libretroshare: retroshare core library * * * * Copyright 2006 by Crypton * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * * published by the Free Software Foundation, either version 3 of the * * License, or (at your option) any later version. * * * * 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 Affero General Public License for more details. * * * * You should have received a copy of the GNU Affero General Public License * * along with this program. If not, see . * * * *******************************************************************************/ #include "util/stacktrace.h" #include "util/argstream.h" CrashStackTrace gCrashStackTrace; #include #include #include #include #include "gui/FriendsDialog.h" #include "gui/GenCertDialog.h" #include "gui/MainWindow.h" #include "gui/NetworkDialog.h" #include "gui/NetworkView.h" #include "gui/QuickStartWizard.h" #include "gui/RetroShareLink.h" #include "gui/SoundManager.h" #include "gui/StartDialog.h" #include "gui/chat/ChatDialog.h" #include "gui/connect/ConfCertDialog.h" #include "gui/common/Emoticons.h" #include "gui/FileTransfer/SearchDialog.h" #include "gui/FileTransfer/TransfersDialog.h" #include "gui/settings/RsharePeerSettings.h" #include "gui/settings/rsharesettings.h" #include "idle/idle.h" #include "lang/languagesupport.h" #include "util/RsGxsUpdateBroadcast.h" #include "util/rsdir.h" #include "util/rstime.h" #include "retroshare/rsinit.h" #ifdef MESSENGER_WINDOW #include "gui/MessengerWindow.h" #endif #ifdef ENABLE_WEBUI # include "gui/settings/WebuiPage.h" #endif #ifdef RS_JSONAPI # include "gui/settings/JsonApiPage.h" #endif // RS_JSONAPI #include "TorControl/TorManager.h" #include "TorControl/TorControlWindow.h" #include "retroshare/rsidentity.h" #include "retroshare/rspeers.h" #ifdef SIGFPE_DEBUG #include #endif #if QT_VERSION >= QT_VERSION_CHECK (5, 0, 0) #ifdef WINDOWS_SYS #include #endif #endif #ifdef Q_OS_WIN #if QT_VERSION >= QT_VERSION_CHECK (5, 7, 0) // see INSTALL.W32 in openssl // Needed as workaround for gcc 5.3.0 because the call to GetProcAddress in cryptlib.c finds an function pointer // to the not existing function _OPENSSL_isservice in the executable running on Windows 7. extern "C" { __declspec(dllexport) __cdecl BOOL _OPENSSL_isservice(void) { DWORD sess; if (ProcessIdToSessionId(GetCurrentProcessId(),&sess)) return sess==0; return FALSE; } } #endif #endif /*** WINDOWS DON'T LIKE THIS - REDEFINES VER numbers. #include ****/ #include #include #include #include "gui/notifyqt.h" #include static void displayWarningAboutDSAKeys() { std::map > unsupported_keys; RsAccounts::GetUnsupportedKeys(unsupported_keys); if(unsupported_keys.empty()) return ; QMessageBox msgBox; QString txt = QObject::tr("You appear to have nodes associated to DSA keys:"); txt += "
    " ; for(std::map >::const_iterator it(unsupported_keys.begin());it!=unsupported_keys.end();++it) { txt += "
  • " + QString::fromStdString(it->first) ; txt += "
      " ; for(uint32_t i=0;isecond.size();++i) txt += "
    • " + QString::fromStdString(it->second[i]) + "
    • " ; txt += "
    " ; txt += "
  • " ; } txt += "
" ; msgBox.setText(txt) ; msgBox.setInformativeText(QObject::tr("DSA keys are not yet supported by this version of RetroShare. All these nodes will be unusable. We're very sorry for that.")); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.setWindowIcon(QIcon(":/icons/logo_128.png")); msgBox.exec(); } #ifdef WINDOWS_SYS #if QT_VERSION >= QT_VERSION_CHECK (5, 0, 0) && QT_VERSION < QT_VERSION_CHECK (5, 3, 0) QStringList filedialog_open_filenames_hook(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { return QFileDialog::getOpenFileNames(parent, caption, dir, filter, selectedFilter, options | QFileDialog::DontUseNativeDialog); } QString filedialog_open_filename_hook(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { return QFileDialog::getOpenFileName(parent, caption, dir, filter, selectedFilter, options | QFileDialog::DontUseNativeDialog); } QString filedialog_save_filename_hook(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { return QFileDialog::getSaveFileName(parent, caption, dir, filter, selectedFilter, options | QFileDialog::DontUseNativeDialog); } QString filedialog_existing_directory_hook(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options) { return QFileDialog::getExistingDirectory(parent, caption, dir, options | QFileDialog::DontUseNativeDialog); } #endif #endif int main(int argc, char *argv[]) { #ifdef WINDOWS_SYS // The current directory of the application is changed when using the native dialog on Windows // This is a quick fix until libretroshare is using a absolute path in the portable Version #if QT_VERSION >= QT_VERSION_CHECK (5, 3, 0) // Do we need a solution in v0.6? #endif #if QT_VERSION >= QT_VERSION_CHECK (5, 0, 0) && QT_VERSION < QT_VERSION_CHECK (5, 3, 0) typedef QStringList (*_qt_filedialog_open_filenames_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); typedef QString (*_qt_filedialog_open_filename_hook) (QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); typedef QString (*_qt_filedialog_save_filename_hook) (QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); typedef QString (*_qt_filedialog_existing_directory_hook)(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options); extern Q_GUI_EXPORT _qt_filedialog_open_filename_hook qt_filedialog_open_filename_hook; extern Q_GUI_EXPORT _qt_filedialog_open_filenames_hook qt_filedialog_open_filenames_hook; extern Q_GUI_EXPORT _qt_filedialog_save_filename_hook qt_filedialog_save_filename_hook; extern Q_GUI_EXPORT _qt_filedialog_existing_directory_hook qt_filedialog_existing_directory_hook; qt_filedialog_open_filename_hook = filedialog_open_filename_hook; qt_filedialog_open_filenames_hook = filedialog_open_filenames_hook; qt_filedialog_save_filename_hook = filedialog_save_filename_hook; qt_filedialog_existing_directory_hook = filedialog_existing_directory_hook; #endif #if QT_VERSION < QT_VERSION_CHECK (5, 0, 0) extern bool Q_GUI_EXPORT qt_use_native_dialogs; qt_use_native_dialogs = false; #endif { /* Set the current directory to the application dir, because the start dir with autostart from the registry run key is not the exe dir */ QApplication app(argc, argv); QDir::setCurrent(QCoreApplication::applicationDirPath()); } #endif #ifdef SIGFPE_DEBUG feenableexcept(FE_INVALID | FE_DIVBYZERO); #endif QStringList args = char_array_to_stringlist(argv+1, argc-1); Q_INIT_RESOURCE(images); Q_INIT_RESOURCE(icons); // Loop through all command-line args/values to get help wanted before RsInit exit. for (int i = 0; i < args.size(); ++i) { QString arg; /* Get the argument name and set a blank value */ arg = args.at(i); if ((arg.toLower() == "-h") || (arg.toLower() == "--help")) { QApplication dummyApp (argc, argv); // needed for QMessageBox Rshare::showUsageMessageBox(); } } // This is needed to allocate rsNotify, so that it can be used to ask for PGP passphrase // RsControl::earlyInitNotificationSystem() ; NotifyQt *notify = NotifyQt::Create(); rsNotify->registerNotifyClient(notify); /* RetroShare Core Objects */ RsInit::InitRsConfig(); RsConfigOptions conf; conf.jsonApiPort = 0 ; // disable JSon API at start. The JSonAPI preference UI will enable it according to saved parameters. argstream as(argc,argv); as >> option('s',"stderr" ,conf.outStderr ,"output to stderr instead of log file." ) >> option('u',"udp" ,conf.udpListenerOnly,"Only listen to UDP." ) >> parameter('c',"base-dir" ,conf.optBaseDir ,"directory", "Set base directory." ,false) >> parameter('l',"log-file" ,conf.logfname ,"logfile" ,"Set Log filename." ,false) >> parameter('d',"debug-level" ,conf.debugLevel ,"level" ,"Set debug level." ,false) >> parameter('i',"ip-address" ,conf.forcedInetAddress,"nnn.nnn.nnn.nnn", "Force IP address to use (if cannot be detected)." ,false) >> parameter('p',"port" ,conf.forcedPort ,"port" ,"Set listenning port to use." ,false) >> parameter('o',"opmode" ,conf.opModeStr ,"opmode" ,"Set Operating mode (Full, NoTurtle, Gaming, Minimal)." ,false); #ifdef RS_JSONAPI as >> parameter('J', "jsonApiPort", conf.jsonApiPort, "jsonApiPort", "Enable JSON API on the specified port", false ) >> parameter('P', "jsonApiBindAddress", conf.jsonApiBindAddress, "jsonApiBindAddress", "JSON API Bind Address.", false); #endif // ifdef RS_JSONAPI #ifdef LOCALNET_TESTING as >> parameter('R',"restrict-port" ,portRestrictions ,"port1-port2","Apply port restriction" ,false); #endif // ifdef LOCALNET_TESTING #ifdef RS_AUTOLOGIN as >> option('a',"auto-login" ,conf.autoLogin ,"AutoLogin (Windows Only) + StartMinimised"); #endif // ifdef RS_AUTOLOGIN conf.main_executable_path = argv[0]; int initResult = RsInit::InitRetroShare(conf); if(initResult == RS_INIT_NO_KEYRING) // happens when we already have accounts, but no pgp key. This is when switching to the openpgp-sdk version. { QApplication dummyApp (argc, argv); // needed for QMessageBox /* Translate into the desired language */ LanguageSupport::translate(LanguageSupport::defaultLanguageCode()); QMessageBox msgBox; msgBox.setText(QObject::tr("This version of RetroShare is using OpenPGP-SDK. As a side effect, it's not using the system shared PGP keyring, but has it's own keyring shared by all RetroShare instances.

You do not appear to have such a keyring, although PGP keys are mentioned by existing RetroShare accounts, probably because you just changed to this new version of the software.")); msgBox.setInformativeText(QObject::tr("Choose between:
  • Ok to copy the existing keyring from gnupg (safest bet), or
  • Close without saving to start fresh with an empty keyring (you will be asked to create a new PGP key to work with RetroShare, or import a previously saved pgp keypair).
  • Cancel to quit and forge a keyring by yourself (needs some PGP skills)
")); msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Discard | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.setWindowIcon(QIcon(":/icons/logo_128.png")); int ret = msgBox.exec(); if(ret == QMessageBox::Cancel) return 0 ; if(ret == QMessageBox::Ok) { if(!RsAccounts::CopyGnuPGKeyrings()) return 0 ; initResult = RsInit::InitRetroShare(conf); displayWarningAboutDSAKeys() ; } else initResult = RS_INIT_OK ; } if (initResult < 0) { /* Error occured */ QApplication dummyApp (argc, argv); // needed for QMessageBox /* Translate into the desired language */ LanguageSupport::translate(LanguageSupport::defaultLanguageCode()); displayWarningAboutDSAKeys(); QMessageBox mb(QMessageBox::Critical, QObject::tr("RetroShare"), "", QMessageBox::Ok); mb.setWindowIcon(QIcon(":/icons/logo_128.png")); switch (initResult) { case RS_INIT_AUTH_FAILED: std::cerr << "RsInit::InitRetroShare AuthGPG::InitAuth failed" << std::endl; mb.setText(QObject::tr("Initialization failed. Wrong or missing installation of PGP.")); break; default: /* Unexpected return code */ std::cerr << "RsInit::InitRetroShare unexpected return code " << initResult << std::endl; mb.setText(QObject::tr("An unexpected error occurred. Please report 'RsInit::InitRetroShare unexpected return code %1'.").arg(initResult)); break; } mb.exec(); return 1; } /* create global settings object path maybe wrong, when no profile exist in this case it can be use only for default values */ RshareSettings::Create (); /* Setup The GUI Stuff */ Rshare rshare(args, argc, argv, QString::fromUtf8(RsAccounts::ConfigDirectory().c_str())); /* Start RetroShare */ QString sDefaultGXSIdToCreate = ""; switch (initResult) { case RS_INIT_OK: { /* Login Dialog */ /* check for existing Certificate */ bool genCert = false; std::list accountIds; if (RsAccounts::GetAccountIds(accountIds) && (accountIds.size() > 0)) { StartDialog sd; if (sd.exec() == QDialog::Rejected) { return 1; } /* if we're logged in */ genCert = sd.requestedNewCert(); } else { genCert = true; } if (genCert) { GenCertDialog gd(false); if (gd.exec () == QDialog::Rejected) return 1; sDefaultGXSIdToCreate = gd.getGXSNickname(); } //splashScreen.show(); } break; case RS_INIT_HAVE_ACCOUNT: { //splashScreen.show(); //splashScreen.showMessage(rshare.translate("SplashScreen", "Load profile"), Qt::AlignHCenter | Qt::AlignBottom); RsPeerId preferredId; RsAccounts::GetPreferredAccountId(preferredId); // true: note auto-login is active Rshare::loadCertificate(preferredId, true); } break; default: /* Unexpected return code */ std::cerr << "RsInit::InitRetroShare unexpected return code " << initResult << std::endl; QMessageBox::warning(0, QObject::tr("RetroShare"), QObject::tr("An unexpected error occured. Please report 'RsInit::InitRetroShare unexpected return code %1'.").arg(initResult)); return 1; } /* recreate global settings object, now with correct path */ RshareSettings::Create(true); Rshare::resetLanguageAndStyle(); SoundManager::create(); bool is_hidden_node = false; bool is_auto_tor = false ; bool is_first_time = false ; RsAccounts::getCurrentAccountOptions(is_hidden_node,is_auto_tor,is_first_time); if(is_auto_tor) { // Now that we know the Tor service running, and we know the SSL id, we can make sure it provides a viable hidden service QString tor_hidden_service_dir = QString::fromStdString(RsAccounts::AccountDirectory()) + QString("/hidden_service/") ; Tor::TorManager *torManager = Tor::TorManager::instance(); torManager->setTorDataDirectory(Rshare::dataDirectory() + QString("/tor/")); torManager->setHiddenServiceDirectory(tor_hidden_service_dir); // re-set it, because now it's changed to the specific location that is run RsDirUtil::checkCreateDirectory(std::string(tor_hidden_service_dir.toUtf8())) ; torManager->setupHiddenService(); if(! torManager->start() || torManager->hasError()) { QMessageBox::critical(NULL,QObject::tr("Cannot start Tor Manager!"),QObject::tr("Tor cannot be started on your system: \n\n")+torManager->errorMessage()) ; return 1 ; } { TorControlDialog tcd(torManager) ; QString error_msg ; tcd.show(); while(tcd.checkForTor(error_msg) != TorControlDialog::TOR_STATUS_OK || tcd.checkForHiddenService() != TorControlDialog::HIDDEN_SERVICE_STATUS_OK) // runs until some status is reached: either tor works, or it fails. { QCoreApplication::processEvents(); rstime::rs_usleep(0.2*1000*1000) ; if(!error_msg.isNull()) { QMessageBox::critical(NULL,QObject::tr("Cannot start Tor"),QObject::tr("Sorry but Tor cannot be started on your system!\n\nThe error reported is:\"")+error_msg+"\"") ; return 1; } } tcd.hide(); if(tcd.checkForHiddenService() != TorControlDialog::HIDDEN_SERVICE_STATUS_OK) { QMessageBox::critical(NULL,QObject::tr("Cannot start a hidden tor service!"),QObject::tr("It was not possible to start a hidden service.")) ; return 1 ; } } } QSplashScreen splashScreen(QPixmap(":/images/logo/logo_splash.png")/* , Qt::WindowStaysOnTopHint*/); splashScreen.show(); splashScreen.showMessage(rshare.translate("SplashScreen", "Load configuration"), Qt::AlignHCenter | Qt::AlignBottom); QCoreApplication::processEvents(); /* stop Retroshare if startup fails */ if (!RsControl::instance()->StartupRetroShare()) { std::cerr << "libretroshare failed to startup!" << std::endl; return 1; } if(is_auto_tor) { // Tor works with viable hidden service. Let's use it! QString service_id ; QString onion_address ; uint16_t service_port ; uint16_t service_target_port ; uint16_t proxy_server_port ; QHostAddress service_target_address ; QHostAddress proxy_server_address ; Tor::TorManager *torManager = Tor::TorManager::instance(); torManager->getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port); torManager->getProxyServerInfo(proxy_server_address,proxy_server_port) ; std::cerr << "Got hidden service info: " << std::endl; std::cerr << " onion address : " << onion_address.toStdString() << std::endl; std::cerr << " service_id : " << service_id.toStdString() << std::endl; std::cerr << " service port : " << service_port << std::endl; std::cerr << " target port : " << service_target_port << std::endl; std::cerr << " target address : " << service_target_address.toString().toStdString() << std::endl; std::cerr << "Setting proxy server to " << service_target_address.toString().toStdString() << ":" << service_target_port << std::endl; rsPeers->setLocalAddress(rsPeers->getOwnId(), service_target_address.toString().toStdString(), service_target_port); rsPeers->setHiddenNode(rsPeers->getOwnId(), onion_address.toStdString(), service_port); rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, proxy_server_address.toString().toStdString(),proxy_server_port) ; } Rshare::initPlugins(); splashScreen.showMessage(rshare.translate("SplashScreen", "Create interface"), Qt::AlignHCenter | Qt::AlignBottom); QCoreApplication::processEvents(); // forces splashscreen to show up RsharePeerSettings::Create(); Emoticons::load(); if (Settings->value(QString::fromUtf8("FirstRun"), true).toBool()) { splashScreen.hide(); Settings->setValue(QString::fromUtf8("FirstRun"), false); SoundManager::initDefault(); #ifdef __APPLE__ /* For OSX, we set the default to "cleanlooks", as the AQUA style hides some input boxes * only on the first run - as the user might want to change it ;) */ QString osx_style("cleanlooks"); Rshare::setStyle(osx_style); Settings->setInterfaceStyle(osx_style); #endif // This is now disabled - as it doesn't add very much. // Need to make sure that defaults are sensible! #ifdef ENABLE_QUICKSTART_WIZARD QuickStartWizard qstartWizard; qstartWizard.exec(); #endif } MainWindow *w = MainWindow::Create (); splashScreen.finish(w); w->processLastArgs(); if (!sDefaultGXSIdToCreate.isEmpty()) { RsIdentityParameters params; params.nickname = sDefaultGXSIdToCreate.toUtf8().constData(); params.isPgpLinked = true; params.mImage.clear(); uint32_t token = 0; rsIdentity->createIdentity(token, params); } // I'm using a signal to transfer the hashing info to the mainwindow, because Qt schedules signals properly to // avoid clashes between infos from threads. // qRegisterMetaType("FileDetail") ; qRegisterMetaType("RsPeerId") ; std::cerr << "connecting signals and slots" << std::endl ; QObject::connect(notify,SIGNAL(gotTurtleSearchResult(qulonglong,FileDetail)),w->transfersDialog->searchDialog ,SLOT(updateFiles(qulonglong,FileDetail))) ; QObject::connect(notify,SIGNAL(deferredSignatureHandlingRequested()),notify,SLOT(handleSignatureEvent()),Qt::QueuedConnection) ; QObject::connect(notify,SIGNAL(chatLobbyTimeShift(int)),notify,SLOT(handleChatLobbyTimeShift(int)),Qt::QueuedConnection) ; QObject::connect(notify,SIGNAL(diskFull(int,int)) ,w ,SLOT(displayDiskSpaceWarning(int,int))) ; QObject::connect(notify,SIGNAL(filesPostModChanged(bool)) ,w ,SLOT(postModDirectories(bool)) ,Qt::QueuedConnection ) ; QObject::connect(notify,SIGNAL(transfersChanged()) ,w->transfersDialog ,SLOT(insertTransfers() )) ; QObject::connect(notify,SIGNAL(publicChatChanged(int)) ,w->friendsDialog ,SLOT(publicChatChanged(int) )); QObject::connect(notify,SIGNAL(neighboursChanged()) ,w->friendsDialog->networkDialog ,SLOT(securedUpdateDisplay())) ; QObject::connect(notify,SIGNAL(chatStatusChanged(const QString&,const QString&,bool)),w->friendsDialog,SLOT(updatePeerStatusString(const QString&,const QString&,bool))); QObject::connect(notify,SIGNAL(ownStatusMessageChanged()),w->friendsDialog,SLOT(loadmypersonalstatus())); // QObject::connect(notify,SIGNAL(logInfoChanged(const QString&)) ,w->friendsDialog->networkDialog,SLOT(setLogInfo(QString))) ; QObject::connect(notify,SIGNAL(discInfoChanged()) ,w->friendsDialog->networkView,SLOT(update()),Qt::QueuedConnection) ; QObject::connect(notify,SIGNAL(errorOccurred(int,int,const QString&)),w,SLOT(displayErrorMessage(int,int,const QString&))) ; w->installGroupChatNotifier(); /* only show window, if not startMinimized */ if (RsInit::getStartMinimised() || Settings->getStartMinimized()) { splashScreen.close(); } else { w->show(); } /* Startup a Timer to keep the gui's updated */ QTimer *timer = new QTimer(w); timer -> connect(timer, SIGNAL(timeout()), notify, SLOT(UpdateGUI())); timer->start(1000); notify->enable() ; // enable notification system after GUI creation, to avoid data races in Qt. #ifdef ENABLE_WEBUI WebuiPage::checkStartWebui(); #endif // ENABLE_WEBUI #ifdef RS_JSONAPI JsonApiPage::checkStartJsonApi(); #endif // RS_JSONAPI // This is done using a timer, because the passphrase request from notify is asynchrouneous and therefore clearing the // passphrase here makes it request for a passphrase when creating the default chat identity. QTimer::singleShot(10000, notify, SLOT(resetCachedPassphrases())) ; /* dive into the endless loop */ int ti = rshare.exec(); delete w ; #ifdef RS_JSONAPI JsonApiPage::checkShutdownJsonApi(); #endif // RS_JSONAPI #ifdef ENABLE_WEBUI WebuiPage::checkShutdownWebui(); #endif // ENABLE_WEBUI /* cleanup */ ChatDialog::cleanupChat(); #ifdef RS_ENABLE_GXS RsGxsUpdateBroadcast::cleanup(); #endif RsControl::instance()->rsGlobalShutDown(); delete(soundManager); soundManager = NULL; Settings->sync(); delete(Settings); return ti ; }