1 /*
2 * SessionClientInit.hpp
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 "SessionClientInit.hpp"
17 #include "SessionInit.hpp"
18 #include "SessionSuspend.hpp"
19 #include "SessionHttpMethods.hpp"
20 #include "SessionDirs.hpp"
21
22 #include "modules/SessionAuthoring.hpp"
23 #include "modules/rmarkdown/SessionRMarkdown.hpp"
24 #include "modules/rmarkdown/SessionBlogdown.hpp"
25 #include "modules/rmarkdown/SessionBookdown.hpp"
26 #include "modules/connections/SessionConnections.hpp"
27 #include "modules/SessionBreakpoints.hpp"
28 #include "modules/SessionDependencyList.hpp"
29 #include "modules/SessionRAddins.hpp"
30 #include "modules/SessionErrors.hpp"
31 #include "modules/SessionFind.hpp"
32 #include "modules/SessionGraphics.hpp"
33 #include "modules/SessionHTMLPreview.hpp"
34 #include "modules/SessionLists.hpp"
35 #include "modules/clang/SessionClang.hpp"
36 #include "modules/SessionMarkers.hpp"
37 #include "modules/SessionPlots.hpp"
38 #include "modules/SessionReticulate.hpp"
39 #include "modules/SessionSVN.hpp"
40 #include "modules/SessionSource.hpp"
41 #include "modules/SessionVCS.hpp"
42 #include "modules/SessionFonts.hpp"
43 #include "modules/SessionSystemResources.hpp"
44 #include "modules/build/SessionBuild.hpp"
45 #include "modules/jobs/SessionJobs.hpp"
46 #include "modules/environment/SessionEnvironment.hpp"
47 #include "modules/presentation/SessionPresentation.hpp"
48 #include "modules/overlay/SessionOverlay.hpp"
49
50 #include <r/session/RSession.hpp>
51 #include <r/session/RClientState.hpp>
52 #include <r/session/RConsoleActions.hpp>
53 #include <r/session/RConsoleHistory.hpp>
54 #include <r/ROptions.hpp>
55
56 #include <core/CrashHandler.hpp>
57 #include <shared_core/json/Json.hpp>
58 #include <core/json/JsonRpc.hpp>
59 #include <core/http/URL.hpp>
60 #include <core/http/Request.hpp>
61 #include <core/http/Response.hpp>
62 #include <core/http/Cookie.hpp>
63 #include <core/http/CSRFToken.hpp>
64 #include <core/system/Environment.hpp>
65
66 #include <session/SessionConsoleProcess.hpp>
67 #include <session/SessionClientEventService.hpp>
68 #include <session/SessionHttpConnection.hpp>
69 #include <session/SessionModuleContext.hpp>
70 #include <session/SessionOptions.hpp>
71 #include <session/SessionPackageProvidedExtension.hpp>
72 #include <session/SessionPersistentState.hpp>
73 #include <session/SessionQuarto.hpp>
74 #include <session/projects/SessionProjectSharing.hpp>
75 #include <session/prefs/UserPrefs.hpp>
76 #include <session/prefs/UserState.hpp>
77
78 #include <session/projects/SessionProjects.hpp>
79
80 #include "session-config.h"
81
82 #ifdef RSTUDIO_SERVER
83 #include <server_core/UrlPorts.hpp>
84 #endif
85
86 using namespace rstudio::core;
87
88 extern "C" const char *locale2charset(const char *);
89
90 namespace rstudio {
91 namespace session {
92 namespace client_init {
93 namespace {
94
userIdentityDisplay(const http::Request & request)95 std::string userIdentityDisplay(const http::Request& request)
96 {
97 std::string userIdentity = request.headerValue(kRStudioUserIdentityDisplay);
98 if (!userIdentity.empty())
99 return userIdentity;
100 else
101 return session::options().userIdentity();
102 }
103
104 #ifdef RSTUDIO_SERVER
makePortTokenCookie(boost::shared_ptr<HttpConnection> ptrConnection,http::Response & response)105 Error makePortTokenCookie(boost::shared_ptr<HttpConnection> ptrConnection,
106 http::Response& response)
107 {
108 // extract the base URL
109 json::JsonRpcRequest request;
110 Error error = parseJsonRpcRequest(ptrConnection->request().body(), &request);
111 if (error)
112 return error;
113 std::string baseURL;
114
115 error = json::readParams(request.params, &baseURL);
116 if (error)
117 return error;
118
119 // save the base URL to persistent state (for forming absolute URLs)
120 persistentState().setActiveClientUrl(baseURL);
121
122 // generate a new port token
123 persistentState().setPortToken(server_core::generateNewPortToken());
124
125 std::string path = ptrConnection->request().rootPath();
126
127 // compute the cookie path; find the first / after the http(s):// preamble. we make the cookie
128 // specific to this session's URL since it's possible for different sessions (paths) to use
129 // different tokens on the same server. This won't be done if the path was passed by the server
130 std::size_t pos = baseURL.find('/', 9);
131 if (pos != std::string::npos && path == kRequestDefaultRootPath)
132 {
133 path = baseURL.substr(pos);
134 }
135 // the root path was defined and we compute the cookie path more securely using internal assumptions
136 // instead of using the URL from the JSON input. In this case, we use the server's perceived current
137 // URI with the last part (/client_init) removed. The result is the session path, same as above.
138 else
139 {
140 path = ptrConnection->request().proxiedUri();
141 boost::algorithm::replace_all(path, ptrConnection->request().uri(), "");
142 http::URL completePath(path);
143 path = completePath.path();
144 }
145
146 // create the cookie; don't set an expiry date as this will be a session cookie
147 http::Cookie cookie(
148 ptrConnection->request(),
149 kPortTokenCookie,
150 persistentState().portToken(),
151 path,
152 options().sameSite(),
153 true, // HTTP only -- client doesn't get to read this token
154 options().useSecureCookies()
155 );
156 response.addCookie(cookie);
157
158 return Success();
159 }
160 #endif
161
162 } // anonymous namespace
163
handleClientInit(const boost::function<void ()> & initFunction,boost::shared_ptr<HttpConnection> ptrConnection)164 void handleClientInit(const boost::function<void()>& initFunction,
165 boost::shared_ptr<HttpConnection> ptrConnection)
166 {
167 // alias options
168 Options& options = session::options();
169
170 // check for valid CSRF headers in server mode
171 if (options.programMode() == kSessionProgramModeServer &&
172 !core::http::validateCSRFHeaders(ptrConnection->request()))
173 {
174 ptrConnection->sendJsonRpcError(Error(json::errc::Unauthorized, ERROR_LOCATION));
175 return;
176 }
177
178 // calculate initialization parameters
179 std::string clientId = persistentState().newActiveClientId();
180 bool resumed = suspend::sessionResumed() || init::isSessionInitialized();
181
182 // if we are resuming then we don't need to worry about events queued up
183 // by R during startup (e.g. printing of the banner) being sent to the
184 // client. so, clear out the events which might be pending in the
185 // client event service and/or queue
186 bool clearEvents = resumed;
187
188 // reset the client event service for the new client (will cause
189 // outstanding http requests from old clients to fail with
190 // InvalidClientId). note that we can't simply stop() the
191 // ClientEventService and start() a new one because in that case the
192 // old client will never get disconnected because it won't get
193 // the InvalidClientId error.
194 clientEventService().setClientId(clientId, clearEvents);
195
196 if (options.programMode() == kSessionProgramModeServer)
197 {
198 // set RSTUDIO_HTTP_REFERER environment variable based on Referer
199 std::string referer = ptrConnection->request().headerValue("referer");
200 core::system::setenv("RSTUDIO_HTTP_REFERER", referer);
201
202 // set RSTUDIO_USER_IDENTITY_DISPLAY environment variable based on
203 // header value (complements RSTUDIO_USER_IDENTITY)
204 core::system::setenv("RSTUDIO_USER_IDENTITY_DISPLAY",
205 userIdentityDisplay(ptrConnection->request()));
206 }
207
208 // prepare session info
209 json::Object sessionInfo;
210 sessionInfo["clientId"] = clientId;
211 sessionInfo["mode"] = options.programMode();
212
213 // build initialization options for client
214 json::Object initOptions;
215 initOptions["restore_workspace"] = options.rRestoreWorkspace();
216 initOptions["run_rprofile"] = options.rRunRprofile();
217 sessionInfo["init_options"] = initOptions;
218
219 sessionInfo["userIdentity"] = userIdentityDisplay(ptrConnection->request());
220 sessionInfo["systemUsername"] = core::system::username();
221
222 // only send log_dir and scratch_dir if we are in desktop mode
223 if (options.programMode() == kSessionProgramModeDesktop)
224 {
225 sessionInfo["log_dir"] = options.userLogPath().getAbsolutePath();
226 sessionInfo["scratch_dir"] = options.userScratchPath().getAbsolutePath();
227 }
228
229 // temp dir
230 FilePath tempDir = rstudio::r::session::utils::tempDir();
231 Error error = tempDir.ensureDirectory();
232 if (error)
233 LOG_ERROR(error);
234 sessionInfo["temp_dir"] = tempDir.getAbsolutePath();
235
236 // R_LIBS_USER
237 sessionInfo["r_libs_user"] = module_context::rLibsUser();
238
239 // user home path
240 sessionInfo["user_home_path"] = session::options().userHomePath().getAbsolutePath();
241
242 // installed client version
243 sessionInfo["client_version"] = http_methods::clientVersion();
244
245 // default prompt
246 sessionInfo["prompt"] = rstudio::r::options::getOption<std::string>("prompt");
247
248 // client state
249 json::Object clientStateObject;
250 rstudio::r::session::clientState().currentState(&clientStateObject);
251 sessionInfo["client_state"] = clientStateObject;
252
253 // source documents
254 json::Array jsonDocs;
255 error = modules::source::clientInitDocuments(&jsonDocs);
256 if (error)
257 LOG_ERROR(error);
258 sessionInfo["source_documents"] = jsonDocs;
259
260 // docs url
261 sessionInfo["docsURL"] = session::options().docsURL();
262
263 // get alias to console_actions and get limit
264 rstudio::r::session::ConsoleActions& consoleActions = rstudio::r::session::consoleActions();
265 sessionInfo["console_actions_limit"] = consoleActions.capacity();
266
267 // check if reticulate's Python session has been initialized
268 sessionInfo["python_initialized"] = modules::reticulate::isPythonInitialized();
269
270 // check if the Python REPL is active
271 sessionInfo["python_repl_active"] = modules::reticulate::isReplActive();
272
273 // propagate RETICULATE_PYTHON if set
274 sessionInfo["reticulate_python"] = core::system::getenv("RETICULATE_PYTHON");
275
276 // get current console language
277 sessionInfo["console_language"] = modules::reticulate::isReplActive() ? "Python" : "R";
278
279 // resumed
280 sessionInfo["resumed"] = resumed;
281 if (resumed)
282 {
283 // console actions
284 json::Object actionsObject;
285 consoleActions.asJson(&actionsObject);
286 sessionInfo["console_actions"] = actionsObject;
287 }
288
289 sessionInfo["rnw_weave_types"] = modules::authoring::supportedRnwWeaveTypes();
290 sessionInfo["latex_program_types"] = modules::authoring::supportedLatexProgramTypes();
291 sessionInfo["tex_capabilities"] = modules::authoring::texCapabilitiesAsJson();
292 sessionInfo["compile_pdf_state"] = modules::authoring::compilePdfStateAsJson();
293
294 sessionInfo["html_capabilities"] = modules::html_preview::capabilitiesAsJson();
295
296 sessionInfo["find_in_files_state"] = modules::find::findInFilesStateAsJson();
297
298 sessionInfo["markers_state"] = modules::markers::markersStateAsJson();
299
300 std::string sessionVersion = std::string(RSTUDIO_VERSION);
301 sessionInfo["rstudio_version"] = sessionVersion;
302
303 // check to ensure the version of this rsession process matches the version
304 // of the rserver that started us - we immediately clear this env var so that
305 // it is not persisted across session suspends
306 std::string version = core::system::getenv(kRStudioVersion);
307 core::system::unsetenv(kRStudioVersion);
308 if (!version.empty() && version != sessionVersion)
309 {
310 module_context::consoleWriteError("Session version " + sessionVersion +
311 " does not match server version " + version + " - "
312 "this is an unsupported configuration, and you may "
313 "experience unexpected issues as a result.\n\n");
314 }
315
316 sessionInfo["user_prefs"] = prefs::allPrefLayers();
317 sessionInfo["user_state"] = prefs::allStateLayers();
318
319 sessionInfo["have_advanced_step_commands"] =
320 modules::breakpoints::haveAdvancedStepCommands();
321
322 // initial working directory
323 std::string initialWorkingDir = module_context::createAliasedPath(
324 dirs::getInitialWorkingDirectory());
325 sessionInfo["initial_working_dir"] = initialWorkingDir;
326 std::string defaultWorkingDir = module_context::createAliasedPath(
327 dirs::getDefaultWorkingDirectory());
328 sessionInfo["default_working_dir"] = defaultWorkingDir;
329
330 // default project dir
331 sessionInfo["default_project_dir"] = options.deprecatedDefaultProjectDir();
332
333 // active project file
334 if (projects::projectContext().hasProject())
335 {
336 sessionInfo["active_project_file"] = module_context::createAliasedPath(
337 projects::projectContext().file());
338 sessionInfo["project_ui_prefs"] = projects::projectContext().uiPrefs();
339 sessionInfo["project_open_docs"] = projects::projectContext().openDocs();
340 sessionInfo["project_supports_sharing"] =
341 projects::projectContext().supportsSharing();
342 sessionInfo["project_parent_browseable"] =
343 projects::projectContext().parentBrowseable();
344 sessionInfo["project_user_data_directory"] =
345 module_context::createAliasedPath(
346 dirs::getProjectUserDataDir(ERROR_LOCATION));
347
348 }
349 else
350 {
351 sessionInfo["active_project_file"] = json::Value();
352 sessionInfo["project_ui_prefs"] = json::Value();
353 sessionInfo["project_open_docs"] = json::Value();
354 sessionInfo["project_supports_sharing"] = false;
355 sessionInfo["project_owned_by_user"] = false;
356 sessionInfo["project_user_data_directory"] = json::Value();
357 }
358
359 sessionInfo["system_encoding"] = std::string(::locale2charset(nullptr));
360
361 std::vector<std::string> vcsAvailable;
362 if (modules::source_control::isGitInstalled())
363 vcsAvailable.push_back(modules::git::kVcsId);
364 if (modules::source_control::isSvnInstalled())
365 vcsAvailable.push_back(modules::svn::kVcsId);
366 sessionInfo["vcs_available"] = boost::algorithm::join(vcsAvailable, ",");
367 sessionInfo["vcs"] = modules::source_control::activeVCSName();
368 sessionInfo["default_ssh_key_dir"] =module_context::createAliasedPath(
369 modules::source_control::defaultSshKeyDir());
370 sessionInfo["is_github_repo"] = modules::git::isGithubRepository();
371
372 // contents of all lists
373 sessionInfo["lists"] = modules::lists::allListsAsJson();
374
375 sessionInfo["console_processes"] =
376 console_process::processesAsJson(console_process::ClientSerialization);
377
378 // send sumatra pdf exe path if we are on windows
379 #ifdef _WIN32
380 sessionInfo["sumatra_pdf_exe_path"] =
381 options.sumatraPath().completePath("SumatraPDF.exe").getAbsolutePath();
382 #endif
383
384 // are build tools enabled
385 if (projects::projectContext().hasProject())
386 {
387 std::string type = projects::projectContext().config().buildType;
388 if ((type == r_util::kBuildTypeNone) && quarto::quartoConfig().is_project)
389 {
390 type = r_util::kBuildTypeQuarto;
391 }
392 sessionInfo["build_tools_type"] = type;
393
394
395 sessionInfo["build_tools_bookdown_website"] =
396 module_context::isBookdownWebsite();
397
398 FilePath buildTargetDir = projects::projectContext().buildTargetPath();
399 if (!buildTargetDir.isEmpty())
400 {
401 sessionInfo["build_target_dir"] = module_context::createAliasedPath(
402 buildTargetDir);
403 sessionInfo["has_pkg_src"] = (type == r_util::kBuildTypePackage) &&
404 buildTargetDir.completeChildPath("src").exists();
405 sessionInfo["has_pkg_vig"] =
406 (type == r_util::kBuildTypePackage) &&
407 buildTargetDir.completeChildPath("vignettes").exists();
408 }
409 else
410 {
411 sessionInfo["build_target_dir"] = json::Value();
412 sessionInfo["has_pkg_src"] = false;
413 sessionInfo["has_pkg_vig"] = false;
414 }
415
416 }
417 else
418 {
419 sessionInfo["build_tools_type"] = r_util::kBuildTypeNone;
420 sessionInfo["build_tools_bookdown_website"] = false;
421 sessionInfo["build_target_dir"] = json::Value();
422 sessionInfo["has_pkg_src"] = false;
423 sessionInfo["has_pkg_vig"] = false;
424 }
425
426 sessionInfo["blogdown_config"] = modules::rmarkdown::blogdown::blogdownConfig();
427 sessionInfo["is_bookdown_project"] = module_context::isBookdownProject();
428 sessionInfo["is_distill_project"] = module_context::isDistillProject();
429
430 sessionInfo["quarto_config"] = quarto::quartoConfigJSON();
431
432 sessionInfo["graphics_backends"] = modules::graphics::supportedBackends();
433
434 sessionInfo["presentation_state"] = modules::presentation::presentationStateAsJson();
435 sessionInfo["presentation_commands"] = options.allowPresentationCommands();
436
437 sessionInfo["tutorial_api_available"] = false;
438 sessionInfo["tutorial_api_client_origin"] = json::Value();
439
440 sessionInfo["build_state"] = modules::build::buildStateAsJson();
441 sessionInfo["devtools_installed"] = module_context::isMinimumDevtoolsInstalled();
442 sessionInfo["have_cairo_pdf"] = modules::plots::haveCairoPdf();
443
444 // console history -- we do this at the end because
445 // restoreBuildRestartContext may have reset it
446 json::Array historyArray;
447 rstudio::r::session::consoleHistory().asJson(&historyArray);
448 sessionInfo["console_history"] = historyArray;
449 sessionInfo["console_history_capacity"] =
450 rstudio::r::session::consoleHistory().capacity();
451
452 sessionInfo["disable_packages"] = module_context::disablePackages();
453
454 sessionInfo["disable_check_for_updates"] =
455 !core::system::getenv("RSTUDIO_DISABLE_CHECK_FOR_UPDATES").empty();
456
457 sessionInfo["allow_vcs_exe_edit"] = options.allowVcsExecutableEdit();
458 sessionInfo["allow_cran_repos_edit"] = options.allowCRANReposEdit();
459 sessionInfo["allow_vcs"] = options.allowVcs();
460 sessionInfo["allow_pkg_install"] = options.allowPackageInstallation();
461 sessionInfo["allow_shell"] = options.allowShell();
462 sessionInfo["allow_terminal_websockets"] = options.allowTerminalWebsockets();
463 sessionInfo["allow_file_download"] = options.allowFileDownloads();
464 sessionInfo["allow_file_upload"] = options.allowFileUploads();
465 sessionInfo["allow_remove_public_folder"] = options.allowRemovePublicFolder();
466 sessionInfo["allow_full_ui"] = options.allowFullUI();
467 sessionInfo["websocket_ping_interval"] = options.webSocketPingInterval();
468 sessionInfo["websocket_connect_timeout"] = options.webSocketConnectTimeout();
469
470 // publishing may be disabled globally or just for external services, and
471 // via configuration options or environment variables
472 bool allowPublish = options.allowPublish() &&
473 core::system::getenv("RSTUDIO_DISABLE_PUBLISH").empty();
474 sessionInfo["allow_publish"] = allowPublish;
475
476 sessionInfo["allow_external_publish"] = options.allowRpubsPublish() &&
477 options.allowExternalPublish() &&
478 core::system::getenv("RSTUDIO_DISABLE_EXTERNAL_PUBLISH").empty() &&
479 allowPublish;
480
481 // allow opening shared projects if it's enabled, and if there's shared
482 // storage from which we can discover the shared projects
483 sessionInfo["allow_open_shared_projects"] =
484 core::system::getenv(kRStudioDisableProjectSharing).empty() &&
485 !options.getOverlayOption(kSessionSharedStoragePath).empty();
486
487 sessionInfo["project_sharing_enumerate_server_users"] = true;
488 sessionInfo["launcher_session"] = false;
489
490 sessionInfo["environment_state"] = modules::environment::environmentStateAsJson();
491 sessionInfo["error_state"] = modules::errors::errorStateAsJson();
492
493 // send whether we should show the user identity
494 sessionInfo["show_identity"] =
495 (options.programMode() == kSessionProgramModeServer) &&
496 options.showUserIdentity();
497
498 sessionInfo["packrat_available"] =
499 module_context::isRequiredPackratInstalled();
500
501 sessionInfo["renv_available"] =
502 module_context::isRequiredRenvInstalled();
503
504 // check rmarkdown package presence and capabilities
505 sessionInfo["rmarkdown_available"] =
506 modules::rmarkdown::rmarkdownPackageAvailable();
507 sessionInfo["knit_params_available"] =
508 modules::rmarkdown::knitParamsAvailable();
509 sessionInfo["knit_working_dir_available"] =
510 modules::rmarkdown::knitWorkingDirAvailable();
511 sessionInfo["ppt_available"] =
512 modules::rmarkdown::pptAvailable();
513
514 sessionInfo["clang_available"] = modules::clang::isAvailable();
515
516 // don't show help home until we figure out a sensible heuristic
517 // sessionInfo["show_help_home"] = options.showHelpHome();
518 sessionInfo["show_help_home"] = false;
519
520 sessionInfo["multi_session"] = options.multiSession();
521
522 json::Object rVersionsJson;
523 rVersionsJson["r_version"] = module_context::rVersion();
524 rVersionsJson["r_version_label"] = module_context::rVersionLabel();
525 rVersionsJson["r_home_dir"] = module_context::rHomeDir();
526 sessionInfo["r_versions_info"] = rVersionsJson;
527
528 sessionInfo["show_user_home_page"] = options.showUserHomePage();
529 sessionInfo["user_home_page_url"] = json::Value();
530
531 sessionInfo["r_addins"] = modules::r_addins::addinRegistryAsJson();
532 sessionInfo["package_provided_extensions"] = modules::ppe::indexer().getPayload();
533
534 sessionInfo["connections_enabled"] = modules::connections::connectionsEnabled();
535 sessionInfo["activate_connections"] = modules::connections::activateConnections();
536 sessionInfo["connection_list"] = modules::connections::connectionsAsJson();
537 sessionInfo["active_connections"] = modules::connections::activeConnectionsAsJson();
538
539 sessionInfo["session_id"] = module_context::activeSession().id();
540 sessionInfo["session_label"] = module_context::activeSession().label();
541
542 sessionInfo["drivers_support_licensing"] = options.supportsDriversLicensing();
543
544 sessionInfo["quit_child_processes_on_exit"] = options.quitChildProcessesOnExit();
545
546 sessionInfo["git_commit_large_file_size"] = options.gitCommitLargeFileSize();
547
548 sessionInfo["default_rsconnect_server"] = options.defaultRSConnectServer();
549
550 sessionInfo["job_state"] = modules::jobs::jobState();
551
552 sessionInfo["launcher_jobs_enabled"] = modules::overlay::launcherJobsFeatureDisplayed();
553
554 json::Object packageDependencies;
555 error = modules::dependency_list::getDependencyList(&packageDependencies);
556 if (error)
557 LOG_ERROR(error);
558 sessionInfo["package_dependencies"] = packageDependencies;
559
560 boost::shared_ptr<modules::system_resources::MemoryUsage> pUsage;
561
562 // Compute memory usage if enabled (the default, but can be disabled)
563 if (prefs::userPrefs().showMemoryUsage())
564 {
565 error = modules::system_resources::getMemoryUsage(&pUsage);
566 if (error)
567 {
568 LOG_ERROR(error);
569 }
570 }
571
572 // Emit memory usage if successfully computed
573 if (pUsage)
574 {
575 sessionInfo["memory_usage"] = pUsage->toJson();
576 }
577
578 // crash handler settings
579 bool canModifyCrashSettings =
580 options.programMode() == kSessionProgramModeDesktop &&
581 crash_handler::configSource() == crash_handler::ConfigSource::User;
582 sessionInfo["crash_handler_settings_modifiable"] = canModifyCrashSettings;
583
584 bool promptForCrashHandlerPermission = canModifyCrashSettings && !crash_handler::hasUserBeenPromptedForPermission();
585 sessionInfo["prompt_for_crash_handler_permission"] = promptForCrashHandlerPermission;
586
587 sessionInfo["project_id"] = session::options().sessionScope().project();
588
589 if (session::options().getBoolOverlayOption(kSessionUserLicenseSoftLimitReached))
590 {
591 sessionInfo["license_message"] =
592 "There are more concurrent users of RStudio Workbench than your license supports. "
593 "Please obtain an updated license to continue using the product.";
594 }
595
596 // session route for load balanced sessions
597 sessionInfo["session_node"] = session::modules::overlay::sessionNode();
598
599 module_context::events().onSessionInfo(&sessionInfo);
600
601 // create response (we always set kEventsPending to false so that the client
602 // won't poll for events until it is ready)
603 json::JsonRpcResponse jsonRpcResponse;
604 jsonRpcResponse.setField(kEventsPending, "false");
605 jsonRpcResponse.setResult(sessionInfo);
606
607 // set response
608 core::http::Response response;
609 core::json::setJsonRpcResponse(jsonRpcResponse, &response);
610
611 #ifdef RSTUDIO_SERVER
612 if (options.programMode() == kSessionProgramModeServer)
613 {
614 Error error = makePortTokenCookie(ptrConnection, response);
615 if (error)
616 {
617 LOG_ERROR(error);
618 }
619 }
620 #endif
621
622 ptrConnection->sendResponse(response);
623
624 // complete initialization of session
625 init::ensureSessionInitialized();
626
627 // notify modules of the client init
628 module_context::events().onClientInit();
629
630 // call the init function
631 initFunction();
632 }
633
634 } // namespace init
635 } // namespace session
636 } // namespace rstudio
637
638