1<?php
2/**
3 * pfcglobalconfig.class.php
4 *
5 * Copyright © 2006 Stephane Gully <stephane.gully@gmail.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, 51 Franklin St, Fifth Floor,
20 * Boston, MA  02110-1301  USA
21 */
22
23require_once dirname(__FILE__)."/pfctools.php";
24require_once dirname(__FILE__)."/pfci18n.class.php";
25require_once dirname(__FILE__).'/pfccontainer.class.php';
26
27/**
28 * pfcGlobalConfig stock configuration data into sessions and initialize some stuff
29 *
30 * @author Stephane Gully <stephane.gully@gmail.com>
31 */
32class pfcGlobalConfig
33{
34  // ------------------
35  // public parameters
36  // ------------------
37
38  /**
39   * <p>This is the only mandatory parameter used to identify the chat server.
40   * You can compare it to the server ip/host like on an IRC server.
41   * If you don't know what to write, just try : <code>$params["serverid"] = md5(__FILE__);</code></p>
42   */
43  var $serverid = '';
44
45  /**
46   * <p>Used to translate the chat text and messages. Accepted values are the <code>i18n/</code> sub directories names.
47   * (By default, this is the local server language.)</p>
48   */
49  var $language = '';
50
51  /**
52   * <p>Set a sepcific encoding for chat labels.
53   * This is really useful when the Web page embedding the chat is not UTF-8 encoded.
54   * This parameter should be the same as the chat web page.
55   * Could be ISO-8859-1 or anything else but it must be supported by iconv php module.
56   * (Default value: UTF-8)</p>
57   */
58  var $output_encoding = 'UTF-8';
59
60  /**
61   * <p>If you have already identified the user (forum, portal...) you can force the user's nickname with this parameter.
62   * Defining a nick will skip the "Please enter your nickname" popup.</p>
63   * <p>Warning : Nicknames must be encoded in UTF-8.
64   * For example, if you get nicks from a databases where they are ISO-8859-1 encoded,
65   * you must convert it: <code>$params["nick"] = iconv("ISO-8859-1", "UTF-8", $bdd_nickname);</code>
66   * (Of course, change the <code>$bdd_nickname</code> parameter for your needs.)</p>
67   * <p>(Default value: "" - means users must choose a nickname when s/he connects.)</p>
68   */
69  var $nick = "";
70
71  /**
72   * <p>This is the maximum nickname length, a longer nickname is forbidden.
73   * (Default value: 15)</p>
74   */
75  var $max_nick_len = 15;
76
77  /**
78   * <p>Setting this to true will forbid the user to change his/her nickname later.
79   * (Default value: false)</p>
80   */
81  var $frozen_nick = false;
82
83  /**
84   * <p>Contains some extra data (metadata) about the user that can be used to customize the display.
85   * For example: the user's gender, age, real name, etc. can be setup in order to display it in the user's info box.
86   * A example for gender is : <code>$params["nickmeta"] = array('gender'=>'f');</code>
87   * (Default value: empty array)</p>
88   */
89  var $nickmeta = array();
90
91  /**
92   * <p>Can be used to set user metadata that is only visible to admins.
93   * (Default value:  <code>array('ip')</code> - means that the user's IP address is shown to admins only)</p>
94   */
95  var $nickmeta_private = array('ip');
96
97  /**
98   * <p>Can be used to hide keys in the final displayed whoisbox.
99   * (Default value:  <code>array()</code> - means that nothing is hidden)</p>
100   */
101  var $nickmeta_key_to_hide = array();
102
103  /**
104   * <p>Set this parameter to true if you want to give admin rights to the connected user.
105   * Attention : if you don't use any external registration system, all your users will be admins.
106   * You have to test current user rights before setting this parameter to true.
107   * (Default value: false)</p>
108   */
109  var $isadmin = false;
110
111  /**
112   * <p>This parameter contains a list of key/value that identify admin access.
113   * The keys are the nicknames and the values are the corresponding passwords.
114   * Note: The "isadmin" parameter does not depend on this variable.
115   * (Default value: nick 'admin' with no password is available. Don't forget to change it.)</p>
116   */
117  var $admins = array("admin" => "");
118
119  /**
120   * <p>When this parameter is true, it gives admin rights to the first connected user on the server.
121   * (Default value: false)</p>
122   */
123  var $firstisadmin  = false;
124
125  /**
126   * <p>Used to change the chat title that is visible just above the messages list.
127   * (Default value: "My Chat")</p>
128   */
129  var $title = '';
130
131  /**
132   * <p>Used to create default rooms (auto-joined at startup). It contains an array of rooms names.
133   * (Default value: one room is created named "My room")</p>
134   */
135  var $channels = array();
136
137  /**
138   * <p>This parameter can be used to restrict channels to users.
139   * If the array is empty, it allows users to create their own channels.
140   * (Default value: empty array)</p>
141   */
142  var $frozen_channels = array();
143
144  /**
145   * <p>The maximum number of allowed channels for each user.
146   * (Default value: 10)</p>
147   */
148  var $max_channels = 10;
149
150  /**
151   * <p>This array contains the nicknames list you want to initiate a private message at chat loading.
152   * Of course, the listed nicknames should be online or it will just be ignored.
153   * (Default value: empty array)</p>
154   */
155  var $privmsg = array();
156
157  /**
158   * <p>This is the maximum number of private message allowed at the same time for one user.
159   * (Default value: 5)</p>
160   */
161  var $max_privmsg = 5;
162
163  /**
164   * <p>This is the time to wait between two refreshes.
165   * A refresh is an HTTP request which asks the server if there are new messages to display.
166   * If there are no new messages, then an empty HTTP response is returned.
167   * This parameter will be dynamically changed depending on the chat activity, see refresh_delay_steps
168   * parameter for more information.
169   * (Default value: 2000 it means 2000 ms or 2 seconds)</p>
170   */
171  var $refresh_delay = 2000;
172
173  /**
174   * <p>This parameter is used to control the refresh_delay value dynamically.
175   * More the chat is active, more the refresh_delay is low, that means more the chat is responsive.
176   * The first parameter is a refresh delay value, the second is a time inactivity boundary etc ...
177   * (Default value: array(2000,20000,3000,60000 ... that means: start with 2s delay after 20s of inactivity,
178   *  3s delay after 60s of inactivity ...)</p>
179   */
180  var $refresh_delay_steps = array(2000,20000,3000,30000,5000,60000,8000,300000,15000,600000,30000);
181
182  /**
183   * <p>This is the time of inactivity to wait before considering a user is disconnected (in milliseconds).
184   * A user is inactive only if s/he closed his/her chat window. A user with an open chat window
185   * is not inactive because s/he sends each <code>refresh_delay</code> an HTTP request.
186   * (Default value: 35000 it means 35000 ms or 35 seconds)</p>
187   */
188  var $timeout = 35000;
189
190  /**
191   * When this parameter is true, all the chatters will be redirected
192   * to the url indicated by the <code>lockurl</code> parameter.
193   * (Default value: false)</p>
194   */
195  var $islocked = false;
196
197  /**
198   * This url is used when <code>islocked</code> parameter is true.
199   * The users will be redirected (http redirect) to this url.
200   * (Default value: http://www.phpfreechat.net)
201   */
202  var $lockurl = 'http://www.phpfreechat.net';
203
204  /**
205   * <p>Contains the list of proxies to ingore.
206   * For example: append 'censor' to the list to disable words censoring.
207   * The list of system proxies can be found in src/proxies/.
208   * Attention: 'checktimeout' and 'checknickchange' proxies should not be disabled or the chat will not work anymore.
209   * (Default value: empty array - no proxies will be skipped)</p>
210   */
211  var $skip_proxies = array();
212
213  /**
214   * <p>This array contains the proxies that will be handled just before to process a command
215   * and just after the system proxies.
216   * You can use this array to execute your own proxy.
217   * (Default value: empty array)</p>
218   */
219  var $post_proxies = array();
220
221  /**
222   * <p>This array ocntains the proxies that will be handled just before system proxies.
223   * You can use this array to execute your own proxy.
224   * (Default value: empty array)</p>
225   */
226  var $pre_proxies = array();
227
228  /**
229   * <p>Contains the proxies configuration.
230   * TODO: explain the possible values for each proxies.</p>
231   */
232  var $proxies_cfg = array("auth"    => array(),
233                           "noflood" => array("charlimit" => 450,
234                                              "msglimit"  => 10,
235                                              "delay"     => 5),
236                           "censor"  => array("words"     => array("fuck","sex","bitch"),
237                                              "replaceby" => "*",
238                                              "regex"     => false),
239                           "log"     => array("path" => ""));
240
241  /**
242   * <p>A custom proxies path. Used to easily plugin your own proxy to the chat without modifying the code.
243   * (Default value: empty path)</p>
244   */
245  var $proxies_path = '';
246
247  /**
248   * <p>Contains the default proxies location.
249   * Do not change this parameter if you don't know what you are doing.
250   * If you try to add your own proxy, check the <code>proxies_path</code> parameter.
251   * (Default value: <code>dirname(__FILE__).'/proxies'</code>)</p>
252   */
253  var $proxies_path_default = '';
254
255  /**
256   * <p>This parameter indicates your own commands directory location.
257   * The chat uses commands to communicate between client and server.
258   * As an example, when a message is sent, the <code>/send your message</code> command is used,
259   * when a nickname is changed, the <code>/nick newnickname</code> command is used.
260   * To create a new command, you have to write it and indicate in this parameter where it is located.
261   * (Default value: empty string - means no custom command path is used)</p>
262   */
263  var $cmd_path = '';
264
265  /**
266   * <p>Contains the default command path used by the system.
267   * Do not change this parameter if you don't know what you are doing.
268   * If you try to add your own command, check the <code>cmd_path</code> parameter.
269   * (Default value: <code>dirname(__FILE__).'/commands'</code>)</p>
270   */
271  var $cmd_path_default = '';
272
273  /**
274   * <p>This is the maximum message length in characters. A longer message is forbidden.
275   * (Default value: 400)</p>
276   */
277  var $max_text_len = 400;
278
279  /**
280   * <p>This is the number of messages kept in the history.
281   * This is what you see when you reload the chat.
282   * The number of messages s/he can see is defined by this parameter.
283   * (Default value: 20</p>
284   */
285  var $max_msg = 20;
286
287  /**
288   * <p>The maximum number of lines displayed in the window.
289   * Old lines will be deleted to save browser's memory on clients.
290   * Default value: 150)</p>
291   */
292  var $max_displayed_lines = 150;
293
294  /**
295   * <p>Setting this to true will send a <code>/quit</code> command when the user closes his/her window.
296   * (NOTE: Doesn't work on Firefox).
297   * This parameter isn't true by default because on IE and Konqueror/Safari,
298   * reloading the window (F5) will generate the same event as closing the window which can be annoying.
299   * (Default value: false)</p>
300   */
301  var $quit_on_closedwindow = true;
302
303  /**
304   * <p>Setting this to true will give the focus to the input text box when connecting to the chat.
305   * It can be useful not to touch the focus when integrating the chat into an existing website
306   * because when the focus is changed, the viewport follows the focus location.
307   * (Default value: true)</p>
308   */
309  var $focus_on_connect = true;
310
311  /**
312   * <p>Setting this to false will oblige user to click on the connect button if s/he wants to chat.
313   * (Default value: true - a connection to the chat is automaticaly performed)</p>
314   */
315  var $connect_at_startup = true;
316
317  /**
318   * <p>Setting it to true will start the chat minimized.
319   * (Default value: false)</p>
320   */
321  var $start_minimized = false;
322
323  /**
324   * <p>Height of the chat area.
325   * (Default value: "440px")</p>
326   */
327  var $height = "440px";
328
329  /**
330   * <p><ul><li>Setting this to 0 will show nothing.</li>
331   * <li>Setting it to 1 will show nicknames changes.</li>
332   * <li>Setting it to 2 will show connect/disconnect notifications.</li>
333   * <li>Setting it to 4 will show kick/ban notifications.</li>
334   * <li>Setting it to 7 (1+2+4) will show all the notifications.</li></ul>
335   * (Default value: 7)</p>
336   */
337  var $shownotice = 7;
338
339  /**
340   * <p>Setting it to false will disable nickname colorization.
341   * (Default value: true)</p>
342   **/
343  var $nickmarker = true;
344
345  /**
346   * <p>Setting it to false will hide the date/hour column.
347   * (Default value: true)</p>
348   */
349  var $clock = true;
350
351  /**
352   * <p>Setting it to false will start the chat without sound notifications.
353   * (Default value: true)</p>
354   */
355  var $startwithsound = true;
356
357  /**
358   * <p>Setting it to true will open all links in a new window.
359   * (Default value: true)</p>
360   */
361  var $openlinknewwindow = true;
362
363  /**
364   * <p>Setting it to false will disable the window title notification.
365   * When a message is received and this parameter is true, the window title is modified with <code>[n]</code>
366   * (n is the number of new posted messages).
367   * (Default value: true)</p>
368   */
369  var $notify_window = true;
370
371  /**
372   * <p>Setting it to true will shorten long URLs entered by users in the chat area.
373   * (Default value: true)</p>
374   */
375  var $short_url = true;
376
377  /**
378   * <p>Final width of the shortened URL in characters.  (This includes the elipsis on shortened URLs.)
379   * This parameter is taken into account only when <code>short_url</code> is true.
380   * (Default value: 40)</p>
381   */
382  var $short_url_width = 40;
383
384  /**
385   * <p>Used to show/hide the ping information near the phpfreechat linkback logo.
386   * The ping is the time between a client request and a server response.
387   * More the ping is low, faster the chat is responding.
388   * (Default value: true)</p>
389   */
390  var $display_ping = true;
391
392  /**
393   * <p>Used to hide the phpfreechat linkback logo.
394   * Be sure that you are conform to the <a href="http://www.phpfreechat.net/license.en.html">license page</a>
395   * before setting this to false!
396   * (Default value: true)</p>
397   */
398  var $display_pfc_logo = true;
399
400  /**
401   * <p>Used to show/hide the images in the channels and pv tabs.
402   * (Default value: true)</p>
403   */
404  var $displaytabimage = true;
405
406  /**
407   * <p>Used to show/hide the close button in the channels tabs.
408   * (Default value: true)</p>
409   */
410  var $displaytabclosebutton = true;
411
412  /**
413   * <p>Used to show/hide online users list at startup.
414   * (Default value: true)</p>
415   */
416  var $showwhosonline = true;
417
418  /**
419   * <p>Used to show/hide the smiley selector at startup.
420   * (Default value: true)</p>
421   */
422  var $showsmileys = true;
423
424  /**
425   * <p>Used to show/hide the showwhosonline button.
426   * (Default value: true)</p>
427   */
428  var $btn_sh_whosonline = true;
429
430  /**
431   * <p>Used to show/hide the showsmileys button.
432   * (Default value: true)</p>
433   */
434  var $btn_sh_smileys = true;
435
436  /**
437   * <p>This is the list of colors that will appears into the bbcode palette.
438   * (Default value: contains an array of basic colors: '#FFFFFF', '#000000', ...)</p>
439   */
440  var $bbcode_colorlist = array('#FFFFFF',
441                                '#000000',
442                                '#000055',
443                                '#008000',
444                                '#FF0000',
445                                '#800000',
446                                '#800080',
447                                '#FF5500',
448                                '#FFFF00',
449                                '#00FF00',
450                                '#008080',
451                                '#00FFFF',
452                                '#0000FF',
453                                '#FF00FF',
454                                '#7F7F7F',
455                                '#D2D2D2');
456
457  /**
458   * <p>This is the list of colors that will be used to automaticaly and randomly colorize the nicknames in the chat.
459   * (Default value: contains an array of basic colors: '#CCCCCC','#000000')</p>
460   */
461  var $nickname_colorlist = array('#CCCCCC',
462                                  '#000000',
463                                  '#3636B2',
464                                  '#2A8C2A',
465                                  '#C33B3B',
466                                  '#C73232',
467                                  '#80267F',
468                                  '#66361F',
469                                  '#D9A641',
470                                  '#3DCC3D',
471                                  '#1A5555',
472                                  '#2F8C74',
473                                  '#4545E6',
474                                  '#B037B0',
475                                  '#4C4C4C',
476                                  '#959595');
477
478  /**
479   * <p>This parameter specifies which theme the chat will use.
480   * A theme is a package that makes it possible to completly change the chat appearance (CSS) and the chat dynamics (JS)
481   * You can find official themes in the <code>themes/</code> directory on your local phpfreechat distribution.
482   * (Default value: 'default')</p>
483   */
484  var $theme = 'default';
485
486  /**
487   * <p>Indicates where the themes are located.
488   * Use this parameter if you want to store your own theme in a special location.
489   * (by default the same as <code>theme_default_path</code>)</p>
490   */
491  var $theme_path = '';
492
493  /**
494   * <p>This url indicates the <code>theme_path</code> location.
495   * It will be used by the browser to load theme resources : images, css, js.
496   * If this parameter is not indicated, the themes will be copied to <code>data_public_path/themes</code>
497   * and this parameter value will be set to <code>data_public_url/theme</code>.
498   * (Default value: '')</p>
499   */
500  var $theme_url = '';
501
502  /**
503   * <p>Indicate where the official pfc default theme is located.
504   * Do not change this parameter if you don't know what you are doing.
505   * If you try to add your own theme, check the <code>theme_path</code> parameter.
506   * (Default value: '' - empty string means <code>dirname(__FILE__).'/../themes'</code> is used automatically)</p>
507   */
508  var $theme_default_path = '';
509
510  /**
511   * <p>This url indicates the <code>theme_default_path</code> location.
512   * Do not change this parameter if you don't know what you are doing.
513   * If you try to add your own theme, check the <code>theme_path</code> parameter.
514   * (Default value: the theme is copied into <code>data_public_path/themes</code>
515   * and this parameter will be set to <code>data_public_url/theme</code>)</p>
516   */
517  var $theme_default_url = '';
518
519  /**
520   * <p>Used to specify the chat container (chat database).
521   * Accepted containers are : File and Mysql (maybe others in the future).
522   * (Default value: 'File')</p>
523   */
524  var $container_type = 'File';
525
526  /**
527   * <p>Used to specify the script that will handle asynchronous requests.
528   * Very useful when the chat (client) script is resource consuming (ex: forum or portal chat integration).
529   * (Default value: '' - means this parameter is automatically calculated)</p>
530   */
531  var $server_script_path = '';
532
533  /**
534   * <p>This url indicates the <code>server_script_path</code>.
535   * It will be used to do AJAX requests from the browser. Therefore, this URL should be a browsable public url.
536   * This parameter is useful when using URL rewriting because basic auto-calculation will fail.
537   * (Default value: '' - means this parameter is automatically calculated)</p>
538   */
539  var $server_script_url = '';
540
541  /**
542   * <p>Used to specify the script path which first displays the chat.
543   * This path will be used to calculate relatives paths for resources: javascript lib and images.
544   * Useful when the php configuration is uncommon. This option can be used to force the automatic detection process.
545   * (Default value: '' - means this parameter is automatically calculated)</p>
546   */
547  var $client_script_path = '';
548
549  /**
550   * <p>Used to store private data like cache, logs and chat history.
551   * Tip: you can optimize your chat performances,
552   * see <a href="http://www.phpfreechat.net/faq.en.html#tmpfs">this FAQ entry</a>.
553   * (Default value: '' - means <code>dirname(__FILE__)."/../data/private"</code> is used automatically)</p>
554   */
555  var $data_private_path = '';
556
557  /**
558   * This path must be reachable by your web server.
559   * Javascript and every resources (theme) files will be stored here.
560   * (Default value: '' - means dirname(__FILE__)."/../data/public" is used automatically)
561   */
562  var $data_public_path = '';
563
564  /**
565   * This URL should link to the <code>data_private_path</code> directory so that
566   * the clients' browsers will be able to load needed javascript files and theme resources.
567   * It can be useful when url rewriting is done on the server.
568   * (Default value: '' - means this parameter is automatically calculated from <code>data_private_path</code>)
569   */
570  var $data_public_url = '';
571
572  /**
573   * <p>This is the prototype javascript library URL.
574   * Use this parameter to use your external library.
575   * (Default value: '' - means <code>data/js/prototype.js</code> is used automatically)</p>
576   */
577  var $prototypejs_url = '';
578
579  /**
580   * <p>When debug is true, some traces will be shown on the chat clients
581   * (Default value: false)</p>
582   */
583  var $debug = false;
584
585  /**
586   * <p>Can be used to setup the chat time zone.
587   * It is the difference in seconds between chat clock and server clock.
588   * (Default value: 0)</p>
589   */
590  var $time_offset = 0;
591
592  /**
593   * <p>How to display the dates in the chat.
594   * (Default value: <code>'d/m/Y'</code>)</p>
595   */
596  var $date_format = 'd/m/Y';
597
598  /**
599   * <p>How to display the time in the chat
600   * (Default value: <code>'H:i:s'</code>)</p>
601   */
602  var $time_format = 'H:i:s';
603
604  /**
605   * <p>This parameter is useful when your chat server is behind a reverse proxy that
606   * forwards client ip address in HTTP_X_FORWARDED_FOR http header.
607   * Some discutions about this parameter are available
608   * on <a href="http://www.phpfreechat.net/forum/viewtopic.php?id=1344">the forum</a>.
609   * (Default value: false)</p>
610   */
611  var $get_ip_from_xforwardedfor = false;
612
613  /**
614   * <p>Most of the chat parameters are stored in a internal cache for performances issues.
615   * It means that for all the clients the chat will have the same parameters. However sometime you need
616   * to customize some parameters for each clients.
617   * For example: the 'language' parameter could depends on the chatter profil so it could interesting to
618   * ignore the cache for this parameter.
619   * The 'dyn_params' contains the parameters that need to be dynamic (not stored in the cache).
620   * (Default value: array())</p>
621   */
622  var $dyn_params = array();
623
624  // ------------------
625  // private parameters
626  // ------------------
627  /**
628   * Contains proxies to execute on each commands.
629   * Filled in the init step, this parameter cannot be overridden.
630   */
631  var $proxies              = array();
632
633  var $smileys             = array();
634  var $errors              = array();
635  var $is_init             = false; // used internaly to know if the chat config is initialized
636  var $version             = ''; // the phpfreechat version: taken from the 'version' file content
637
638  var $_sys_proxies         = array("lock", "checktimeout", "checknickchange", "auth", "noflood", "censor", "log");
639  var $_dyn_params          = array("nick","isadmin","islocked","admins","frozen_channels", "channels", "privmsg", "nickmeta","time_offset","date_format","time_format");
640  var $_params_type         = array();
641  var $_query_string        = '';
642
643  function pfcGlobalConfig( $params = array() )
644  {
645    // @todo find a cleaner way to forward serverid to i18n functions
646    $GLOBALS['serverid'] = isset($params['serverid']) ? $params['serverid'] : '_serverid_';
647    // setup the locales for the translated messages
648    pfcI18N::Init(isset($params['language']) ? $params['language'] : '');
649
650    // check the serverid is really defined
651    if (!isset($params["serverid"]))
652      $this->errors[] = _pfc("'%s' parameter is mandatory by default use '%s' value", "serverid", "md5(__FILE__)");
653    $this->serverid = $params["serverid"];
654
655    // setup data_private_path because _GetCacheFile needs it
656    if (!isset($params["data_private_path"]))
657      $this->data_private_path = dirname(__FILE__)."/../data/private";
658    else
659      $this->data_private_path = $params["data_private_path"];
660
661    // check if a cached configuration already exists
662    // don't load parameters if the cache exists
663    $cachefile = $this->_GetCacheFile();
664    if (!file_exists($cachefile))
665    {
666      // first of all, save our current state in order to be able to check for variable types later
667      $this->_saveParamsTypes();
668
669      if (!isset($params["data_public_path"]))
670        $this->data_public_path  = dirname(__FILE__)."/../data/public";
671      else
672        $this->data_public_path = $params["data_public_path"];
673
674      // if the user didn't specify the server_script_url, then remember it and
675      // append QUERY_STRING to it
676      if (!isset($params['server_script_url']))
677        $this->_query_string = isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != '' ?
678          '?'.$_SERVER['QUERY_STRING'] :
679          '';
680
681      // load users container or keep default one
682      if (isset($params["container_type"]))
683        $this->container_type = $params["container_type"];
684
685      // load default container's config
686      $ct =& pfcContainer::Instance($this->container_type, true);
687      $ct_cfg = $ct->getDefaultConfig();
688      foreach( $ct_cfg as $k => $v )
689      {
690        $attr = "container_cfg_".$k;
691        if (!isset($this->$attr))
692          $this->$attr = $v;
693      }
694
695      // load all user's parameters which will override default ones
696      foreach ( $params as $k => $v )
697      {
698        if (!isset($this->$k))
699          $this->errors[] = _pfc("Error: undefined or obsolete parameter '%s', please correct or remove this parameter", $k);
700        if (preg_match('/^_/',$k))
701          $this->errors[] = _pfc("Error: '%s' is a private parameter, you are not allowed to change it", $k);
702
703        if ($k == "proxies_cfg")
704        {
705          // don't replace all the proxy_cfg parameters, just replace the specified ones
706          foreach ( $params["proxies_cfg"] as $k2 => $v2 )
707          {
708            if (is_array($v2))
709              foreach( $v2 as $k3 => $v3)
710                $this->proxies_cfg[$k2][$k3] = $v3;
711            else
712              $this->proxies_cfg[$k2] = $v2;
713          }
714        }
715        else
716          $this->$k = $v;
717      }
718    }
719
720    // load dynamic parameter even if the config exists in the cache
721    if (isset($params['dyn_params']) && is_array($params['dyn_params']))
722      $this->_dyn_params = array_merge($this->_dyn_params,$params['dyn_params']);
723    foreach ( $this->_dyn_params as $dp )
724      if (isset($params[$dp]))
725        $this->$dp = $params[$dp];
726
727    // 'channels' is now a dynamic parameter, just check if I need to initialize it or not
728    if (is_array($this->channels) &&
729        count($this->channels) == 0 &&
730        !isset($params['channels']))
731      $this->channels = array(_pfc("My room"));
732
733    // now load or save the configuration in the cache
734    $this->synchronizeWithCache();
735
736    // to be sure the container instance is initialized
737    $ct =& pfcContainer::Instance($this->container_type, true);
738
739    // This is a dirty workaround which fix a infinite loop when:
740    // 'frozen_nick' is true
741    // 'nick' length is > 'max_nick_len'
742    $this->nick = $this->filterNickname($this->nick);
743  }
744
745  function &Instance( $params = array(), $destroy_instance = false )
746  {
747    static $i;
748    if ($destroy_instance)
749      $i = NULL;
750    else
751      if (!isset($i))
752        $i = new pfcGlobalConfig( $params );
753    return $i;
754  }
755
756  /**
757   * This function saves all the parameters types in order to check later if the types are ok
758   */
759  function _saveParamsTypes()
760  {
761    $vars = get_object_vars($this);
762    foreach($vars as $k => $v)
763    {
764      if (is_string($v))                $this->_params_type["string"][]  = $k;
765      else if (is_bool($v))             $this->_params_type["bool"][]    = $k;
766      else if (is_array($v))            $this->_params_type["array"][]   = $k;
767      else if (is_int($v) && $v>0)      $this->_params_type["positivenumeric"][] = $k;
768      else $this->_params_type["misc"][] = $k;
769    }
770  }
771
772  /**
773   * Initialize the phpfreechat configuration
774   * this initialisation is done once at startup then it is stored into a session cache
775   */
776  function init()
777  {
778    $ok = true;
779
780    // check the parameters types
781    $array_params = $this->_params_type["array"];
782    foreach( $array_params as $ap )
783    {
784      if (!is_array($this->$ap))
785        $this->errors[] = _pfc("'%s' parameter must be an array", $ap);
786    }
787    $numerical_positive_params = $this->_params_type["positivenumeric"];
788    foreach( $numerical_positive_params as $npp )
789    {
790      if (!is_int($this->$npp) || $this->$npp < 0)
791        $this->errors[] = _pfc("'%s' parameter must be a positive number", $npp);
792    }
793    $boolean_params = $this->_params_type["bool"];
794    foreach( $boolean_params as $bp )
795    {
796      if (!is_bool($this->$bp))
797        $this->errors[] = _pfc("'%s' parameter must be a boolean", $bp);
798    }
799    $string_params = $this->_params_type["string"];
800    foreach( $string_params as $sp )
801    {
802      if (!is_string($this->$sp))
803        $this->errors[] = _pfc("'%s' parameter must be a charatere string", $sp);
804    }
805
806    if ($this->title == "")           $this->title        = _pfc("My Chat");
807
808    // first of all, check the used functions
809    $f_list["file_get_contents"] = _pfc("You need %s", "PHP 4 >= 4.3.0 or PHP 5");
810    $err_session_x = "You need PHP 4 or PHP 5";
811    $f_list["session_start"]   = $err_session_x;
812    $f_list["session_destroy"] = $err_session_x;
813    $f_list["session_id"]      = $err_session_x;
814    $f_list["session_name"]    = $err_session_x;
815    $err_preg_x = _pfc("You need %s", "PHP 3 >= 3.0.9 or PHP 4 or PHP 5");
816    $f_list["preg_match"]      = $err_preg_x;
817    $f_list["preg_replace"]    = $err_preg_x;
818    $f_list["preg_split"]      = $err_preg_x;
819    $err_ob_x = _pfc("You need %s", "PHP 4 or PHP 5");
820    $f_list["ob_start"]        = $err_ob_x;
821    $f_list["ob_get_contents"] = $err_ob_x;
822    $f_list["ob_end_clean"]    = $err_ob_x;
823    $f_list["get_object_vars"] = _pfc("You need %s", "PHP 4 or PHP 5");
824    $this->errors = array_merge($this->errors, check_functions_exist($f_list));
825
826    //    $this->errors = array_merge($this->errors, @test_writable_dir($this->data_public_path, "data_public_path"));
827    $this->errors = array_merge($this->errors, @test_writable_dir($this->data_private_path, "data_private_path"));
828    $this->errors = array_merge($this->errors, @test_writable_dir($this->data_private_path."/cache", "data_private_path/cache"));
829
830
831    // install the public directory content
832    $dir = dirname(__FILE__)."/../data/public/js";
833    $dh = opendir($dir);
834    while (false !== ($file = readdir($dh)))
835    {
836      $f_src = $dir.'/'.$file;
837      $f_dst = $this->data_public_path.'/js/'.$file;
838      if ($file == "." || $file == ".." || !is_file($f_src)) continue; // skip . and .. generic files
839      // install js files only if the destination doesn't exists or if the destination timestamp is older than the source timestamp
840      if (!file_exists($f_dst) || filemtime($f_dst) < filemtime($f_src) )
841      {
842        mkdir_r($this->data_public_path.'/js/');
843        copy( $f_src, $f_dst );
844      }
845      if (!file_exists($f_dst)) $this->errors[] = _pfc("%s doesn't exist, data_public_path cannot be installed", $f_dst);
846    }
847    closedir($dh);
848
849
850    // ---
851    // test client script
852    // try to find the path into server configuration
853    if ($this->client_script_path == '')
854      $this->client_script_path = pfc_GetScriptFilename();
855
856    if ($this->server_script_url == '' && $this->server_script_path == '')
857    {
858      $filetotest = $this->client_script_path;
859      // do not take into account the url parameters
860      if (preg_match("/(.*)\?(.*)/", $filetotest, $res))
861        $filetotest = $res[1];
862      if ( !file_exists($filetotest) )
863        $this->errors[] = _pfc("%s doesn't exist", $filetotest);
864      $this->server_script_url  = './'.basename($filetotest).$this->_query_string;
865    }
866
867    // calculate datapublic url
868    if ($this->data_public_url == "")
869      $this->data_public_url = pfc_RelativePath($this->client_script_path, $this->data_public_path);
870
871    if ($this->server_script_path == '')
872      $this->server_script_path = $this->client_script_path;
873
874    // ---
875    // test server script
876    if ($this->server_script_url == '')
877    {
878      $filetotest = $this->server_script_path;
879      // do not take into account the url parameters
880      if (preg_match("/(.*)\?(.*)/",$this->server_script_path, $res))
881        $filetotest = $res[1];
882      if ( !file_exists($filetotest) )
883        $this->errors[] = _pfc("%s doesn't exist", $filetotest);
884      $this->server_script_url = pfc_RelativePath($this->client_script_path, $this->server_script_path).'/'.basename($filetotest).$this->_query_string;
885    }
886
887    // check if the theme_path parameter are correctly setup
888    if ($this->theme_default_path == '' || !is_dir($this->theme_default_path))
889      $this->theme_default_path = dirname(__FILE__).'/../themes';
890    if ($this->theme_path == '' || !is_dir($this->theme_path))
891      $this->theme_path = $this->theme_default_path;
892
893    // If the user didn't give any theme_default_url value,
894    // copy the default theme resources in a public directory
895    if ($this->theme_default_url == '')
896    {
897      mkdir_r($this->data_public_path.'/themes/default');
898      if (!is_dir($this->data_public_path.'/themes/default'))
899        $this->errors[] = _pfc("cannot create %s", $this->data_public_path.'/themes/default');
900      else
901      {
902        $ret = copy_r( dirname(__FILE__).'/../themes/default',
903                       $this->data_public_path.'/themes/default' );
904        if (!$ret)
905          $this->errors[] = _pfc("cannot copy %s in %s",
906                                 dirname(__FILE__).'/../themes/default',
907                                 $this->data_public_path.'/themes/default');
908      }
909      $this->theme_default_url = $this->data_public_url.'/themes';
910    }
911    if ($this->theme_url == '')
912    {
913      mkdir_r($this->data_public_path.'/themes/'.$this->theme);
914      if (!is_dir($this->data_public_path.'/themes/'.$this->theme))
915        $this->errors[] = _pfc("cannot create %s", $this->data_public_path.'/themes/'.$this->theme);
916      else
917      {
918        $ret = copy_r( $this->theme_path.'/'.$this->theme,
919                       $this->data_public_path.'/themes/'.$this->theme );
920        if (!$ret)
921          $this->errors[] = _pfc("cannot copy %s in %s",
922                                 $this->theme_path.'/'.$this->theme,
923                                 $this->data_public_path.'/themes/'.$this->theme);
924      }
925      $this->theme_url = $this->data_public_url.'/themes';
926    }
927
928    // if the user do not have an existing prototype.js library, we use the embeded one
929    if ($this->prototypejs_url == '') $this->prototypejs_url = $this->data_public_url.'/js/prototype.js';
930
931    // ---
932    // run specific container initialisation
933    $ct =& pfcContainer::Instance();
934    $ct_errors = $ct->init($this);
935    $this->errors = array_merge($this->errors, $ct_errors);
936
937    // check if the wanted language is known
938    $lg_list = pfcI18N::GetAcceptedLanguage();
939    if ( $this->language != "" && !in_array($this->language, $lg_list) )
940      $this->errors[] = _pfc("'%s' parameter is not valid. Available values are : '%s'", "language", implode(", ", $lg_list));
941
942    // calculate the proxies chaine
943    $this->proxies = array();
944    foreach($this->pre_proxies as $px)
945    {
946      if (!in_array($px,$this->skip_proxies) && !in_array($px,$this->proxies))
947        $this->proxies[] = $px;
948
949    }
950    foreach($this->_sys_proxies as $px)
951    {
952      if (!in_array($px,$this->skip_proxies) && !in_array($px,$this->proxies))
953        $this->proxies[] = $px;
954
955    }
956    foreach($this->post_proxies as $px)
957    {
958      if (!in_array($px,$this->skip_proxies) && !in_array($px,$this->proxies))
959        $this->proxies[] = $px;
960
961    }
962
963    if (in_array('log',$this->proxies)) {
964      // test the LOCK_EX feature because the log proxy needs to write in a file
965      $filename = $this->data_private_path.'/filemtime2.test';
966      if (is_writable(dirname($filename)))
967      {
968        $data1 = time();
969        file_put_contents($filename, $data1, LOCK_EX);
970        $data2 = file_get_contents($filename);
971        if ($data1 != $data2) {
972              unset($this->proxies[array_search('log',$this->proxies)]);
973        }
974      }
975    }
976
977    // save the proxies path
978    $this->proxies_path_default = dirname(__FILE__).'/proxies';
979    // check the customized proxies path
980    if ($this->proxies_path != '' && !is_dir($this->proxies_path))
981      $this->errors[] = _pfc("'%s' directory doesn't exist", $this->proxies_path);
982    if ($this->proxies_path == '') $this->proxies_path = $this->proxies_path_default;
983
984    // save the commands path
985    $this->cmd_path_default = dirname(__FILE__).'/commands';
986    if ($this->cmd_path == '') $this->cmd_path = $this->cmd_path_default;
987
988    // load smileys from file
989    $this->loadSmileyTheme();
990
991    // load version number from file
992    $this->version = trim(file_get_contents(dirname(__FILE__)."/../version.txt"));
993
994    $this->is_init = (count($this->errors) == 0);
995  }
996
997  function isInit()
998  {
999    return $this->is_init;
1000  }
1001
1002  function &getErrors()
1003  {
1004    return $this->errors;
1005  }
1006
1007  function loadSmileyTheme()
1008  {
1009    $theme = file($this->getFilePathFromTheme("smileys/theme.txt"));
1010    $result = array();
1011    foreach($theme as $line)
1012    {
1013      $line = trim($line);
1014      if (preg_match("/^#.*/",$line))
1015        continue;
1016      else if (preg_match("/([a-z_\-0-9\.]+)(.*)$/i",$line,$res))
1017      {
1018        $smiley_file = 'smileys/'.$res[1];
1019        $smiley_str = trim($res[2])."\n";
1020        $smiley_str = str_replace("\n", "", $smiley_str);
1021        $smiley_str = str_replace("\t", " ", $smiley_str);
1022        $smiley_str_tab = explode(" ", $smiley_str);
1023        foreach($smiley_str_tab as $str)
1024          $result[$smiley_file][] = htmlspecialchars(addslashes($str));
1025      }
1026    }
1027    $this->smileys =& $result;
1028  }
1029
1030  function getId()
1031  {
1032    return $this->serverid;
1033  }
1034
1035  function _GetCacheFile($serverid = "", $data_private_path = "")
1036  {
1037    if ($serverid == '')          $serverid = $this->getId();
1038    if ($data_private_path == '') $data_private_path = $this->data_private_path;
1039    return $data_private_path.'/cache/'.$serverid.'.php';
1040  }
1041
1042  function destroyCache()
1043  {
1044    $cachefile = $this->_GetCacheFile();
1045    if (!file_exists($cachefile))
1046      return false;
1047    $this->is_init = false;
1048    // destroy the cache lock file
1049    $cachefile_lock = $cachefile."_lock";
1050    if (file_exists($cachefile_lock)) @unlink($cachefile_lock);
1051    // destroy the cache file
1052    return @unlink($cachefile);
1053  }
1054
1055  /**
1056   * Save the pfcConfig object into cache if it doesn't exists yet
1057   * else restore the old pfcConfig object
1058   */
1059  function synchronizeWithCache()
1060  {
1061    $cachefile = $this->_GetCacheFile();
1062    $cachefile_lock = $cachefile."_lock";
1063
1064    if (file_exists($cachefile))
1065    {
1066      // if a cache file exists, remove the lock file because config has been succesfully stored
1067      if (file_exists($cachefile_lock)) @unlink($cachefile_lock);
1068
1069      include $cachefile;
1070      foreach($pfc_conf as $key => $val)
1071        // the dynamics parameters must not be cached
1072        if (!in_array($key,$this->_dyn_params))
1073          $this->$key = $val;
1074
1075      return true; // synchronized
1076    }
1077    else
1078    {
1079      if (file_exists($cachefile_lock))
1080      {
1081        // delete too old lockfiles (more than 15 seconds)
1082        $locktime = filemtime($cachefile_lock);
1083        if ($locktime+15 < time())
1084          unlink($cachefile_lock);
1085        else
1086          return false; // do nothing if the lock file exists
1087      }
1088      else
1089        @touch($cachefile_lock); // create the lockfile
1090
1091      if (!$this->isInit())
1092        $this->init();
1093      $errors =& $this->getErrors();
1094      if (count($errors) == 0)
1095      {
1096      // save the validated config in cache
1097      $this->saveInCache();
1098      }
1099      else
1100        @unlink($cachefile_lock); // destroy the lock file for the next attempt
1101      return false; // new cache created
1102    }
1103  }
1104  function saveInCache()
1105  {
1106    $cachefile = $this->_GetCacheFile();
1107    $data = '<?php ';
1108
1109    $conf = get_object_vars($this);
1110    $keys = array_keys($conf);
1111    foreach($keys as $k)
1112      if (preg_match('/^_.*/',$k))
1113        unset($conf[$k]);
1114
1115    // remove dynamic parameters
1116    foreach($this->_dyn_params as $k)
1117      unset($conf[$k]);
1118
1119    $data .= '$pfc_conf = '.var_export($conf,true).";\n";
1120    $data .= '?>';
1121
1122    file_put_contents($cachefile, $data/*serialize(get_object_vars($this))*/);
1123  }
1124
1125  function isDefaultFile($file)
1126  {
1127    $fexists1 = file_exists($this->theme_path."/default/".$file);
1128    $fexists2 = file_exists($this->theme_path."/".$this->theme."/".$file);
1129    return ($this->theme == "default" ? $fexists1 : !$fexists2);
1130  }
1131
1132  function getFilePathFromTheme($file)
1133  {
1134    if (file_exists($this->theme_path."/".$this->theme."/".$file))
1135      return $this->theme_path."/".$this->theme."/".$file;
1136    else
1137      if (file_exists($this->theme_default_path."/default/".$file))
1138        return $this->theme_default_path."/default/".$file;
1139      else
1140      {
1141        $this->destroyCache();
1142        die(_pfc("Error: '%s' could not be found, please check your themepath '%s' and your theme '%s' are correct", $file, $this->theme_path, $this->theme));
1143      }
1144  }
1145
1146  function getFileUrlFromTheme($file)
1147  {
1148    if (file_exists($this->theme_path.'/'.$this->theme.'/'.$file))
1149      return $this->theme_url.'/'.$this->theme.'/'.$file;
1150    else
1151      if (file_exists($this->theme_default_path.'/default/'.$file))
1152        return $this->theme_default_url.'/default/'.$file;
1153      else
1154        return 'notfound';
1155  }
1156
1157
1158  function filterNickname($nickname)
1159  {
1160    $nickname = trim($nickname);
1161    require_once dirname(__FILE__)."/../lib/utf8/utf8_substr.php";
1162    $nickname = (string)utf8_substr($nickname, 0, $this->max_nick_len);
1163    return $nickname;
1164  }
1165}
1166
1167?>
1168