]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/gnusocial.php
Merge branch 'remote-media-blacklist-mk2' into 'nightly'
[quix0rs-gnu-social.git] / lib / gnusocial.php
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
21 if (!defined('GNUSOCIAL')) { exit(1); }
22
23 global $config, $_server, $_path;
24
25 /**
26  * Global configuration setup and management.
27  */
28 class GNUsocial
29 {
30     protected static $config_files = array();
31     protected static $have_config;
32     protected static $is_api;
33     protected static $is_ajax;
34     protected static $plugins = array();
35
36     /**
37      * Configure and instantiate a plugin into the current configuration.
38      * Class definitions will be loaded from standard paths if necessary.
39      * Note that initialization events won't be fired until later.
40      *
41      * @param string $name class name & plugin file/subdir name
42      * @param array $attrs key/value pairs of public attributes to set on plugin instance
43      *
44      * @throws ServerException if plugin can't be found
45      */
46     public static function addPlugin($name, array $attrs=array())
47     {
48         $name = ucfirst($name);
49
50         if (isset(self::$plugins[$name])) {
51             // We have already loaded this plugin. Don't try to
52             // do it again with (possibly) different values.
53             // Försten till kvarn får mala.
54             return true;
55         }
56
57         $pluginclass = "{$name}Plugin";
58
59         if (!class_exists($pluginclass)) {
60
61             $files = array("local/plugins/{$pluginclass}.php",
62                            "local/plugins/{$name}/{$pluginclass}.php",
63                            "local/{$pluginclass}.php",
64                            "local/{$name}/{$pluginclass}.php",
65                            "plugins/{$pluginclass}.php",
66                            "plugins/{$name}/{$pluginclass}.php");
67
68             foreach ($files as $file) {
69                 $fullpath = INSTALLDIR.'/'.$file;
70                 if (@file_exists($fullpath)) {
71                     include_once($fullpath);
72                     break;
73                 }
74             }
75             if (!class_exists($pluginclass)) {
76                 throw new ServerException("Plugin $name not found.", 500);
77             }
78         }
79
80         // Doesn't this $inst risk being garbage collected or something?
81         // TODO: put into a static array that makes sure $inst isn't lost.
82         $inst = new $pluginclass();
83         foreach ($attrs as $aname => $avalue) {
84             $inst->$aname = $avalue;
85         }
86
87         // Record activated plugins for later display/config dump
88         self::$plugins[$name] = $attrs;
89
90         return true;
91     }
92
93     public static function delPlugin($name)
94     {
95         // Remove our plugin if it was previously loaded
96         $name = ucfirst($name);
97         if (isset(self::$plugins[$name])) {
98             unset(self::$plugins[$name]);
99         }
100
101         // make sure initPlugins will avoid this
102         common_config_set('plugins', 'disable-'.$name, true);
103
104         return true;
105     }
106
107     /**
108      * Get a list of activated plugins in this process.
109      * @return array of (string $name, array $args) pairs
110      */
111     public static function getActivePlugins()
112     {
113         return self::$plugins;
114     }
115
116     /**
117      * Initialize, or re-initialize, GNU social global configuration
118      * and plugins.
119      *
120      * If switching site configurations during script execution, be
121      * careful when working with leftover objects -- global settings
122      * affect many things and they may not behave as you expected.
123      *
124      * @param $server optional web server hostname for picking config
125      * @param $path optional URL path for picking config
126      * @param $conffile optional configuration file path
127      *
128      * @throws NoConfigException if config file can't be found
129      */
130     public static function init($server=null, $path=null, $conffile=null)
131     {
132         Router::clear();
133
134         self::initDefaults($server, $path);
135         self::loadConfigFile($conffile);
136
137         $sprofile = common_config('site', 'profile');
138         if (!empty($sprofile)) {
139             self::loadSiteProfile($sprofile);
140         }
141         // Load settings from database; note we need autoload for this
142         Config::loadSettings();
143
144         self::verifyLoadedConfig();
145
146         self::initPlugins();
147     }
148
149     /**
150      * Get identifier of the currently active site configuration
151      * @return string
152      */
153     public static function currentSite()
154     {
155         return common_config('site', 'nickname');
156     }
157
158     /**
159      * Change site configuration to site specified by nickname,
160      * if set up via Status_network. If not, sites other than
161      * the current will fail horribly.
162      *
163      * May throw exception or trigger a fatal error if the given
164      * site is missing or configured incorrectly.
165      *
166      * @param string $nickname
167      */
168     public static function switchSite($nickname)
169     {
170         if ($nickname == self::currentSite()) {
171             return true;
172         }
173
174         $sn = Status_network::getKV('nickname', $nickname);
175         if (empty($sn)) {
176             return false;
177             throw new Exception("No such site nickname '$nickname'");
178         }
179
180         $server = $sn->getServerName();
181         self::init($server);
182     }
183
184     /**
185      * Pull all local sites from status_network table.
186      *
187      * Behavior undefined if site is not configured via Status_network.
188      *
189      * @return array of nicknames
190      */
191     public static function findAllSites()
192     {
193         $sites = array();
194         $sn = new Status_network();
195         $sn->find();
196         while ($sn->fetch()) {
197             $sites[] = $sn->nickname;
198         }
199         return $sites;
200     }
201
202     /**
203      * Fire initialization events for all instantiated plugins.
204      */
205     protected static function initPlugins()
206     {
207         // User config may have already added some of these plugins, with
208         // maybe configured parameters. The self::addPlugin function will
209         // ignore the new call if it has already been instantiated.
210
211         // Load core plugins
212         foreach (common_config('plugins', 'core') as $name => $params) {
213             call_user_func('self::addPlugin', $name, $params);
214         }
215
216         // Load default plugins
217         foreach (common_config('plugins', 'default') as $name => $params) {
218             $key = 'disable-' . $name;
219             if (common_config('plugins', $key)) {
220                 continue;
221             }
222
223             // TODO: We should be able to avoid this is_null and assume $params
224             // is an array, since that's how it is typed in addPlugin
225             if (is_null($params)) {
226                 self::addPlugin($name);
227             } else if (is_array($params)) {
228                 if (count($params) == 0) {
229                     self::addPlugin($name);
230                 } else {
231                     $keys = array_keys($params);
232                     if (is_string($keys[0])) {
233                         self::addPlugin($name, $params);
234                     } else {
235                         foreach ($params as $paramset) {
236                             self::addPlugin($name, $paramset);
237                         }
238                     }
239                 }
240             }
241         }
242
243         // XXX: if plugins should check the schema at runtime, do that here.
244         if (common_config('db', 'schemacheck') == 'runtime') {
245             Event::handle('CheckSchema');
246         }
247
248         // Give plugins a chance to initialize in a fully-prepared environment
249         Event::handle('InitializePlugin');
250     }
251
252     /**
253      * Quick-check if configuration has been established.
254      * Useful for functions which may get used partway through
255      * initialization to back off from fancier things.
256      *
257      * @return bool
258      */
259     public static function haveConfig()
260     {
261         return self::$have_config;
262     }
263
264     /**
265      * Returns a list of configuration files that have
266      * been loaded for this instance of GNU social.
267      */
268     public static function configFiles()
269     {
270         return self::$config_files;
271     }
272
273     public static function isApi()
274     {
275         return self::$is_api;
276     }
277
278     public static function setApi($mode)
279     {
280         self::$is_api = $mode;
281     }
282
283     public static function isAjax()
284     {
285         return self::$is_ajax;
286     }
287
288     public static function setAjax($mode)
289     {
290         self::$is_ajax = $mode;
291     }
292
293     /**
294      * Build default configuration array
295      * @return array
296      */
297     protected static function defaultConfig()
298     {
299         global $_server, $_path;
300         require(INSTALLDIR.'/lib/default.php');
301         return $default;
302     }
303
304     /**
305      * Establish default configuration based on given or default server and path
306      * Sets global $_server, $_path, and $config
307      */
308     public static function initDefaults($server, $path)
309     {
310         global $_server, $_path, $config, $_PEAR;
311
312         Event::clearHandlers();
313         self::$plugins = array();
314
315         // try to figure out where we are. $server and $path
316         // can be set by including module, else we guess based
317         // on HTTP info.
318
319         if (isset($server)) {
320             $_server = $server;
321         } else {
322             $_server = array_key_exists('SERVER_NAME', $_SERVER) ?
323               strtolower($_SERVER['SERVER_NAME']) :
324             null;
325         }
326
327         if (isset($path)) {
328             $_path = $path;
329         } else {
330             $_path = (array_key_exists('SERVER_NAME', $_SERVER) && array_key_exists('SCRIPT_NAME', $_SERVER)) ?
331               self::_sn_to_path($_SERVER['SCRIPT_NAME']) :
332             null;
333         }
334
335         // Set config values initially to default values
336         $default = self::defaultConfig();
337         $config = $default;
338
339         // default configuration, overwritten in config.php
340         // Keep DB_DataObject's db config synced to ours...
341
342         $config['db'] = &$_PEAR->getStaticProperty('DB_DataObject','options');
343
344         $config['db'] = $default['db'];
345
346         if (function_exists('date_default_timezone_set')) {
347             /* Work internally in UTC */
348             date_default_timezone_set('UTC');
349         }
350     }
351
352     public static function loadSiteProfile($name)
353     {
354         global $config;
355         $settings = SiteProfile::getSettings($name);
356         $config = array_replace_recursive($config, $settings);
357     }
358
359     protected static function _sn_to_path($sn)
360     {
361         $past_root = substr($sn, 1);
362         $last_slash = strrpos($past_root, '/');
363         if ($last_slash > 0) {
364             $p = substr($past_root, 0, $last_slash);
365         } else {
366             $p = '';
367         }
368         return $p;
369     }
370
371     /**
372      * Load the default or specified configuration file.
373      * Modifies global $config and may establish plugins.
374      *
375      * @throws NoConfigException
376      */
377     protected static function loadConfigFile($conffile=null)
378     {
379         global $_server, $_path, $config;
380
381         // From most general to most specific:
382         // server-wide, then vhost-wide, then for a path,
383         // finally for a dir (usually only need one of the last two).
384
385         if (isset($conffile)) {
386             $config_files = array($conffile);
387         } else {
388             $config_files = array('/etc/gnusocial/config.php',
389                                   '/etc/gnusocial/config.d/'.$_server.'.php');
390
391             if (strlen($_path) > 0) {
392                 $config_files[] = '/etc/gnusocial/config.d/'.$_server.'_'.$_path.'.php';
393             }
394
395             $config_files[] = INSTALLDIR.'/config.php';
396         }
397
398         self::$have_config = false;
399
400         foreach ($config_files as $_config_file) {
401             if (@file_exists($_config_file)) {
402                 // Ignore 0-byte config files
403                 if (filesize($_config_file) > 0) {
404                     include($_config_file);
405                     self::$config_files[] = $_config_file;
406                     self::$have_config = true;
407                 }
408             }
409         }
410
411         if (!self::$have_config) {
412             throw new NoConfigException("No configuration file found.",
413                                         $config_files);
414         }
415
416         // Check for database server; must exist!
417
418         if (empty($config['db']['database'])) {
419             throw new ServerException("No database server for this site.");
420         }
421     }
422
423     /**
424      * Verify that the loaded config is good. Not complete, but will
425      * throw exceptions on common configuration problems I hope.
426      *
427      * Might make changes to the filesystem, to created dirs, but will
428      * not make database changes.
429      */
430     static function verifyLoadedConfig()
431     {
432         if (common_config('htmlpurifier', 'Cache.DefinitionImpl') === 'Serializer'
433                 && !is_dir(common_config('htmlpurifier', 'Cache.SerializerPath'))) {
434             if (!mkdir(common_config('htmlpurifier', 'Cache.SerializerPath'))) {
435                 throw new ConfigException('Could not create HTMLPurifier cache dir: '._ve(common_config('htmlpurifier', 'Cache.SerializerPath')));
436             }
437         }
438
439         if (!is_array(common_config('public', 'autosource'))) {
440             throw new ConfigException('Configuration option public/autosource is not an array.');
441         }
442     }
443
444     /**
445      * Are we running from the web with HTTPS?
446      *
447      * @return boolean true if we're running with HTTPS; else false
448      */
449
450     static function isHTTPS()
451     {
452         if (common_config('site', 'sslproxy')) {
453             return true;
454         }
455
456         // There are some exceptions to this; add them here!
457         if (empty($_SERVER['HTTPS'])) {
458             return false;
459         }
460
461         // If it is _not_ "off", it is on, so "true".
462         return strtolower($_SERVER['HTTPS']) !== 'off';
463     }
464
465     /**
466      * Can we use HTTPS? Then do! Only return false if it's not configured ("never").
467      */
468     static function useHTTPS()
469     {
470         return self::isHTTPS() || common_config('site', 'ssl') != 'never';
471     }
472 }
473
474 class NoConfigException extends Exception
475 {
476     public $configFiles;
477
478     function __construct($msg, $configFiles) {
479         parent::__construct($msg);
480         $this->configFiles = $configFiles;
481     }
482 }