You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

786 lines
24 KiB

преди 3 години
  1. /**
  2. * The Settings module reads the settings out of settings.json and provides
  3. * this information to the other modules
  4. *
  5. * TODO muxator 2020-04-14:
  6. *
  7. * 1) get rid of the reloadSettings() call at module loading;
  8. * 2) provide a factory method that configures the settings module at runtime,
  9. * reading the file name either from command line parameters, from a function
  10. * argument, or falling back to a default.
  11. */
  12. /*
  13. * 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
  14. *
  15. * Licensed under the Apache License, Version 2.0 (the "License");
  16. * you may not use this file except in compliance with the License.
  17. * You may obtain a copy of the License at
  18. *
  19. * http://www.apache.org/licenses/LICENSE-2.0
  20. *
  21. * Unless required by applicable law or agreed to in writing, software
  22. * distributed under the License is distributed on an "AS-IS" BASIS,
  23. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  24. * See the License for the specific language governing permissions and
  25. * limitations under the License.
  26. */
  27. const absolutePaths = require('./AbsolutePaths');
  28. const fs = require('fs');
  29. const os = require('os');
  30. const path = require('path');
  31. const argv = require('./Cli').argv;
  32. const npm = require('npm/lib/npm.js');
  33. const jsonminify = require('jsonminify');
  34. const log4js = require('log4js');
  35. const randomString = require('./randomstring');
  36. const suppressDisableMsg = ' -- To suppress these warning messages change suppressErrorsInPadText to true in your settings.json\n';
  37. const _ = require('underscore');
  38. /* Root path of the installation */
  39. exports.root = absolutePaths.findEtherpadRoot();
  40. console.log(`All relative paths will be interpreted relative to the identified Etherpad base dir: ${exports.root}`);
  41. /*
  42. * At each start, Etherpad generates a random string and appends it as query
  43. * parameter to the URLs of the static assets, in order to force their reload.
  44. * Subsequent requests will be cached, as long as the server is not reloaded.
  45. *
  46. * For the rationale behind this choice, see
  47. * https://github.com/ether/etherpad-lite/pull/3958
  48. *
  49. * ACHTUNG: this may prevent caching HTTP proxies to work
  50. * TODO: remove the "?v=randomstring" parameter, and replace with hashed filenames instead
  51. */
  52. exports.randomVersionString = randomString(4);
  53. console.log(`Random string used for versioning assets: ${exports.randomVersionString}`);
  54. /**
  55. * The app title, visible e.g. in the browser window
  56. */
  57. exports.title = 'Etherpad';
  58. /**
  59. * The app favicon fully specified url, visible e.g. in the browser window
  60. */
  61. exports.favicon = 'favicon.ico';
  62. exports.faviconPad = `../${exports.favicon}`;
  63. exports.faviconTimeslider = `../../${exports.favicon}`;
  64. /*
  65. * Skin name.
  66. *
  67. * Initialized to null, so we can spot an old configuration file and invite the
  68. * user to update it before falling back to the default.
  69. */
  70. exports.skinName = null;
  71. exports.skinVariants = 'super-light-toolbar super-light-editor light-background';
  72. /**
  73. * The IP ep-lite should listen to
  74. */
  75. exports.ip = '0.0.0.0';
  76. /**
  77. * The Port ep-lite should listen to
  78. */
  79. exports.port = process.env.PORT || 9001;
  80. /**
  81. * Should we suppress Error messages from being in Pad Contents
  82. */
  83. exports.suppressErrorsInPadText = false;
  84. /**
  85. * The SSL signed server key and the Certificate Authority's own certificate
  86. * default case: ep-lite does *not* use SSL. A signed server key is not required in this case.
  87. */
  88. exports.ssl = false;
  89. /**
  90. * socket.io transport methods
  91. **/
  92. exports.socketTransportProtocols = ['xhr-polling', 'jsonp-polling', 'htmlfile'];
  93. /*
  94. * The Type of the database
  95. */
  96. exports.dbType = 'dirty';
  97. /**
  98. * This setting is passed with dbType to ueberDB to set up the database
  99. */
  100. exports.dbSettings = {filename: path.join(exports.root, 'var/dirty.db')};
  101. /**
  102. * The default Text of a new pad
  103. */
  104. exports.defaultPadText = 'Welcome to Etherpad!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad on Github: https:\/\/github.com\/ether\/etherpad-lite\n';
  105. /**
  106. * The default Pad Settings for a user (Can be overridden by changing the setting
  107. */
  108. exports.padOptions = {
  109. noColors: false,
  110. showControls: true,
  111. showChat: true,
  112. showLineNumbers: true,
  113. useMonospaceFont: false,
  114. userName: false,
  115. userColor: false,
  116. rtl: false,
  117. alwaysShowChat: false,
  118. chatAndUsers: false,
  119. lang: 'en-gb',
  120. },
  121. /**
  122. * Whether certain shortcut keys are enabled for a user in the pad
  123. */
  124. exports.padShortcutEnabled = {
  125. altF9: true,
  126. altC: true,
  127. delete: true,
  128. cmdShift2: true,
  129. return: true,
  130. esc: true,
  131. cmdS: true,
  132. tab: true,
  133. cmdZ: true,
  134. cmdY: true,
  135. cmdB: true,
  136. cmdI: true,
  137. cmdU: true,
  138. cmd5: true,
  139. cmdShiftL: true,
  140. cmdShiftN: true,
  141. cmdShift1: true,
  142. cmdShiftC: true,
  143. cmdH: true,
  144. ctrlHome: true,
  145. pageUp: true,
  146. pageDown: true,
  147. },
  148. /**
  149. * The toolbar buttons and order.
  150. */
  151. exports.toolbar = {
  152. left: [
  153. ['bold', 'italic', 'underline', 'strikethrough'],
  154. ['orderedlist', 'unorderedlist', 'indent', 'outdent'],
  155. ['undo', 'redo'],
  156. ['clearauthorship'],
  157. ],
  158. right: [
  159. ['importexport', 'timeslider', 'savedrevision'],
  160. ['settings', 'embed'],
  161. ['showusers'],
  162. ],
  163. timeslider: [
  164. ['timeslider_export', 'timeslider_settings', 'timeslider_returnToPad'],
  165. ],
  166. };
  167. /**
  168. * A flag that requires any user to have a valid session (via the api) before accessing a pad
  169. */
  170. exports.requireSession = false;
  171. /**
  172. * A flag that prevents users from creating new pads
  173. */
  174. exports.editOnly = false;
  175. /**
  176. * Max age that responses will have (affects caching layer).
  177. */
  178. exports.maxAge = 1000 * 60 * 60 * 6; // 6 hours
  179. /**
  180. * A flag that shows if minification is enabled or not
  181. */
  182. exports.minify = true;
  183. /**
  184. * The path of the abiword executable
  185. */
  186. exports.abiword = null;
  187. /**
  188. * The path of the libreoffice executable
  189. */
  190. exports.soffice = null;
  191. /**
  192. * The path of the tidy executable
  193. */
  194. exports.tidyHtml = null;
  195. /**
  196. * Should we support none natively supported file types on import?
  197. */
  198. exports.allowUnknownFileEnds = true;
  199. /**
  200. * The log level of log4js
  201. */
  202. exports.loglevel = 'INFO';
  203. /**
  204. * Disable IP logging
  205. */
  206. exports.disableIPlogging = false;
  207. /**
  208. * Number of seconds to automatically reconnect pad
  209. */
  210. exports.automaticReconnectionTimeout = 0;
  211. /**
  212. * Disable Load Testing
  213. */
  214. exports.loadTest = false;
  215. /**
  216. * Enable indentation on new lines
  217. */
  218. exports.indentationOnNewLine = true;
  219. /*
  220. * log4js appender configuration
  221. */
  222. exports.logconfig = {appenders: [{type: 'console'}]};
  223. /*
  224. * Session Key, do not sure this.
  225. */
  226. exports.sessionKey = false;
  227. /*
  228. * Trust Proxy, whether or not trust the x-forwarded-for header.
  229. */
  230. exports.trustProxy = false;
  231. /*
  232. * Settings controlling the session cookie issued by Etherpad.
  233. */
  234. exports.cookie = {
  235. /*
  236. * Value of the SameSite cookie property. "Lax" is recommended unless
  237. * Etherpad will be embedded in an iframe from another site, in which case
  238. * this must be set to "None". Note: "None" will not work (the browser will
  239. * not send the cookie to Etherpad) unless https is used to access Etherpad
  240. * (either directly or via a reverse proxy with "trustProxy" set to true).
  241. *
  242. * "Strict" is not recommended because it has few security benefits but
  243. * significant usability drawbacks vs. "Lax". See
  244. * https://stackoverflow.com/q/41841880 for discussion.
  245. */
  246. sameSite: 'Lax',
  247. };
  248. /*
  249. * This setting is used if you need authentication and/or
  250. * authorization. Note: /admin always requires authentication, and
  251. * either authorization by a module, or a user with is_admin set
  252. */
  253. exports.requireAuthentication = false;
  254. exports.requireAuthorization = false;
  255. exports.users = {};
  256. /*
  257. * Show settings in admin page, by default it is true
  258. */
  259. exports.showSettingsInAdminPage = true;
  260. /*
  261. * By default, when caret is moved out of viewport, it scrolls the minimum
  262. * height needed to make this line visible.
  263. */
  264. exports.scrollWhenFocusLineIsOutOfViewport = {
  265. /*
  266. * Percentage of viewport height to be additionally scrolled.
  267. */
  268. percentage: {
  269. editionAboveViewport: 0,
  270. editionBelowViewport: 0,
  271. },
  272. /*
  273. * Time (in milliseconds) used to animate the scroll transition. Set to 0 to
  274. * disable animation
  275. */
  276. duration: 0,
  277. /*
  278. * Percentage of viewport height to be additionally scrolled when user presses arrow up
  279. * in the line of the top of the viewport.
  280. */
  281. percentageToScrollWhenUserPressesArrowUp: 0,
  282. /*
  283. * Flag to control if it should scroll when user places the caret in the last
  284. * line of the viewport
  285. */
  286. scrollWhenCaretIsInTheLastLineOfViewport: false,
  287. };
  288. /*
  289. * Expose Etherpad version in the web interface and in the Server http header.
  290. *
  291. * Do not enable on production machines.
  292. */
  293. exports.exposeVersion = false;
  294. /*
  295. * Override any strings found in locale directories
  296. */
  297. exports.customLocaleStrings = {};
  298. /*
  299. * From Etherpad 1.8.3 onwards, import and export of pads is always rate
  300. * limited.
  301. *
  302. * The default is to allow at most 10 requests per IP in a 90 seconds window.
  303. * After that the import/export request is rejected.
  304. *
  305. * See https://github.com/nfriedly/express-rate-limit for more options
  306. */
  307. exports.importExportRateLimiting = {
  308. // duration of the rate limit window (milliseconds)
  309. windowMs: 90000,
  310. // maximum number of requests per IP to allow during the rate limit window
  311. max: 10,
  312. };
  313. /*
  314. * From Etherpad 1.9.0 onwards, commits from individual users are rate limited
  315. *
  316. * The default is to allow at most 10 changes per IP in a 1 second window.
  317. * After that the change is rejected.
  318. *
  319. * See https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#websocket-single-connection-prevent-flooding for more options
  320. */
  321. exports.commitRateLimiting = {
  322. // duration of the rate limit window (seconds)
  323. duration: 1,
  324. // maximum number of chanes per IP to allow during the rate limit window
  325. points: 10,
  326. };
  327. /*
  328. * From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported
  329. * file is always bounded.
  330. *
  331. * File size is specified in bytes. Default is 50 MB.
  332. */
  333. exports.importMaxFileSize = 50 * 1024 * 1024;
  334. // checks if abiword is avaiable
  335. exports.abiwordAvailable = function () {
  336. if (exports.abiword != null) {
  337. return os.type().indexOf('Windows') != -1 ? 'withoutPDF' : 'yes';
  338. } else {
  339. return 'no';
  340. }
  341. };
  342. exports.sofficeAvailable = function () {
  343. if (exports.soffice != null) {
  344. return os.type().indexOf('Windows') != -1 ? 'withoutPDF' : 'yes';
  345. } else {
  346. return 'no';
  347. }
  348. };
  349. exports.exportAvailable = function () {
  350. const abiword = exports.abiwordAvailable();
  351. const soffice = exports.sofficeAvailable();
  352. if (abiword == 'no' && soffice == 'no') {
  353. return 'no';
  354. } else if ((abiword == 'withoutPDF' && soffice == 'no') || (abiword == 'no' && soffice == 'withoutPDF')) {
  355. return 'withoutPDF';
  356. } else {
  357. return 'yes';
  358. }
  359. };
  360. // Provide git version if available
  361. exports.getGitCommit = function () {
  362. let version = '';
  363. try {
  364. let rootPath = exports.root;
  365. if (fs.lstatSync(`${rootPath}/.git`).isFile()) {
  366. rootPath = fs.readFileSync(`${rootPath}/.git`, 'utf8');
  367. rootPath = rootPath.split(' ').pop().trim();
  368. } else {
  369. rootPath += '/.git';
  370. }
  371. const ref = fs.readFileSync(`${rootPath}/HEAD`, 'utf-8');
  372. if (ref.startsWith('ref: ')) {
  373. const refPath = `${rootPath}/${ref.substring(5, ref.indexOf('\n'))}`;
  374. version = fs.readFileSync(refPath, 'utf-8');
  375. } else {
  376. version = ref;
  377. }
  378. version = version.substring(0, 7);
  379. } catch (e) {
  380. console.warn(`Can't get git version for server header\n${e.message}`);
  381. }
  382. return version;
  383. };
  384. // Return etherpad version from package.json
  385. exports.getEpVersion = function () {
  386. return require('ep_etherpad-lite/package.json').version;
  387. };
  388. /**
  389. * Receives a settingsObj and, if the property name is a valid configuration
  390. * item, stores it in the module's exported properties via a side effect.
  391. *
  392. * This code refactors a previous version that copied & pasted the same code for
  393. * both "settings.json" and "credentials.json".
  394. */
  395. function storeSettings(settingsObj) {
  396. for (const i in settingsObj) {
  397. // test if the setting starts with a lowercase character
  398. if (i.charAt(0).search('[a-z]') !== 0) {
  399. console.warn(`Settings should start with a lowercase character: '${i}'`);
  400. }
  401. // we know this setting, so we overwrite it
  402. // or it's a settings hash, specific to a plugin
  403. if (exports[i] !== undefined || i.indexOf('ep_') == 0) {
  404. if (_.isObject(settingsObj[i]) && !_.isArray(settingsObj[i])) {
  405. exports[i] = _.defaults(settingsObj[i], exports[i]);
  406. } else {
  407. exports[i] = settingsObj[i];
  408. }
  409. } else {
  410. // this setting is unknown, output a warning and throw it away
  411. console.warn(`Unknown Setting: '${i}'. This setting doesn't exist or it was removed`);
  412. }
  413. }
  414. }
  415. /*
  416. * If stringValue is a numeric string, or its value is "true" or "false", coerce
  417. * them to appropriate JS types. Otherwise return stringValue as-is.
  418. *
  419. * Please note that this function is used for converting types for default
  420. * values in the settings file (for example: "${PORT:9001}"), and that there is
  421. * no coercition for "null" values.
  422. *
  423. * If the user wants a variable to be null by default, he'll have to use the
  424. * short syntax "${ABIWORD}", and not "${ABIWORD:null}": the latter would result
  425. * in the literal string "null", instead.
  426. */
  427. function coerceValue(stringValue) {
  428. // cooked from https://stackoverflow.com/questions/175739/built-in-way-in-javascript-to-check-if-a-string-is-a-valid-number
  429. const isNumeric = !isNaN(stringValue) && !isNaN(parseFloat(stringValue) && isFinite(stringValue));
  430. if (isNumeric) {
  431. // detected numeric string. Coerce to a number
  432. return +stringValue;
  433. }
  434. // the boolean literal case is easy.
  435. if (stringValue === 'true') {
  436. return true;
  437. }
  438. if (stringValue === 'false') {
  439. return false;
  440. }
  441. // otherwise, return this value as-is
  442. return stringValue;
  443. }
  444. /**
  445. * Takes a javascript object containing Etherpad's configuration, and returns
  446. * another object, in which all the string properties whose value is of the form
  447. * "${ENV_VAR}" or "${ENV_VAR:default_value}" got their value replaced with the
  448. * contents of the given environment variable, or with a default value.
  449. *
  450. * By definition, an environment variable's value is always a string. However,
  451. * the code base makes use of the various json types. To maintain compatiblity,
  452. * some heuristics is applied:
  453. *
  454. * - if ENV_VAR does not exist in the environment, null is returned;
  455. * - if ENV_VAR's value is "true" or "false", it is converted to the js boolean
  456. * values true or false;
  457. * - if ENV_VAR's value looks like a number, it is converted to a js number
  458. * (details in the code).
  459. *
  460. * The following is a scheme of the behaviour of this function:
  461. *
  462. * +---------------------------+---------------+------------------+
  463. * | Configuration string in | Value of | Resulting confi- |
  464. * | settings.json | ENV_VAR | guration value |
  465. * |---------------------------|---------------|------------------|
  466. * | "${ENV_VAR}" | "some_string" | "some_string" |
  467. * | "${ENV_VAR}" | "9001" | 9001 |
  468. * | "${ENV_VAR}" | undefined | null |
  469. * | "${ENV_VAR:some_default}" | "some_string" | "some_string" |
  470. * | "${ENV_VAR:some_default}" | undefined | "some_default" |
  471. * +---------------------------+---------------+------------------+
  472. *
  473. * IMPLEMENTATION NOTE: variable substitution is performed doing a round trip
  474. * conversion to/from json, using a custom replacer parameter in
  475. * JSON.stringify(), and parsing the JSON back again. This ensures that
  476. * environment variable replacement is performed even on nested objects.
  477. *
  478. * see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter
  479. */
  480. function lookupEnvironmentVariables(obj) {
  481. const stringifiedAndReplaced = JSON.stringify(obj, (key, value) => {
  482. /*
  483. * the first invocation of replacer() is with an empty key. Just go on, or
  484. * we would zap the entire object.
  485. */
  486. if (key === '') {
  487. return value;
  488. }
  489. /*
  490. * If we received from the configuration file a number, a boolean or
  491. * something that is not a string, we can be sure that it was a literal
  492. * value. No need to perform any variable substitution.
  493. *
  494. * The environment variable expansion syntax "${ENV_VAR}" is just a string
  495. * of specific form, after all.
  496. */
  497. if (typeof value !== 'string') {
  498. return value;
  499. }
  500. /*
  501. * Let's check if the string value looks like a variable expansion (e.g.:
  502. * "${ENV_VAR}" or "${ENV_VAR:default_value}")
  503. */
  504. // MUXATOR 2019-03-21: we could use named capture groups here once we migrate to nodejs v10
  505. const match = value.match(/^\$\{([^:]*)(:((.|\n)*))?\}$/);
  506. if (match === null) {
  507. // no match: use the value literally, without any substitution
  508. return value;
  509. }
  510. /*
  511. * We found the name of an environment variable. Let's read its actual value
  512. * and its default value, if given
  513. */
  514. const envVarName = match[1];
  515. const envVarValue = process.env[envVarName];
  516. const defaultValue = match[3];
  517. if ((envVarValue === undefined) && (defaultValue === undefined)) {
  518. console.warn(`Environment variable "${envVarName}" does not contain any value for configuration key "${key}", and no default was given. Returning null.`);
  519. /*
  520. * We have to return null, because if we just returned undefined, the
  521. * configuration item "key" would be stripped from the returned object.
  522. */
  523. return null;
  524. }
  525. if ((envVarValue === undefined) && (defaultValue !== undefined)) {
  526. console.debug(`Environment variable "${envVarName}" not found for configuration key "${key}". Falling back to default value.`);
  527. return coerceValue(defaultValue);
  528. }
  529. // envVarName contained some value.
  530. /*
  531. * For numeric and boolean strings let's convert it to proper types before
  532. * returning it, in order to maintain backward compatibility.
  533. */
  534. console.debug(`Configuration key "${key}" will be read from environment variable "${envVarName}"`);
  535. return coerceValue(envVarValue);
  536. });
  537. const newSettings = JSON.parse(stringifiedAndReplaced);
  538. return newSettings;
  539. }
  540. /**
  541. * - reads the JSON configuration file settingsFilename from disk
  542. * - strips the comments
  543. * - replaces environment variables calling lookupEnvironmentVariables()
  544. * - returns a parsed Javascript object
  545. *
  546. * The isSettings variable only controls the error logging.
  547. */
  548. function parseSettings(settingsFilename, isSettings) {
  549. let settingsStr = '';
  550. let settingsType, notFoundMessage, notFoundFunction;
  551. if (isSettings) {
  552. settingsType = 'settings';
  553. notFoundMessage = 'Continuing using defaults!';
  554. notFoundFunction = console.warn;
  555. } else {
  556. settingsType = 'credentials';
  557. notFoundMessage = 'Ignoring.';
  558. notFoundFunction = console.info;
  559. }
  560. try {
  561. // read the settings file
  562. settingsStr = fs.readFileSync(settingsFilename).toString();
  563. } catch (e) {
  564. notFoundFunction(`No ${settingsType} file found in ${settingsFilename}. ${notFoundMessage}`);
  565. // or maybe undefined!
  566. return null;
  567. }
  568. try {
  569. settingsStr = jsonminify(settingsStr).replace(',]', ']').replace(',}', '}');
  570. const settings = JSON.parse(settingsStr);
  571. console.info(`${settingsType} loaded from: ${settingsFilename}`);
  572. const replacedSettings = lookupEnvironmentVariables(settings);
  573. return replacedSettings;
  574. } catch (e) {
  575. console.error(`There was an error processing your ${settingsType} file from ${settingsFilename}: ${e.message}`);
  576. process.exit(1);
  577. }
  578. }
  579. exports.reloadSettings = function reloadSettings() {
  580. // Discover where the settings file lives
  581. const settingsFilename = absolutePaths.makeAbsolute(argv.settings || 'settings.json');
  582. // Discover if a credential file exists
  583. const credentialsFilename = absolutePaths.makeAbsolute(argv.credentials || 'credentials.json');
  584. // try to parse the settings
  585. const settings = parseSettings(settingsFilename, true);
  586. // try to parse the credentials
  587. const credentials = parseSettings(credentialsFilename, false);
  588. storeSettings(settings);
  589. storeSettings(credentials);
  590. log4js.configure(exports.logconfig);// Configure the logging appenders
  591. log4js.setGlobalLogLevel(exports.loglevel);// set loglevel
  592. process.env.DEBUG = `socket.io:${exports.loglevel}`; // Used by SocketIO for Debug
  593. log4js.replaceConsole();
  594. if (!exports.skinName) {
  595. console.warn('No "skinName" parameter found. Please check out settings.json.template and update your settings.json. Falling back to the default "colibris".');
  596. exports.skinName = 'colibris';
  597. }
  598. // checks if skinName has an acceptable value, otherwise falls back to "colibris"
  599. if (exports.skinName) {
  600. const skinBasePath = path.join(exports.root, 'src', 'static', 'skins');
  601. const countPieces = exports.skinName.split(path.sep).length;
  602. if (countPieces != 1) {
  603. console.error(`skinName must be the name of a directory under "${skinBasePath}". This is not valid: "${exports.skinName}". Falling back to the default "colibris".`);
  604. exports.skinName = 'colibris';
  605. }
  606. // informative variable, just for the log messages
  607. let skinPath = path.normalize(path.join(skinBasePath, exports.skinName));
  608. // what if someone sets skinName == ".." or "."? We catch him!
  609. if (absolutePaths.isSubdir(skinBasePath, skinPath) === false) {
  610. console.error(`Skin path ${skinPath} must be a subdirectory of ${skinBasePath}. Falling back to the default "colibris".`);
  611. exports.skinName = 'colibris';
  612. skinPath = path.join(skinBasePath, exports.skinName);
  613. }
  614. if (fs.existsSync(skinPath) === false) {
  615. console.error(`Skin path ${skinPath} does not exist. Falling back to the default "colibris".`);
  616. exports.skinName = 'colibris';
  617. skinPath = path.join(skinBasePath, exports.skinName);
  618. }
  619. console.info(`Using skin "${exports.skinName}" in dir: ${skinPath}`);
  620. }
  621. if (exports.abiword) {
  622. // Check abiword actually exists
  623. if (exports.abiword != null) {
  624. fs.exists(exports.abiword, (exists) => {
  625. if (!exists) {
  626. const abiwordError = 'Abiword does not exist at this path, check your settings file.';
  627. if (!exports.suppressErrorsInPadText) {
  628. exports.defaultPadText = `${exports.defaultPadText}\nError: ${abiwordError}${suppressDisableMsg}`;
  629. }
  630. console.error(`${abiwordError} File location: ${exports.abiword}`);
  631. exports.abiword = null;
  632. }
  633. });
  634. }
  635. }
  636. if (exports.soffice) {
  637. fs.exists(exports.soffice, (exists) => {
  638. if (!exists) {
  639. const sofficeError = 'soffice (libreoffice) does not exist at this path, check your settings file.';
  640. if (!exports.suppressErrorsInPadText) {
  641. exports.defaultPadText = `${exports.defaultPadText}\nError: ${sofficeError}${suppressDisableMsg}`;
  642. }
  643. console.error(`${sofficeError} File location: ${exports.soffice}`);
  644. exports.soffice = null;
  645. }
  646. });
  647. }
  648. if (!exports.sessionKey) {
  649. const sessionkeyFilename = absolutePaths.makeAbsolute(argv.sessionkey || './SESSIONKEY.txt');
  650. try {
  651. exports.sessionKey = fs.readFileSync(sessionkeyFilename, 'utf8');
  652. console.info(`Session key loaded from: ${sessionkeyFilename}`);
  653. } catch (e) {
  654. console.info(`Session key file "${sessionkeyFilename}" not found. Creating with random contents.`);
  655. exports.sessionKey = randomString(32);
  656. fs.writeFileSync(sessionkeyFilename, exports.sessionKey, 'utf8');
  657. }
  658. } else {
  659. console.warn('Declaring the sessionKey in the settings.json is deprecated. This value is auto-generated now. Please remove the setting from the file. -- If you are seeing this error after restarting using the Admin User Interface then you can ignore this message.');
  660. }
  661. if (exports.dbType === 'dirty') {
  662. const dirtyWarning = 'DirtyDB is used. This is fine for testing but not recommended for production.';
  663. if (!exports.suppressErrorsInPadText) {
  664. exports.defaultPadText = `${exports.defaultPadText}\nWarning: ${dirtyWarning}${suppressDisableMsg}`;
  665. }
  666. exports.dbSettings.filename = absolutePaths.makeAbsolute(exports.dbSettings.filename);
  667. console.warn(`${dirtyWarning} File location: ${exports.dbSettings.filename}`);
  668. }
  669. if (exports.ip === '') {
  670. // using Unix socket for connectivity
  671. console.warn('The settings file contains an empty string ("") for the "ip" parameter. The "port" parameter will be interpreted as the path to a Unix socket to bind at.');
  672. }
  673. };
  674. // initially load settings
  675. exports.reloadSettings();