[education/rkward] rkward/rbackend: Avoid requiring bundled certs for embedded R's libcurl in AppImage
Thomas Friedrichsmeier
null at kde.org
Sat Oct 31 09:51:36 GMT 2026
Git commit cd83b4af7a954b6bf82fc9f5b802c824e979582b by Thomas Friedrichsmeier.
Committed on 31/10/2026 at 09:50.
Pushed by tfry into branch 'master'.
Avoid requiring bundled certs for embedded R's libcurl in AppImage
Needs testing.
M +38 -19 rkward/rbackend/rkfrontendtransmitter.cpp
https://invent.kde.org/education/rkward/-/commit/cd83b4af7a954b6bf82fc9f5b802c824e979582b
diff --git a/rkward/rbackend/rkfrontendtransmitter.cpp b/rkward/rbackend/rkfrontendtransmitter.cpp
index e4887519b..9b593b13d 100644
--- a/rkward/rbackend/rkfrontendtransmitter.cpp
+++ b/rkward/rbackend/rkfrontendtransmitter.cpp
@@ -67,16 +67,15 @@ bool pathIsChildOf(const QString &parent, const QString &child) {
return QFileInfo(child).canonicalFilePath().startsWith(QFileInfo(parent).canonicalFilePath());
}
-void removeFromPathList(const char *varname, bool (*shouldRemove)(const QString &path)) {
+void filterEnvPath(QProcessEnvironment *env, const QString &varname, bool (*shouldRemove)(const QString &path)) {
#ifdef Q_OS_WIN
# define PATH_VAR_SEP u';'
#else
# define PATH_VAR_SEP u':'
#endif
- auto var = qgetenv(varname);
- if (var.isEmpty()) return;
+ if (!env->contains(varname)) return;
- const auto list = QString::fromLocal8Bit(var).split(PATH_VAR_SEP);
+ const auto list = env->value(varname).split(PATH_VAR_SEP);
QStringList newlist;
for (const auto &str : list) {
if (shouldRemove(str)) {
@@ -85,7 +84,7 @@ void removeFromPathList(const char *varname, bool (*shouldRemove)(const QString
newlist.append(str);
}
}
- qputenv(varname, newlist.join(PATH_VAR_SEP).toLocal8Bit());
+ env->insert(varname, newlist.join(PATH_VAR_SEP));
}
RKFrontendTransmitter::RKFrontendTransmitter(RKRBackendProtocolFrontend *frontend) : RKAbstractTransmitter(), frontend(frontend) {
@@ -186,16 +185,36 @@ void RKFrontendTransmitter::run() {
backend = new QProcess(this);
connect(backend, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &RKFrontendTransmitter::backendExit);
- QStringList env = QProcess::systemEnvironment();
+ auto env = QProcessEnvironment::systemEnvironment();
// Try to synchronize language selection in frontend and backend
- int index = env.indexOf(QRegularExpression(QStringLiteral("^LANGUAGE=.*"), QRegularExpression::CaseInsensitiveOption)); // clazy:exclude=use-static-qregularexpression ; it'll only be craeted once
- if (index >= 0) env.removeAt(index);
- env.append(u"LANGUAGE="_s + QLocale().name().section(u'_', 0, 0));
-
- if (RKSessionVars::runningInAppImage() && RKSessionVars::isPathInAppImage(RKSessionVars::RBinary())) {
- RK_DEBUG(RBACKEND, DL_DEBUG, "Detected running from AppImage with external R.");
- removeFromPathList("LD_LIBRARY_PATH", RKSessionVars::isPathInAppImage);
- removeFromPathList("PATH", RKSessionVars::isPathInAppImage);
+ env.insert(u"LANGUAGE"_s, QLocale().name().section(u'_', 0, 0));
+
+ if (RKSessionVars::runningInAppImage() && !RKSessionVars::isPathInAppImage(RKSessionVars::RBinary())) {
+ RK_DEBUG(RBACKEND, DL_INFO, "Detected running from AppImage with external R.");
+ filterEnvPath(&env, u"LD_LIBRARY_PATH"_s, RKSessionVars::isPathInAppImage);
+ filterEnvPath(&env, u"PATH"_s, RKSessionVars::isPathInAppImage);
+ } else if (RKSessionVars::runningInAppImage()) {
+ RK_DEBUG(RBACKEND, DL_INFO, "Detected running from AppImage with embedded R.");
+ if (!env.contains(u"CURL_CA_BUNDLE"_s)) {
+ // R inside AppImage uses embedded libcurl, which does not know where to look for system ca certs.
+ // Do our best to help it. Note that we *did* warn users about limited features in embedded R, though.
+ // See also: https://curl.se/mail/lib-2022-05/0038.html
+ const auto ca_curl_bundle_locations = QStringList({u"/etc/ssl/ca-bundle.pem"_s,
+ u"/etc/ssl/certs/ca-certificates.crt"_s,
+ u"/etc/ssl/cert.pem"_s,
+ u"/etc/pki/tls/certs/ca-bundle.crt"_s,
+ u"/etc/pki/tls/cert.pem"_s,
+ u"/etc/pki/tls/cacert.pem"_s,
+ u"/usr/local/share/certs/ca-root-nss.crt "_s,
+ u"/usr/share/ssl/certs/ca-bundle.crt"_s,
+ u"/usr/share/curl/curl-ca-bundle.crt"_s});
+ for (const auto &candidate : ca_curl_bundle_locations) {
+ if (QFile::exists(candidate)) {
+ env.insert(u"CURL_CA_BUNDLE"_s, candidate);
+ break;
+ }
+ }
+ }
}
QStringList args;
@@ -243,16 +262,16 @@ void RKFrontendTransmitter::run() {
backend_lib = findBackendLibAtPath(RKCommonFunctions::getRKWardAppDir() + u"/../Frameworks"_s); // MacOS bundle: rkward in /MacOS, lib in /Framweorks
# endif
# if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
- env.append(QStringLiteral("RK_BACKEND_LIB=") + backend_lib);
+ env.insert(QStringLiteral("RK_BACKEND_LIB"), backend_lib);
# else
- env.append(QStringLiteral("RK_BACKEND_LIB=") + QFileInfo(backend_lib).fileName());
+ env.insert(QStringLiteral("RK_BACKEND_LIB"), QFileInfo(backend_lib).fileName());
QTemporaryDir rkward_only_dir(QDir::tempPath() + u"/rkward_only"_s);
QFile(QFileInfo(backend_lib).absolutePath()).link(rkward_only_dir.filePath(QStringLiteral("_rkward_only_dlpath")));
- env.append(QStringLiteral("RK_ADD_LDPATH=./_rkward_only_dlpath"));
- env.append(QStringLiteral("RK_LD_CWD=") + rkward_only_dir.path());
+ env.insert(QStringLiteral("RK_ADD_LDPATH"), u"./_rkward_only_dlpath"_s);
+ env.insert(QStringLiteral("RK_LD_CWD"), rkward_only_dir.path());
# endif
#endif
- backend->setEnvironment(env);
+ backend->setProcessEnvironment(env);
#ifndef Q_OS_MACOS
// Needed on for paths with spaces. R CMD is too simple to deal with those, even if we provide proper quoting.
More information about the rkward-tracker
mailing list