0byt3m1n1
Path:
C:
/
Program Files
/
RStudio
/
resources
/
app
/
R
/
modules
/
[
Home
]
File: SessionConnectionsInstaller.R
# # SessionConnectionsInstaller.R # # Copyright (C) 2022 by Posit Software, PBC # # Unless you have received this program directly from Posit Software pursuant # to the terms of a commercial license agreement with Posit Software, then # this program is licensed to you under the terms of version 3 of the # GNU Affero General Public License. This program is distributed WITHOUT # ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the # AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. # # .rs.addFunction("odbcBundleOsName", function() { osMapping <- list ( linux = "linux", windows = "windows", darwin = "osx" ) if (!tolower(Sys.info()["sysname"]) %in% names(osMapping)) stop("Operating system \"", Sys.info()["sysname"], "\" is unsupported.") osMapping[[tolower(Sys.info()[["sysname"]])]] }) .rs.addFunction("odbcOsBitness", function() { if (grepl("64", Sys.info()["machine"])) { "64" } else { "32" } }) .rs.addFunction("odbcIsWow", function() { identical(tolower(Sys.info()["sysname"])[[1]], "windows") && identical(.rs.odbcOsBitness(), "32") && nchar(Sys.getenv("ProgramW6432")) > 0 }) .rs.addFunction("odbcBundleName", function(placeholder) { osName <- .rs.odbcBundleOsName() bitness <- .rs.odbcOsBitness() bundleName <- gsub("\\(os\\)", osName, placeholder) bundleName <- gsub("\\(bitness\\)", bitness, bundleName) bundleName }) .rs.addFunction("odbcBundleDownload", function(url, placeholder, bundleTemp) { bundleName <- .rs.odbcBundleName(placeholder) bundleUrl <- file.path(url, bundleName) if (exists(bundleTemp)) unlink(bundleTemp, recursive = TRUE) dir.create(bundleTemp, recursive = TRUE) bundleFileTemp <- file.path(bundleTemp, bundleName) download.file(bundleUrl, bundleFileTemp) bundleFileTemp }) .rs.addFunction("odbcBundleExtract", function(bundleFileTemp, installPath) { untar(bundleFileTemp, exdir = installPath) }) .rs.addFunction("odbcBundleCheckPrereqsUnixodbc", function() { identical( suppressWarnings( system2( "odbcinst", stdout = getOption("odbc.installer.verbose", FALSE), stderr = getOption("odbc.installer.verbose", FALSE) ) ), 1L ) }) .rs.addFunction("odbcBundleCheckPrereqsBrew", function() { identical( suppressWarnings( system2( "brew", stdout = getOption("odbc.installer.verbose", FALSE), stderr = getOption("odbc.installer.verbose", FALSE) ) ), 1L ) }) .rs.addFunction("odbcBundleCheckPrereqsOsx", function() { if (!.rs.odbcBundleCheckPrereqsUnixodbc()) { if (!.rs.odbcBundleCheckPrereqsBrew()) { stop( "unixODBC is required but missing, you can install from http://www.unixodbc.org/. ", "Alternatively, install Brew and RStudio will install unixODBC automatically, ", "you can install Brew by running: ", "/usr/bin/ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"" ) } message("Installing unixODBC...") system2( "brew", args = list("install", "unixodbc") ) } if (!.rs.odbcBundleCheckPrereqsUnixodbc()) stop("Failed to install unixODBC, please install from www.unixodbc.org") }) .rs.addFunction("odbcBundleCheckPrereqsLinux", function() { if (!.rs.odbcBundleCheckPrereqsUnixodbc()) stop("unixODBC is not installed, please install from www.unixodbc.org") }) .rs.addFunction("odbcBundleRegistryAdd", function(entries) { validateEntry <- function(entry) { tryCatch({ verify <- readRegistry(entry$path, "HLM", view = .rs.odbcOsBitness()) identical(verify[[entry$key]], entry$value) }, error = function(e) { FALSE }) } odbcFileEscape <- function(value) { gsub("\\\\", "\\\\\\\\", value) } if (all(sapply(entries, function(e) validateEntry(e)))) return() bitness <- .rs.odbcOsBitness() allAdded <- TRUE for (entry in entries) { fullPath <- file.path("HKEY_LOCAL_MACHINE", entry$path, fsep = "\\") system2( "REG", args = list( "ADD", shQuote(fullPath), "/v", shQuote(entry$key), "/t", "REG_SZ", "/d", shQuote(entry$value), "/f", paste("/reg:", bitness, sep = "") ), stdout = getOption("odbc.installer.verbose", FALSE), stderr = getOption("odbc.installer.verbose", FALSE) ) if (!validateEntry(entry)) { allAdded <- FALSE break } } if (!allAdded) { if (.rs.odbcIsWow()) { stop("Failed to install x86 driver in x64 machine, retry running as administrator.") } message("Could not add registry keys from R, retrying using registry prompt.") addReg <- tempfile(fileext = ".reg") lineEntries <- sapply(entries, function(entry) { fullPath <- file.path("HKEY_LOCAL_MACHINE", entry$path, fsep = "\\") c( paste("[", fullPath, "]", sep = ""), paste( "\"", entry$key, "\"=\"", odbcFileEscape(entry$value), "\"", sep = "" ), "" ) }) lines <- c( "REGEDIT4", "", lineEntries ) writeLines(lines, addReg) message("Waiting for ", addReg, " to be registered.") system2( "explorer", addReg ) allEntriesValid <- function() { all(sapply(entries, function(e) validateEntry(e))) } registryStart <- Sys.time() registryWait <- 30 while (!allEntriesValid() && Sys.time() < registryStart + registryWait) { Sys.sleep(1) cat(".") } if (!allEntriesValid()) { stop("Failed to add all registry keys using registry file.") } } }) .rs.addFunction("odbcBundleRegistryRemove", function(entries) { validateEntry <- function(entry) { tryCatch({ regEntry <- readRegistry(entry$path, "HLM", view = .rs.odbcOsBitness()) if (!is.null(entry$key)) is.null(regEntry[[entry$key]]) else FALSE }, error = function(e) { TRUE }) } if (all(sapply(entries, function(e) validateEntry(e)))) return() bitness <- .rs.odbcOsBitness() for (entry in entries) { fullPath <- file.path("HKEY_LOCAL_MACHINE", entry$path, fsep = "\\") if (!is.null(entry$key)) { regArgs <- list( "DELETE", shQuote(fullPath), "/v", shQuote(entry$key), "/f", paste("/reg:", bitness, sep = "") ) } else { regArgs <- list( "DELETE", shQuote(fullPath), "/f", paste("/reg:", bitness, sep = "") ) } system2( "REG", args = regArgs ) } if (all(sapply(entries, function(e) validateEntry(e)))) return() addReg <- tempfile(fileext = ".reg") lineEntries <- sapply(entries, function(entry) { fullPath <- file.path("HKEY_LOCAL_MACHINE", entry$path, fsep = "\\") if (is.null(entry$key)) { paste("[-", fullPath, "]", sep = "") } else { c( paste("[", fullPath, "]", sep = ""), paste( "\"", entry$key, "\"=-", sep = "" ), "" ) } }) lines <- c( "REGEDIT4", "", unlist(lineEntries) ) writeLines(lines, addReg) system2( "explorer", addReg ) }) .rs.addFunction("odbcBundleRegistryDelete", function(path) { system2( "REG", args = list( "DELETE", shQuote(path), "/f" ) ) identical(ret, 0L) }) .rs.addFunction("odbcBundleCheckPrereqsWindows", function() { }) .rs.addFunction("odbcBundleCheckPrereqs", function() { osPrereqs <- list( osx = .rs.odbcBundleCheckPrereqsOsx, windows = .rs.odbcBundleCheckPrereqsWindows, linux = .rs.odbcBundleCheckPrereqsLinux ) prereqs <- osPrereqs[[.rs.odbcBundleOsName()]] prereqs() }) .rs.addFunction("odbcBundleOdbcinstPathWithOdbcinst", function() { config <- system2("odbcinst", "-j", stdout = TRUE) odbciniEntry <- config[grepl("odbcinst.ini", config)] gsub("^[^/\\\\]*", "", odbciniEntry) }) .rs.addFunction("odbcBundleOdbcinstPathUseHome", function() { normalizePath("~/.odbcinst.ini", mustWork = FALSE) }) .rs.addFunction("odbcBundleOdbcinstPath", function() { osOdbcinstPath <- list( osx = .rs.odbcBundleOdbcinstPathWithOdbcinst, windows = .rs.odbcBundleOdbcinstPathWithOdbcinst, linux = .rs.odbcBundleOdbcinstPathUseHome ) osOdbcinstPath[[.rs.odbcBundleOsName()]]() }) .rs.addFunction("odbcBundleReadIni", function(odbcinstPath) { # return nothing if file doesn't exist if (!file.exists(odbcinstPath)) return(list()) lines <- readLines(odbcinstPath) data <- list() currentDriver <- "__header__" for (line in lines) { # Is header? if (grepl("^ *\\[[^]]+\\] *", line)) { currentDriver <- gsub("^ *\\[|\\] *", "", line) data[[currentDriver]] <- "" } else if (!grepl("^ *$", line)) { if (identical(data[[currentDriver]], "")) data[[currentDriver]] <- line else data[[currentDriver]] <- c( data[[currentDriver]], line ) } } data }) .rs.addFunction("odbcBundleWriteIni", function(odbcinstPath, data) { lines <- c() for (name in names(data)) { lines <- c( lines, if (identical(name, "__header__")) "" else paste("[", name, "]", sep = "") ) lines <- c( lines, unlist(data[[name]]), "" ) } if (is.null(lines)) lines <- c("") writeLines(lines, odbcinstPath) }) .rs.addFunction("odbcOdbcInstLibPath", function() { odbcinstLib <- NULL odbcinstBin <- Sys.which("odbcinst") if (nchar(odbcinstBin) == 0) { warning("Could not find path to odbcinst.") } else { odbcinstLink <- Sys.readlink(odbcinstBin) if (!is.na(odbcinstLink)) { odbcinstBinPath <- normalizePath(file.path(dirname(odbcinstBin), dirname(odbcinstLink))) odbcinstLibPath <- normalizePath(file.path(odbcinstBinPath, "..", "lib")) odbcinstLib <- dir(odbcinstLibPath, pattern = "libodbcinst.*\\.dylib", full.names = TRUE)[[1]] } } odbcinstLib }) .rs.addFunction("odbcBundleRegisterLinux", function(name, driverPath, version, installPath) { # Find odbcinst.ini file odbcinstPath <- .rs.odbcBundleOdbcinstPath() # Read odbcinst.ini odbcinst <- .rs.odbcBundleReadIni(odbcinstPath) # Set odbcinst.ini entries odbcinst[[name]] <- list( paste("Driver", "=", driverPath), paste("Version", "=", version), paste("Installer", "=", "RStudio") ) # Write odbcinst.ini .rs.odbcBundleWriteIni(odbcinstPath, odbcinst) }) .rs.addFunction("odbcBundleDriverIniPath", function(driverPath) { dir(driverPath, pattern = "rstudio\\..*odbc\\.ini", recursive = TRUE, full.names = T) }) .rs.addFunction("odbcBundleRegisterOSX", function(name, driverPath, version, installPath) { # Update odbcinst.ini .rs.odbcBundleRegisterLinux(name, driverPath, version, installPath) # Find driver.ini file driverIniFile <- .rs.odbcBundleDriverIniPath(installPath) if (length(driverIniFile) == 0) { warning("Could not find '", name, "' driver INI file under: ", installPath) } else { # Read driver.ini driverIni <- .rs.odbcBundleReadIni(driverIniFile) # In OSX register to use unixODBC odbcinstLib <- .rs.odbcOdbcInstLibPath() if (!is.null(odbcinstLib)) { driverIni[["Driver"]] <- c( driverIni[["Driver"]], paste("ODBCInstLib", "=", odbcinstLib) ) } # Write odbcinst.ini .rs.odbcBundleWriteIni(driverIniFile, driverIni) } }) .rs.addFunction("odbcBundleRegisterWindows", function(name, driverPath, version, installPath) { .rs.odbcBundleRegistryAdd( list( list( path = file.path("SOFTWARE", "ODBC", "ODBCINST.INI", "ODBC Drivers", fsep = "\\"), key = name, value = "installed" ), list( path = file.path("SOFTWARE", "ODBC", "ODBCINST.INI", name, fsep = "\\"), key = "Driver", value = driverPath ), list( path = file.path("SOFTWARE", "ODBC", "ODBCINST.INI", name, fsep = "\\"), key = "Setup", value = driverPath ), list( path = file.path("SOFTWARE", "ODBC", "ODBCINST.INI", name, fsep = "\\"), key = "Version", value = version ), list( path = file.path("SOFTWARE", "ODBC", "ODBCINST.INI", name, fsep = "\\"), key = "Installer", value = "RStudio" ) ) ) }) .rs.addFunction("odbcBundleFindDriver", function(name, installPath, libraryPattern) { osExtensions <- list( osx = "dylib$", windows = "dll$", linux = "so$" ) osExtension <- osExtensions[[.rs.odbcBundleOsName()]] driverName <- .rs.connectionStripRStudioDriver(name) if (is.null(libraryPattern) || nchar(libraryPattern) == 0) { libraryPattern <- paste( driverName, "[^/\\\\]+\\.", osExtension, sep = "" ) } # apply dynamic patterns osName <- .rs.odbcBundleOsName() bitness <- .rs.odbcOsBitness() libraryPattern <- gsub("\\(os\\)", osName, libraryPattern) libraryPattern <- gsub("\\(bitness\\)", bitness, libraryPattern) allFiles <- dir(installPath, recursive = TRUE, full.names = TRUE) driverPath <- allFiles[grepl(libraryPattern, allFiles, ignore.case = TRUE)] if (!identical(length(driverPath), 1L)) stop("Failed to find ", library, " inside driver bundle.") normalizePath(driverPath) }) .rs.addFunction("odbcBundleRegister", function(name, driverPath, version, installPath) { osRegistrations <- list( osx = .rs.odbcBundleRegisterOSX, windows = .rs.odbcBundleRegisterWindows, linux = .rs.odbcBundleRegisterLinux ) osRegistration <- osRegistrations[[.rs.odbcBundleOsName()]] osRegistration(name, driverPath, version, installPath) }) .rs.addFunction("odbcBundleValidate", function(bundleFile, md5) { if (!is.null(md5) && nchar(md5) > 0) { validMd5s <- strsplit(as.character(md5), "[ \n,]+")[[1]] bundleMd5 <- tools::md5sum(bundleFile) if (!bundleMd5 %in% validMd5s) { stop("Failed to validate bundle with signature ", md5, " but got ", bundleMd5, " instead.") } } }) .rs.addFunction("odbcBundleInstall", function( name, url, placeholder, installPath, libraryPattern = NULL, md5 = NULL, version = "") { name <- .rs.connectionStripRStudioDriver(name) dir.create(installPath, recursive = TRUE, showWarnings = FALSE) installPath <- file.path( normalizePath(installPath, mustWork = FALSE), tolower(name) ) bundleTemp <- tempfile() on.exit(unlink(bundleTemp, recursive = TRUE), add = TRUE) message("Installing") message(" Driver: ", name) message(" Version: ", version) message(" Path: ", installPath) message("Checking prerequisites") .rs.odbcBundleCheckPrereqs() message("Downloading bundle") bundleFileTemp <- .rs.odbcBundleDownload(url, placeholder, bundleTemp) message("Validating bundle") .rs.odbcBundleValidate(bundleFileTemp, md5) message("Extracting bundle") .rs.odbcBundleExtract(bundleFileTemp, installPath) message("Inspecting driver") driverPath <- .rs.odbcBundleFindDriver(name, installPath, libraryPattern) message("Registering driver") .rs.odbcBundleRegister(name, driverPath, version, installPath) message("") message("Installation complete") invisible(NULL) }) .rs.addFunction("connectionOdbcRStudioDriver", function() { " with Posit Driver" }) .rs.addFunction("connectionStripRStudioDriver", function(connection) { gsub(paste(" |", trimws(.rs.connectionOdbcRStudioDriver()), sep = ""), "", connection) })