Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

154 строки
5.4 KiB

3 лет назад
  1. /**
  2. * Library for deterministic relative filename expansion for Etherpad.
  3. */
  4. /*
  5. * 2018 - muxator
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS-IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. */
  19. const log4js = require('log4js');
  20. const path = require('path');
  21. const _ = require('underscore');
  22. const absPathLogger = log4js.getLogger('AbsolutePaths');
  23. /*
  24. * findEtherpadRoot() computes its value only on first invocation.
  25. * Subsequent invocations are served from this variable.
  26. */
  27. let etherpadRoot = null;
  28. /**
  29. * If stringArray's last elements are exactly equal to lastDesiredElements,
  30. * returns a copy in which those last elements are popped, or false otherwise.
  31. *
  32. * @param {string[]} stringArray - The input array.
  33. * @param {string[]} lastDesiredElements - The elements to remove from the end
  34. * of the input array.
  35. * @return {string[]|boolean} The shortened array, or false if there was no
  36. * overlap.
  37. */
  38. const popIfEndsWith = function (stringArray, lastDesiredElements) {
  39. if (stringArray.length <= lastDesiredElements.length) {
  40. absPathLogger.debug(`In order to pop "${lastDesiredElements.join(path.sep)}" from "${stringArray.join(path.sep)}", it should contain at least ${lastDesiredElements.length + 1} elements`);
  41. return false;
  42. }
  43. const lastElementsFound = _.last(stringArray, lastDesiredElements.length);
  44. if (_.isEqual(lastElementsFound, lastDesiredElements)) {
  45. return _.initial(stringArray, lastDesiredElements.length);
  46. }
  47. absPathLogger.debug(`${stringArray.join(path.sep)} does not end with "${lastDesiredElements.join(path.sep)}"`);
  48. return false;
  49. };
  50. /**
  51. * Heuristically computes the directory in which Etherpad is installed.
  52. *
  53. * All the relative paths have to be interpreted against this absolute base
  54. * path. Since the Windows package install has a different layout on disk, it is
  55. * dealt with as a special case.
  56. *
  57. * The path is computed only on first invocation. Subsequent invocations return
  58. * a cached value.
  59. *
  60. * The cached value is stored in AbsolutePaths.etherpadRoot via a side effect.
  61. *
  62. * @return {string} The identified absolute base path. If such path cannot be
  63. * identified, prints a log and exits the application.
  64. */
  65. exports.findEtherpadRoot = function () {
  66. if (etherpadRoot !== null) {
  67. return etherpadRoot;
  68. }
  69. const findRoot = require('find-root');
  70. const foundRoot = findRoot(__dirname);
  71. const splitFoundRoot = foundRoot.split(path.sep);
  72. /*
  73. * On Unix platforms and on Windows manual installs, foundRoot's value will
  74. * be:
  75. *
  76. * <BASE_DIR>\src
  77. */
  78. let maybeEtherpadRoot = popIfEndsWith(splitFoundRoot, ['src']);
  79. if ((maybeEtherpadRoot === false) && (process.platform === 'win32')) {
  80. /*
  81. * If we did not find the path we are expecting, and we are running under
  82. * Windows, we may still be running from a prebuilt package, whose directory
  83. * structure is different:
  84. *
  85. * <BASE_DIR>\node_modules\ep_etherpad-lite
  86. */
  87. maybeEtherpadRoot = popIfEndsWith(splitFoundRoot, ['node_modules', 'ep_etherpad-lite']);
  88. }
  89. if (maybeEtherpadRoot === false) {
  90. absPathLogger.error(`Could not identity Etherpad base path in this ${process.platform} installation in "${foundRoot}"`);
  91. process.exit(1);
  92. }
  93. // SIDE EFFECT on this module-level variable
  94. etherpadRoot = maybeEtherpadRoot.join(path.sep);
  95. if (path.isAbsolute(etherpadRoot)) {
  96. return etherpadRoot;
  97. }
  98. absPathLogger.error(`To run, Etherpad has to identify an absolute base path. This is not: "${etherpadRoot}"`);
  99. process.exit(1);
  100. };
  101. /**
  102. * Receives a filesystem path in input. If the path is absolute, returns it
  103. * unchanged. If the path is relative, an absolute version of it is returned,
  104. * built prepending exports.findEtherpadRoot() to it.
  105. *
  106. * @param {string} somePath - an absolute or relative path
  107. * @return {string} An absolute path. If the input path was already absolute,
  108. * it is returned unchanged. Otherwise it is interpreted
  109. * relative to exports.root.
  110. */
  111. exports.makeAbsolute = function (somePath) {
  112. if (path.isAbsolute(somePath)) {
  113. return somePath;
  114. }
  115. const rewrittenPath = path.normalize(path.join(exports.findEtherpadRoot(), somePath));
  116. absPathLogger.debug(`Relative path "${somePath}" can be rewritten to "${rewrittenPath}"`);
  117. return rewrittenPath;
  118. };
  119. /**
  120. * Returns whether arbitraryDir is a subdirectory of parent.
  121. *
  122. * @param {string} parent - a path to check arbitraryDir against
  123. * @param {string} arbitraryDir - the function will check if this directory is
  124. * a subdirectory of the base one
  125. * @return {boolean}
  126. */
  127. exports.isSubdir = function (parent, arbitraryDir) {
  128. // modified from: https://stackoverflow.com/questions/37521893/determine-if-a-path-is-subdirectory-of-another-in-node-js#45242825
  129. const relative = path.relative(parent, arbitraryDir);
  130. const isSubdir = !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);
  131. return isSubdir;
  132. };