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