1 /*
2 * SessionUpdates.cpp
3 *
4 * Copyright (C) 2021 by RStudio, PBC
5 *
6 * Unless you have received this program directly from RStudio pursuant
7 * to the terms of a commercial license agreement with RStudio, then
8 * this program is licensed to you under the terms of version 3 of the
9 * GNU Affero General Public License. This program is distributed WITHOUT
10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13 *
14 */
15
16 #include "SessionUpdates.hpp"
17
18 #include <shared_core/Error.hpp>
19 #include <core/Exec.hpp>
20 #include <core/system/Process.hpp>
21 #include <core/system/Environment.hpp>
22
23 #include <boost/bind/bind.hpp>
24
25 #include <session/SessionModuleContext.hpp>
26
27 #include <session/prefs/UserPrefs.hpp>
28
29 #include <string>
30
31 #include "session-config.h"
32
33 using namespace rstudio::core;
34 using namespace boost::placeholders;
35
36 namespace rstudio {
37 namespace session {
38 namespace modules {
39 namespace updates {
40 namespace {
41
jsonFromProcessResult(const core::system::ProcessResult & result)42 json::Object jsonFromProcessResult(const core::system::ProcessResult& result)
43 {
44 json::Object obj;
45 std::stringstream output(result.stdOut);
46 // The output looks like:
47 // key1=value1
48 // key2=value2
49 // ...
50 for (std::string line; std::getline(output, line); )
51 {
52 size_t pos = line.find('=');
53 if (pos > 0)
54 {
55 obj[line.substr(0, pos)] = line.substr(pos + 1,
56 line.length() - (pos + 1));
57 }
58 }
59 return obj;
60 }
61
beginUpdateCheck(bool manual,const boost::function<void (const core::system::ProcessResult &)> & onCompleted)62 void beginUpdateCheck(bool manual,
63 const boost::function<void(const core::system::ProcessResult&)>& onCompleted)
64 {
65 using namespace module_context;
66
67 // Find the path to R
68 FilePath rProgramPath;
69 Error error = module_context::rScriptPath(&rProgramPath);
70 if (error)
71 {
72 return;
73 }
74
75 // Find the path to the script we need to source
76 FilePath modulesPath = session::options().modulesRSourcePath();;
77 std::string scriptPath = core::string_utils::utf8ToSystem(
78 modulesPath.completePath("SessionUpdates.R").getAbsolutePath());
79
80 // Arguments
81 std::vector<std::string> args;
82 args.push_back("--vanilla");
83
84 #if defined(_WIN32)
85 if (prefs::userPrefs().useInternet2())
86 {
87 args.push_back("--internet2");
88 }
89 #endif
90
91 args.push_back("-s");
92 args.push_back("-e");
93
94 // Build the command to send to R
95 std::string cmd;
96 cmd.append("source('");
97 cmd.append(string_utils::jsLiteralEscape(scriptPath));
98 cmd.append("'); downloadUpdateInfo('");
99 cmd.append(http::util::urlEncode(RSTUDIO_VERSION));
100 cmd.append("', '");
101 #if defined(_WIN32)
102 cmd.append("windows");
103 #elif defined(__APPLE__)
104 cmd.append("mac");
105 #else
106 cmd.append("linux");
107 #endif
108 cmd.append("', ");
109 cmd.append(manual ? "TRUE" : "FALSE");
110 cmd.append(", ");
111 cmd.append(haveSecureDownloadFileMethod() ? "TRUE" : "FALSE");
112 cmd.append(", '");
113 cmd.append(downloadFileMethod("auto"));
114 cmd.append("'");
115 cmd.append(")");
116 args.push_back(cmd);
117
118 LOG_DEBUG_MESSAGE("Checking for updates with command: " + cmd);
119
120 // Set options
121 core::system::ProcessOptions options;
122 options.terminateChildren = true;
123
124 module_context::processSupervisor().runProgram(
125 rProgramPath.getAbsolutePath(),
126 args,
127 std::string(),
128 options,
129 onCompleted);
130 }
131
endRPCUpdateCheck(const json::JsonRpcFunctionContinuation & cont,const core::system::ProcessResult & result)132 void endRPCUpdateCheck(const json::JsonRpcFunctionContinuation& cont,
133 const core::system::ProcessResult& result)
134 {
135 json::JsonRpcResponse response;
136 response.setResult(jsonFromProcessResult(result));
137 cont(Success(), &response);
138 }
139
checkForUpdates(const json::JsonRpcRequest & request,const json::JsonRpcFunctionContinuation & cont)140 void checkForUpdates(const json::JsonRpcRequest& request,
141 const json::JsonRpcFunctionContinuation& cont)
142 {
143 bool manual = false;
144 Error error = json::readParam(request.params, 0, &manual);
145 if (error)
146 {
147 json::JsonRpcResponse response;
148 cont(error, &response);
149 return;
150 }
151 beginUpdateCheck(manual, boost::bind(endRPCUpdateCheck, cont, _1));
152 }
153
154 } // anonymous namespace
155
initialize()156 Error initialize()
157 {
158 using boost::bind;
159 using namespace module_context;
160
161 ExecBlock initBlock;
162 initBlock.addFunctions()
163 (bind(registerAsyncRpcMethod, "check_for_updates", checkForUpdates))
164 ;
165 return initBlock.execute();
166 }
167
168 } // namespace updates
169 } // namespace modules
170 } // namespace session
171 } // namespace rstudio
172