2021-01-21 21:06:52 +00:00
|
|
|
'use strict';
|
2011-05-14 17:42:04 +01:00
|
|
|
/**
|
2020-04-14 01:10:19 +02:00
|
|
|
* The Settings module reads the settings out of settings.json and provides
|
2011-05-30 15:53:11 +01:00
|
|
|
* this information to the other modules
|
2020-04-14 01:10:19 +02:00
|
|
|
*
|
|
|
|
* TODO muxator 2020-04-14:
|
|
|
|
*
|
|
|
|
* 1) get rid of the reloadSettings() call at module loading;
|
|
|
|
* 2) provide a factory method that configures the settings module at runtime,
|
|
|
|
* reading the file name either from command line parameters, from a function
|
|
|
|
* argument, or falling back to a default.
|
2011-05-30 15:53:11 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2011-08-11 15:26:41 +01:00
|
|
|
* 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
2011-05-14 17:42:04 +01:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
const absolutePaths = require('./AbsolutePaths');
|
2021-09-16 00:24:40 -04:00
|
|
|
const deepEqual = require('fast-deep-equal/es6');
|
2020-11-23 13:24:19 -05:00
|
|
|
const fs = require('fs');
|
|
|
|
const os = require('os');
|
|
|
|
const path = require('path');
|
|
|
|
const argv = require('./Cli').argv;
|
|
|
|
const jsonminify = require('jsonminify');
|
|
|
|
const log4js = require('log4js');
|
|
|
|
const randomString = require('./randomstring');
|
2021-02-21 20:28:19 +00:00
|
|
|
const suppressDisableMsg = ' -- To suppress these warning messages change ' +
|
|
|
|
'suppressErrorsInPadText to true in your settings.json\n';
|
2020-11-23 13:24:19 -05:00
|
|
|
const _ = require('underscore');
|
2012-02-26 13:07:51 +01:00
|
|
|
|
2021-09-15 23:59:17 -04:00
|
|
|
const logger = log4js.getLogger('settings');
|
|
|
|
|
2021-11-10 18:41:53 -05:00
|
|
|
// Exported values that settings.json and credentials.json cannot override.
|
|
|
|
const nonSettings = [
|
|
|
|
'credentialsFilename',
|
|
|
|
'settingsFilename',
|
|
|
|
];
|
|
|
|
|
2021-09-23 03:05:42 -04:00
|
|
|
// This is a function to make it easy to create a new instance. It is important to not reuse a
|
|
|
|
// config object after passing it to log4js.configure() because that method mutates the object. :(
|
2023-10-22 18:26:58 +02:00
|
|
|
const defaultLogConfig = () => ({appenders: {console: {type: 'console'}},
|
|
|
|
categories: {
|
|
|
|
default: {appenders: ['console'], level: 'info'},
|
|
|
|
}});
|
2021-09-23 03:05:42 -04:00
|
|
|
const defaultLogLevel = 'INFO';
|
|
|
|
|
|
|
|
const initLogging = (logLevel, config) => {
|
2021-09-16 00:24:40 -04:00
|
|
|
// log4js.configure() modifies exports.logconfig so check for equality first.
|
|
|
|
const logConfigIsDefault = deepEqual(config, defaultLogConfig());
|
2021-09-23 03:05:42 -04:00
|
|
|
log4js.configure(config);
|
2023-10-22 18:26:58 +02:00
|
|
|
log4js.getLogger('console');
|
|
|
|
|
|
|
|
// Overwrites for console output methods
|
|
|
|
console.debug = logger.debug.bind(logger);
|
|
|
|
console.log = logger.info.bind(logger);
|
|
|
|
console.warn = logger.warn.bind(logger);
|
|
|
|
console.error = logger.error.bind(logger);
|
|
|
|
|
2021-09-16 00:24:40 -04:00
|
|
|
// Log the warning after configuring log4js to increase the chances the user will see it.
|
|
|
|
if (!logConfigIsDefault) logger.warn('The logconfig setting is deprecated.');
|
2021-09-23 03:05:42 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// Initialize logging as early as possible with reasonable defaults. Logging will be re-initialized
|
|
|
|
// with the user's chosen log level and logger config after the settings have been loaded.
|
|
|
|
initLogging(defaultLogLevel, defaultLogConfig());
|
|
|
|
|
2012-02-26 13:07:51 +01:00
|
|
|
/* Root path of the installation */
|
2018-08-22 00:47:47 +02:00
|
|
|
exports.root = absolutePaths.findEtherpadRoot();
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.info('All relative paths will be interpreted relative to the identified ' +
|
2021-02-21 20:28:19 +00:00
|
|
|
`Etherpad base dir: ${exports.root}`);
|
2021-11-10 18:41:53 -05:00
|
|
|
exports.settingsFilename = absolutePaths.makeAbsolute(argv.settings || 'settings.json');
|
|
|
|
exports.credentialsFilename = absolutePaths.makeAbsolute(argv.credentials || 'credentials.json');
|
2011-05-14 17:22:25 +01:00
|
|
|
|
2012-11-02 12:30:57 +01:00
|
|
|
/**
|
|
|
|
* The app title, visible e.g. in the browser window
|
|
|
|
*/
|
2020-11-23 13:24:19 -05:00
|
|
|
exports.title = 'Etherpad';
|
2012-11-02 12:30:57 +01:00
|
|
|
|
2012-11-04 11:26:17 +01:00
|
|
|
/**
|
2021-04-20 00:53:22 -04:00
|
|
|
* Pathname of the favicon you want to use. If null, the skin's favicon is
|
|
|
|
* used if one is provided by the skin, otherwise the default Etherpad favicon
|
|
|
|
* is used. If this is a relative path it is interpreted as relative to the
|
|
|
|
* Etherpad root directory.
|
2012-11-04 11:26:17 +01:00
|
|
|
*/
|
2021-04-20 00:53:22 -04:00
|
|
|
exports.favicon = null;
|
2012-11-04 11:26:17 +01:00
|
|
|
|
2018-08-19 03:36:18 +02:00
|
|
|
/*
|
|
|
|
* Skin name.
|
|
|
|
*
|
|
|
|
* Initialized to null, so we can spot an old configuration file and invite the
|
|
|
|
* user to update it before falling back to the default.
|
|
|
|
*/
|
|
|
|
exports.skinName = null;
|
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
exports.skinVariants = 'super-light-toolbar super-light-editor light-background';
|
2020-04-07 17:33:40 +02:00
|
|
|
|
2011-07-30 16:39:53 +01:00
|
|
|
/**
|
|
|
|
* The IP ep-lite should listen to
|
|
|
|
*/
|
2020-11-23 13:24:19 -05:00
|
|
|
exports.ip = '0.0.0.0';
|
2013-11-26 10:20:59 +02:00
|
|
|
|
2011-05-30 15:53:11 +01:00
|
|
|
/**
|
|
|
|
* The Port ep-lite should listen to
|
|
|
|
*/
|
2012-10-25 10:21:34 -07:00
|
|
|
exports.port = process.env.PORT || 9001;
|
2012-11-22 10:12:58 +01:00
|
|
|
|
2015-04-06 00:13:38 +01:00
|
|
|
/**
|
|
|
|
* Should we suppress Error messages from being in Pad Contents
|
|
|
|
*/
|
|
|
|
exports.suppressErrorsInPadText = false;
|
|
|
|
|
2012-11-22 10:12:58 +01:00
|
|
|
/**
|
|
|
|
* The SSL signed server key and the Certificate Authority's own certificate
|
|
|
|
* default case: ep-lite does *not* use SSL. A signed server key is not required in this case.
|
|
|
|
*/
|
|
|
|
exports.ssl = false;
|
|
|
|
|
2012-12-02 18:28:28 +01:00
|
|
|
/**
|
2012-12-02 18:44:39 +01:00
|
|
|
* socket.io transport methods
|
2012-12-02 18:28:28 +01:00
|
|
|
**/
|
|
|
|
exports.socketTransportProtocols = ['xhr-polling', 'jsonp-polling', 'htmlfile'];
|
|
|
|
|
2021-02-14 19:04:29 +00:00
|
|
|
exports.socketIo = {
|
|
|
|
/**
|
|
|
|
* Maximum permitted client message size (in bytes).
|
|
|
|
*
|
|
|
|
* All messages from clients that are larger than this will be rejected. Large values make it
|
|
|
|
* possible to paste large amounts of text, and plugins may require a larger value to work
|
|
|
|
* properly, but increasing the value increases susceptibility to denial of service attacks
|
|
|
|
* (malicious clients can exhaust memory).
|
|
|
|
*/
|
|
|
|
maxHttpBufferSize: 10000,
|
|
|
|
};
|
|
|
|
|
2011-05-30 15:53:11 +01:00
|
|
|
/*
|
|
|
|
* The Type of the database
|
|
|
|
*/
|
2020-11-23 13:24:19 -05:00
|
|
|
exports.dbType = 'dirty';
|
2011-05-30 15:53:11 +01:00
|
|
|
/**
|
|
|
|
* This setting is passed with dbType to ueberDB to set up the database
|
|
|
|
*/
|
2020-11-23 13:24:19 -05:00
|
|
|
exports.dbSettings = {filename: path.join(exports.root, 'var/dirty.db')};
|
2012-07-08 18:59:46 +02:00
|
|
|
|
2011-05-30 15:53:11 +01:00
|
|
|
/**
|
|
|
|
* The default Text of a new pad
|
|
|
|
*/
|
2021-02-21 20:28:19 +00:00
|
|
|
exports.defaultPadText = [
|
|
|
|
'Welcome to Etherpad!',
|
|
|
|
'',
|
|
|
|
'This 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!',
|
|
|
|
'',
|
|
|
|
'Etherpad on Github: https://github.com/ether/etherpad-lite',
|
|
|
|
].join('\n');
|
2011-11-21 01:45:37 -05:00
|
|
|
|
2015-04-11 21:22:00 +01:00
|
|
|
/**
|
|
|
|
* The default Pad Settings for a user (Can be overridden by changing the setting
|
|
|
|
*/
|
|
|
|
exports.padOptions = {
|
2020-11-23 13:24:19 -05:00
|
|
|
noColors: false,
|
|
|
|
showControls: true,
|
|
|
|
showChat: true,
|
|
|
|
showLineNumbers: true,
|
|
|
|
useMonospaceFont: false,
|
2021-12-04 21:36:02 -05:00
|
|
|
userName: null,
|
|
|
|
userColor: null,
|
2020-11-23 13:24:19 -05:00
|
|
|
rtl: false,
|
|
|
|
alwaysShowChat: false,
|
|
|
|
chatAndUsers: false,
|
2021-12-04 21:33:38 -05:00
|
|
|
lang: null,
|
2021-05-11 16:56:06 -04:00
|
|
|
};
|
2016-01-21 07:38:41 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether certain shortcut keys are enabled for a user in the pad
|
|
|
|
*/
|
|
|
|
exports.padShortcutEnabled = {
|
2020-11-23 13:24:19 -05:00
|
|
|
altF9: true,
|
|
|
|
altC: true,
|
|
|
|
delete: true,
|
|
|
|
cmdShift2: true,
|
|
|
|
return: true,
|
|
|
|
esc: true,
|
|
|
|
cmdS: true,
|
|
|
|
tab: true,
|
|
|
|
cmdZ: true,
|
|
|
|
cmdY: true,
|
|
|
|
cmdB: true,
|
|
|
|
cmdI: true,
|
|
|
|
cmdU: true,
|
|
|
|
cmd5: true,
|
|
|
|
cmdShiftL: true,
|
|
|
|
cmdShiftN: true,
|
|
|
|
cmdShift1: true,
|
|
|
|
cmdShiftC: true,
|
|
|
|
cmdH: true,
|
|
|
|
ctrlHome: true,
|
|
|
|
pageUp: true,
|
|
|
|
pageDown: true,
|
2021-05-11 16:56:06 -04:00
|
|
|
};
|
2015-04-11 21:22:00 +01:00
|
|
|
|
2013-03-09 14:57:42 -08:00
|
|
|
/**
|
|
|
|
* The toolbar buttons and order.
|
|
|
|
*/
|
|
|
|
exports.toolbar = {
|
|
|
|
left: [
|
2020-11-23 13:24:19 -05:00
|
|
|
['bold', 'italic', 'underline', 'strikethrough'],
|
|
|
|
['orderedlist', 'unorderedlist', 'indent', 'outdent'],
|
|
|
|
['undo', 'redo'],
|
|
|
|
['clearauthorship'],
|
2013-03-09 14:57:42 -08:00
|
|
|
],
|
|
|
|
right: [
|
2020-11-23 13:24:19 -05:00
|
|
|
['importexport', 'timeslider', 'savedrevision'],
|
|
|
|
['settings', 'embed'],
|
|
|
|
['showusers'],
|
2014-03-30 13:02:41 +02:00
|
|
|
],
|
|
|
|
timeslider: [
|
2020-11-23 13:24:19 -05:00
|
|
|
['timeslider_export', 'timeslider_settings', 'timeslider_returnToPad'],
|
|
|
|
],
|
|
|
|
};
|
2013-03-09 14:57:42 -08:00
|
|
|
|
2011-11-21 01:45:37 -05:00
|
|
|
/**
|
|
|
|
* A flag that requires any user to have a valid session (via the api) before accessing a pad
|
|
|
|
*/
|
|
|
|
exports.requireSession = false;
|
|
|
|
|
2011-11-21 12:44:33 -05:00
|
|
|
/**
|
|
|
|
* A flag that prevents users from creating new pads
|
|
|
|
*/
|
|
|
|
exports.editOnly = false;
|
|
|
|
|
2012-02-06 23:04:02 -08:00
|
|
|
/**
|
|
|
|
* Max age that responses will have (affects caching layer).
|
|
|
|
*/
|
2020-11-23 13:24:19 -05:00
|
|
|
exports.maxAge = 1000 * 60 * 60 * 6; // 6 hours
|
2012-02-06 23:04:02 -08:00
|
|
|
|
2011-05-30 15:53:11 +01:00
|
|
|
/**
|
|
|
|
* A flag that shows if minification is enabled or not
|
|
|
|
*/
|
2011-05-28 18:09:17 +01:00
|
|
|
exports.minify = true;
|
2011-05-14 17:22:25 +01:00
|
|
|
|
2011-07-19 19:48:11 +01:00
|
|
|
/**
|
|
|
|
* The path of the abiword executable
|
|
|
|
*/
|
|
|
|
exports.abiword = null;
|
|
|
|
|
2015-10-20 19:46:08 +01:00
|
|
|
/**
|
|
|
|
* The path of the libreoffice executable
|
|
|
|
*/
|
|
|
|
exports.soffice = null;
|
|
|
|
|
2014-07-22 15:46:31 +01:00
|
|
|
/**
|
|
|
|
* Should we support none natively supported file types on import?
|
|
|
|
*/
|
|
|
|
exports.allowUnknownFileEnds = true;
|
|
|
|
|
2011-08-17 17:45:47 +01:00
|
|
|
/**
|
|
|
|
* The log level of log4js
|
|
|
|
*/
|
2021-09-23 03:05:42 -04:00
|
|
|
exports.loglevel = defaultLogLevel;
|
2011-08-17 17:45:47 +01:00
|
|
|
|
2013-10-19 21:37:11 +02:00
|
|
|
/**
|
|
|
|
* Disable IP logging
|
|
|
|
*/
|
|
|
|
exports.disableIPlogging = false;
|
|
|
|
|
2017-04-04 11:09:24 -03:00
|
|
|
/**
|
|
|
|
* Number of seconds to automatically reconnect pad
|
|
|
|
*/
|
|
|
|
exports.automaticReconnectionTimeout = 0;
|
|
|
|
|
2015-05-18 16:24:41 +01:00
|
|
|
/**
|
2015-02-16 23:02:19 +00:00
|
|
|
* Disable Load Testing
|
|
|
|
*/
|
|
|
|
exports.loadTest = false;
|
|
|
|
|
2021-04-13 10:10:56 +02:00
|
|
|
/**
|
|
|
|
* Disable dump of objects preventing a clean exit
|
|
|
|
*/
|
|
|
|
exports.dumpOnUncleanExit = false;
|
|
|
|
|
2015-10-13 18:39:23 -03:00
|
|
|
/**
|
|
|
|
* Enable indentation on new lines
|
|
|
|
*/
|
|
|
|
exports.indentationOnNewLine = true;
|
|
|
|
|
2013-01-13 12:20:49 +01:00
|
|
|
/*
|
2019-04-16 00:17:56 +02:00
|
|
|
* log4js appender configuration
|
|
|
|
*/
|
2021-09-23 03:05:42 -04:00
|
|
|
exports.logconfig = defaultLogConfig();
|
2013-01-13 12:20:49 +01:00
|
|
|
|
2013-02-13 21:51:09 +00:00
|
|
|
/*
|
2023-07-03 16:58:49 -04:00
|
|
|
* Deprecated cookie signing key.
|
2019-04-16 00:17:56 +02:00
|
|
|
*/
|
2023-07-03 16:58:49 -04:00
|
|
|
exports.sessionKey = null;
|
2013-02-13 21:51:09 +00:00
|
|
|
|
2013-04-24 12:19:41 +02:00
|
|
|
/*
|
2019-04-16 00:17:56 +02:00
|
|
|
* Trust Proxy, whether or not trust the x-forwarded-for header.
|
|
|
|
*/
|
2013-04-24 12:19:41 +02:00
|
|
|
exports.trustProxy = false;
|
|
|
|
|
2020-10-02 23:53:05 -04:00
|
|
|
/*
|
|
|
|
* Settings controlling the session cookie issued by Etherpad.
|
|
|
|
*/
|
|
|
|
exports.cookie = {
|
2023-07-03 16:58:49 -04:00
|
|
|
keyRotationInterval: 1 * 24 * 60 * 60 * 1000,
|
2020-10-02 23:53:05 -04:00
|
|
|
/*
|
|
|
|
* Value of the SameSite cookie property. "Lax" is recommended unless
|
|
|
|
* Etherpad will be embedded in an iframe from another site, in which case
|
|
|
|
* this must be set to "None". Note: "None" will not work (the browser will
|
|
|
|
* not send the cookie to Etherpad) unless https is used to access Etherpad
|
|
|
|
* (either directly or via a reverse proxy with "trustProxy" set to true).
|
|
|
|
*
|
|
|
|
* "Strict" is not recommended because it has few security benefits but
|
|
|
|
* significant usability drawbacks vs. "Lax". See
|
|
|
|
* https://stackoverflow.com/q/41841880 for discussion.
|
|
|
|
*/
|
|
|
|
sameSite: 'Lax',
|
2021-12-22 23:42:19 -05:00
|
|
|
sessionLifetime: 10 * 24 * 60 * 60 * 1000,
|
2021-12-23 01:45:38 -05:00
|
|
|
sessionRefreshInterval: 1 * 24 * 60 * 60 * 1000,
|
2020-10-02 23:53:05 -04:00
|
|
|
};
|
|
|
|
|
2019-04-16 00:17:56 +02:00
|
|
|
/*
|
|
|
|
* This setting is used if you need authentication and/or
|
2012-04-19 14:25:12 +02:00
|
|
|
* authorization. Note: /admin always requires authentication, and
|
2019-04-16 00:17:56 +02:00
|
|
|
* either authorization by a module, or a user with is_admin set
|
|
|
|
*/
|
2012-04-19 14:25:12 +02:00
|
|
|
exports.requireAuthentication = false;
|
|
|
|
exports.requireAuthorization = false;
|
|
|
|
exports.users = {};
|
2012-04-02 18:45:37 +02:00
|
|
|
|
2016-05-22 20:58:51 +05:30
|
|
|
/*
|
2019-04-16 00:17:56 +02:00
|
|
|
* Show settings in admin page, by default it is true
|
|
|
|
*/
|
2016-05-22 20:58:51 +05:30
|
|
|
exports.showSettingsInAdminPage = true;
|
|
|
|
|
2018-01-03 18:57:28 -03:00
|
|
|
/*
|
2019-04-16 00:17:56 +02:00
|
|
|
* By default, when caret is moved out of viewport, it scrolls the minimum
|
|
|
|
* height needed to make this line visible.
|
|
|
|
*/
|
2018-01-03 18:57:28 -03:00
|
|
|
exports.scrollWhenFocusLineIsOutOfViewport = {
|
|
|
|
/*
|
2019-04-16 00:17:56 +02:00
|
|
|
* Percentage of viewport height to be additionally scrolled.
|
|
|
|
*/
|
2020-11-23 13:24:19 -05:00
|
|
|
percentage: {
|
|
|
|
editionAboveViewport: 0,
|
|
|
|
editionBelowViewport: 0,
|
2018-01-03 18:57:28 -03:00
|
|
|
},
|
2019-04-16 00:17:56 +02:00
|
|
|
|
2018-01-03 18:57:28 -03:00
|
|
|
/*
|
2019-04-16 00:17:56 +02:00
|
|
|
* Time (in milliseconds) used to animate the scroll transition. Set to 0 to
|
|
|
|
* disable animation
|
|
|
|
*/
|
2020-11-23 13:24:19 -05:00
|
|
|
duration: 0,
|
2019-04-16 00:17:56 +02:00
|
|
|
|
2018-01-03 18:57:28 -03:00
|
|
|
/*
|
2019-04-16 00:17:56 +02:00
|
|
|
* Percentage of viewport height to be additionally scrolled when user presses arrow up
|
|
|
|
* in the line of the top of the viewport.
|
2018-01-03 18:57:28 -03:00
|
|
|
*/
|
2020-11-23 13:24:19 -05:00
|
|
|
percentageToScrollWhenUserPressesArrowUp: 0,
|
2019-04-16 00:17:56 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Flag to control if it should scroll when user places the caret in the last
|
|
|
|
* line of the viewport
|
|
|
|
*/
|
2020-11-23 13:24:19 -05:00
|
|
|
scrollWhenCaretIsInTheLastLineOfViewport: false,
|
2018-01-03 18:57:28 -03:00
|
|
|
};
|
|
|
|
|
2019-04-15 16:02:46 +02:00
|
|
|
/*
|
2019-04-15 17:03:06 +02:00
|
|
|
* Expose Etherpad version in the web interface and in the Server http header.
|
2019-04-15 16:02:46 +02:00
|
|
|
*
|
|
|
|
* Do not enable on production machines.
|
|
|
|
*/
|
|
|
|
exports.exposeVersion = false;
|
|
|
|
|
2020-05-19 08:21:31 -04:00
|
|
|
/*
|
|
|
|
* Override any strings found in locale directories
|
|
|
|
*/
|
|
|
|
exports.customLocaleStrings = {};
|
|
|
|
|
2020-04-04 20:39:33 +00:00
|
|
|
/*
|
|
|
|
* From Etherpad 1.8.3 onwards, import and export of pads is always rate
|
|
|
|
* limited.
|
|
|
|
*
|
|
|
|
* The default is to allow at most 10 requests per IP in a 90 seconds window.
|
|
|
|
* After that the import/export request is rejected.
|
|
|
|
*
|
|
|
|
* See https://github.com/nfriedly/express-rate-limit for more options
|
|
|
|
*/
|
|
|
|
exports.importExportRateLimiting = {
|
|
|
|
// duration of the rate limit window (milliseconds)
|
2020-11-23 13:24:19 -05:00
|
|
|
windowMs: 90000,
|
2020-04-04 20:39:33 +00:00
|
|
|
|
|
|
|
// maximum number of requests per IP to allow during the rate limit window
|
2020-11-23 13:24:19 -05:00
|
|
|
max: 10,
|
2020-04-04 20:39:33 +00:00
|
|
|
};
|
|
|
|
|
2020-07-19 22:44:24 +01:00
|
|
|
/*
|
|
|
|
* From Etherpad 1.9.0 onwards, commits from individual users are rate limited
|
|
|
|
*
|
|
|
|
* The default is to allow at most 10 changes per IP in a 1 second window.
|
|
|
|
* After that the change is rejected.
|
|
|
|
*
|
|
|
|
* See https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#websocket-single-connection-prevent-flooding for more options
|
|
|
|
*/
|
|
|
|
exports.commitRateLimiting = {
|
|
|
|
// duration of the rate limit window (seconds)
|
2020-11-23 13:24:19 -05:00
|
|
|
duration: 1,
|
2020-07-19 22:44:24 +01:00
|
|
|
|
|
|
|
// maximum number of chanes per IP to allow during the rate limit window
|
2020-11-23 13:24:19 -05:00
|
|
|
points: 10,
|
2020-07-19 22:44:24 +01:00
|
|
|
};
|
|
|
|
|
2020-04-07 01:07:10 +02:00
|
|
|
/*
|
|
|
|
* From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported
|
|
|
|
* file is always bounded.
|
|
|
|
*
|
|
|
|
* File size is specified in bytes. Default is 50 MB.
|
|
|
|
*/
|
|
|
|
exports.importMaxFileSize = 50 * 1024 * 1024;
|
|
|
|
|
2021-02-07 11:32:57 +00:00
|
|
|
/*
|
2021-02-13 00:31:36 -05:00
|
|
|
* Disable Admin UI tests
|
2021-02-07 11:32:57 +00:00
|
|
|
*/
|
|
|
|
exports.enableAdminUITests = false;
|
|
|
|
|
2023-07-03 20:52:49 +02:00
|
|
|
/*
|
|
|
|
* Enable auto conversion of pad Ids to lowercase.
|
|
|
|
* e.g. /p/EtHeRpAd to /p/etherpad
|
|
|
|
*/
|
|
|
|
exports.lowerCasePadIds = false;
|
2021-02-07 11:32:57 +00:00
|
|
|
|
2019-04-16 00:17:56 +02:00
|
|
|
// checks if abiword is avaiable
|
2021-01-21 21:06:52 +00:00
|
|
|
exports.abiwordAvailable = () => {
|
2019-03-10 01:46:55 +01:00
|
|
|
if (exports.abiword != null) {
|
2021-01-21 21:06:52 +00:00
|
|
|
return os.type().indexOf('Windows') !== -1 ? 'withoutPDF' : 'yes';
|
2019-03-10 01:46:55 +01:00
|
|
|
} else {
|
2020-11-23 13:24:19 -05:00
|
|
|
return 'no';
|
2011-12-18 00:18:35 -05:00
|
|
|
}
|
2013-03-24 01:18:44 +01:00
|
|
|
};
|
2011-12-18 00:18:35 -05:00
|
|
|
|
2021-01-21 21:06:52 +00:00
|
|
|
exports.sofficeAvailable = () => {
|
2019-03-10 01:46:55 +01:00
|
|
|
if (exports.soffice != null) {
|
2021-01-21 21:06:52 +00:00
|
|
|
return os.type().indexOf('Windows') !== -1 ? 'withoutPDF' : 'yes';
|
2015-12-17 21:54:04 -06:00
|
|
|
} else {
|
2020-11-23 13:24:19 -05:00
|
|
|
return 'no';
|
2015-12-17 21:54:04 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-01-21 21:06:52 +00:00
|
|
|
exports.exportAvailable = () => {
|
2020-11-23 13:24:19 -05:00
|
|
|
const abiword = exports.abiwordAvailable();
|
|
|
|
const soffice = exports.sofficeAvailable();
|
2015-12-17 21:54:04 -06:00
|
|
|
|
2021-01-21 21:06:52 +00:00
|
|
|
if (abiword === 'no' && soffice === 'no') {
|
2020-11-23 13:24:19 -05:00
|
|
|
return 'no';
|
2021-01-21 21:06:52 +00:00
|
|
|
} else if ((abiword === 'withoutPDF' && soffice === 'no') ||
|
|
|
|
(abiword === 'no' && soffice === 'withoutPDF')) {
|
2020-11-23 13:24:19 -05:00
|
|
|
return 'withoutPDF';
|
2015-12-17 21:54:04 -06:00
|
|
|
} else {
|
2020-11-23 13:24:19 -05:00
|
|
|
return 'yes';
|
2015-12-17 21:54:04 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-02-11 17:59:05 +00:00
|
|
|
// Provide git version if available
|
2021-01-21 21:06:52 +00:00
|
|
|
exports.getGitCommit = () => {
|
2020-11-23 13:24:19 -05:00
|
|
|
let version = '';
|
2019-03-10 01:46:55 +01:00
|
|
|
try {
|
2020-11-23 13:24:19 -05:00
|
|
|
let rootPath = exports.root;
|
|
|
|
if (fs.lstatSync(`${rootPath}/.git`).isFile()) {
|
|
|
|
rootPath = fs.readFileSync(`${rootPath}/.git`, 'utf8');
|
2015-10-07 15:43:29 +02:00
|
|
|
rootPath = rootPath.split(' ').pop().trim();
|
|
|
|
} else {
|
|
|
|
rootPath += '/.git';
|
|
|
|
}
|
2020-11-23 13:24:19 -05:00
|
|
|
const ref = fs.readFileSync(`${rootPath}/HEAD`, 'utf-8');
|
|
|
|
if (ref.startsWith('ref: ')) {
|
|
|
|
const refPath = `${rootPath}/${ref.substring(5, ref.indexOf('\n'))}`;
|
|
|
|
version = fs.readFileSync(refPath, 'utf-8');
|
2020-10-22 15:05:12 -07:00
|
|
|
} else {
|
|
|
|
version = ref;
|
|
|
|
}
|
2015-02-11 17:59:05 +00:00
|
|
|
version = version.substring(0, 7);
|
2020-11-23 13:24:19 -05:00
|
|
|
} catch (e) {
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.warn(`Can't get git version for server header\n${e.message}`);
|
2015-02-11 17:59:05 +00:00
|
|
|
}
|
|
|
|
return version;
|
2020-11-23 13:24:19 -05:00
|
|
|
};
|
2015-02-11 17:59:05 +00:00
|
|
|
|
2015-04-11 00:13:04 +02:00
|
|
|
// Return etherpad version from package.json
|
2021-01-21 21:06:52 +00:00
|
|
|
exports.getEpVersion = () => require('../../package.json').version;
|
2015-04-11 00:13:04 +02:00
|
|
|
|
2019-03-09 10:06:51 +01:00
|
|
|
/**
|
|
|
|
* Receives a settingsObj and, if the property name is a valid configuration
|
|
|
|
* item, stores it in the module's exported properties via a side effect.
|
|
|
|
*
|
|
|
|
* This code refactors a previous version that copied & pasted the same code for
|
|
|
|
* both "settings.json" and "credentials.json".
|
|
|
|
*/
|
2021-01-21 21:06:52 +00:00
|
|
|
const storeSettings = (settingsObj) => {
|
2021-05-11 16:56:06 -04:00
|
|
|
for (const i of Object.keys(settingsObj || {})) {
|
2021-11-10 18:41:53 -05:00
|
|
|
if (nonSettings.includes(i)) {
|
|
|
|
logger.warn(`Ignoring setting: '${i}'`);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-03-09 10:06:51 +01:00
|
|
|
// test if the setting starts with a lowercase character
|
2020-11-23 13:24:19 -05:00
|
|
|
if (i.charAt(0).search('[a-z]') !== 0) {
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.warn(`Settings should start with a lowercase character: '${i}'`);
|
2019-03-09 10:06:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// we know this setting, so we overwrite it
|
|
|
|
// or it's a settings hash, specific to a plugin
|
2021-01-21 21:06:52 +00:00
|
|
|
if (exports[i] !== undefined || i.indexOf('ep_') === 0) {
|
2021-02-21 20:28:19 +00:00
|
|
|
if (_.isObject(settingsObj[i]) && !Array.isArray(settingsObj[i])) {
|
2019-03-09 10:06:51 +01:00
|
|
|
exports[i] = _.defaults(settingsObj[i], exports[i]);
|
|
|
|
} else {
|
|
|
|
exports[i] = settingsObj[i];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// this setting is unknown, output a warning and throw it away
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.warn(`Unknown Setting: '${i}'. This setting doesn't exist or it was removed`);
|
2019-03-09 10:06:51 +01:00
|
|
|
}
|
|
|
|
}
|
2021-01-21 21:06:52 +00:00
|
|
|
};
|
2019-03-09 10:06:51 +01:00
|
|
|
|
2019-03-21 21:32:39 +01:00
|
|
|
/*
|
|
|
|
* If stringValue is a numeric string, or its value is "true" or "false", coerce
|
|
|
|
* them to appropriate JS types. Otherwise return stringValue as-is.
|
2020-04-21 03:40:49 +02:00
|
|
|
*
|
|
|
|
* Please note that this function is used for converting types for default
|
|
|
|
* values in the settings file (for example: "${PORT:9001}"), and that there is
|
|
|
|
* no coercition for "null" values.
|
|
|
|
*
|
|
|
|
* If the user wants a variable to be null by default, he'll have to use the
|
|
|
|
* short syntax "${ABIWORD}", and not "${ABIWORD:null}": the latter would result
|
|
|
|
* in the literal string "null", instead.
|
2019-03-21 21:32:39 +01:00
|
|
|
*/
|
2021-01-21 21:06:52 +00:00
|
|
|
const coerceValue = (stringValue) => {
|
2020-11-23 13:24:19 -05:00
|
|
|
// cooked from https://stackoverflow.com/questions/175739/built-in-way-in-javascript-to-check-if-a-string-is-a-valid-number
|
|
|
|
const isNumeric = !isNaN(stringValue) && !isNaN(parseFloat(stringValue) && isFinite(stringValue));
|
2019-03-21 21:32:39 +01:00
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
if (isNumeric) {
|
|
|
|
// detected numeric string. Coerce to a number
|
2019-03-21 21:32:39 +01:00
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
return +stringValue;
|
|
|
|
}
|
2019-03-21 21:32:39 +01:00
|
|
|
|
2021-06-06 02:14:50 -04:00
|
|
|
switch (stringValue) {
|
|
|
|
case 'true': return true;
|
|
|
|
case 'false': return false;
|
|
|
|
case 'undefined': return undefined;
|
|
|
|
case 'null': return null;
|
|
|
|
default: return stringValue;
|
2020-11-23 13:24:19 -05:00
|
|
|
}
|
2021-01-21 21:06:52 +00:00
|
|
|
};
|
2019-03-21 21:32:39 +01:00
|
|
|
|
2019-03-09 23:01:21 +01:00
|
|
|
/**
|
|
|
|
* Takes a javascript object containing Etherpad's configuration, and returns
|
2019-03-21 22:18:59 +01:00
|
|
|
* another object, in which all the string properties whose value is of the form
|
2019-03-21 01:37:19 +01:00
|
|
|
* "${ENV_VAR}" or "${ENV_VAR:default_value}" got their value replaced with the
|
|
|
|
* contents of the given environment variable, or with a default value.
|
2019-03-09 23:01:21 +01:00
|
|
|
*
|
2019-03-21 22:18:59 +01:00
|
|
|
* By definition, an environment variable's value is always a string. However,
|
|
|
|
* the code base makes use of the various json types. To maintain compatiblity,
|
|
|
|
* some heuristics is applied:
|
2019-03-09 23:01:21 +01:00
|
|
|
*
|
|
|
|
* - if ENV_VAR does not exist in the environment, null is returned;
|
|
|
|
* - if ENV_VAR's value is "true" or "false", it is converted to the js boolean
|
|
|
|
* values true or false;
|
|
|
|
* - if ENV_VAR's value looks like a number, it is converted to a js number
|
|
|
|
* (details in the code).
|
|
|
|
*
|
2019-03-21 22:18:59 +01:00
|
|
|
* The following is a scheme of the behaviour of this function:
|
|
|
|
*
|
|
|
|
* +---------------------------+---------------+------------------+
|
|
|
|
* | Configuration string in | Value of | Resulting confi- |
|
|
|
|
* | settings.json | ENV_VAR | guration value |
|
|
|
|
* |---------------------------|---------------|------------------|
|
|
|
|
* | "${ENV_VAR}" | "some_string" | "some_string" |
|
|
|
|
* | "${ENV_VAR}" | "9001" | 9001 |
|
|
|
|
* | "${ENV_VAR}" | undefined | null |
|
2019-03-21 01:37:19 +01:00
|
|
|
* | "${ENV_VAR:some_default}" | "some_string" | "some_string" |
|
|
|
|
* | "${ENV_VAR:some_default}" | undefined | "some_default" |
|
2019-03-21 22:18:59 +01:00
|
|
|
* +---------------------------+---------------+------------------+
|
|
|
|
*
|
|
|
|
* IMPLEMENTATION NOTE: variable substitution is performed doing a round trip
|
|
|
|
* conversion to/from json, using a custom replacer parameter in
|
|
|
|
* JSON.stringify(), and parsing the JSON back again. This ensures that
|
|
|
|
* environment variable replacement is performed even on nested objects.
|
2019-03-09 23:01:21 +01:00
|
|
|
*
|
|
|
|
* see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter
|
|
|
|
*/
|
2021-01-21 21:06:52 +00:00
|
|
|
const lookupEnvironmentVariables = (obj) => {
|
2019-03-09 23:01:21 +01:00
|
|
|
const stringifiedAndReplaced = JSON.stringify(obj, (key, value) => {
|
|
|
|
/*
|
|
|
|
* the first invocation of replacer() is with an empty key. Just go on, or
|
|
|
|
* we would zap the entire object.
|
|
|
|
*/
|
|
|
|
if (key === '') {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we received from the configuration file a number, a boolean or
|
|
|
|
* something that is not a string, we can be sure that it was a literal
|
|
|
|
* value. No need to perform any variable substitution.
|
|
|
|
*
|
|
|
|
* The environment variable expansion syntax "${ENV_VAR}" is just a string
|
|
|
|
* of specific form, after all.
|
|
|
|
*/
|
|
|
|
if (typeof value !== 'string') {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Let's check if the string value looks like a variable expansion (e.g.:
|
2019-03-21 01:37:19 +01:00
|
|
|
* "${ENV_VAR}" or "${ENV_VAR:default_value}")
|
2019-03-09 23:01:21 +01:00
|
|
|
*/
|
2019-03-21 01:37:19 +01:00
|
|
|
// MUXATOR 2019-03-21: we could use named capture groups here once we migrate to nodejs v10
|
2020-03-27 06:33:39 +01:00
|
|
|
const match = value.match(/^\$\{([^:]*)(:((.|\n)*))?\}$/);
|
2019-03-09 23:01:21 +01:00
|
|
|
|
2021-01-21 21:06:52 +00:00
|
|
|
if (match == null) {
|
2019-03-09 23:01:21 +01:00
|
|
|
// no match: use the value literally, without any substitution
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2019-03-21 01:37:19 +01:00
|
|
|
/*
|
|
|
|
* We found the name of an environment variable. Let's read its actual value
|
|
|
|
* and its default value, if given
|
|
|
|
*/
|
2019-03-09 23:01:21 +01:00
|
|
|
const envVarName = match[1];
|
|
|
|
const envVarValue = process.env[envVarName];
|
2019-03-21 01:37:19 +01:00
|
|
|
const defaultValue = match[3];
|
2019-03-09 23:01:21 +01:00
|
|
|
|
2019-03-21 01:37:19 +01:00
|
|
|
if ((envVarValue === undefined) && (defaultValue === undefined)) {
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.warn(`Environment variable "${envVarName}" does not contain any value for ` +
|
|
|
|
`configuration key "${key}", and no default was given. Using null. ` +
|
|
|
|
'THIS BEHAVIOR MAY CHANGE IN A FUTURE VERSION OF ETHERPAD; you should ' +
|
|
|
|
'explicitly use "null" as the default if you want to continue to use null.');
|
2019-03-09 23:01:21 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We have to return null, because if we just returned undefined, the
|
|
|
|
* configuration item "key" would be stripped from the returned object.
|
|
|
|
*/
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-03-21 01:37:19 +01:00
|
|
|
if ((envVarValue === undefined) && (defaultValue !== undefined)) {
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.debug(`Environment variable "${envVarName}" not found for ` +
|
|
|
|
`configuration key "${key}". Falling back to default value.`);
|
2019-03-21 01:37:19 +01:00
|
|
|
|
|
|
|
return coerceValue(defaultValue);
|
|
|
|
}
|
|
|
|
|
2019-03-09 23:01:21 +01:00
|
|
|
// envVarName contained some value.
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For numeric and boolean strings let's convert it to proper types before
|
|
|
|
* returning it, in order to maintain backward compatibility.
|
|
|
|
*/
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.debug(
|
2021-02-21 20:28:19 +00:00
|
|
|
`Configuration key "${key}" will be read from environment variable "${envVarName}"`);
|
2019-03-09 23:01:21 +01:00
|
|
|
|
2019-03-21 21:32:39 +01:00
|
|
|
return coerceValue(envVarValue);
|
2019-03-09 23:01:21 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
const newSettings = JSON.parse(stringifiedAndReplaced);
|
|
|
|
|
|
|
|
return newSettings;
|
2021-01-21 21:06:52 +00:00
|
|
|
};
|
2019-03-09 23:01:21 +01:00
|
|
|
|
2019-03-10 00:36:53 +01:00
|
|
|
/**
|
|
|
|
* - reads the JSON configuration file settingsFilename from disk
|
|
|
|
* - strips the comments
|
2019-03-09 23:01:21 +01:00
|
|
|
* - replaces environment variables calling lookupEnvironmentVariables()
|
2019-03-10 00:36:53 +01:00
|
|
|
* - returns a parsed Javascript object
|
|
|
|
*
|
|
|
|
* The isSettings variable only controls the error logging.
|
|
|
|
*/
|
2021-01-21 21:06:52 +00:00
|
|
|
const parseSettings = (settingsFilename, isSettings) => {
|
2020-11-23 13:24:19 -05:00
|
|
|
let settingsStr = '';
|
2019-03-10 01:46:55 +01:00
|
|
|
|
2019-03-10 00:36:53 +01:00
|
|
|
let settingsType, notFoundMessage, notFoundFunction;
|
2015-12-02 11:54:37 +00:00
|
|
|
|
2019-03-10 00:36:53 +01:00
|
|
|
if (isSettings) {
|
2020-11-23 13:24:19 -05:00
|
|
|
settingsType = 'settings';
|
|
|
|
notFoundMessage = 'Continuing using defaults!';
|
2021-09-15 23:59:17 -04:00
|
|
|
notFoundFunction = logger.warn.bind(logger);
|
2019-03-10 00:36:53 +01:00
|
|
|
} else {
|
2020-11-23 13:24:19 -05:00
|
|
|
settingsType = 'credentials';
|
|
|
|
notFoundMessage = 'Ignoring.';
|
2021-09-15 23:59:17 -04:00
|
|
|
notFoundFunction = logger.info.bind(logger);
|
2012-07-08 18:59:46 +02:00
|
|
|
}
|
2011-05-14 17:22:25 +01:00
|
|
|
|
2019-03-10 01:46:55 +01:00
|
|
|
try {
|
2020-11-23 13:24:19 -05:00
|
|
|
// read the settings file
|
2019-03-10 00:36:53 +01:00
|
|
|
settingsStr = fs.readFileSync(settingsFilename).toString();
|
2020-11-23 13:24:19 -05:00
|
|
|
} catch (e) {
|
2019-03-10 00:36:53 +01:00
|
|
|
notFoundFunction(`No ${settingsType} file found in ${settingsFilename}. ${notFoundMessage}`);
|
2015-12-02 11:54:37 +00:00
|
|
|
|
2019-03-10 00:36:53 +01:00
|
|
|
// or maybe undefined!
|
|
|
|
return null;
|
2011-05-14 17:22:25 +01:00
|
|
|
}
|
|
|
|
|
2019-03-10 00:26:36 +01:00
|
|
|
try {
|
2020-11-23 13:24:19 -05:00
|
|
|
settingsStr = jsonminify(settingsStr).replace(',]', ']').replace(',}', '}');
|
2019-03-10 00:36:53 +01:00
|
|
|
|
|
|
|
const settings = JSON.parse(settingsStr);
|
|
|
|
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.info(`${settingsType} loaded from: ${settingsFilename}`);
|
2019-03-10 00:36:53 +01:00
|
|
|
|
2019-03-09 23:01:21 +01:00
|
|
|
const replacedSettings = lookupEnvironmentVariables(settings);
|
|
|
|
|
|
|
|
return replacedSettings;
|
2020-11-23 13:24:19 -05:00
|
|
|
} catch (e) {
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.error(`There was an error processing your ${settingsType} ` +
|
|
|
|
`file from ${settingsFilename}: ${e.message}`);
|
2019-03-10 00:36:53 +01:00
|
|
|
|
2019-03-10 00:26:36 +01:00
|
|
|
process.exit(1);
|
2015-12-02 11:54:37 +00:00
|
|
|
}
|
2021-01-21 21:06:52 +00:00
|
|
|
};
|
2019-03-10 00:36:53 +01:00
|
|
|
|
2021-01-21 21:06:52 +00:00
|
|
|
exports.reloadSettings = () => {
|
2021-11-10 18:41:53 -05:00
|
|
|
const settings = parseSettings(exports.settingsFilename, true);
|
|
|
|
const credentials = parseSettings(exports.credentialsFilename, false);
|
2019-03-09 10:06:51 +01:00
|
|
|
storeSettings(settings);
|
|
|
|
storeSettings(credentials);
|
2015-12-02 11:54:37 +00:00
|
|
|
|
2021-09-23 03:05:42 -04:00
|
|
|
initLogging(exports.loglevel, exports.logconfig);
|
2012-11-06 17:35:05 +01:00
|
|
|
|
2018-08-19 03:36:18 +02:00
|
|
|
if (!exports.skinName) {
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.warn('No "skinName" parameter found. Please check out settings.json.template and ' +
|
|
|
|
'update your settings.json. Falling back to the default "colibris".');
|
2020-11-23 13:24:19 -05:00
|
|
|
exports.skinName = 'colibris';
|
2018-08-19 03:36:18 +02:00
|
|
|
}
|
|
|
|
|
2020-04-19 02:58:35 +02:00
|
|
|
// checks if skinName has an acceptable value, otherwise falls back to "colibris"
|
2018-08-19 03:36:18 +02:00
|
|
|
if (exports.skinName) {
|
2020-11-23 13:24:19 -05:00
|
|
|
const skinBasePath = path.join(exports.root, 'src', 'static', 'skins');
|
2018-08-19 03:36:18 +02:00
|
|
|
const countPieces = exports.skinName.split(path.sep).length;
|
|
|
|
|
2021-01-21 21:06:52 +00:00
|
|
|
if (countPieces !== 1) {
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.error(`skinName must be the name of a directory under "${skinBasePath}". This is ` +
|
|
|
|
`not valid: "${exports.skinName}". Falling back to the default "colibris".`);
|
2018-08-19 03:36:18 +02:00
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
exports.skinName = 'colibris';
|
2018-08-19 03:36:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// informative variable, just for the log messages
|
2021-02-10 01:12:43 -05:00
|
|
|
let skinPath = path.join(skinBasePath, exports.skinName);
|
2018-08-19 03:36:18 +02:00
|
|
|
|
|
|
|
// what if someone sets skinName == ".." or "."? We catch him!
|
|
|
|
if (absolutePaths.isSubdir(skinBasePath, skinPath) === false) {
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.error(`Skin path ${skinPath} must be a subdirectory of ${skinBasePath}. ` +
|
|
|
|
'Falling back to the default "colibris".');
|
2018-08-19 03:36:18 +02:00
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
exports.skinName = 'colibris';
|
2018-08-19 03:36:18 +02:00
|
|
|
skinPath = path.join(skinBasePath, exports.skinName);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fs.existsSync(skinPath) === false) {
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.error(`Skin path ${skinPath} does not exist. Falling back to the default "colibris".`);
|
2020-11-23 13:24:19 -05:00
|
|
|
exports.skinName = 'colibris';
|
2018-08-19 03:36:18 +02:00
|
|
|
skinPath = path.join(skinBasePath, exports.skinName);
|
|
|
|
}
|
|
|
|
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.info(`Using skin "${exports.skinName}" in dir: ${skinPath}`);
|
2018-08-19 03:36:18 +02:00
|
|
|
}
|
|
|
|
|
2019-03-10 01:46:55 +01:00
|
|
|
if (exports.abiword) {
|
2015-01-04 14:47:08 +00:00
|
|
|
// Check abiword actually exists
|
2019-03-10 01:46:55 +01:00
|
|
|
if (exports.abiword != null) {
|
2020-11-23 13:24:19 -05:00
|
|
|
fs.exists(exports.abiword, (exists) => {
|
2015-01-04 14:47:08 +00:00
|
|
|
if (!exists) {
|
2020-11-23 13:24:19 -05:00
|
|
|
const abiwordError = 'Abiword does not exist at this path, check your settings file.';
|
2019-03-10 01:46:55 +01:00
|
|
|
if (!exports.suppressErrorsInPadText) {
|
2021-02-21 20:28:19 +00:00
|
|
|
exports.defaultPadText += `\nError: ${abiwordError}${suppressDisableMsg}`;
|
2015-04-06 00:13:38 +01:00
|
|
|
}
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.error(`${abiwordError} File location: ${exports.abiword}`);
|
2015-01-04 14:47:08 +00:00
|
|
|
exports.abiword = null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-10 01:46:55 +01:00
|
|
|
if (exports.soffice) {
|
2020-11-23 13:24:19 -05:00
|
|
|
fs.exists(exports.soffice, (exists) => {
|
2019-03-10 01:46:55 +01:00
|
|
|
if (!exists) {
|
2021-02-21 20:28:19 +00:00
|
|
|
const sofficeError =
|
|
|
|
'soffice (libreoffice) does not exist at this path, check your settings file.';
|
2015-12-17 21:54:04 -06:00
|
|
|
|
2019-03-10 01:46:55 +01:00
|
|
|
if (!exports.suppressErrorsInPadText) {
|
2021-02-21 20:28:19 +00:00
|
|
|
exports.defaultPadText += `\nError: ${sofficeError}${suppressDisableMsg}`;
|
2015-12-17 21:54:04 -06:00
|
|
|
}
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.error(`${sofficeError} File location: ${exports.soffice}`);
|
2015-12-17 21:54:04 -06:00
|
|
|
exports.soffice = null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-07-03 16:58:49 -04:00
|
|
|
const sessionkeyFilename = absolutePaths.makeAbsolute(argv.sessionkey || './SESSIONKEY.txt');
|
2015-04-11 18:45:14 +02:00
|
|
|
if (!exports.sessionKey) {
|
|
|
|
try {
|
2020-11-23 13:24:19 -05:00
|
|
|
exports.sessionKey = fs.readFileSync(sessionkeyFilename, 'utf8');
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.info(`Session key loaded from: ${sessionkeyFilename}`);
|
2023-07-03 16:58:49 -04:00
|
|
|
} catch (err) { /* ignored */ }
|
|
|
|
const keyRotationEnabled = exports.cookie.keyRotationInterval && exports.cookie.sessionLifetime;
|
|
|
|
if (!exports.sessionKey && !keyRotationEnabled) {
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.info(
|
2021-02-21 20:28:19 +00:00
|
|
|
`Session key file "${sessionkeyFilename}" not found. Creating with random contents.`);
|
2015-04-11 18:45:14 +02:00
|
|
|
exports.sessionKey = randomString(32);
|
2020-11-23 13:24:19 -05:00
|
|
|
fs.writeFileSync(sessionkeyFilename, exports.sessionKey, 'utf8');
|
2015-04-06 00:13:38 +01:00
|
|
|
}
|
2015-04-11 18:45:14 +02:00
|
|
|
} else {
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.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.');
|
2013-02-13 21:51:09 +00:00
|
|
|
}
|
2023-07-03 16:58:49 -04:00
|
|
|
if (exports.sessionKey) {
|
|
|
|
logger.warn(`The sessionKey setting and ${sessionkeyFilename} file are deprecated; ` +
|
|
|
|
'use automatic key rotation instead (see the cookie.keyRotationInterval setting).');
|
|
|
|
}
|
2013-02-13 21:51:09 +00:00
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
if (exports.dbType === 'dirty') {
|
2021-01-21 21:06:52 +00:00
|
|
|
const dirtyWarning = 'DirtyDB is used. This is not recommended for production.';
|
2019-03-10 01:46:55 +01:00
|
|
|
if (!exports.suppressErrorsInPadText) {
|
2021-02-21 20:28:19 +00:00
|
|
|
exports.defaultPadText += `\nWarning: ${dirtyWarning}${suppressDisableMsg}`;
|
2015-04-06 00:13:38 +01:00
|
|
|
}
|
2018-08-22 01:16:33 +02:00
|
|
|
|
|
|
|
exports.dbSettings.filename = absolutePaths.makeAbsolute(exports.dbSettings.filename);
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.warn(`${dirtyWarning} File location: ${exports.dbSettings.filename}`);
|
2011-05-14 17:22:25 +01:00
|
|
|
}
|
2020-03-29 22:27:22 +00:00
|
|
|
|
2020-11-23 13:24:19 -05:00
|
|
|
if (exports.ip === '') {
|
2020-03-29 22:27:22 +00:00
|
|
|
// using Unix socket for connectivity
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.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.');
|
2020-03-29 22:27:22 +00:00
|
|
|
}
|
2021-02-15 08:52:38 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* At each start, Etherpad generates a random string and appends it as query
|
|
|
|
* parameter to the URLs of the static assets, in order to force their reload.
|
|
|
|
* Subsequent requests will be cached, as long as the server is not reloaded.
|
|
|
|
*
|
|
|
|
* For the rationale behind this choice, see
|
|
|
|
* https://github.com/ether/etherpad-lite/pull/3958
|
|
|
|
*
|
|
|
|
* ACHTUNG: this may prevent caching HTTP proxies to work
|
|
|
|
* TODO: remove the "?v=randomstring" parameter, and replace with hashed filenames instead
|
|
|
|
*/
|
|
|
|
exports.randomVersionString = randomString(4);
|
2021-09-15 23:59:17 -04:00
|
|
|
logger.info(`Random string used for versioning assets: ${exports.randomVersionString}`);
|
2013-03-24 01:18:44 +01:00
|
|
|
};
|
2012-04-20 17:03:37 +02:00
|
|
|
|
2021-06-06 05:26:52 -04:00
|
|
|
exports.exportedForTestingOnly = {
|
|
|
|
parseSettings,
|
|
|
|
};
|
|
|
|
|
2012-11-06 17:35:05 +01:00
|
|
|
// initially load settings
|
|
|
|
exports.reloadSettings();
|