#include "GuestWidget.h" #include "ui_GuestWidget.h" #include "megaapi.h" #include "MegaApplication.h" #include "QMegaMessageBox.h" #include "DialogOpener.h" #include #include "platform/Platform.h" #include "gui/Login2FA.h" #include "gui/GuiUtilities.h" #include #include #if QT_VERSION >= 0x050000 #include #endif using namespace mega; GuestWidget::GuestWidget(QWidget *parent) : GuestWidget(((MegaApplication *)qApp)->getMegaApi(), parent) { } GuestWidget::GuestWidget(MegaApi *megaApi, QWidget *parent) :mSSLSecureConnectionFailed(false), QWidget(parent), ui(new Ui::GuestWidget) { ui->setupUi(this); #ifdef _WIN32 if(getenv("QT_SCREEN_SCALE_FACTORS") || getenv("QT_SCALE_FACTOR")) { //do not use WA_TranslucentBackground when using custom scale factors in windows setStyleSheet(styleSheet().append(QString::fromUtf8("#wGuestWidgetIn{border-radius: 0px;}" ) )); } else #endif { setAttribute(Qt::WA_TranslucentBackground); } ui->lEmail->setStyleSheet(QString::fromAscii("QLineEdit {color: black;}")); ui->lPassword->setStyleSheet(QString::fromAscii("QLineEdit {color: black;}")); ui->leCode->setStyleSheet(QString::fromAscii("QLineEdit {color: black;}")); reset_UI_props(); app = (MegaApplication *)qApp; this->megaApi = megaApi; preferences = Preferences::instance(); closing = false; loggingStarted = false; delegateListener = new QTMegaRequestListener(megaApi, this); megaApi->addRequestListener(delegateListener); ui->sLoginTitle->setCurrentWidget(ui->pLoginTitle); ui->sPages->setCurrentWidget(ui->pLogin); state = GuestWidgetState::LOGIN; ui->lLogin2FAError->hide(); connect(ui->leCode, &QLineEdit::textChanged, this, &GuestWidget::hide2FaLoginError); connect(ui->lEmail, &QLineEdit::textChanged, this, &GuestWidget::hideLoginError); connect(ui->lPassword, &QLineEdit::textChanged, this, &GuestWidget::hideLoginError); connect(static_cast(qApp), SIGNAL(fetchNodesAfterBlock()), this, SLOT(fetchNodesAfterBlockCallbak())); connect(static_cast(qApp), SIGNAL(setupWizardCreated()), this, SLOT(connectToSetupWizard())); resetFocus(); } GuestWidget::~GuestWidget() { delete delegateListener; delete ui; } void GuestWidget::setTexts(const QString& s1, const QString& s2) { ui->lEmail->setText(s1); ui->lPassword->setText(s2); } std::pair GuestWidget::getTexts() { return std::make_pair(ui->lEmail->text(), ui->lPassword->text()); } void GuestWidget::onRequestStart(MegaApi*, MegaRequest* request) { if (request->getType() == MegaRequest::TYPE_LOGIN) { ui->lProgress->setText(tr("Logging in...")); page_progress(); } if (request->getType() == MegaRequest::TYPE_CREATE_ACCOUNT) { ui->lProgress->setText(tr("Creating account...")); page_progress(); } else if (request->getType() == MegaRequest::TYPE_LOGOUT && request->getFlag()) { closing = true; page_logout(); } } void GuestWidget::page_fetchnodes() { ui->lProgress->setText(tr("Fetching file list...")); ui->progressBar->setValue(-1); page_progress(); } void GuestWidget::onRequestFinish(MegaApi *, MegaRequest *request, MegaError *error) { if (request->getType() == MegaRequest::TYPE_LOGOUT) { if(loggingStarted) { loggingStarted = false; //This message is shown every time the user tries to login and the SSL fails if(request->getParamType() == MegaError::API_ESSL) { mSSLSecureConnectionFailed = true; showSSLSecureConnectionErrorMessage(request); megaApi->localLogout(); } } if(mWhyAmISeeingThisDialog) { mWhyAmISeeingThisDialog->close(); } reset_UI_props(); closing = false; page_login(); return; } if (closing) { return; } switch (request->getType()) { case MegaRequest::TYPE_LOGIN: { if (error->getErrorCode() == MegaError::API_EMFAREQUIRED || error->getErrorCode() == MegaError::API_EFAILED || error->getErrorCode() == MegaError::API_EEXPIRED) { ui->bCancel->setVisible(false); } if (error->getErrorCode() == MegaError::API_OK) { if (loggingStarted) { preferences->setAccountStateInGeneral(Preferences::STATE_LOGGED_OK); auto email = request->getEmail(); static_cast(qApp)->fetchNodes(QString::fromUtf8(email ? email : "")); if (!preferences->hasLoggedIn()) { preferences->setHasLoggedIn(QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000); } } //Login successful -> unset the SSL fail flag mSSLSecureConnectionFailed = false; page_fetchnodes(); break; } if (loggingStarted) { preferences->setAccountStateInGeneral(Preferences::STATE_LOGGED_FAILED); if (error->getErrorCode() == MegaError::API_ENOENT) { incorrectCredentialsMessageReceived = true; } else if (error->getErrorCode() == MegaError::API_EMFAREQUIRED) { page_login2FA(); return; } else if (error->getErrorCode() == MegaError::API_EINCOMPLETE) { QMegaMessageBox::warning(nullptr, tr("Error"), tr("Please check your e-mail and click the link to confirm your account."), QMessageBox::Ok); } else if (error->getErrorCode() == MegaError::API_ETOOMANY) { QMegaMessageBox::warning(nullptr, tr("Error"), tr("You have attempted to log in too many times.[BR]Please wait until %1 and try again.") .replace(QString::fromUtf8("[BR]"), QString::fromUtf8("\n")) .arg(QTime::currentTime().addSecs(3600).toString(QString::fromUtf8("hh:mm"))) , QMessageBox::Ok); } else if (error->getErrorCode() == MegaError::API_EBLOCKED) { QMegaMessageBox::critical(nullptr, tr("Error"), tr("Your account has been blocked. Please contact support@mega.co.nz")); } else if (error->getErrorCode() == MegaError::API_EFAILED || error->getErrorCode() == MegaError::API_EEXPIRED) { showLogin2FaError(); page_login2FA(); return; } //Do not show this error if the SSL Secure connection has failed else if (error->getErrorCode() != MegaError::API_ESSL && !mSSLSecureConnectionFailed) { QMegaMessageBox::warning(nullptr, tr("Error"), QCoreApplication::translate("MegaError", error->getErrorString()), QMessageBox::Ok); } loggingStarted = false; } page_login(); break; } case MegaRequest::TYPE_FETCH_NODES: { if (error->getErrorCode() != MegaError::API_OK) { loggingStarted = false; if (error->getErrorCode() != MegaError::API_EBLOCKED) { page_login(); } break; } if (loggingStarted) { if (!megaApi->isFilesystemAvailable()) { page_login(); return; } } break; } case MegaRequest::TYPE_RESEND_VERIFICATION_EMAIL: { int e = error->getErrorCode(); if (e == MegaError::API_OK) { ui->lEmailSent->setStyleSheet(QString::fromUtf8("#lEmailSent {font-size: 11px; color: #666666;}")); ui->lEmailSent->setText(tr("Email sent")); } else { ui->lEmailSent->setStyleSheet(QString::fromUtf8("#lEmailSent {font-size: 11px; color: #F0373A;}")); if (e == MegaError::API_ETEMPUNAVAIL) { ui->lEmailSent->setText(QString::fromUtf8("Email already sent")); } else { ui->lEmailSent->setText(QString::fromUtf8("%1").arg(QCoreApplication::translate("MegaError", error->getErrorString()))); } } ui->lEmailSent->setVisible(true); Utilities::animateProperty(ui->lEmailSent, 400, "opacity", ui->lEmailSent->property("opacity"), 1.0); int animationTime = 500; QTimer::singleShot(10000-animationTime, this, [this, animationTime] () { Utilities::animateProperty(ui->lEmailSent, animationTime, "opacity", 1.0, 0.5); QTimer::singleShot(animationTime, this, [this] () { ui->bVerifyEmail->setEnabled(true); }); }); break; } } } void GuestWidget::onRequestUpdate(MegaApi*, MegaRequest *request) { GuiUtilities::updateDataRequestProgressBar(ui->progressBar, request); } void GuestWidget::resetFocus() { if(state == GuestWidgetState::LOGIN) { if (ui->lEmail->text().isEmpty()) { ui->lEmail->setFocus(); } ui->bLogin->setDefault(true); } if(state == GuestWidgetState::LOGIN2FA) { ui->leCode->setFocus(); ui->bLogin2FaNext->setDefault(true); } } void GuestWidget::disableListener() { if (!delegateListener) { return; } delete delegateListener; delegateListener = NULL; } void GuestWidget::enableListener() { if (delegateListener) { return; } delegateListener = new QTMegaRequestListener(megaApi, this); megaApi->addRequestListener(delegateListener); } void GuestWidget::initialize() { if(mWhyAmISeeingThisDialog) { mWhyAmISeeingThisDialog->close(); } reset_UI_props(); closing = false; loggingStarted = false; ui->lEmail->clear(); resetFocus(); page_login(); } void GuestWidget::resetPageAfterBlock() { switch(state) { case GuestWidgetState::LOGIN: { page_login(); break; } case GuestWidgetState::PROGRESS: //Notice: we are not considering fetchnode & loging out as different pages: the progress text would already be set { page_progress(); break; } case GuestWidgetState::SETTINGUP: { page_settingUp(); break; } case GuestWidgetState::LOGIN2FA: { page_login2FA(); break; } default: assert(false && "Unexpected state to reset Guest Widget to"); break; } } void GuestWidget::showLoginError(const QString &errorMessage) const { constexpr int animationTimeMillis{300}; ui->lLoginErrors->setText(errorMessage); ui->sLoginTitle->setCurrentWidget(ui->pLoginErrors); Utilities::animatePartialFadein(ui->lLoginErrors, animationTimeMillis); } void GuestWidget::showLogin2FaError() const { constexpr int animationTimeMillis{300}; Utilities::animatePartialFadein(ui->lLogin2FAError, animationTimeMillis); ui->lLogin2FAError->setText(ui->lLogin2FAError->text().toUpper()); ui->lLogin2FAError->show(); } void GuestWidget::showSSLSecureConnectionErrorMessage(MegaRequest *request) const { QMegaMessageBox::critical(nullptr, QString::fromUtf8("MEGAsync"), tr("Our SSL key can't be verified. You could be affected by a man-in-the-middle attack or your antivirus software could be intercepting your communications and causing this problem. Please disable it and try again.") + QString::fromUtf8(" (Issuer: %1)").arg(QString::fromUtf8(request->getText() ? request->getText() : "Unknown"))); } void GuestWidget::hideLoginError() { const bool isLoginErrorBeingShowed{ui->sLoginTitle->currentWidget() == ui->pLoginErrors}; if(isLoginErrorBeingShowed) { constexpr int transitionTimeMillis{100}; Utilities::animatePartialFadeout(ui->lLoginErrors, transitionTimeMillis); QTimer::singleShot(transitionTimeMillis, this, [&](){ui->sLoginTitle->setCurrentWidget(ui->pLoginTitle);}); } } void GuestWidget::hide2FaLoginError() { if(ui->lLogin2FAError->isVisible()) { constexpr int animationTimeMillis{100}; Utilities::animatePartialFadeout(ui->lLogin2FAError, animationTimeMillis); QTimer::singleShot(animationTimeMillis, this, [&](){ui->lLogin2FAError->hide();}); } } void GuestWidget::setBlockState(int lockType) { switch(lockType) { case MegaApi::ACCOUNT_BLOCKED_VERIFICATION_EMAIL: { page_lockedEmailAccount(); break; } case MegaApi::ACCOUNT_BLOCKED_VERIFICATION_SMS: { page_lockedSMSAccount(); break; } case MegaApi::ACCOUNT_NOT_BLOCKED: default: { resetPageAfterBlock(); break; } } } void GuestWidget::on_bLogin_clicked() { mEmail = ui->lEmail->text().toLower().trimmed(); mPassword = ui->lPassword->text(); if (!mEmail.length()) { showLoginError(tr("Please, enter your e-mail address")); return; } if (!mEmail.contains(QChar::fromAscii('@')) || !mEmail.contains(QChar::fromAscii('.'))) { showLoginError(tr("Please, enter a valid e-mail address")); return; } if (!mPassword.length()) { showLoginError(tr("Please, enter your password")); ui->lPassword->setFocus(); return; } DialogOpener::removeDialogByClass(); megaApi->login(mEmail.toUtf8().constData(), mPassword.toUtf8().constData()); loggingStarted = true; } void GuestWidget::on_bCreateAccount_clicked() { DialogOpener::removeDialogByClass(); emit forwardAction(SetupWizard::PAGE_NEW_ACCOUNT); } void GuestWidget::on_bSettings_clicked() { QPoint p = ui->bSettings->mapToGlobal(QPoint(ui->bSettings->width() - 2, ui->bSettings->height())); #ifdef __APPLE__ QPointer iod = this; #endif app->showTrayMenu(&p); #ifdef __APPLE__ if (!iod) { return; } if (!this->rect().contains(this->mapFromGlobal(QCursor::pos()))) { this->hide(); } #endif } void GuestWidget::on_bForgotPassword_clicked() { Utilities::openUrl(QUrl(QString::fromUtf8("mega://#recovery"))); } void GuestWidget::on_bCancel_clicked() { if (closing) { megaApi->localLogout(); return; } if (megaApi->isLoggedIn()) { closing = true; megaApi->logout(true, nullptr); page_logout(); } else { loggingStarted = false; megaApi->localLogout(); page_login(); } } void GuestWidget::on_bVerifySMSLogout_clicked() { app->unlink(); } void GuestWidget::on_bVerifyEmailLogout_clicked() { app->unlink(); } void GuestWidget::on_bVerifyEmail_clicked() { ui->lEmailSent->setProperty("opacity", 0.0); ui->bVerifyEmail->setEnabled(false); megaApi->resendVerificationEmail(delegateListener); } void GuestWidget::on_bVerifySMS_clicked() { static_cast(qApp)->goToMyCloud(); } void GuestWidget::on_bWhyAmIseen_clicked() { if (!mWhyAmISeeingThisDialog) { QString title {QString::fromUtf8(QT_TRANSLATE_NOOP("MegaInfoMessage", "Locked Accounts"))}; QString firstP {QString::fromUtf8(QT_TRANSLATE_NOOP("MegaInfoMessage","It is possible that you are using the same password for your MEGA account as for other services, and that at least one of these other services has suffered a data breach."))}; QString secondP {QString::fromUtf8(QT_TRANSLATE_NOOP("MegaInfoMessage","Your password leaked and is now being used by bad actors to log into your accounts, including, but not limited to, your MEGA account."))}; mWhyAmISeeingThisDialog = new MegaInfoMessage(QString::fromUtf8(QT_TRANSLATE_NOOP("MegaInfoMessage","Why am I seeing this?")),title, firstP, secondP, QIcon(QString::fromUtf8(":/images/locked_account_ico.png")).pixmap(70.0, 70.0)); } DialogOpener::showDialog(mWhyAmISeeingThisDialog); } void GuestWidget::fetchNodesAfterBlockCallbak() { loggingStarted = true; page_fetchnodes(); } void GuestWidget::connectToSetupWizard() { auto setupWizard = static_cast(qApp)->getSetupWizard(); if (setupWizard) { connect(setupWizard.data(), SIGNAL(pageChanged(int)), this, SLOT(onSetupWizardPageChanged(int))); } } void GuestWidget::onSetupWizardPageChanged(int page) { switch(page) { case SetupWizard::PAGE_MODE: { page_settingUp(); break; } case SetupWizard::PAGE_PROGRESS: { page_progress();// this should already be managed by requests callbacks that also set the proper text // but calling it again is idempotent break; } case SetupWizard::PAGE_LOGOUT: { page_logout(); break; } default: { page_login(); break; } } } void GuestWidget::page_login() { if (ui->sPages->currentWidget() == ui->pLogin) { return; } ui->sPages->setStyleSheet(QString::fromUtf8("image: url(\"://images/login_background.png\");")); ui->sPages->style()->unpolish(ui->sPages); ui->sPages->style()->polish(ui->sPages); ui->lPassword->clear(); ui->sPages->setCurrentWidget(ui->pLogin); if(incorrectCredentialsMessageReceived) { incorrectCredentialsMessageReceived = false; showLoginError(tr("Incorrect email and/or password.")); } else { hideLoginError(); } state = GuestWidgetState::LOGIN; resetFocus(); emit onPageLogin(); } void GuestWidget::page_progress() { if (ui->sPages->currentWidget() == ui->pProgress) { return; } ui->bCancel->setVisible(true); ui->bCancel->setEnabled(true); ui->sPages->setStyleSheet(QString::fromUtf8("image: url(\"://images/login_background.png\");")); ui->sPages->style()->unpolish(ui->sPages); ui->sPages->style()->polish(ui->sPages); ui->progressBar->setMaximum(0); ui->progressBar->setValue(-1); ui->sPages->setCurrentWidget(ui->pProgress); state = GuestWidgetState::PROGRESS; } void GuestWidget::page_settingUp() { if (ui->sPages->currentWidget() == ui->pSettingUp) { return; } ui->sPages->setStyleSheet(QString::fromUtf8("image: url(\"://images/login_intermediate.png\");")); ui->sPages->style()->unpolish(ui->sPages); ui->sPages->style()->polish(ui->sPages); ui->sPages->setCurrentWidget(ui->pSettingUp); state = GuestWidgetState::SETTINGUP; } void GuestWidget::page_logout() { ui->lProgress->setText(tr("Logging out...")); ui->progressBar->setValue(-1); page_progress(); } void GuestWidget::page_lockedEmailAccount() { if (ui->sPages->currentWidget() == ui->pVerifyEmailAccount) { return; } ui->sPages->setStyleSheet(QString::fromUtf8("image: url(\"://images/login_background.png\");")); ui->sPages->style()->unpolish(ui->sPages); ui->sPages->style()->polish(ui->sPages); ui->sPages->setCurrentWidget(ui->pVerifyEmailAccount); } void GuestWidget::page_lockedSMSAccount() { if (ui->sPages->currentWidget() == ui->pVerifySMSAccount) { return; } ui->sPages->setStyleSheet(QString::fromUtf8("image: url(\"://images/login_background.png\");")); ui->sPages->style()->unpolish(ui->sPages); ui->sPages->style()->polish(ui->sPages); ui->sPages->setCurrentWidget(ui->pVerifySMSAccount); } void GuestWidget::page_login2FA() { if (ui->sPages->currentWidget() == ui->pLogin2FA) { return; } ui->lLostAuthCode->setText(tr("[A]Lost your authenticator device?[/A]") .replace(QString::fromUtf8("[A]"), QString::fromUtf8("")) .replace(QString::fromUtf8("[/A]"), QString::fromUtf8(""))); ui->sPages->setCurrentWidget(ui->pLogin2FA); ui->sPages->setStyleSheet(QStringLiteral("image: url(\":/images/login_plain_background.png\");")); state = GuestWidgetState::LOGIN2FA; resetFocus(); } void GuestWidget::reset_UI_props() { ui->lEmailSent->setVisible(false); ui->bVerifyEmail->setEnabled(true); ui->leCode->clear(); ui->lLogin2FAError->hide(); } void GuestWidget::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { ui->retranslateUi(this); } QWidget::changeEvent(event); } void GuestWidget::on_bLogin2FaNext_clicked() { QRegExp re(QString::fromUtf8("\\d\\d\\d\\d\\d\\d")); const QString pin{ui->leCode->text().trimmed()}; if (pin.isEmpty() || !re.exactMatch(pin)) { showLogin2FaError(); } else { megaApi->multiFactorAuthLogin(mEmail.toUtf8(), mPassword.toUtf8().constData(), pin.toUtf8().constData()); } } void GuestWidget::on_bLoging2FaCancel_clicked() { megaApi->localLogout(); page_login(); loggingStarted = false; } void GuestWidget::on_bLogin2FaHelp_clicked() { QString helpUrl = Preferences::BASE_URL + QString::fromAscii("/recovery"); Utilities::openUrl(QUrl(helpUrl)); }