void QMLManager::retrieveUserid() { if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) != 302) { appendTextToLog(QStringLiteral("Cloud storage connection not working correctly: %1").arg(QString(reply->readAll()))); setStartPageText(RED_FONT + tr("Cannot connect to cloud storage") + END_FONT); setAccessingCloud(-1); alreadySaving = false; return; } setCredentialStatus(VALID); QString userid(prefs.userid); if (userid.isEmpty()) { if (same_string(prefs.cloud_storage_email, "") || same_string(prefs.cloud_storage_password, "")) { appendTextToLog("cloud user name or password are empty, can't retrieve web user id"); setAccessingCloud(-1); alreadySaving = false; return; } appendTextToLog(QStringLiteral("calling getUserid with user %1").arg(prefs.cloud_storage_email)); userid = locationProvider->getUserid(prefs.cloud_storage_email, prefs.cloud_storage_password); } if (!userid.isEmpty()) { // overwrite the existing userid free(prefs.userid); prefs.userid = strdup(qPrintable(userid)); QSettings s; s.setValue("subsurface_webservice_uid", prefs.userid); s.sync(); } setCredentialStatus(VALID); setStartPageText("Cloud credentials valid, loading dives..."); git_storage_update_progress(true, "load dives with valid credentials"); // this only gets called with "alreadySaving" already locked loadDivesWithValidCredentials(); }
void QMLManager::loadDivesWithValidCredentials() { QString url; if (getCloudURL(url)) { QString errorString(get_error_string()); appendTextToLog(errorString); setStartPageText(RED_FONT + tr("Cloud storage error: %1").arg(errorString) + END_FONT); setAccessingCloud(-1); alreadySaving = false; return; } QByteArray fileNamePrt = QFile::encodeName(url); git_repository *git; const char *branch; int error; if (check_git_sha(fileNamePrt.data(), &git, &branch) == 0) { qDebug() << "local cache was current, no need to modify dive list"; appendTextToLog("Cloud sync shows local cache was current"); setLoadFromCloud(true); setAccessingCloud(-1); alreadySaving = false; return; } appendTextToLog("Cloud sync brought newer data, reloading the dive list"); clear_dive_file_data(); if (git != dummy_git_repository) { appendTextToLog(QString("have repository and branch %1").arg(branch)); error = git_load_dives(git, branch); } else { appendTextToLog(QString("didn't receive valid git repo, try again")); error = parse_file(fileNamePrt.data()); } setAccessingCloud(-1); if (!error) { report_error("filename is now %s", fileNamePrt.data()); const char *error_string = get_error_string(); appendTextToLog(error_string); set_filename(fileNamePrt.data(), true); } else { report_error("failed to open file %s", fileNamePrt.data()); QString errorString(get_error_string()); appendTextToLog(errorString); setStartPageText(RED_FONT + tr("Cloud storage error: %1").arg(errorString) + END_FONT); alreadySaving = false; return; } prefs.unit_system = informational_prefs.unit_system; if (informational_prefs.unit_system == IMPERIAL) informational_prefs.units = IMPERIAL_units; prefs.units = informational_prefs.units; DiveListModel::instance()->clear(); process_dives(false, false); DiveListModel::instance()->addAllDives(); appendTextToLog(QStringLiteral("%1 dives loaded").arg(dive_table.nr)); if (dive_table.nr == 0) setStartPageText(tr("Cloud storage open successfully. No dives in dive list.")); setLoadFromCloud(true); alreadySaving = false; }
void QMLManager::saveChangesLocal() { if (unsaved_changes()) { git_storage_update_progress(true, "saving dives locally"); // reset the timers if (!loadFromCloud()) { // this seems silly, but you need a common ancestor in the repository in // order to be able to merge che changes later appendTextToLog("Don't save dives without loading from the cloud, first."); return; } if (alreadySaving) { appendTextToLog("save operation already in progress, can't save locally"); return; } alreadySaving = true; bool glo = prefs.git_local_only; prefs.git_local_only = true; if (save_dives(existing_filename)) { appendTextToLog(get_error_string()); setAccessingCloud(-1); prefs.git_local_only = glo; alreadySaving = false; return; } prefs.git_local_only = glo; mark_divelist_changed(false); git_storage_update_progress(false, "done with local save"); alreadySaving = false; } else { appendTextToLog("local save requested with no unsaved changes"); } }
void QMLManager::revertToNoCloudIfNeeded() { if (currentGitLocalOnly) { // we tried to connect to the cloud for the first time and that failed currentGitLocalOnly = false; prefs.git_local_only = true; } if (oldStatus() == NOCLOUD) { // we tried to switch to a cloud account and had previously used local data, // but connecting to the cloud account (and subsequently merging the local // and cloud data) failed - so let's delete the cloud credentials and go // back to NOCLOUD mode in order to prevent us from losing the locally stored // dives if (syncToCloud() == false) { appendTextToLog(QStringLiteral("taking things back offline since sync with cloud failed")); prefs.git_local_only = syncToCloud(); } free(prefs.cloud_storage_email); prefs.cloud_storage_email = NULL; free(prefs.cloud_storage_password); prefs.cloud_storage_password = NULL; setCloudUserName(""); setCloudPassword(""); setCredentialStatus(INCOMPLETE); set_filename(NOCLOUD_LOCALSTORAGE, true); setStartPageText(RED_FONT + tr("Failed to connect to cloud server, reverting to no cloud status") + END_FONT); } setAccessingCloud(-1); alreadySaving = false; }
void QMLManager::saveChangesCloud() { git_storage_update_progress(true, "start save change to cloud"); if (!loadFromCloud()) { appendTextToLog("Don't save dives without loading from the cloud, first."); return; } bool glo = prefs.git_local_only; bool cbs = prefs.cloud_background_sync; // first we need to store any unsaved changes to the local repo saveChangesLocal(); if (alreadySaving) { appendTextToLog("save operation in progress already, can't sync with server"); return; } prefs.git_local_only = false; alreadySaving = true; loadDivesWithValidCredentials(); alreadySaving = false; git_storage_update_progress(false, "finished syncing dive list to cloud server"); setAccessingCloud(-1); prefs.git_local_only = glo; prefs.cloud_background_sync = cbs; alreadySaving = false; }
QMLManager::QMLManager() : m_locationServiceEnabled(false), m_verboseEnabled(false), reply(0), deletedDive(0), deletedTrip(0), m_updateSelectedDive(-1), m_selectedDiveTimestamp(0), m_credentialStatus(UNKNOWN), m_lastDevicePixelRatio(1.0), alreadySaving(false) { m_instance = this; connect(qobject_cast<QApplication *>(QApplication::instance()), &QApplication::applicationStateChanged, this, &QMLManager::applicationStateChanged); appendTextToLog(getUserAgent()); appendTextToLog(QStringLiteral("build with Qt Version %1, runtime from Qt Version %2").arg(QT_VERSION_STR).arg(qVersion())); qDebug() << "Starting" << getUserAgent(); qDebug() << QStringLiteral("build with Qt Version %1, runtime from Qt Version %2").arg(QT_VERSION_STR).arg(qVersion()); setStartPageText(tr("Starting...")); setAccessingCloud(-1); setShowPin(false); // create location manager service locationProvider = new GpsLocation(&appendTextToLogStandalone, this); connect(locationProvider, SIGNAL(haveSourceChanged()), this, SLOT(hasLocationSourceChanged())); setLocationServiceAvailable(locationProvider->hasLocationsSource()); set_git_update_cb(&gitProgressCB); // make sure we know if the current cloud repo has been successfully synced syncLoadFromCloud(); }
void QMLManager::openLocalThenRemote(QString url) { clear_dive_file_data(); QByteArray fileNamePrt = QFile::encodeName(url); bool glo = prefs.git_local_only; prefs.git_local_only = true; int error = parse_file(fileNamePrt.data()); setAccessingCloud(-1); prefs.git_local_only = glo; if (error) { appendTextToLog(QStringLiteral("loading dives from cache failed %1").arg(error)); } else { // if we can load from the cache, we know that we have at least a valid email if (credentialStatus() == UNKNOWN) setCredentialStatus(VALID_EMAIL); prefs.unit_system = informational_prefs.unit_system; if (informational_prefs.unit_system == IMPERIAL) informational_prefs.units = IMPERIAL_units; prefs.units = informational_prefs.units; process_dives(false, false); DiveListModel::instance()->clear(); DiveListModel::instance()->addAllDives(); appendTextToLog(QStringLiteral("%1 dives loaded from cache").arg(dive_table.nr)); } set_filename(fileNamePrt.data(), true); appendTextToLog(QStringLiteral("have cloud credentials, trying to connect")); tryRetrieveDataFromBackend(); }
void QMLManager::saveChangesCloud(bool forceRemoteSync) { if (!unsaved_changes() && !forceRemoteSync) { appendTextToLog("asked to save changes but no unsaved changes"); return; } if (alreadySaving) { appendTextToLog("save operation in progress already"); return; } // first we need to store any unsaved changes to the local repo saveChangesLocal(); // if the user asked not to push to the cloud we are done if (prefs.git_local_only && !forceRemoteSync) return; if (!loadFromCloud()) { appendTextToLog("Don't save dives without loading from the cloud, first."); return; } bool glo = prefs.git_local_only; git_storage_update_progress(false, "start save change to cloud"); prefs.git_local_only = false; alreadySaving = true; loadDivesWithValidCredentials(); alreadySaving = false; git_storage_update_progress(false, "finished syncing dive list to cloud server"); setAccessingCloud(-1); prefs.git_local_only = glo; }
void QMLManager::handleError(QNetworkReply::NetworkError nError) { QString errorString = reply->errorString(); qDebug() << "handleError" << nError << errorString; setStartPageText(RED_FONT + tr("Cannot open cloud storage: %1").arg(errorString) + END_FONT); reply->abort(); reply->deleteLater(); setAccessingCloud(-1); }
void QMLManager::handleSslErrors(const QList<QSslError> &errors) { setStartPageText(RED_FONT + tr("Cannot open cloud storage: Error creating https connection") + END_FONT); Q_FOREACH (QSslError e, errors) { qDebug() << e.errorString(); } reply->abort(); reply->deleteLater(); setAccessingCloud(-1); }
void QMLManager::loadDiveProgress(int percent) { QString text(tr("Loading dive list from cloud storage.")); setAccessingCloud(percent); while (percent > 0) { text.append("."); percent -= 10; } setStartPageText(text); }
void QMLManager::checkCredentialsAndExecute(execute_function_type execute) { // if the cloud credentials are present, we should try to get the GPS Webservice ID // and (if we haven't done so) load the dive list if (!same_string(prefs.cloud_storage_email, "") && !same_string(prefs.cloud_storage_password, "")) { setAccessingCloud(0); setStartPageText(tr("Testing cloud credentials")); appendTextToLog("Have credentials, let's see if they are valid"); CloudStorageAuthenticate *csa = new CloudStorageAuthenticate(this); csa->backend(prefs.cloud_storage_email, prefs.cloud_storage_password, cloudPin()); // let's wait here for the signal to avoid too many more nested functions QTimer myTimer; myTimer.setSingleShot(true); QEventLoop loop; connect(csa, &CloudStorageAuthenticate::finishedAuthenticate, &loop, &QEventLoop::quit); connect(&myTimer, &QTimer::timeout, &loop, &QEventLoop::quit); myTimer.start(5000); loop.exec(); if (!myTimer.isActive()) { // got no response from the server setStartPageText(RED_FONT + tr("No response from cloud server to validate the credentials") + END_FONT); revertToNoCloudIfNeeded(); return; } myTimer.stop(); setCloudPin(""); if (prefs.cloud_verification_status != CS_VERIFIED) { // here we need to enter the PIN appendTextToLog(QStringLiteral("Need to verify the email address - enter PIN in desktop app")); setStartPageText(RED_FONT + tr("Cannot connect to cloud storage - cloud account not verified") + END_FONT); revertToNoCloudIfNeeded(); setShowPin(true); return; } if (showPin()) setShowPin(false); // now check the redirect URL to make sure everything is set up on the cloud server connect(manager(), &QNetworkAccessManager::authenticationRequired, this, &QMLManager::provideAuth, Qt::UniqueConnection); QUrl url(CLOUDREDIRECTURL); request = QNetworkRequest(url); request.setRawHeader("User-Agent", getUserAgent().toUtf8()); request.setRawHeader("Accept", "text/html"); reply = manager()->get(request); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleError(QNetworkReply::NetworkError))); connect(reply, &QNetworkReply::sslErrors, this, &QMLManager::handleSslErrors); connect(reply, &QNetworkReply::finished, this, execute, Qt::UniqueConnection); } }
void QMLManager::saveChangesLocal() { if (unsaved_changes()) { git_storage_update_progress(true, "saving dives locally"); // reset the timers if (credentialStatus() == NOCLOUD) { if (same_string(existing_filename, "")) { char *filename = NOCLOUD_LOCALSTORAGE; if (git_create_local_repo(filename)) appendTextToLog(get_error_string()); set_filename(filename, true); GeneralSettingsObjectWrapper s(this); s.setDefaultFilename(filename); s.setDefaultFileBehavior(LOCAL_DEFAULT_FILE); qDebug() << "setting default file to" << filename; } } else if (!loadFromCloud()) { // this seems silly, but you need a common ancestor in the repository in // order to be able to merge che changes later appendTextToLog("Don't save dives without loading from the cloud, first."); return; } if (alreadySaving) { appendTextToLog("save operation already in progress, can't save locally"); return; } alreadySaving = true; bool glo = prefs.git_local_only; prefs.git_local_only = true; if (save_dives(existing_filename)) { appendTextToLog(get_error_string()); set_filename(NULL, true); setAccessingCloud(-1); prefs.git_local_only = glo; alreadySaving = false; return; } prefs.git_local_only = glo; mark_divelist_changed(false); git_storage_update_progress(false, "done with local save"); alreadySaving = false; } else { appendTextToLog("local save requested with no unsaved changes"); } }
void QMLManager::checkCredentialsAndExecute(execute_function_type execute) { // if the cloud credentials are present, we should try to get the GPS Webservice ID // and (if we haven't done so) load the dive list if (!same_string(prefs.cloud_storage_email, "") && !same_string(prefs.cloud_storage_password, "")) { setAccessingCloud(0); setStartPageText(tr("Testing cloud credentials")); appendTextToLog("Have credentials, let's see if they are valid"); connect(manager(), &QNetworkAccessManager::authenticationRequired, this, &QMLManager::provideAuth, Qt::UniqueConnection); connect(manager(), &QNetworkAccessManager::finished, this, execute, Qt::UniqueConnection); QUrl url(CLOUDREDIRECTURL); request = QNetworkRequest(url); request.setRawHeader("User-Agent", getUserAgent().toUtf8()); request.setRawHeader("Accept", "text/html"); reply = manager()->get(request); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleError(QNetworkReply::NetworkError))); connect(reply, &QNetworkReply::sslErrors, this, &QMLManager::handleSslErrors); } }
QMLManager::QMLManager() : m_locationServiceEnabled(false), m_verboseEnabled(false), reply(0), deletedDive(0), deletedTrip(0), m_credentialStatus(UNKNOWN), m_lastDevicePixelRatio(1.0) { m_instance = this; appendTextToLog(getUserAgent()); appendTextToLog(QStringLiteral("build with Qt Version %1, runtime from Qt Version %2").arg(QT_VERSION_STR).arg(qVersion())); qDebug() << "Starting" << getUserAgent(); qDebug() << QStringLiteral("build with Qt Version %1, runtime from Qt Version %2").arg(QT_VERSION_STR).arg(qVersion()); setStartPageText(tr("Starting...")); setAccessingCloud(false); // create location manager service locationProvider = new GpsLocation(&appendTextToLogStandalone, this); set_git_update_cb(&gitProgressCB); // make sure we know if the current cloud repo has been successfully synced syncLoadFromCloud(); }
void QMLManager::finishSetup() { // Initialize cloud credentials. setCloudUserName(prefs.cloud_storage_email); setCloudPassword(prefs.cloud_storage_password); setSyncToCloud(!prefs.git_local_only); // if the cloud credentials are valid, we should get the GPS Webservice ID as well QString url; if (!cloudUserName().isEmpty() && !cloudPassword().isEmpty() && getCloudURL(url) == 0) { // we know that we are the first ones to access git storage, so we don't need to test, // but we need to make sure we stay the only ones accessing git storage alreadySaving = true; openLocalThenRemote(url); } else if (!same_string(existing_filename, "")) { setCredentialStatus(NOCLOUD); appendTextToLog(tr("working in no-cloud mode")); int error = parse_file(existing_filename); if (error) { // we got an error loading the local file appendTextToLog(QString("got error %2 when parsing file %1").arg(existing_filename, get_error_string())); set_filename(NULL, ""); } else { // successfully opened the local file, now add thigs to the dive list consumeFinishedLoad(0); setAccessingCloud(-1); appendTextToLog(QString("working in no-cloud mode, finished loading %1 dives from %2").arg(dive_table.nr).arg(existing_filename)); } } else { setCredentialStatus(INCOMPLETE); appendTextToLog(tr("no cloud credentials")); setStartPageText(RED_FONT + tr("Please enter valid cloud credentials.") + END_FONT); } setDistanceThreshold(prefs.distance_threshold); setTimeThreshold(prefs.time_threshold / 60); }
void QMLManager::loadDivesWithValidCredentials() { QString url; timestamp_t currentDiveTimestamp = selectedDiveTimestamp(); if (getCloudURL(url)) { QString errorString(get_error_string()); appendTextToLog(errorString); setStartPageText(RED_FONT + tr("Cloud storage error: %1").arg(errorString) + END_FONT); revertToNoCloudIfNeeded(); return; } QByteArray fileNamePrt = QFile::encodeName(url); git_repository *git; const char *branch; int error; if (check_git_sha(fileNamePrt.data(), &git, &branch) == 0) { qDebug() << "local cache was current, no need to modify dive list"; appendTextToLog("Cloud sync shows local cache was current"); goto successful_exit; } appendTextToLog("Cloud sync brought newer data, reloading the dive list"); clear_dive_file_data(); if (git != dummy_git_repository) { appendTextToLog(QString("have repository and branch %1").arg(branch)); error = git_load_dives(git, branch); } else { appendTextToLog(QString("didn't receive valid git repo, try again")); error = parse_file(fileNamePrt.data()); } setAccessingCloud(-1); if (!error) { report_error("filename is now %s", fileNamePrt.data()); const char *error_string = get_error_string(); appendTextToLog(error_string); set_filename(fileNamePrt.data(), true); } else { report_error("failed to open file %s", fileNamePrt.data()); QString errorString(get_error_string()); appendTextToLog(errorString); revertToNoCloudIfNeeded(); return; } consumeFinishedLoad(currentDiveTimestamp); successful_exit: alreadySaving = false; setLoadFromCloud(true); // if we came from local storage mode, let's merge the local data into the local cache // for the remote data - which then later gets merged with the remote data if necessary if (oldStatus() == NOCLOUD) { git_storage_update_progress(false, "import dives from nocloud local storage"); dive_table.preexisting = dive_table.nr; mergeLocalRepo(); DiveListModel::instance()->clear(); DiveListModel::instance()->addAllDives(); appendTextToLog(QStringLiteral("%1 dives loaded after importing nocloud local storage").arg(dive_table.nr)); saveChangesLocal(); if (syncToCloud() == false) { appendTextToLog(QStringLiteral("taking things back offline now that storage is synced")); prefs.git_local_only = syncToCloud(); } } setAccessingCloud(-1); // if we got here just for an initial connection to the cloud, reset to offline if (currentGitLocalOnly) { currentGitLocalOnly = false; prefs.git_local_only = true; } return; }
void QMLManager::loadDiveProgress(int percent) { setAccessingCloud(percent); }