From a123589efb1ba9d32e5ad738b33742fb19ebb2c2 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 11 Mar 2024 21:41:14 +0100 Subject: [PATCH] core: convert git_info to std::string Quite a bit of fallout in users of this structure. Conveniently, since git-access.cpp is now C++ we can move some helpers from the monstrous qthelper.cpp to git-access.cpp. Signed-off-by: Berthold Stoeger --- core/checkcloudconnection.cpp | 15 +- core/file.cpp | 8 +- core/git-access.cpp | 262 ++++++++++++++++----------------- core/git-access.h | 44 +++--- core/load-git.cpp | 10 +- core/qthelper.cpp | 50 ++----- core/qthelper.h | 4 +- core/save-git.cpp | 28 ++-- core/save-xml.cpp | 1 - core/subsurfacestartup.cpp | 7 +- mobile-widgets/qmlmanager.cpp | 7 +- tests/testgitstorage.cpp | 169 ++++++++++----------- tests/testparseperformance.cpp | 4 +- 13 files changed, 290 insertions(+), 319 deletions(-) diff --git a/core/checkcloudconnection.cpp b/core/checkcloudconnection.cpp index 96a5587c5..854451eb7 100644 --- a/core/checkcloudconnection.cpp +++ b/core/checkcloudconnection.cpp @@ -202,17 +202,18 @@ void CheckCloudConnection::gotContinent(QNetworkReply *reply) extern "C" bool canReachCloudServer(struct git_info *info) { if (verbose) - qWarning() << "Cloud storage: checking connection to cloud server" << info->url; + qWarning() << "Cloud storage: checking connection to cloud server" << info->url.c_str(); bool connection = CheckCloudConnection().checkServer(); - if (strstr(info->url, prefs.cloud_base_url) == nullptr) { + if (info->url.find(prefs.cloud_base_url) == std::string::npos) { // we switched the cloud URL - likely because we couldn't reach the server passed in // the strstr with the offset is designed so we match the right component in the name; // the cloud_base_url ends with a '/', so we need the text starting at "git/..." - char *newremote = format_string("%s%s", prefs.cloud_base_url, strstr(info->url, "org/git/") + 4); - if (verbose) - qDebug() << "updating remote to: " << newremote; - free((void*)info->url); - info->url = newremote; + size_t pos = info->url.find("org/git/"); + if (pos != std::string::npos) { + info->url = format_string_std("%s%s", prefs.cloud_base_url, info->url.c_str() + pos + 4); + if (verbose) + qDebug() << "updating remote to: " << info->url.c_str(); + } } return connection; } diff --git a/core/file.cpp b/core/file.cpp index a4391ebe9..bbab9e1fc 100644 --- a/core/file.cpp +++ b/core/file.cpp @@ -13,6 +13,7 @@ #include "dive.h" #include "divelog.h" #include "subsurface-string.h" +#include "format.h" #include "errorhelper.h" #include "file.h" #include "git-access.h" @@ -250,7 +251,7 @@ static int parse_file_buffer(const char *filename, std::string &mem, struct dive return parse_xml_buffer(filename, mem.data(), mem.size(), log, NULL); } -extern "C" bool remote_repo_uptodate(const char *filename, struct git_info *info) +bool remote_repo_uptodate(const char *filename, struct git_info *info) { std::string current_sha = saved_git_id; @@ -278,14 +279,11 @@ extern "C" int parse_file(const char *filename, struct divelog *log) * Opening the cloud storage repository failed for some reason * give up here and don't send errors about git repositories */ - if (info.is_subsurface_cloud) { - cleanup_git_info(&info); + if (info.is_subsurface_cloud) return -1; - } } int ret = git_load_dives(&info, log); - cleanup_git_info(&info); return ret; } diff --git a/core/git-access.cpp b/core/git-access.cpp index 10f9e8224..de50bc987 100644 --- a/core/git-access.cpp +++ b/core/git-access.cpp @@ -17,8 +17,12 @@ #include #include #include +#include +#include +#include #include "subsurface-string.h" +#include "format.h" #include "membuffer.h" #include "strndup.h" #include "qthelper.h" @@ -117,12 +121,20 @@ static int transfer_progress_cb(const git_transfer_progress *stats, void *) // the initial push to sync the repos is mapped to 10% of overall progress static int push_transfer_progress_cb(unsigned int current, unsigned int total, size_t, void *) { - char buf[80]; - snprintf(buf, sizeof(buf), translate("gettextFromC", "Transfer to storage (%d/%d)"), current, total); - return git_storage_update_progress(buf); + std::string buf = casprintf_loc(translate("gettextFromC", "Transfer to storage (%d/%d)"), current, total); + return git_storage_update_progress(buf.c_str()); } -extern "C" char *get_local_dir(const char *url, const char *branch) +std::string normalize_cloud_name(const std::string &remote_in) +{ + // replace ssrf-cloud-XX.subsurface... names with cloud.subsurface... names + // that trailing '/' is to match old code + QString ri = QString::fromStdString(remote_in); + ri.replace(QRegularExpression(CLOUD_HOST_PATTERN), CLOUD_HOST_GENERIC "/"); + return ri.toStdString(); +} + +std::string get_local_dir(const std::string &url, const std::string &branch) { SHA_CTX ctx; unsigned char hash[20]; @@ -133,9 +145,6 @@ extern "C" char *get_local_dir(const char *url, const char *branch) // which server to pick changed, or because the user is on a different continent), // then the hash and therefore the local directory would change. To prevent that // from happening, normalize the cloud string to always use the old default name. - // That's trivial with QString operations and painful to do right in plain C, so - // let's be lazy and call a C++ helper function - // just remember to free the string we get back std::string remote = normalize_cloud_name(url); // That zero-byte update is so that we don't get hash @@ -143,19 +152,18 @@ extern "C" char *get_local_dir(const char *url, const char *branch) SHA1_Init(&ctx); SHA1_Update(&ctx, remote.c_str(), remote.size()); SHA1_Update(&ctx, "", 1); - SHA1_Update(&ctx, branch, strlen(branch)); + SHA1_Update(&ctx, branch.c_str(), branch.size()); SHA1_Final(hash, &ctx); - return format_string("%s/cloudstorage/%02x%02x%02x%02x%02x%02x%02x%02x", + return format_string_std("%s/cloudstorage/%02x%02x%02x%02x%02x%02x%02x%02x", system_default_directory(), hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7]); } -static char *move_local_cache(struct git_info *info) +static std::string move_local_cache(struct git_info *info) { - char *old_path = get_local_dir(info->url, info->branch); - char *new_path = move_away(old_path); - free(old_path); + std::string old_path = get_local_dir(info->url, info->branch); + std::string new_path = move_away(old_path); return new_path; } @@ -248,15 +256,12 @@ extern "C" int credential_ssh_cb(git_cred **out, // TODO: We need a way to differentiate between password and private key authentication if (allowed_types & GIT_CREDTYPE_SSH_KEY) { - char *priv_key = format_string("%s/%s", system_default_directory(), "ssrf_remote.key"); - if (!access(priv_key, F_OK)) { + std::string priv_key = std::string(system_default_directory()) + "/ssrf_remote.key"; + if (!access(priv_key.c_str(), F_OK)) { if (exceeded_auth_attempts()) return GIT_EUSER; - int ret = git_cred_ssh_key_new(out, username, NULL, priv_key, passphrase); - free(priv_key); - return ret; + return git_cred_ssh_key_new(out, username, NULL, priv_key.c_str(), passphrase); } - free(priv_key); } if (allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT) { @@ -346,7 +351,7 @@ static int try_to_git_merge(struct git_info *info, git_reference **local_p, git_ git_commit *local_commit, *remote_commit, *base_commit; git_index *merged_index; git_merge_options merge_options; - struct membuffer msg = { 0, 0, NULL}; + struct membufferpp msg; if (verbose) { char outlocal[41], outremote[41]; @@ -450,7 +455,6 @@ static int try_to_git_merge(struct git_info *info, git_reference **local_p, git_ git_signature_free(author); if (verbose) report_info("git storage: successfully merged repositories"); - free_buffer(&msg); return 0; } @@ -458,7 +462,6 @@ diverged_error: return report_error("%s", translate("gettextFromC", "Remote storage and local data diverged")); write_error: - free_buffer(&msg); return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: writing the data failed (%s)"), giterr_last()->message); } @@ -467,11 +470,10 @@ write_error: // and ask them to retry the operation (which will then refresh the data from the cloud server) static int cleanup_local_cache(struct git_info *info) { - char *backup_path = move_local_cache(info); - report_info("git storage: problems with local cache, moved to %s", backup_path); + std::string backup_path = move_local_cache(info); + report_info("git storage: problems with local cache, moved to %s", backup_path.c_str()); report_error("%s", translate("gettextFromC", "Problems with local cache of Subsurface cloud data")); - report_error(translate("gettextFromC", "Moved cache data to %s. Please try the operation again."), backup_path); - free(backup_path); + report_error(translate("gettextFromC", "Moved cache data to %s. Please try the operation again."), backup_path.c_str()); return -1; } @@ -576,17 +578,17 @@ static int check_remote_status(struct git_info *info, git_remote *origin) if (verbose) report_info("git storage: check remote status\n"); - if (git_branch_lookup(&local_ref, info->repo, info->branch, GIT_BRANCH_LOCAL)) { - report_info("git storage: branch %s is missing in local repo", info->branch); + if (git_branch_lookup(&local_ref, info->repo, info->branch.c_str(), GIT_BRANCH_LOCAL)) { + report_info("git storage: branch %s is missing in local repo", info->branch.c_str()); if (info->is_subsurface_cloud) return cleanup_local_cache(info); else - return report_error("Git cache branch %s no longer exists", info->branch); + return report_error("Git cache branch %s no longer exists", info->branch.c_str()); } if (git_branch_upstream(&remote_ref, local_ref)) { /* so there is no upstream branch for our branch; that's a problem. * let's push our branch */ - report_info("git storage: branch %s is missing in remote, pushing branch", info->branch); + report_info("git storage: branch %s is missing in remote, pushing branch", info->branch.c_str()); git_strarray refspec; git_reference_list(&refspec, info->repo); git_push_options opts = GIT_PUSH_OPTIONS_INIT; @@ -608,62 +610,69 @@ static int check_remote_status(struct git_info *info, git_remote *origin) return error; } +static std::string getProxyString() +{ + if (prefs.proxy_type == QNetworkProxy::HttpProxy) { + if (prefs.proxy_auth) + return format_string_std("http://%s:%s@%s:%d", prefs.proxy_user, prefs.proxy_pass, + prefs.proxy_host, prefs.proxy_port); + else + return format_string_std("http://%s:%d", prefs.proxy_host, prefs.proxy_port); + } + return std::string(); +} + /* this is (so far) only used by the git storage tests to remove a remote branch * it will print out errors, but not return an error (as this isn't a function that * we test as part of the tests, it's a helper to not leave loads of dead branches on * the server) */ -extern "C" void delete_remote_branch(git_repository *repo, const char *remote, const char *branch) +void delete_remote_branch(git_repository *repo, const std::string &remote, const std::string &branch) { - int error; - char *proxy_string; git_remote *origin; git_config *conf; /* set up the config and proxy information in order to connect to the server */ git_repository_config(&conf, repo); - if (getProxyString(&proxy_string)) { - git_config_set_string(conf, "http.proxy", proxy_string); - free(proxy_string); + std::string proxy_string = getProxyString(); + if (!proxy_string.empty()) { + git_config_set_string(conf, "http.proxy", proxy_string.c_str()); } else { git_config_delete_entry(conf, "http.proxy"); } if (git_remote_lookup(&origin, repo, "origin")) { - report_info("git storage: repository '%s' origin lookup failed (%s)", remote, giterr_last() ? giterr_last()->message : "(unspecified)"); + report_info("git storage: repository '%s' origin lookup failed (%s)", remote.c_str(), giterr_last() ? giterr_last()->message : "(unspecified)"); return; } /* fetch the remote state */ git_fetch_options f_opts = GIT_FETCH_OPTIONS_INIT; auth_attempt = 0; f_opts.callbacks.credentials = credential_https_cb; - error = git_remote_fetch(origin, NULL, &f_opts, NULL); - if (error) { + if (git_remote_fetch(origin, NULL, &f_opts, NULL)) { report_info("git storage: remote fetch failed (%s)\n", giterr_last() ? giterr_last()->message : "authentication failed"); return; } /* delete the remote branch by pushing to ":refs/heads/" */ git_strarray refspec; - char *branch_ref = format_string(":refs/heads/%s", branch); + std::string branch_ref = std::string(":refs/heads/") + branch; + char *dummy = branch_ref.data(); refspec.count = 1; - refspec.strings = &branch_ref; + refspec.strings = &dummy; git_push_options p_opts = GIT_PUSH_OPTIONS_INIT; auth_attempt = 0; p_opts.callbacks.credentials = credential_https_cb; - error = git_remote_push(origin, &refspec, &p_opts); - free(branch_ref); - if (error) { - report_info("git storage: unable to delete branch '%s'", branch); + if (git_remote_push(origin, &refspec, &p_opts)) { + report_info("git storage: unable to delete branch '%s'", branch.c_str()); report_info("git storage: error was (%s)\n", giterr_last() ? giterr_last()->message : "(unspecified)"); } git_remote_free(origin); return; } -extern "C" int sync_with_remote(struct git_info *info) +int sync_with_remote(struct git_info *info) { int error; git_remote *origin; - char *proxy_string; git_config *conf; if (git_local_only) { @@ -672,14 +681,14 @@ extern "C" int sync_with_remote(struct git_info *info) return 0; } if (verbose) - report_info("git storage: sync with remote %s[%s]\n", info->url, info->branch); + report_info("git storage: sync with remote %s[%s]\n", info->url.c_str(), info->branch.c_str()); git_storage_update_progress(translate("gettextFromC", "Sync with cloud storage")); git_repository_config(&conf, info->repo); - if (info->transport == RT_HTTPS && getProxyString(&proxy_string)) { + std::string proxy_string = getProxyString(); + if (info->transport == RT_HTTPS && !proxy_string.empty()) { if (verbose) - report_info("git storage: set proxy to \"%s\"\n", proxy_string); - git_config_set_string(conf, "http.proxy", proxy_string); - free(proxy_string); + report_info("git storage: set proxy to \"%s\"\n", proxy_string.c_str()); + git_config_set_string(conf, "http.proxy", proxy_string.c_str()); } else { if (verbose) report_info("git storage: delete proxy setting\n"); @@ -693,9 +702,9 @@ extern "C" int sync_with_remote(struct git_info *info) error = git_remote_lookup(&origin, info->repo, "origin"); if (error) { const char *msg = giterr_last()->message; - report_info("git storage: repo %s origin lookup failed with: %s", info->url, msg); + report_info("git storage: repo %s origin lookup failed with: %s", info->url.c_str(), msg); if (!info->is_subsurface_cloud) - report_error("Repository '%s' origin lookup failed (%s)", info->url, msg); + report_error("Repository '%s' origin lookup failed (%s)", info->url.c_str(), msg); return 0; } @@ -726,7 +735,7 @@ extern "C" int sync_with_remote(struct git_info *info) if (info->is_subsurface_cloud) report_error("Cannot sync with cloud server, working with offline copy"); else - report_error("Unable to fetch remote '%s'", info->url); + report_error("Unable to fetch remote '%s'", info->url.c_str()); // If we returned GIT_EUSER during authentication, giterr_last() returns NULL report_info("git storage: remote fetch failed (%s)\n", giterr_last() ? giterr_last()->message : "authentication failed"); // Since we failed to sync with online repository, enter offline mode @@ -748,18 +757,17 @@ static bool update_local_repo(struct git_info *info) if (!git_repository_head(&head, info->repo)) { const char *name; if (!git_branch_name(&name, head)) { - if (strcmp(name, info->branch)) { - char *branchref = format_string("refs/heads/%s", info->branch); - report_info("git storage: setting cache branch from '%s' to '%s'", name, info->branch); - git_repository_set_head(info->repo, branchref); - free(branchref); + if (info->branch != name) { + std::string branchref = "refs/heads/" + info->branch; + report_info("git storage: setting cache branch from '%s' to '%s'", name, info->branch.c_str()); + git_repository_set_head(info->repo, branchref.c_str()); } } git_reference_free(head); } /* make sure we have the correct origin - the cloud server URL could have changed */ - if (git_remote_set_url(info->repo, "origin", info->url)) { - report_info("git storage: failed to update origin to '%s'", info->url); + if (git_remote_set_url(info->repo, "origin", info->url.c_str())) { + report_info("git storage: failed to update origin to '%s'", info->url.c_str()); return false; } @@ -771,7 +779,6 @@ static bool update_local_repo(struct git_info *info) static int repository_create_cb(git_repository **out, const char *path, int bare, void *) { - char *proxy_string; git_config *conf; int ret = git_repository_init(out, path, bare); @@ -782,11 +789,11 @@ static int repository_create_cb(git_repository **out, const char *path, int bare } git_repository_config(&conf, *out); - if (getProxyString(&proxy_string)) { + std::string proxy_string = getProxyString(); + if (!proxy_string.empty()) { if (verbose) - report_info("git storage: set proxy to \"%s\"\n", proxy_string); - git_config_set_string(conf, "http.proxy", proxy_string); - free(proxy_string); + report_info("git storage: set proxy to \"%s\"\n", proxy_string.c_str()); + git_config_set_string(conf, "http.proxy", proxy_string.c_str()); } else { if (verbose) report_info("git storage: delete proxy setting\n"); @@ -800,34 +807,30 @@ static int repository_create_cb(git_repository **out, const char *path, int bare static bool create_and_push_remote(struct git_info *info) { git_config *conf; - char *variable_name, *head; if (verbose) report_info("git storage: create and push remote\n"); /* first make sure the directory for the local cache exists */ - subsurface_mkdir(info->localdir); + subsurface_mkdir(info->localdir.c_str()); - head = format_string("refs/heads/%s", info->branch); + std::string head = "refs/heads/" + info->branch; /* set up the origin to point to our remote */ git_repository_init_options init_opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; - init_opts.origin_url = info->url; - init_opts.initial_head = head; + init_opts.origin_url = info->url.c_str(); + init_opts.initial_head = head.c_str(); /* now initialize the repository with */ - git_repository_init_ext(&info->repo, info->localdir, &init_opts); + git_repository_init_ext(&info->repo, info->localdir.c_str(), &init_opts); /* create a config so we can set the remote tracking branch */ git_repository_config(&conf, info->repo); - variable_name = format_string("branch.%s.remote", info->branch); - git_config_set_string(conf, variable_name, "origin"); - free(variable_name); + std::string variable_name = "branch." + info->branch + ".remote"; + git_config_set_string(conf, variable_name.c_str(), "origin"); - variable_name = format_string("branch.%s.merge", info->branch); - git_config_set_string(conf, variable_name, head); - free(head); - free(variable_name); + variable_name = "branch." + info->branch + ".merge"; + git_config_set_string(conf, variable_name.c_str(), head.c_str()); /* finally create an empty commit and push it to the remote */ if (do_git_save(info, false, true)) @@ -853,18 +856,18 @@ static bool create_local_repo(struct git_info *info) opts.repository_cb = repository_create_cb; opts.fetch_opts.callbacks.certificate_check = certificate_check_cb; - opts.checkout_branch = info->branch; + opts.checkout_branch = info->branch.c_str(); if (info->is_subsurface_cloud && !canReachCloudServer(info)) { report_info("git storage: cannot reach remote server"); return false; } if (verbose > 1) report_info("git storage: calling git_clone()\n"); - error = git_clone(&info->repo, info->url, info->localdir, &opts); + error = git_clone(&info->repo, info->url.c_str(), info->localdir.c_str(), &opts); if (verbose > 1) report_info("git storage: returned from git_clone() with return value %d\n", error); if (error) { - report_info("git storage: clone of %s failed", info->url); + report_info("git storage: clone of %s failed", info->url.c_str()); const char *msg = ""; if (giterr_last()) { msg = giterr_last()->message; @@ -872,9 +875,9 @@ static bool create_local_repo(struct git_info *info) } else { report_info("git storage: giterr_last() is null\n"); } - char *pattern = format_string("reference 'refs/remotes/origin/%s' not found", info->branch); + std::string pattern = format_string_std("reference 'refs/remotes/origin/%s' not found", info->branch.c_str()); // it seems that we sometimes get 'Reference' and sometimes 'reference' - if (includes_string_caseinsensitive(msg, pattern)) { + if (includes_string_caseinsensitive(msg, pattern.c_str())) { /* we're trying to open the remote branch that corresponds * to our cloud storage and the branch doesn't exist. * So we need to create the branch and push it to the remote */ @@ -887,19 +890,18 @@ static bool create_local_repo(struct git_info *info) report_error("%s", translate("gettextFromC", "Error connecting to Subsurface cloud storage")); #endif } else { - report_error(translate("gettextFromC", "git clone of %s failed (%s)"), info->url, msg); + report_error(translate("gettextFromC", "git clone of %s failed (%s)"), info->url.c_str(), msg); } - free(pattern); } return !error; } -static enum remote_transport url_to_remote_transport(const char *remote) +static enum remote_transport url_to_remote_transport(const std::string &remote) { /* figure out the remote transport */ - if (strncmp(remote, "ssh://", 6) == 0) + if (starts_with(remote, "ssh://")) return RT_SSH; - else if (strncmp(remote, "https://", 8) == 0) + else if (starts_with(remote.c_str(), "https://")) return RT_HTTPS; else return RT_OTHER; @@ -910,24 +912,24 @@ static bool get_remote_repo(struct git_info *info) struct stat st; if (verbose > 1) { - report_info("git storage: accessing %s\n", info->url); + report_info("git storage: accessing %s\n", info->url.c_str()); } git_storage_update_progress(translate("gettextFromC", "Synchronising data file")); /* Do we already have a local cache? */ - if (!subsurface_stat(info->localdir, &st)) { + if (!subsurface_stat(info->localdir.c_str(), &st)) { int error; if (verbose) report_info("git storage: update local repo\n"); - error = git_repository_open(&info->repo, info->localdir); + error = git_repository_open(&info->repo, info->localdir.c_str()); if (error) { const char *msg = giterr_last()->message; - report_info("git storage: unable to open local cache at %s: %s", info->localdir, msg); + report_info("git storage: unable to open local cache at %s: %s", info->localdir.c_str(), msg); if (info->is_subsurface_cloud) (void)cleanup_local_cache(info); else - report_error("Unable to open git cache repository at %s: %s", info->localdir, msg); + report_error("Unable to open git cache repository at %s: %s", info->localdir.c_str(), msg); return false; } @@ -955,17 +957,17 @@ static bool get_remote_repo(struct git_info *info) * Remove the user name from the url if it exists, and * save it in 'info->username'. */ -static void extract_username(struct git_info *info, char *url) +std::string extract_username(struct git_info *info, const std::string &url) { char c; - char *p = url; + const char *p = url.c_str(); while ((c = *p++) >= 'a' && c <= 'z') /* nothing */; if (c != ':') - return; + return url; if (*p++ != '/' || *p++ != '/') - return; + return url; /* * Ok, we found "[a-z]*://" and we think we have a real @@ -974,38 +976,38 @@ static void extract_username(struct git_info *info, char *url) */ info->transport = url_to_remote_transport(url); - char *at = strchr(p, '@'); + const char *at = strchr(p, '@'); if (!at) - return; + return url; /* was this the @ that denotes an account? that means it was before the * first '/' after the protocol:// - so let's find a '/' after that and compare */ - char *slash = strchr(p, '/'); + const char *slash = strchr(p, '/'); if (!slash || at > slash) - return; + return url; /* grab the part between "protocol://" and "@" as encoded email address * (that's our username) and move the rest of the URL forward, remembering * to copy the closing NUL as well */ - info->username = strndup(p, at - p); - memmove(p, at + 1, strlen(at + 1) + 1); + info->username = std::string(p, at - p); /* * Ugly, ugly. Parsing the remote repo user name also sets * it in the preferences. We should do this somewhere else! */ - prefs.cloud_storage_email_encoded = strdup(info->username); + prefs.cloud_storage_email_encoded = strdup(info->username.c_str()); + + return url.substr(at + 1 - url.c_str()); } -extern "C" void cleanup_git_info(struct git_info *info) +git_info::git_info() : repo(nullptr), is_subsurface_cloud(0), transport(RT_LOCAL) { - if (info->repo) - git_repository_free(info->repo); - free((void *)info->url); - free((void *)info->branch); - free((void *)info->username); - free((void *)info->localdir); - memset(info, 0, sizeof(*info)); +} + +git_info::~git_info() +{ + if (repo) + git_repository_free(repo); } /* @@ -1018,16 +1020,14 @@ extern "C" void cleanup_git_info(struct git_info *info) * https://host/repo[branch] * file://repo[branch] */ -extern "C" bool is_git_repository(const char *filename, struct git_info *info) +bool is_git_repository(const char *filename, struct git_info *info) { int flen, blen; int offset = 1; - char *url, *branch; /* we are looking at a new potential remote, but we haven't synced with it */ git_remote_sync_successful = false; - memset(info, 0, sizeof(*info)); info->transport = RT_LOCAL; flen = strlen(filename); if (!flen || filename[--flen] != ']') @@ -1070,11 +1070,11 @@ extern "C" bool is_git_repository(const char *filename, struct git_info *info) * The actual git reading/writing routines can use this * to generate proper error messages. */ - url = format_string("%.*s", flen, filename); - branch = format_string("%.*s", blen, filename + flen + offset); + std::string url(filename, flen); + std::string branch(filename + flen + offset, blen); /* Extract the username from the url string */ - extract_username(info, url); + url = extract_username(info, url); info->url = url; info->branch = branch; @@ -1099,10 +1099,10 @@ extern "C" bool is_git_repository(const char *filename, struct git_info *info) */ switch (info->transport) { case RT_LOCAL: - info->localdir = strdup(url); + info->localdir = url; break; default: - info->localdir = get_local_dir(info->url, info->branch); + info->localdir = get_local_dir(info->url.c_str(), info->branch).c_str(); break; } @@ -1112,19 +1112,19 @@ extern "C" bool is_git_repository(const char *filename, struct git_info *info) * * This is used to create more user friendly error message and warnings. */ - info->is_subsurface_cloud = (strstr(info->url, prefs.cloud_base_url) != NULL); + info->is_subsurface_cloud = (strstr(info->url.c_str(), prefs.cloud_base_url) != NULL); return true; } -extern "C" bool open_git_repository(struct git_info *info) +bool open_git_repository(struct git_info *info) { /* * If the repository is local, just open it. There's nothing * else to do. */ if (info->transport == RT_LOCAL) { - const char *url = info->localdir; + const char *url = info->localdir.c_str(); if (git_repository_open(&info->repo, url)) { if (verbose) @@ -1145,15 +1145,13 @@ extern "C" bool open_git_repository(struct git_info *info) return get_remote_repo(info); } -extern "C" int git_create_local_repo(const char *filename) +int git_create_local_repo(const std::string &filename) { git_repository *repo; - char *path = strdup(filename); - char *branch = strchr(path, '['); - if (branch) - *branch = '\0'; - int ret = git_repository_init(&repo, path, false); - free(path); + + auto idx = filename.find('['); + std::string path = filename.substr(0, idx); + int ret = git_repository_init(&repo, path.c_str(), false); if (ret != 0) (void)report_error("Create local repo failed with error code %d", ret); git_repository_free(repo); diff --git a/core/git-access.h b/core/git-access.h index 38bac45c2..8dedebed5 100644 --- a/core/git-access.h +++ b/core/git-access.h @@ -22,20 +22,38 @@ extern "C" { enum remote_transport { RT_LOCAL, RT_HTTPS, RT_SSH, RT_OTHER }; +extern bool git_local_only; +extern bool git_remote_sync_successful; +extern void clear_git_id(void); +extern void set_git_id(const struct git_oid *); +void set_git_update_cb(int(*)(const char *)); +int git_storage_update_progress(const char *text); +int get_authorship(git_repository *repo, git_signature **authorp); + +#ifdef __cplusplus +} + +#include + struct git_oid; struct git_repository; struct divelog; struct git_info { - const char *url; - const char *branch; - const char *username; - const char *localdir; + std::string url; + std::string branch; + std::string username; + std::string localdir; struct git_repository *repo; unsigned is_subsurface_cloud:1; enum remote_transport transport; + git_info(); + ~git_info(); }; +extern std::string saved_git_id; +extern std::string get_sha(git_repository *repo, const std::string &branch); +extern std::string get_local_dir(const std::string &, const std::string &); extern bool is_git_repository(const char *filename, struct git_info *info); extern bool open_git_repository(struct git_info *info); extern bool remote_repo_uptodate(const char *filename, struct git_info *info); @@ -43,23 +61,7 @@ extern int sync_with_remote(struct git_info *); extern int git_save_dives(struct git_info *, bool select_only); extern int git_load_dives(struct git_info *, struct divelog *log); extern int do_git_save(struct git_info *, bool select_only, bool create_empty); -extern void cleanup_git_info(struct git_info *); -extern bool git_local_only; -extern bool git_remote_sync_successful; -extern void clear_git_id(void); -extern void set_git_id(const struct git_oid *); -void set_git_update_cb(int(*)(const char *)); -int git_storage_update_progress(const char *text); -char *get_local_dir(const char *, const char *); -int git_create_local_repo(const char *filename); -int get_authorship(git_repository *repo, git_signature **authorp); - -#ifdef __cplusplus -} - -#include -extern std::string saved_git_id; -extern std::string get_sha(git_repository *repo, const char *branch); +extern int git_create_local_repo(const std::string &filename); #endif #endif // GITACCESS_H diff --git a/core/load-git.cpp b/core/load-git.cpp index 96a510ed0..9c12fdfe5 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1895,11 +1895,11 @@ static int do_git_load(git_repository *repo, const char *branch, struct git_pars return ret; } -std::string get_sha(git_repository *repo, const char *branch) +std::string get_sha(git_repository *repo, const std::string &branch) { char git_id_buffer[GIT_OID_HEXSZ + 1]; git_commit *commit; - if (find_commit(repo, branch, &commit)) + if (find_commit(repo, branch.c_str(), &commit)) return std::string(); git_oid_tostr(git_id_buffer, sizeof(git_id_buffer), (const git_oid *)commit); return std::string(git_id_buffer); @@ -1913,7 +1913,7 @@ std::string get_sha(git_repository *repo, const char *branch) * If it is a git repository, we return zero for success, * or report an error and return 1 if the load failed. */ -extern "C" int git_load_dives(struct git_info *info, struct divelog *log) +int git_load_dives(struct git_info *info, struct divelog *log) { int ret; struct git_parser_state state; @@ -1921,8 +1921,8 @@ extern "C" int git_load_dives(struct git_info *info, struct divelog *log) state.log = log; if (!info->repo) - return report_error("Unable to open git repository '%s[%s]'", info->url, info->branch); - ret = do_git_load(info->repo, info->branch, &state); + return report_error("Unable to open git repository '%s[%s]'", info->url.c_str(), info->branch.c_str()); + ret = do_git_load(info->repo, info->branch.c_str(), &state); finish_active_dive(&state); finish_active_trip(&state); return ret; diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 5246323f0..e5a9285aa 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -22,7 +22,6 @@ #include "tag.h" #include "imagedownloader.h" #include "xmlparams.h" -#include "core/git-access.h" // for CLOUD_HOST definitions #include #include #include @@ -331,31 +330,31 @@ extern "C" xsltStylesheetPtr get_stylesheet(const char *name) return xslt; } -extern "C" char *move_away(const char *old_path) +std::string move_away(const std::string &old_path) { if (verbose > 1) - qDebug() << "move away" << old_path; - QDir oldDir(old_path); + qDebug() << "move away" << old_path.c_str(); + QDir oldDir(old_path.c_str()); QDir newDir; - QString newPath; + std::string newPath; int i = 0; do { - newPath = QString(old_path) + QString(".%1").arg(++i); - newDir.setPath(newPath); + newPath = old_path + "." + std::to_string(++i); + newDir.setPath(newPath.c_str()); } while(newDir.exists()); if (verbose > 1) - qDebug() << "renaming to" << newPath; - if (!oldDir.rename(old_path, newPath)) { + qDebug() << "renaming to" << newPath.c_str(); + if (!oldDir.rename(old_path.c_str(), newPath.c_str())) { if (verbose) - qDebug() << "rename of" << old_path << "to" << newPath << "failed"; + qDebug() << "rename of" << old_path.c_str() << "to" << newPath.c_str() << "failed"; // this next one we only try on Windows... if we are on a different platform // we simply give up and return an empty string #ifdef WIN32 - if (subsurface_dir_rename(old_path, qPrintable(newPath)) == 0) + if (subsurface_dir_rename(old_path.c_str(), newPath.c_str()) == 0) #endif - return strdup(""); + return std::string(); } - return copy_qstring(newPath); + return newPath; } std::string get_file_name(const char *fileName) @@ -1375,31 +1374,6 @@ extern "C" char *cloud_url() return copy_qstring(filename); } -std::string normalize_cloud_name(const char *remote_in) -{ - // replace ssrf-cloud-XX.subsurface... names with cloud.subsurface... names - // that trailing '/' is to match old code - QString ri(remote_in); - ri.replace(QRegularExpression(CLOUD_HOST_PATTERN), CLOUD_HOST_GENERIC "/"); - return ri.toStdString(); -} - -extern "C" bool getProxyString(char **buffer) -{ - if (prefs.proxy_type == QNetworkProxy::HttpProxy) { - QString proxy; - if (prefs.proxy_auth) - proxy = QString("http://%1:%2@%3:%4").arg(prefs.proxy_user).arg(prefs.proxy_pass) - .arg(prefs.proxy_host).arg(prefs.proxy_port); - else - proxy = QString("http://%1:%2").arg(prefs.proxy_host).arg(prefs.proxy_port); - if (buffer) - *buffer = copy_qstring(proxy); - return true; - } - return false; -} - extern "C" void subsurface_mkdir(const char *dir) { QDir directory; diff --git a/core/qthelper.h b/core/qthelper.h index d0601ef49..c06846a58 100644 --- a/core/qthelper.h +++ b/core/qthelper.h @@ -103,8 +103,8 @@ extern QString (*changesCallback)(); void uiNotification(const QString &msg); std::string get_changes_made(); std::string subsurface_user_agent(); -std::string normalize_cloud_name(const char *remote_in); std::string get_file_name(const char *fileName); +std::string move_away(const std::string &path); #if defined __APPLE__ #define TITLE_OR_TEXT(_t, _m) "", _t + "\n" + _m @@ -122,12 +122,10 @@ extern "C" { struct git_info; -bool getProxyString(char **buffer); bool canReachCloudServer(struct git_info *); void updateWindowTitle(); void subsurface_mkdir(const char *dir); void copy_image_and_overwrite(const char *cfileName, const char *path, const char *cnewName); -char *move_away(const char *path); const char *local_file_path(struct picture *picture); char *cloud_url(); char *hashfile_name_string(); diff --git a/core/save-git.cpp b/core/save-git.cpp index 1fd0270c2..9b7734544 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -1177,19 +1177,19 @@ static int create_new_commit(struct git_info *info, git_oid *tree_id, bool creat git_commit *commit; git_tree *tree; - ret = git_branch_lookup(&ref, info->repo, info->branch, GIT_BRANCH_LOCAL); + ret = git_branch_lookup(&ref, info->repo, info->branch.c_str(), GIT_BRANCH_LOCAL); switch (ret) { default: - return report_error("Bad branch '%s' (%s)", info->branch, strerror(errno)); + return report_error("Bad branch '%s' (%s)", info->branch.c_str(), strerror(errno)); case GIT_EINVALIDSPEC: - return report_error("Invalid branch name '%s'", info->branch); + return report_error("Invalid branch name '%s'", info->branch.c_str()); case GIT_ENOTFOUND: /* We'll happily create it */ ref = NULL; parent = try_to_find_parent(saved_git_id.c_str(), info->repo); break; case 0: if (git_reference_peel(&parent, ref, GIT_OBJ_COMMIT)) - return report_error("Unable to look up parent in branch '%s'", info->branch); + return report_error("Unable to look up parent in branch '%s'", info->branch.c_str()); if (!saved_git_id.empty()) { if (!existing_filename.empty() && verbose) @@ -1238,8 +1238,8 @@ static int create_new_commit(struct git_info *info, git_oid *tree_id, bool creat git_signature_free(author); if (!ref) { - if (git_branch_create(&ref, info->repo, info->branch, commit, 0)) - return report_error("Failed to create branch '%s'", info->branch); + if (git_branch_create(&ref, info->repo, info->branch.c_str(), commit, 0)) + return report_error("Failed to create branch '%s'", info->branch.c_str()); } /* * If it's a checked-out branch, try to also update the working @@ -1253,12 +1253,12 @@ static int create_new_commit(struct git_info *info, git_oid *tree_id, bool creat const git_error *err = giterr_last(); const char *errstr = err ? err->message : strerror(errno); report_error("Git branch '%s' is checked out, but worktree is dirty (%s)", - info->branch, errstr); + info->branch.c_str(), errstr); } } if (git_reference_set_target(&ref, ref, &commit_id, "Subsurface save event")) - return report_error("Failed to update branch '%s'", info->branch); + return report_error("Failed to update branch '%s'", info->branch.c_str()); /* * if this was the empty commit to initialize a new repo, don't remember the @@ -1294,14 +1294,14 @@ static int write_git_tree(git_repository *repo, const struct dir *tree, git_oid return ret; } -extern "C" int do_git_save(struct git_info *info, bool select_only, bool create_empty) +int do_git_save(struct git_info *info, bool select_only, bool create_empty) { struct dir tree; git_oid id; bool cached_ok; if (!info->repo) - return report_error("Unable to open git repository '%s[%s]'", info->url, info->branch); + return report_error("Unable to open git repository '%s[%s]'", info->url.c_str(), info->branch.c_str()); if (verbose) report_info("git storage: do git save\n"); @@ -1335,12 +1335,12 @@ extern "C" int do_git_save(struct git_info *info, bool select_only, bool create_ return report_error("creating commit failed"); /* now sync the tree with the remote server */ - if (info->url && !git_local_only) + if (!info->url.empty() && !git_local_only) return sync_with_remote(info); return 0; } -extern "C" int git_save_dives(struct git_info *info, bool select_only) +int git_save_dives(struct git_info *info, bool select_only) { /* * First, just try to open the local git repo without @@ -1357,7 +1357,7 @@ extern "C" int git_save_dives(struct git_info *info, bool select_only) * at least the local state will be saved early in * case something goes wrong. */ - if (!git_repository_open(&info->repo, info->localdir)) + if (!git_repository_open(&info->repo, info->localdir.c_str())) return do_git_save(info, select_only, false); /* @@ -1369,7 +1369,7 @@ extern "C" int git_save_dives(struct git_info *info, bool select_only) * This shouldn't be the common case. */ if (!open_git_repository(info)) - return report_error(translate("gettextFromC", "Failed to save dives to %s[%s] (%s)"), info->url, info->branch, strerror(errno)); + return report_error(translate("gettextFromC", "Failed to save dives to %s[%s] (%s)"), info->url.c_str(), info->branch.c_str(), strerror(errno)); return do_git_save(info, select_only, false); } diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 3230b837d..68ebd7158 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -823,7 +823,6 @@ extern "C" int save_dives_logic(const char *filename, const bool select_only, bo if (is_git_repository(filename, &info)) { error = git_save_dives(&info, select_only); - cleanup_git_info(&info); return error; } diff --git a/core/subsurfacestartup.cpp b/core/subsurfacestartup.cpp index eb824fe8e..443552243 100644 --- a/core/subsurfacestartup.cpp +++ b/core/subsurfacestartup.cpp @@ -45,7 +45,7 @@ extern "C" void print_version() extern "C" void print_files() { - struct git_info info = { }; + struct git_info info; const char *filename; printf("\nFile locations:\n\n"); @@ -58,12 +58,11 @@ extern "C" void print_files() /* strdup so the free below works in either case */ filename = strdup("No valid cloud credentials set.\n"); } - if (info.localdir) { - printf("Local git storage: %s\n", info.localdir); + if (!info.localdir.empty()) { + printf("Local git storage: %s\n", info.localdir.c_str()); } else { printf("Unable to get local git directory\n"); } - cleanup_git_info(&info); printf("Cloud URL: %s\n", filename); free((void *)filename); char *tmp = hashfile_name_string(); diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index bf54ba3bc..eb608fe31 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -804,7 +804,7 @@ void QMLManager::loadDivesWithValidCredentials() appendTextToLog("Switching from no cloud mode; keep in memory dive data"); } if (info.repo) { - appendTextToLog(QString("have repository and branch %1").arg(info.branch)); + appendTextToLog(QString("have repository and branch %1").arg(info.branch.c_str())); error = git_load_dives(&info, &divelog); } else { appendTextToLog(QString("didn't receive valid git repo, try again")); @@ -823,7 +823,6 @@ void QMLManager::loadDivesWithValidCredentials() } consumeFinishedLoad(); } - cleanup_git_info(&info); setLoadFromCloud(true); @@ -1501,7 +1500,7 @@ void QMLManager::openNoCloudRepo() appendTextToLog(QString("User asked not to connect to cloud, using %1 as repo.").arg(filename)); if (is_git_repository(qPrintable(filename), &info) && !open_git_repository(&info)) { // repo doesn't exist, create it and write the empty dive list to it - git_create_local_repo(qPrintable(filename)); + git_create_local_repo(filename.toStdString()); save_dives(qPrintable(filename)); existing_filename = filename.toStdString(); auto s = qPrefLog::instance(); @@ -1517,7 +1516,7 @@ void QMLManager::saveChangesLocal() if (qPrefCloudStorage::cloud_verification_status() == qPrefCloudStorage::CS_NOCLOUD) { if (existing_filename.empty()) { QString filename = nocloud_localstorage(); - git_create_local_repo(qPrintable(filename)); + git_create_local_repo(filename.toStdString()); existing_filename = filename.toStdString(); auto s = qPrefLog::instance(); s->set_default_filename(qPrintable(filename)); diff --git a/tests/testgitstorage.cpp b/tests/testgitstorage.cpp index 997b913dd..ea6116e4c 100644 --- a/tests/testgitstorage.cpp +++ b/tests/testgitstorage.cpp @@ -7,6 +7,8 @@ #include "core/divelist.h" #include "core/divelog.h" #include "core/file.h" +#include "core/subsurface-string.h" +#include "core/format.h" #include "core/qthelper.h" #include "core/subsurfacestartup.h" #include "core/settings/qPrefProxy.h" @@ -23,34 +25,36 @@ #endif // provide declarations for two local helper functions in git-access.c -extern "C" char *get_local_dir(const char *remote, const char *branch); -extern "C" void delete_remote_branch(git_repository *repo, const char *remote, const char *branch); +std::string get_local_dir(const std::string &remote, const std::string &branch); +void delete_remote_branch(git_repository *repo, const std::string &remote, const std::string &branch); -QString email; -QString gitUrl; -QString cloudTestRepo; -QString localCacheDir; -QString localCacheRepo; -QString randomBranch; +Q_DECLARE_METATYPE(std::string); -static void moveDir(QString oldName, QString newName) +std::string email; +std::string gitUrl; +std::string cloudTestRepo; +std::string localCacheDir; +std::string localCacheRepo; +std::string randomBranch; + +static void moveDir(const std::string &oldName, const std::string &newName) { - QDir oldDir(oldName); - QDir newDir(newName); + QDir oldDir(oldName.c_str()); + QDir newDir(newName.c_str()); QCOMPARE(newDir.removeRecursively(), true); - QCOMPARE(oldDir.rename(oldName, newName), true); + QCOMPARE(oldDir.rename(oldName.c_str(), newName.c_str()), true); } static void localRemoteCleanup() { // cleanup the local cache dir - struct git_info info = { }; - QDir localCacheDirectory(localCacheDir); + struct git_info info; + QDir localCacheDirectory(localCacheDir.c_str()); QCOMPARE(localCacheDirectory.removeRecursively(), true); // when this is first executed, we expect the branch not to exist on the remote server; // if that's true, this will print a harmless error to stderr - is_git_repository(qPrintable(cloudTestRepo), &info) && open_git_repository(&info); + is_git_repository(cloudTestRepo.c_str(), &info) && open_git_repository(&info); // this odd comparison is used to tell that we were able to connect to the remote repo; // in the error case we get the full cloudTestRepo name back as "branch" @@ -65,8 +69,6 @@ static void localRemoteCleanup() // and since this will have created a local repo, remove that one, again so the tests start clean QCOMPARE(localCacheDirectory.removeRecursively(), true); - - cleanup_git_info(&info); } void TestGitStorage::initTestCase() @@ -88,50 +90,50 @@ void TestGitStorage::initTestCase() // // To reduce the risk of collisions on the server, we have ten accounts set up for this purpose // please don't use them for other reasons as they will get deleted regularly - email = qgetenv("SSRF_USER_EMAIL"); - QString password = qgetenv("SSRF_USER_PASSWORD"); + email = qgetenv("SSRF_USER_EMAIL").toStdString(); + std::string password(qgetenv("SSRF_USER_PASSWORD").data()); - if (email.isEmpty()) { + if (email.empty()) { #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - email = QString("gitstorage%1@hohndel.org").arg(QRandomGenerator::global()->bounded(10)); + email = format_string_std("gitstorage%d@hohndel.org", QRandomGenerator::global()->bounded(10)); #else // on Qt 5.9 we go back to using qsrand()/qrand() qsrand(time(NULL)); - email = QString("gitstorage%1@hohndel.org").arg(qrand() % 10); + email = format_string_std("gitstorage%d@hohndel.org", qrand() % 10); #endif } - if (password.isEmpty()) + if (password.empty()) password = "please-only-use-this-in-the-git-tests"; gitUrl = prefs.cloud_base_url; - if (gitUrl.right(1) != "/") + if (gitUrl.empty() || gitUrl.back() != '/') gitUrl += "/"; gitUrl += "git"; - prefs.cloud_storage_email_encoded = copy_qstring(email); - prefs.cloud_storage_password = copy_qstring(password); + prefs.cloud_storage_email_encoded = strdup(email.c_str()); + prefs.cloud_storage_password = strdup(password.c_str()); gitUrl += "/" + email; // all user storage for historical reasons always uses the user's email both as // repo name and as branch. To allow us to keep testing and not step on parallel // runs we'll use actually random branch names - yes, this still has a chance of // conflict, but I'm not going to implement a distributed lock manager for this - if (email.startsWith("gitstorage")) { + if (starts_with(email, "gitstorage")) { #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - randomBranch = QString::number(QRandomGenerator::global()->bounded(0x1000000), 16) + - QString::number(QRandomGenerator::global()->bounded(0x1000000), 16); + randomBranch = format_string_std("%x%x", QRandomGenerator::global()->bounded(0x1000000), + QRandomGenerator::global()->bounded(0x1000000)); #else // on Qt 5.9 we go back to using qsrand()/qrand() -- if we get to this code, qsrand() was already called // even on a 32bit system RAND_MAX is at least 32767 so this will also give us 12 random hex digits - randomBranch = QString::number(qrand() % 0x1000, 16) + QString::number(qrand() % 0x1000, 16) + - QString::number(qrand() % 0x1000, 16) + QString::number(qrand() % 0x1000, 16); + randomBranch = format_string_std("%x%x%x%x", qrand() % 0x1000, qrand() % 0x1000, + qrand() % 0x1000, qrand() % 0x1000); #endif } else { // user supplied their own credentials, fall back to the usual "email is branch" pattern randomBranch = email; } - cloudTestRepo = gitUrl + QStringLiteral("[%1]").arg(randomBranch); - localCacheDir = get_local_dir(qPrintable(gitUrl), qPrintable(randomBranch)); - localCacheRepo = localCacheDir + QStringLiteral("[%1]").arg(randomBranch); - qDebug() << "repo used:" << cloudTestRepo; - qDebug() << "local cache:" << localCacheRepo; + cloudTestRepo = gitUrl + "[" + randomBranch + ']'; + localCacheDir = get_local_dir(gitUrl.c_str(), randomBranch.c_str()); + localCacheRepo = localCacheDir + "[" + randomBranch + "]"; + qDebug() << "repo used:" << cloudTestRepo.c_str(); + qDebug() << "local cache:" << localCacheRepo.c_str(); // make sure we deal with any proxy settings that are needed QNetworkProxy proxy; @@ -152,7 +154,7 @@ void TestGitStorage::initTestCase() // cleanup local and remote branches localRemoteCleanup(); - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); } void TestGitStorage::cleanupTestCase() @@ -169,15 +171,16 @@ void TestGitStorage::testGitStorageLocal_data() { // Test different paths we may encounter (since storage depends on user name) // as well as with and without "file://" URL prefix. - QTest::addColumn("testDirName"); - QTest::addColumn("prefixRead"); - QTest::addColumn("prefixWrite"); - QTest::newRow("ASCII path") << "./gittest" << "" << ""; - QTest::newRow("Non ASCII path") << "./gittest_éèêôàüäößíñóúäåöø" << "" << ""; - QTest::newRow("ASCII path with file:// prefix on read") << "./gittest2" << "file://" << ""; - QTest::newRow("Non ASCII path with file:// prefix on read") << "./gittest2_éèêôàüäößíñóúäåöø" << "" << "file://"; - QTest::newRow("ASCII path with file:// prefix on write") << "./gittest3" << "file://" << ""; - QTest::newRow("Non ASCII path with file:// prefix on write") << "./gittest3_éèêôàüäößíñóúäåöø" << "" << "file://"; + using namespace std::string_literals; // For std::string literals: "some string"s. + QTest::addColumn("testDirName"); + QTest::addColumn("prefixRead"); + QTest::addColumn("prefixWrite"); + QTest::newRow("ASCII path") << "./gittest"s << ""s << ""s; + QTest::newRow("Non ASCII path") << "./gittest_éèêôàüäößíñóúäåöø"s << ""s << ""s; + QTest::newRow("ASCII path with file:// prefix on read") << "./gittest2"s << "file://"s << ""s; + QTest::newRow("Non ASCII path with file:// prefix on read") << "./gittest2_éèêôàüäößíñóúäåöø"s << ""s << "file://"s; + QTest::newRow("ASCII path with file:// prefix on write") << "./gittest3"s << "file://"s << ""s; + QTest::newRow("Non ASCII path with file:// prefix on write") << "./gittest3_éèêôàüäößíñóúäåöø"s << ""s << "file://"s; } void TestGitStorage::testGitStorageLocal() @@ -185,19 +188,19 @@ void TestGitStorage::testGitStorageLocal() // test writing and reading back from local git storage git_repository *repo; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/SampleDivesV2.ssrf", &divelog), 0); - QFETCH(QString, testDirName); - QFETCH(QString, prefixRead); - QFETCH(QString, prefixWrite); - QDir testDir(testDirName); + QFETCH(std::string, testDirName); + QFETCH(std::string, prefixRead); + QFETCH(std::string, prefixWrite); + QDir testDir(testDirName.c_str()); QCOMPARE(testDir.removeRecursively(), true); - QCOMPARE(QDir().mkdir(testDirName), true); - QString repoNameRead = prefixRead + testDirName; - QString repoNameWrite = prefixWrite + testDirName; - QCOMPARE(git_repository_init(&repo, qPrintable(testDirName), false), 0); - QCOMPARE(save_dives(qPrintable(repoNameWrite + "[test]")), 0); + QCOMPARE(QDir().mkdir(testDirName.c_str()), true); + std::string repoNameRead = prefixRead + testDirName; + std::string repoNameWrite = prefixWrite + testDirName; + QCOMPARE(git_repository_init(&repo, testDirName.c_str(), false), 0); + QCOMPARE(save_dives((repoNameWrite + "[test]").c_str()), 0); QCOMPARE(save_dives("./SampleDivesV3.ssrf"), 0); clear_dive_file_data(); - QCOMPARE(parse_file(qPrintable(repoNameRead + "[test]"), &divelog), 0); + QCOMPARE(parse_file((repoNameRead + "[test]").c_str(), &divelog), 0); QCOMPARE(save_dives("./SampleDivesV3viagit.ssrf"), 0); QFile org("./SampleDivesV3.ssrf"); org.open(QFile::ReadOnly); @@ -216,9 +219,9 @@ void TestGitStorage::testGitStorageCloud() // connect to the ssrftest repository on the cloud server // and repeat the same test as before with the local git storage QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/SampleDivesV2.ssrf", &divelog), 0); - QCOMPARE(save_dives(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(save_dives("./SampleDivesV3viacloud.ssrf"), 0); QFile org("./SampleDivesV3.ssrf"); org.open(QFile::ReadOnly); @@ -236,19 +239,19 @@ void TestGitStorage::testGitStorageCloudOfflineSync() // make a change to local cache repo (pretending that we did some offline changes) // and then open the remote one again and check that things were propagated correctly // read the local repo from the previous test and add dive 10 - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test10.xml", &divelog), 0); // calling process_loaded_dives() sorts the table, but calling add_imported_dives() // causes it to try to update the window title... let's not do that process_loaded_dives(); // now save only to the local cache but not to the remote server git_local_only = true; - QCOMPARE(save_dives(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); QCOMPARE(save_dives("./SampleDivesV3plus10local.ssrf"), 0); clear_dive_file_data(); // now pretend that we are online again and open the cloud storage and compare git_local_only = false; - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(save_dives("./SampleDivesV3plus10viacloud.ssrf"), 0); QFile org("./SampleDivesV3plus10local.ssrf"); org.open(QFile::ReadOnly); @@ -260,10 +263,10 @@ void TestGitStorage::testGitStorageCloudOfflineSync() QString written = outS.readAll(); QCOMPARE(readin, written); // write back out to cloud storage, move away the local cache, open again and compare - QCOMPARE(save_dives(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); moveDir(localCacheDir, localCacheDir + "save"); - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(save_dives("./SampleDivesV3plus10fromcloud.ssrf"), 0); org.close(); org.open(QFile::ReadOnly); @@ -292,10 +295,10 @@ void TestGitStorage::testGitStorageCloudMerge() // (1) open the repo, add dive test11 and save to the cloud git_local_only = false; - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test11.xml", &divelog), 0); process_loaded_dives(); - QCOMPARE(save_dives(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); // (2) switch to the second client by moving the old cache back in place @@ -304,15 +307,15 @@ void TestGitStorage::testGitStorageCloudMerge() // (3) open the repo from the old cache and add dive test12 while offline git_local_only = true; - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test12.xml", &divelog), 0); process_loaded_dives(); - QCOMPARE(save_dives(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); // (4) now take things back online git_local_only = false; - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); clear_dive_file_data(); // (5) now we should have all the dives in our repo on the second client @@ -324,7 +327,7 @@ void TestGitStorage::testGitStorageCloudMerge() QCOMPARE(save_dives("./SampleDivesV3plus10-11-12.ssrf"), 0); // then load from the cloud clear_dive_file_data(); - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); process_loaded_dives(); QCOMPARE(save_dives("./SampleDivesV3plus10-11-12-merged.ssrf"), 0); // finally compare what we have @@ -341,7 +344,7 @@ void TestGitStorage::testGitStorageCloudMerge() // (6) move ourselves back to the first client and compare data there moveDir(localCacheDir + "client1", localCacheDir); - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); process_loaded_dives(); QCOMPARE(save_dives("./SampleDivesV3plus10-11-12-merged-client1.ssrf"), 0); QFile client1("./SampleDivesV3plus10-11-12-merged-client1.ssrf"); @@ -357,13 +360,13 @@ void TestGitStorage::testGitStorageCloudMerge2() // edit the same dive in the cloud repo // merge // (1) open repo, delete second dive, save offline - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); process_loaded_dives(); struct dive *dive = get_dive(1); delete_single_dive(1); QCOMPARE(save_dives("./SampleDivesMinus1.ssrf"), 0); git_local_only = true; - QCOMPARE(save_dives(qPrintable(localCacheRepo)), 0); + QCOMPARE(save_dives(localCacheRepo.c_str()), 0); git_local_only = false; clear_dive_file_data(); @@ -371,21 +374,21 @@ void TestGitStorage::testGitStorageCloudMerge2() moveDir(localCacheDir, localCacheDir + "save"); // (3) now we open the cloud storage repo and modify that second dive - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); process_loaded_dives(); dive = get_dive(1); QVERIFY(dive != NULL); free(dive->notes); dive->notes = strdup("These notes have been modified by TestGitStorage"); - QCOMPARE(save_dives(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); // (4) move the saved local cache backinto place and try to open the cloud repo // -> this forces a merge moveDir(localCacheDir + "save", localCacheDir); - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(save_dives("./SampleDivesMinus1-merged.ssrf"), 0); - QCOMPARE(save_dives(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); QFile org("./SampleDivesMinus1-merged.ssrf"); org.open(QFile::ReadOnly); QFile out("./SampleDivesMinus1.ssrf"); @@ -406,7 +409,7 @@ void TestGitStorage::testGitStorageCloudMerge3() // (1) open repo, edit notes of first three dives - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); process_loaded_dives(); struct dive *dive; QVERIFY((dive = get_dive(0)) != 0); @@ -418,11 +421,11 @@ void TestGitStorage::testGitStorageCloudMerge3() QVERIFY((dive = get_dive(2)) != 0); free(dive->notes); dive->notes = strdup("Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"); - QCOMPARE(save_dives(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); // (2) make different edits offline - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); process_loaded_dives(); QVERIFY((dive = get_dive(0)) != 0); free(dive->notes); @@ -434,14 +437,14 @@ void TestGitStorage::testGitStorageCloudMerge3() free(dive->notes); dive->notes = strdup("single line dive notes"); git_local_only = true; - QCOMPARE(save_dives(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); git_local_only = false; clear_dive_file_data(); // (3) simulate a second system by moving the cache away and open the cloud storage repo and modify // those first dive notes differently while online moveDir(localCacheDir, localCacheDir + "save"); - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); process_loaded_dives(); QVERIFY((dive = get_dive(0)) != 0); free(dive->notes); @@ -452,12 +455,12 @@ void TestGitStorage::testGitStorageCloudMerge3() QVERIFY((dive = get_dive(2)) != 0); free(dive->notes); dive->notes = strdup("Line 2\nLine 3\nLine 4\nLine 5"); // keep the middle, remove first and last"); - QCOMPARE(save_dives(qPrintable(cloudTestRepo)), 0); + QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); // (4) move the saved local cache back into place and open the cloud repo -> this forces a merge moveDir(localCacheDir + "save", localCacheDir); - QCOMPARE(parse_file(qPrintable(cloudTestRepo), &divelog), 0); + QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(save_dives("./SampleDivesMerge3.ssrf"), 0); // we are not trying to compare this to a pre-determined result... what this test // checks is that there are no parsing errors with the merge diff --git a/tests/testparseperformance.cpp b/tests/testparseperformance.cpp index ec28540e0..4c8f66b56 100644 --- a/tests/testparseperformance.cpp +++ b/tests/testparseperformance.cpp @@ -42,8 +42,8 @@ void TestParsePerformance::initTestCase() QNetworkProxy::setApplicationProxy(proxy); // now cleanup the cache dir in case there's something weird from previous runs - QString localCacheDir(get_local_dir(LARGE_TEST_REPO, "git")); - QDir localCacheDirectory(localCacheDir); + std::string localCacheDir = get_local_dir(LARGE_TEST_REPO, "git"); + QDir localCacheDirectory(localCacheDir.c_str()); QCOMPARE(localCacheDirectory.removeRecursively(), true); }