1 /*
2  * SessionPersistentState.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 <session/SessionPersistentState.hpp>
17 
18 #include <core/Log.hpp>
19 #include <shared_core/Error.hpp>
20 #include <shared_core/FilePath.hpp>
21 #include <core/FileSerializer.hpp>
22 #include <core/system/System.hpp>
23 
24 #include <session/SessionOptions.hpp>
25 #include <session/SessionModuleContext.hpp>
26 
27 #include "session-config.h"
28 
29 #ifdef RSTUDIO_SERVER
30 #include <server_core/UrlPorts.hpp>
31 #endif
32 
33 using namespace rstudio::core;
34 
35 namespace rstudio {
36 namespace session {
37 
38 namespace {
39 const char * const kActiveClientId = "active-client-id";
40 const char * const kAbend = "abend";
41 }
42 
persistentState()43 PersistentState& persistentState()
44 {
45    static PersistentState instance;
46    return instance;
47 }
48 
initialize()49 Error PersistentState::initialize()
50 {
51    serverMode_ = (session::options().programMode() ==
52                   kSessionProgramModeServer);
53 
54    // always the same so that we can supporrt a restart of
55    // the session without reloading the client page
56    desktopClientId_ = "33e600bb-c1b1-46bf-b562-ab5cba070b0e";
57 
58    // scoped/project settings
59    FilePath scratchPath = module_context::scopedScratchPath();
60    FilePath statePath = scratchPath.completePath("persistent-state");
61    Error error = settings_.initialize(statePath);
62    if (error)
63       return error;
64 
65    // session settings
66    scratchPath = module_context::sessionScratchPath();
67    statePath = scratchPath.completePath("session-persistent-state");
68    return sessionSettings_.initialize(statePath);
69 }
70 
activeClientId()71 std::string PersistentState::activeClientId()
72 {
73    if (serverMode_)
74    {
75       std::string activeClientId = sessionSettings_.get(kActiveClientId);
76       if (!activeClientId.empty())
77          return activeClientId;
78       else
79          return newActiveClientId();
80    }
81    else
82    {
83       return desktopClientId_;
84    }
85 }
86 
newActiveClientId()87 std::string PersistentState::newActiveClientId()
88 {
89    if (serverMode_)
90    {
91       std::string newId = core::system::generateUuid();
92       sessionSettings_.set(kActiveClientId, newId);
93       return newId;
94    }
95    else
96    {
97       return desktopClientId_;
98    }
99 }
100 
activeClientUrl() const101 std::string PersistentState::activeClientUrl() const
102 {
103    return settings_.get("activeClientUrl", "");
104 }
105 
setActiveClientUrl(const std::string & url)106 void PersistentState::setActiveClientUrl(const std::string& url)
107 {
108    settings_.set("activeClientUrl", url);
109 }
110 
111 // abend tracking only applies to server mode
112 
hadAbend()113 bool PersistentState::hadAbend()
114 {
115    if (serverMode_)
116    {
117       return sessionSettings_.getInt(kAbend, false);
118    }
119    else
120    {
121       return false;
122    }
123 }
124 
setAbend(bool abend)125 void PersistentState::setAbend(bool abend)
126 {
127    if (serverMode_)
128    {
129       sessionSettings_.set(kAbend, abend);
130    }
131 }
132 
activeEnvironmentName() const133 std::string PersistentState::activeEnvironmentName() const
134 {
135    return settings_.get("activeEnvironmentName", "R_GlobalEnv");
136 }
137 
setActiveEnvironmentName(std::string environmentName)138 void PersistentState::setActiveEnvironmentName(std::string environmentName)
139 {
140    settings_.set("activeEnvironmentName", environmentName);
141 }
142 
143 
portToken() const144 std::string PersistentState::portToken() const
145 {
146    return settings_.get("portToken",
147 #ifdef RSTUDIO_SERVER
148    // on RStudio Server, we have a fallback default so that we're guaranteed to have a port token to
149    // work with (better a predictable obfuscated value than a raw or busted one)
150    kDefaultPortToken
151 #else
152    // Desktop doesn't use port tokens
153    ""
154 #endif
155    );
156 }
157 
setPortToken(const std::string & token)158 void PersistentState::setPortToken(const std::string& token)
159 {
160    settings_.set("portToken", token);
161 }
162 
getStoredHash(const std::string & hashName) const163 std::string PersistentState::getStoredHash(const std::string& hashName) const
164 {
165    return settings_.get(hashName + "Hash", "");
166 }
167 
setStoredHash(const std::string & hashName,const std::string & hashValue)168 void PersistentState::setStoredHash(const std::string& hashName,
169                                     const std::string& hashValue)
170 {
171    settings_.set(hashName + "Hash", hashValue);
172 }
173 
environmentMonitoring() const174 bool PersistentState::environmentMonitoring() const
175 {
176    return settings_.getBool("environmentMonitoring", true);
177 }
178 
setEnvironmentMonitoring(bool monitoring)179 void PersistentState::setEnvironmentMonitoring(bool monitoring)
180 {
181    return settings_.set("environmentMonitoring", monitoring);
182 }
183 
reusedStandalonePort() const184 std::string PersistentState::reusedStandalonePort() const
185 {
186    if (session::options().getBoolOverlayOption(kLauncherSessionOption))
187       return sessionSettings_.get("reusedStandalonePort", "");
188 
189    return std::string();
190 }
191 
setReusedStandalonePort(const std::string & port)192 void PersistentState::setReusedStandalonePort(const std::string& port)
193 {
194    if (session::options().getBoolOverlayOption(kLauncherSessionOption))
195       sessionSettings_.set("reusedStandalonePort", port);
196 }
197 
reusedSessionProxyPort() const198 std::string PersistentState::reusedSessionProxyPort() const
199 {
200    if (session::options().getBoolOverlayOption(kLauncherSessionOption))
201       return sessionSettings_.get("reusedSessionProxyPort", "");
202 
203    return std::string();
204 }
205 
setReusedSessionProxyPort(const std::string & port)206 void PersistentState::setReusedSessionProxyPort(const std::string& port)
207 {
208    if (session::options().getBoolOverlayOption(kLauncherSessionOption))
209       sessionSettings_.set("reusedSessionProxyPort", port);
210 }
211 
212 } // namespace session
213 } // namespace rstudio
214