gnusocial.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. <?php
  2. /**
  3. * StatusNet - the distributed open-source microblogging tool
  4. * Copyright (C) 2009-2010 StatusNet, Inc.
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. if (!defined('GNUSOCIAL')) { exit(1); }
  21. global $config, $_server, $_path;
  22. /**
  23. * Global configuration setup and management.
  24. */
  25. class GNUsocial
  26. {
  27. protected static $config_files = array();
  28. protected static $have_config;
  29. protected static $is_api;
  30. protected static $is_ajax;
  31. protected static $plugins = array();
  32. /**
  33. * Configure and instantiate a plugin into the current configuration.
  34. * Class definitions will be loaded from standard paths if necessary.
  35. * Note that initialization events won't be fired until later.
  36. *
  37. * @param string $name class name & plugin file/subdir name
  38. * @param array $attrs key/value pairs of public attributes to set on plugin instance
  39. *
  40. * @throws ServerException if plugin can't be found
  41. */
  42. public static function addPlugin($name, array $attrs=array())
  43. {
  44. $name = ucfirst($name);
  45. if (isset(self::$plugins[$name])) {
  46. // We have already loaded this plugin. Don't try to
  47. // do it again with (possibly) different values.
  48. // Försten till kvarn får mala.
  49. return true;
  50. }
  51. $pluginclass = "{$name}Plugin";
  52. if (!class_exists($pluginclass)) {
  53. $files = array("local/plugins/{$pluginclass}.php",
  54. "local/plugins/{$name}/{$pluginclass}.php",
  55. "local/{$pluginclass}.php",
  56. "local/{$name}/{$pluginclass}.php",
  57. "plugins/{$pluginclass}.php",
  58. "plugins/{$name}/{$pluginclass}.php");
  59. foreach ($files as $file) {
  60. $fullpath = INSTALLDIR.'/'.$file;
  61. if (@file_exists($fullpath)) {
  62. include_once($fullpath);
  63. break;
  64. }
  65. }
  66. if (!class_exists($pluginclass)) {
  67. throw new ServerException("Plugin $name not found.", 500);
  68. }
  69. }
  70. // Doesn't this $inst risk being garbage collected or something?
  71. // TODO: put into a static array that makes sure $inst isn't lost.
  72. $inst = new $pluginclass();
  73. foreach ($attrs as $aname => $avalue) {
  74. $inst->$aname = $avalue;
  75. }
  76. // Record activated plugins for later display/config dump
  77. self::$plugins[$name] = $attrs;
  78. return true;
  79. }
  80. public static function delPlugin($name)
  81. {
  82. // Remove our plugin if it was previously loaded
  83. $name = ucfirst($name);
  84. if (isset(self::$plugins[$name])) {
  85. unset(self::$plugins[$name]);
  86. }
  87. // make sure initPlugins will avoid this
  88. common_config_set('plugins', 'disable-'.$name, true);
  89. return true;
  90. }
  91. /**
  92. * Get a list of activated plugins in this process.
  93. * @return array of (string $name, array $args) pairs
  94. */
  95. public static function getActivePlugins()
  96. {
  97. return self::$plugins;
  98. }
  99. /**
  100. * Initialize, or re-initialize, GNU social global configuration
  101. * and plugins.
  102. *
  103. * If switching site configurations during script execution, be
  104. * careful when working with leftover objects -- global settings
  105. * affect many things and they may not behave as you expected.
  106. *
  107. * @param $server optional web server hostname for picking config
  108. * @param $path optional URL path for picking config
  109. * @param $conffile optional configuration file path
  110. *
  111. * @throws NoConfigException if config file can't be found
  112. */
  113. public static function init($server=null, $path=null, $conffile=null)
  114. {
  115. Router::clear();
  116. self::initDefaults($server, $path);
  117. self::loadConfigFile($conffile);
  118. $sprofile = common_config('site', 'profile');
  119. if (!empty($sprofile)) {
  120. self::loadSiteProfile($sprofile);
  121. }
  122. // Load settings from database; note we need autoload for this
  123. Config::loadSettings();
  124. self::initPlugins();
  125. }
  126. /**
  127. * Get identifier of the currently active site configuration
  128. * @return string
  129. */
  130. public static function currentSite()
  131. {
  132. return common_config('site', 'nickname');
  133. }
  134. /**
  135. * Change site configuration to site specified by nickname,
  136. * if set up via Status_network. If not, sites other than
  137. * the current will fail horribly.
  138. *
  139. * May throw exception or trigger a fatal error if the given
  140. * site is missing or configured incorrectly.
  141. *
  142. * @param string $nickname
  143. */
  144. public static function switchSite($nickname)
  145. {
  146. if ($nickname == self::currentSite()) {
  147. return true;
  148. }
  149. $sn = Status_network::getKV('nickname', $nickname);
  150. if (empty($sn)) {
  151. return false;
  152. throw new Exception("No such site nickname '$nickname'");
  153. }
  154. $server = $sn->getServerName();
  155. self::init($server);
  156. }
  157. /**
  158. * Pull all local sites from status_network table.
  159. *
  160. * Behavior undefined if site is not configured via Status_network.
  161. *
  162. * @return array of nicknames
  163. */
  164. public static function findAllSites()
  165. {
  166. $sites = array();
  167. $sn = new Status_network();
  168. $sn->find();
  169. while ($sn->fetch()) {
  170. $sites[] = $sn->nickname;
  171. }
  172. return $sites;
  173. }
  174. /**
  175. * Fire initialization events for all instantiated plugins.
  176. */
  177. protected static function initPlugins()
  178. {
  179. // User config may have already added some of these plugins, with
  180. // maybe configured parameters. The self::addPlugin function will
  181. // ignore the new call if it has already been instantiated.
  182. // Load core plugins
  183. foreach (common_config('plugins', 'core') as $name => $params) {
  184. call_user_func('self::addPlugin', $name, $params);
  185. }
  186. // Load default plugins
  187. foreach (common_config('plugins', 'default') as $name => $params) {
  188. $key = 'disable-' . $name;
  189. if (common_config('plugins', $key)) {
  190. continue;
  191. }
  192. // TODO: We should be able to avoid this is_null and assume $params
  193. // is an array, since that's how it is typed in addPlugin
  194. if (is_null($params)) {
  195. self::addPlugin($name);
  196. } else if (is_array($params)) {
  197. if (count($params) == 0) {
  198. self::addPlugin($name);
  199. } else {
  200. $keys = array_keys($params);
  201. if (is_string($keys[0])) {
  202. self::addPlugin($name, $params);
  203. } else {
  204. foreach ($params as $paramset) {
  205. self::addPlugin($name, $paramset);
  206. }
  207. }
  208. }
  209. }
  210. }
  211. // XXX: if plugins should check the schema at runtime, do that here.
  212. if (common_config('db', 'schemacheck') == 'runtime') {
  213. Event::handle('CheckSchema');
  214. }
  215. // Give plugins a chance to initialize in a fully-prepared environment
  216. Event::handle('InitializePlugin');
  217. }
  218. /**
  219. * Quick-check if configuration has been established.
  220. * Useful for functions which may get used partway through
  221. * initialization to back off from fancier things.
  222. *
  223. * @return bool
  224. */
  225. public static function haveConfig()
  226. {
  227. return self::$have_config;
  228. }
  229. /**
  230. * Returns a list of configuration files that have
  231. * been loaded for this instance of GNU social.
  232. */
  233. public static function configFiles()
  234. {
  235. return self::$config_files;
  236. }
  237. public static function isApi()
  238. {
  239. return self::$is_api;
  240. }
  241. public static function setApi($mode)
  242. {
  243. self::$is_api = $mode;
  244. }
  245. public static function isAjax()
  246. {
  247. return self::$is_ajax;
  248. }
  249. public static function setAjax($mode)
  250. {
  251. self::$is_ajax = $mode;
  252. }
  253. /**
  254. * Build default configuration array
  255. * @return array
  256. */
  257. protected static function defaultConfig()
  258. {
  259. global $_server, $_path;
  260. require(INSTALLDIR.'/lib/default.php');
  261. return $default;
  262. }
  263. /**
  264. * Establish default configuration based on given or default server and path
  265. * Sets global $_server, $_path, and $config
  266. */
  267. public static function initDefaults($server, $path)
  268. {
  269. global $_server, $_path, $config, $_PEAR;
  270. Event::clearHandlers();
  271. self::$plugins = array();
  272. // try to figure out where we are. $server and $path
  273. // can be set by including module, else we guess based
  274. // on HTTP info.
  275. if (isset($server)) {
  276. $_server = $server;
  277. } else {
  278. $_server = array_key_exists('SERVER_NAME', $_SERVER) ?
  279. strtolower($_SERVER['SERVER_NAME']) :
  280. null;
  281. }
  282. if (isset($path)) {
  283. $_path = $path;
  284. } else {
  285. $_path = (array_key_exists('SERVER_NAME', $_SERVER) && array_key_exists('SCRIPT_NAME', $_SERVER)) ?
  286. self::_sn_to_path($_SERVER['SCRIPT_NAME']) :
  287. null;
  288. }
  289. // Set config values initially to default values
  290. $default = self::defaultConfig();
  291. $config = $default;
  292. // default configuration, overwritten in config.php
  293. // Keep DB_DataObject's db config synced to ours...
  294. $config['db'] = &$_PEAR->getStaticProperty('DB_DataObject','options');
  295. $config['db'] = $default['db'];
  296. if (function_exists('date_default_timezone_set')) {
  297. /* Work internally in UTC */
  298. date_default_timezone_set('UTC');
  299. }
  300. }
  301. public static function loadSiteProfile($name)
  302. {
  303. global $config;
  304. $settings = SiteProfile::getSettings($name);
  305. $config = array_replace_recursive($config, $settings);
  306. }
  307. protected static function _sn_to_path($sn)
  308. {
  309. $past_root = substr($sn, 1);
  310. $last_slash = strrpos($past_root, '/');
  311. if ($last_slash > 0) {
  312. $p = substr($past_root, 0, $last_slash);
  313. } else {
  314. $p = '';
  315. }
  316. return $p;
  317. }
  318. /**
  319. * Load the default or specified configuration file.
  320. * Modifies global $config and may establish plugins.
  321. *
  322. * @throws NoConfigException
  323. */
  324. protected static function loadConfigFile($conffile=null)
  325. {
  326. global $_server, $_path, $config;
  327. // From most general to most specific:
  328. // server-wide, then vhost-wide, then for a path,
  329. // finally for a dir (usually only need one of the last two).
  330. if (isset($conffile)) {
  331. $config_files = array($conffile);
  332. } else {
  333. $config_files = array('/etc/gnusocial/config.php',
  334. '/etc/gnusocial/config.d/'.$_server.'.php');
  335. if (strlen($_path) > 0) {
  336. $config_files[] = '/etc/gnusocial/config.d/'.$_server.'_'.$_path.'.php';
  337. }
  338. $config_files[] = INSTALLDIR.'/config.php';
  339. }
  340. self::$have_config = false;
  341. foreach ($config_files as $_config_file) {
  342. if (@file_exists($_config_file)) {
  343. // Ignore 0-byte config files
  344. if (filesize($_config_file) > 0) {
  345. include($_config_file);
  346. self::$config_files[] = $_config_file;
  347. self::$have_config = true;
  348. }
  349. }
  350. }
  351. if (!self::$have_config) {
  352. throw new NoConfigException("No configuration file found.",
  353. $config_files);
  354. }
  355. // Check for database server; must exist!
  356. if (empty($config['db']['database'])) {
  357. throw new ServerException("No database server for this site.");
  358. }
  359. }
  360. /**
  361. * Are we running from the web with HTTPS?
  362. *
  363. * @return boolean true if we're running with HTTPS; else false
  364. */
  365. static function isHTTPS()
  366. {
  367. if (common_config('site', 'sslproxy')) {
  368. return true;
  369. }
  370. // There are some exceptions to this; add them here!
  371. if (empty($_SERVER['HTTPS'])) {
  372. return false;
  373. }
  374. // If it is _not_ "off", it is on, so "true".
  375. return strtolower($_SERVER['HTTPS']) !== 'off';
  376. }
  377. /**
  378. * Can we use HTTPS? Then do! Only return false if it's not configured ("never").
  379. */
  380. static function useHTTPS()
  381. {
  382. return self::isHTTPS() || common_config('site', 'ssl') != 'never';
  383. }
  384. }
  385. class NoConfigException extends Exception
  386. {
  387. public $configFiles;
  388. function __construct($msg, $configFiles) {
  389. parent::__construct($msg);
  390. $this->configFiles = $configFiles;
  391. }
  392. }