OSDN Git Service

Allow pluguins to authenticate before calling ticketForPlugin() function
[nucleus-jp/nucleus-jp-ancient.git] / euc / nucleus / libs / globalfunctions.php
1 <?php
2
3 /*
4  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
5  * Copyright (C) 2002-2007 The Nucleus Group
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  * (see nucleus/documentation/index.html#license for more info)
12  */
13 /**
14  * @license http://nucleuscms.org/license.txt GNU General Public License
15  * @copyright Copyright (C) 2002-2007 The Nucleus Group
16  * @version $Id: globalfunctions.php,v 1.10 2007-04-04 21:00:14 kmorimatsu Exp $
17  * $NucleusJP: globalfunctions.php,v 1.9 2007/03/27 12:13:56 kimitake Exp $
18  */
19
20 // needed if we include globalfunctions from install.php
21 global $nucleus, $CONF, $DIR_LIBS, $DIR_LANG, $manager, $member;
22
23 //$nucleus['version'] = 'v3.3SVN';
24 //$nucleus['codename'] = 'Lithium';
25 $nucleus['version'] = 'v3.3';
26 $nucleus['codename'] = '';
27
28 checkVars(array('nucleus', 'CONF', 'DIR_LIBS', 'MYSQL_HOST', 'MYSQL_USER', 'MYSQL_PASSWORD', 'MYSQL_DATABASE', 'DIR_LANG', 'DIR_PLUGINS', 'HTTP_GET_VARS', 'HTTP_POST_VARS', 'HTTP_COOKIE_VARS', 'HTTP_ENV_VARS', 'HTTP_SESSION_VARS', 'HTTP_POST_FILES', 'HTTP_SERVER_VARS', 'GLOBALS', 'argv', 'argc', '_GET', '_POST', '_COOKIE', '_ENV', '_SESSION', '_SERVER', '_FILES'));
29
30 $CONF['debug'] = 0;
31 if ($CONF['debug']) {
32         error_reporting(E_ALL); // report all errors!
33 } else {
34         error_reporting(E_ERROR | E_WARNING | E_PARSE);
35 }
36
37 // Avoid notices
38 if (!isset($CONF['Self'])) {
39         $CONF['Self'] = $_SERVER['PHP_SELF'];
40 }
41
42 /*
43         Indicates when Nucleus should display startup errors. Set to 1 if you want
44         the error enabled (default), false otherwise
45
46         alertOnHeadersSent
47                 Displays an error when visiting a public Nucleus page and headers have
48                 been sent out to early. This usually indicates an error in either a
49                 configuration file or a language file, and could cause Nucleus to
50                 malfunction
51         alertOnSecurityRisk
52                 Displays an error only when visiting the admin area, and when one or
53                 more of the installation files (install.php, install.sql, upgrades/
54                 directory) are still on the server.
55 */
56 $CONF['alertOnHeadersSent'] = 1;
57 $CONF['alertOnSecurityRisk'] = 1;
58 $CONF['ItemURL'] = $CONF['Self'];
59 $CONF['ArchiveURL'] = $CONF['Self'];
60 $CONF['ArchiveListURL'] = $CONF['Self'];
61 $CONF['MemberURL'] = $CONF['Self'];
62 $CONF['SearchURL'] = $CONF['Self'];
63 $CONF['BlogURL'] = $CONF['Self'];
64 $CONF['CategoryURL'] = $CONF['Self'];
65
66 // switch URLMode back to normal when $CONF['Self'] ends in .php
67 // this avoids urls like index.php/item/13/index.php/item/15
68 if (!isset($CONF['URLMode']) || (($CONF['URLMode'] == 'pathinfo') && (substr($CONF['Self'], strlen($CONF['Self']) - 4) == '.php'))) {
69         $CONF['URLMode'] = 'normal';
70 }
71
72 if (getNucleusPatchLevel() > 0) {
73         $nucleus['version'] .= '/' . getNucleusPatchLevel();
74 }
75
76 // Avoid notices
77 if (!isset($CONF['installscript'])) {
78         $CONF['installscript'] = 0;
79 }
80
81 // we will use postVar, getVar, ... methods instead of HTTP_GET_VARS or _GET
82 if ($CONF['installscript'] != 1) { // vars were already included in install.php
83         if (phpversion() >= '4.1.0') {
84                 include_once($DIR_LIBS . 'vars4.1.0.php');
85         } else {
86                 include_once($DIR_LIBS . 'vars4.0.6.php');
87         }
88 }
89
90 // sanitize option
91 $bLoggingSanitizedResult=0;
92 $bSanitizeAndContinue=0;
93
94 $orgRequestURI = serverVar('REQUEST_URI');
95 sanitizeParams();
96
97 // get all variables that can come from the request and put them in the global scope
98 $blogid = requestVar('blogid');
99 $itemid = intRequestVar('itemid');
100 $catid = intRequestVar('catid');
101 $skinid = requestVar('skinid');
102 $memberid = requestVar('memberid');
103 $archivelist = requestVar('archivelist');
104 $imagepopup = requestVar('imagepopup');
105 $archive = requestVar('archive');
106 $query = requestVar('query');
107 $highlight = requestVar('highlight');
108 $amount = requestVar('amount');
109 $action = requestVar('action');
110 $nextaction = requestVar('nextaction');
111 $maxresults = requestVar('maxresults');
112 $startpos = intRequestVar('startpos');
113 $errormessage = '';
114 $error = '';
115 $virtualpath = ((getVar('virtualpath') != null) ? getVar('virtualpath') : serverVar('PATH_INFO'));
116
117 if (!headers_sent() ) {
118         header('Generator: Nucleus CMS ' . $nucleus['version']);
119 }
120
121 // include core classes that are needed for login & plugin handling
122 include($DIR_LIBS . 'mysql.php');
123 include($DIR_LIBS . 'MEMBER.php');
124 include($DIR_LIBS . 'ACTIONLOG.php');
125 include($DIR_LIBS . 'MANAGER.php');
126 include($DIR_LIBS . 'PLUGIN.php');
127
128 $manager =& MANAGER::instance();
129
130 // make sure there's no unnecessary escaping:
131 set_magic_quotes_runtime(0);
132
133 // Avoid notices
134 if (!isset($CONF['UsingAdminArea'])) {
135         $CONF['UsingAdminArea'] = 0;
136 }
137
138 // only needed when updating logs
139 if ($CONF['UsingAdminArea']) {
140         include($DIR_LIBS . 'xmlrpc.inc.php');  // XML-RPC client classes
141         include_once($DIR_LIBS . 'ADMIN.php');
142 }
143
144 // connect to database
145 sql_connect();
146 $SQLCount = 0;
147
148 // logs sanitized result if need
149 if ($orgRequestURI!==serverVar('REQUEST_URI')) {
150         $msg = "Sanitized [" . serverVar('REMOTE_ADDR') . "] ";
151         $msg .= $orgRequestURI . " -> " . serverVar('REQUEST_URI');
152     if ($bLoggingSanitizedResult) {
153         addToLog(WARNING, $msg);
154     }
155     if (!$bSanitizeAndContinue) {
156         die("");
157     }
158 }
159
160 // makes sure database connection gets closed on script termination
161 register_shutdown_function('sql_disconnect');
162
163 // read config
164 getConfig();
165
166 // automatically use simpler toolbar for mozilla
167 if (($CONF['DisableJsTools'] == 0) && strstr(serverVar('HTTP_USER_AGENT'), 'Mozilla/5.0') && strstr(serverVar('HTTP_USER_AGENT'), 'Gecko') ) {
168         $CONF['DisableJsTools'] = 2;
169 }
170
171 // login if cookies set
172 $member = new MEMBER();
173
174 // login/logout when required or renew cookies
175 if ($action == 'login') {
176         // Form Authentication
177         $login = postVar('login');
178         $pw = postVar('password');
179         $shared = intPostVar('shared'); // shared computer or not
180
181         if ($member->login($login, $pw) ) {
182
183                 $member->newCookieKey();
184                 $member->setCookies($shared);
185
186                 // allows direct access to parts of the admin area after logging in
187                 if ($nextaction) {
188                         $action = $nextaction;
189                 }
190
191                 $manager->notify('LoginSuccess', array('member' => &$member) );
192                 $errormessage = '';
193                 ACTIONLOG::add(INFO, "Login successful for $login (sharedpc=$shared)");
194         } else {
195                 // errormessage for [%errordiv%]
196                 $errormessage = 'Login failed for ' . $login;
197
198                 $manager->notify('LoginFailed', array('username' => $login) );
199                 ACTIONLOG::add(INFO, $errormessage);
200         }
201 /*
202
203 Backed out for now: See http://forum.nucleuscms.org/viewtopic.php?t=3684 for details
204
205 } elseif (serverVar('PHP_AUTH_USER') && serverVar('PHP_AUTH_PW')) {
206         // HTTP Authentication
207         $login  = serverVar('PHP_AUTH_USER');
208         $pw     = serverVar('PHP_AUTH_PW');
209
210         if ($member->login($login, $pw) ) {
211                 $manager->notify('LoginSuccess',array('member' => &$member));
212                 ACTIONLOG::add(INFO, "HTTP authentication successful for $login");
213         } else {
214                 $manager->notify('LoginFailed',array('username' => $login));
215                 ACTIONLOG::add(INFO, 'HTTP authentication failed for ' . $login);
216
217                 //Since bad credentials, generate an apropriate error page
218                 header("WWW-Authenticate: Basic realm=\"Nucleus CMS {$nucleus['version']}\"");
219                 header('HTTP/1.0 401 Unauthorized');
220                 echo 'Invalid username or password';
221                 exit;
222         }
223 */
224
225 } elseif (($action == 'logout') && (!headers_sent() ) && cookieVar($CONF['CookiePrefix'] . 'user') ) {
226         // remove cookies on logout
227         setcookie($CONF['CookiePrefix'] . 'user', '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
228         setcookie($CONF['CookiePrefix'] . 'loginkey', '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
229         $manager->notify('Logout', array('username' => cookieVar($CONF['CookiePrefix'] . 'user') ) );
230 } elseif (cookieVar($CONF['CookiePrefix'] . 'user') ) {
231         // Cookie Authentication
232         $res = $member->cookielogin(cookieVar($CONF['CookiePrefix'] . 'user'), cookieVar($CONF['CookiePrefix'] . 'loginkey') );
233
234         // renew cookies when not on a shared computer
235         if ($res && (cookieVar($CONF['CookiePrefix'] . 'sharedpc') != 1) && (!headers_sent() ) ) {
236                 $member->setCookies();
237         }
238 }
239
240 // login completed
241 $manager->notify('PostAuthentication', array('loggedIn' => $member->isLoggedIn() ) );
242 ticketForPlugin();
243
244 // first, let's see if the site is disabled or not. always allow admin area access.
245 if ($CONF['DisableSite'] && !$member->isAdmin() && !$CONF['UsingAdminArea']) {
246         redirect($CONF['DisableSiteURL']);
247         exit;
248 }
249
250 // load other classes
251 include($DIR_LIBS . 'PARSER.php');
252 include($DIR_LIBS . 'SKIN.php');
253 include($DIR_LIBS . 'TEMPLATE.php');
254 include($DIR_LIBS . 'BLOG.php');
255 include($DIR_LIBS . 'BODYACTIONS.php');
256 include($DIR_LIBS . 'COMMENTS.php');
257 include($DIR_LIBS . 'COMMENT.php');
258 //include($DIR_LIBS . 'ITEM.php');
259 include($DIR_LIBS . 'NOTIFICATION.php');
260 include($DIR_LIBS . 'BAN.php');
261 include($DIR_LIBS . 'PAGEFACTORY.php');
262 include($DIR_LIBS . 'SEARCH.php');
263 include($DIR_LIBS . 'entity.php');
264
265
266 // set lastVisit cookie (if allowed)
267 if (!headers_sent() ) {
268         if ($CONF['LastVisit']) {
269                 setcookie($CONF['CookiePrefix'] . 'lastVisit', time(), time() + 2592000, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
270         } else {
271                 setcookie($CONF['CookiePrefix'] . 'lastVisit', '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
272         }
273 }
274
275 // read language file, only after user has been initialized
276 $language = getLanguageName();
277 include($DIR_LANG . ereg_replace( '[\\|/]', '', $language) . '.php');
278
279 /*
280         Backed out for now: See http://forum.nucleuscms.org/viewtopic.php?t=3684 for details
281
282 // To remove after v2.5 is released and language files have been updated.
283 // Including this makes sure that language files for v2.5beta can still be used for v2.5final
284 // without having weird _SETTINGS_EXTAUTH string showing up in the admin area.
285 if (!defined('_MEMBERS_BYPASS'))
286 {
287         define('_SETTINGS_EXTAUTH',                     'Enable External Authentication');
288         define('_WARNING_EXTAUTH',                      'Warning: Enable only if needed.');
289         define('_MEMBERS_BYPASS',                       'Use External Authentication');
290 }
291
292 */
293
294 // make sure the archivetype skinvar keeps working when _ARCHIVETYPE_XXX not defined
295 if (!defined('_ARCHIVETYPE_MONTH') ) {
296         define('_ARCHIVETYPE_DAY', 'day');
297         define('_ARCHIVETYPE_MONTH', 'month');
298 }
299
300 // decode path_info
301 if ($CONF['URLMode'] == 'pathinfo') {
302         // initialize keywords if this hasn't been done before
303         if ($CONF['ItemKey'] == '') {
304                 $CONF['ItemKey'] = 'item';
305         }
306
307         if ($CONF['ArchiveKey'] == '') {
308                 $CONF['ArchiveKey'] = 'archive';
309         }
310
311         if ($CONF['ArchivesKey'] == '') {
312                 $CONF['ArchivesKey'] = 'archives';
313         }
314
315         if ($CONF['MemberKey'] == '') {
316                 $CONF['MemberKey'] = 'member';
317         }
318
319         if ($CONF['BlogKey'] == '') {
320                 $CONF['BlogKey'] = 'blog';
321         }
322
323         if ($CONF['CategoryKey'] == '') {
324                 $CONF['CategoryKey'] = 'category';
325         }
326
327         if ($CONF['SpecialskinKey'] == '') {
328                 $CONF['SpecialskinKey'] = 'special';
329         }
330
331         $parsed = false;
332         $manager->notify(
333                 'ParseURL',
334                 array(
335                         'type' => basename(serverVar('SCRIPT_NAME') ), // e.g. item, blog, ...
336                         'info' => $virtualpath,
337                         'complete' => &$parsed
338                 )
339         );
340
341         if (!$parsed) {
342                 // default implementation
343                 $data = explode("/", $virtualpath );
344                 for ($i = 0; $i < sizeof($data); $i++) {
345                         switch ($data[$i]) {
346                                 case $CONF['ItemKey']: // item/1 (blogid)
347                                         $i++;
348
349                                         if ($i < sizeof($data) ) {
350                                                 $itemid = intval($data[$i]);
351                                         }
352                                         break;
353
354                                 case $CONF['ArchivesKey']: // archives/1 (blogid)
355                                         $i++;
356
357                                         if ($i < sizeof($data) ) {
358                                                 $archivelist = intval($data[$i]);
359                                         }
360                                         break;
361
362                                 case $CONF['ArchiveKey']: // two possibilities: archive/yyyy-mm or archive/1/yyyy-mm (with blogid)
363                                         if ((($i + 1) < sizeof($data) ) && (!strstr($data[$i + 1], '-') ) ) {
364                                                 $blogid = intval($data[++$i]);
365                                         }
366
367                                         $i++;
368
369                                         if ($i < sizeof($data) ) {
370                                                 $archive = $data[$i];
371                                         }
372                                         break;
373
374                                 case 'blogid': // blogid/1
375                                 case $CONF['BlogKey']: // blog/1
376                                         $i++;
377
378                                         if ($i < sizeof($data) ) {
379                                                 $blogid = intval($data[$i]);
380                                         }
381                                         break;
382
383                                 case $CONF['CategoryKey']: // category/1 (catid)
384                                 case 'catid':
385                                         $i++;
386
387                                         if ($i < sizeof($data) ) {
388                                                 $catid = intval($data[$i]);
389                                         }
390                                         break;
391
392                                 case $CONF['MemberKey']:
393                                         $i++;
394
395                                         if ($i < sizeof($data) ) {
396                                                 $memberid = intval($data[$i]);
397                                         }
398                                         break;
399
400                 case $CONF['SpecialskinKey']:
401                     $i++;
402
403                                         if ($i < sizeof($data) ) {
404                         $_REQUEST['special'] = $data[$i];
405                     }
406                     break;
407
408                                 default:
409                                         // skip...
410                         }
411                 }
412         }
413 }
414
415 function intPostVar($name) {
416         return intval(postVar($name) );
417 }
418
419 function intGetVar($name) {
420         return intval(getVar($name) );
421 }
422
423 function intRequestVar($name) {
424         return intval(requestVar($name) );
425 }
426
427 function intCookieVar($name) {
428         return intval(cookieVar($name) );
429 }
430
431 /**
432   * returns the currently used version (100 = 1.00, 101 = 1.01, etc...)
433   */
434 function getNucleusVersion() {
435         return 330;
436 }
437
438 /**
439  * power users can install patches in between nucleus releases. These patches
440  * usually add new functionality in the plugin API and allow those to
441  * be tested without having to install CVS.
442  */
443 function getNucleusPatchLevel() {
444         return 0;
445 }
446
447 /**
448   * Connects to mysql server
449   */
450 function sql_connect() {
451         global $MYSQL_HOST, $MYSQL_USER, $MYSQL_PASSWORD, $MYSQL_DATABASE, $MYSQL_CONN;
452
453         $MYSQL_CONN = @mysql_connect($MYSQL_HOST, $MYSQL_USER, $MYSQL_PASSWORD) or startUpError('<p>Could not connect to MySQL database.</p>', 'Connect Error');
454         mysql_select_db($MYSQL_DATABASE) or startUpError('<p>Could not select database: ' . mysql_error() . '</p>', 'Connect Error');
455
456         return $MYSQL_CONN;
457 }
458
459 /**
460  * returns a prefixed nucleus table name
461  */
462 function sql_table($name) {
463         global $MYSQL_PREFIX;
464
465         if ($MYSQL_PREFIX) {
466                 return $MYSQL_PREFIX . 'nucleus_' . $name;
467         } else {
468                 return 'nucleus_' . $name;
469         }
470 }
471
472 function sendContentType($contenttype, $pagetype = '', $charset = _CHARSET) {
473         global $manager, $CONF;
474
475         if (!headers_sent() ) {
476                 // if content type is application/xhtml+xml, only send it to browsers
477                 // that can handle it (IE6 cannot). Otherwise, send text/html
478
479                 // v2.5: For admin area pages, keep sending text/html (unless it's a debug version)
480                 //       application/xhtml+xml still causes too much problems with the javascript implementations
481
482                 // v3.3: ($CONF['UsingAdminArea'] && !$CONF['debug']) gets removed,
483                 //       application/xhtml+xml seems to be working, so we're going to use it if we can.
484                 //
485                 // Note: reverted the following function in JP version
486                 //
487         /*
488                 // v3.3 code
489                 if (
490                                 ($contenttype == 'application/xhtml+xml')
491                         &&      (!stristr(serverVar('HTTP_ACCEPT'), 'application/xhtml+xml') )
492                         ) {
493                         $contenttype = 'text/html';
494                 }
495         */
496                 // v3.2x code
497                 if (
498                                 ($contenttype == 'application/xhtml+xml')
499                         &&      (($CONF['UsingAdminArea'] && !$CONF['debug']) || !stristr(serverVar('HTTP_ACCEPT'),'application/xhtml+xml'))
500                         )
501                 {
502                         $contenttype = 'text/html';
503                 }
504
505                 $manager->notify(
506                         'PreSendContentType',
507                         array(
508                                 'contentType' => &$contenttype,
509                                 'charset' => &$charset,
510                                 'pageType' => $pagetype
511                         )
512                 );
513
514                 // strip strange characters
515                 $contenttype = preg_replace('|[^a-z0-9-+./]|i', '', $contenttype);
516                 $charset = preg_replace('|[^a-z0-9-_]|i', '', $charset);
517
518                 if ($charset != '') {
519                         header('Content-Type: ' . $contenttype . '; charset=' . $charset);
520                 } else {
521                         header('Content-Type: ' . $contenttype);
522                 }
523         }
524 }
525
526 /**
527  * Errors before the database connection has been made
528  */
529 function startUpError($msg, $title) {
530         ?>
531         <html xmlns="http://www.w3.org/1999/xhtml">
532                 <head><meta http-equiv="Content-Type" content="text/html; charset=EUC-JP" />
533                 <title><?php echo htmlspecialchars($title)?></title></head>
534                 <body>
535                         <h1><?php echo htmlspecialchars($title)?></h1>
536                         <?php echo $msg?>
537                 </body>
538         </html>
539         <?php   exit;
540 }
541
542 /**
543   * disconnects from SQL server
544   */
545 function sql_disconnect() {
546         @mysql_close();
547 }
548
549 /**
550   * executes an SQL query
551   */
552 function sql_query($query) {
553         global $SQLCount;
554         $SQLCount++;
555         $res = mysql_query($query) or print("mySQL error with query $query: " . mysql_error() . '<p />');
556         return $res;
557 }
558
559
560 /**
561  * Highlights a specific query in a given HTML text (not within HTML tags) and returns it
562  *
563  * @param $text
564  *              text to be highlighted
565  * @param $expression
566  *              regular expression to be matched (can be an array of expressions as well)
567  * @param $highlight
568  *              highlight to be used (use \\0 to indicate the matched expression)
569  *
570  */
571 function highlight($text, $expression, $highlight) {
572         if (!$highlight || !$expression) {
573                 return $text;
574         }
575
576         if (is_array($expression) && (count($expression) == 0) ) {
577                 return $text;
578         }
579
580         // add a tag in front (is needed for preg_match_all to work correct)
581         $text = '<!--h-->' . $text;
582
583         // split the HTML up so we have HTML tags
584         // $matches[0][i] = HTML + text
585         // $matches[1][i] = HTML
586         // $matches[2][i] = text
587         preg_match_all('/(<[^>]+>)([^<>]*)/', $text, $matches);
588
589         // throw it all together again while applying the highlight to the text pieces
590         $result = '';
591         for ($i = 0; $i < sizeof($matches[2]); $i++) {
592                 if ($i != 0) {
593                         $result .= $matches[1][$i];
594                 }
595
596                 if (is_array($expression) ) {
597                         foreach ($expression as $regex) {
598                                 if ($regex) {
599                                         $matches[2][$i] = @eregi_replace($regex, $highlight, $matches[2][$i]);
600                                 }
601                         }
602
603                         $result .= $matches[2][$i];
604                 } else {
605                         $result .= @eregi_replace($expression, $highlight, $matches[2][$i]);
606                 }
607         }
608
609         return $result;
610 }
611
612 /**
613  * Parses a query into an array of expressions that can be passed on to the highlight method
614  */
615 function parseHighlight($query) {
616         // TODO: add more intelligent splitting logic
617
618         // get rid of quotes
619         $query = preg_replace('/\'|"/', '', $query);
620
621         if (!query) {
622                 return array();
623         }
624
625         $aHighlight = explode(' ', $query);
626
627         for ($i = 0; $i < count($aHighlight); $i++) {
628                 $aHighlight[$i] = trim($aHighlight[$i]);
629
630                 if (strlen($aHighlight[$i]) < 3) {
631                         unset($aHighlight[$i]);
632                 }
633         }
634
635         if (count($aHighlight) == 1) {
636                 return $aHighlight[0];
637         } else {
638                 return $aHighlight;
639         }
640 }
641
642 /**
643   * Checks if email address is valid
644   */
645 function isValidMailAddress($address) {
646         if (preg_match('/^[a-zA-Z+0-9\._-]+@[a-zA-Z0-9\._-]+\.[A-Za-z]{2,5}$/', $address)) {
647                 return 1;
648         } else {
649                 return 0;
650         }
651 }
652
653
654 // some helper functions
655 function getBlogIDFromName($name) {
656         return quickQuery('SELECT bnumber as result FROM ' . sql_table('blog') . ' WHERE bshortname="' . addslashes($name) . '"');
657 }
658
659 function getBlogNameFromID($id) {
660         return quickQuery('SELECT bname as result FROM ' . sql_table('blog') . ' WHERE bnumber=' . intval($id) );
661 }
662
663 function getBlogIDFromItemID($itemid) {
664         return quickQuery('SELECT iblog as result FROM ' . sql_table('item') . ' WHERE inumber=' . intval($itemid) );
665 }
666
667 function getBlogIDFromCommentID($commentid) {
668         return quickQuery('SELECT cblog as result FROM ' . sql_table('comment') . ' WHERE cnumber=' . intval($commentid) );
669 }
670
671 function getBlogIDFromCatID($catid) {
672         return quickQuery('SELECT cblog as result FROM ' . sql_table('category') . ' WHERE catid=' . intval($catid) );
673 }
674
675 function getCatIDFromName($name) {
676         return quickQuery('SELECT catid as result FROM ' . sql_table('category') . ' WHERE cname="' . addslashes($name) . '"');
677 }
678
679 function quickQuery($q) {
680         $res = sql_query($q);
681         $obj = mysql_fetch_object($res);
682         return $obj->result;
683 }
684
685 function getPluginNameFromPid($pid) {
686         $res = sql_query('SELECT pfile FROM ' . sql_table('plugin') . ' WHERE pid=' . intval($pid) );
687         $obj = mysql_fetch_object($res);
688         return $obj->pfile;
689 }
690
691 function selector() {
692         global $itemid, $blogid, $memberid, $query, $amount, $archivelist, $maxresults;
693         global $archive, $skinid, $blog, $memberinfo, $CONF, $member;
694         global $imagepopup, $catid;
695         global $manager;
696
697         $actionNames = array('addcomment', 'sendmessage', 'createaccount', 'forgotpassword', 'votepositive', 'votenegative', 'plugin');
698         $action = requestVar('action');
699
700         if (in_array($action, $actionNames) ) {
701                 global $DIR_LIBS, $errormessage;
702                 include_once($DIR_LIBS . 'ACTION.php');
703                 $a = new ACTION();
704                 $errorInfo = $a->doAction($action);
705
706                 if ($errorInfo) {
707                         $errormessage = $errorInfo['message'];
708                 }
709         }
710
711         // show error when headers already sent out
712         if (headers_sent() && $CONF['alertOnHeadersSent']) {
713
714                 // try to get line number/filename (extra headers_sent params only exists in PHP 4.3+)
715                 if (function_exists('version_compare') && version_compare('4.3.0', phpversion(), '<=') ) {
716                         headers_sent($hsFile, $hsLine);
717                         $extraInfo = ' in <code>' . $hsFile . '</code> line <code>' . $hsLine . '</code>';
718                 } else {
719                         $extraInfo = '';
720                 }
721
722                 startUpError(
723                         '<p>The page headers have already been sent out' . $extraInfo . '. This could cause Nucleus not to work in the expected way.</p><p>Usually, this is caused by spaces or newlines at the end of the <code>config.php</code> file, at the end of the language file or at the end of a plugin file. Please check this and try again.</p><p>If you don\'t want to see this error message again, without solving the problem, set <code>$CONF[\'alertOnHeadersSent\']</code> in <code>globalfunctions.php</code> to <code>0</code></p>',
724                         'Page headers already sent'
725                 );
726                 exit;
727         }
728
729         // make is so ?archivelist without blogname or blogid shows the archivelist
730         // for the default weblog
731         if (serverVar('QUERY_STRING') == 'archivelist') {
732                 $archivelist = $CONF['DefaultBlog'];
733         }
734
735         // now decide which type of skin we need
736         if ($itemid) {
737                 // itemid given -> only show that item
738                 $type = 'item';
739
740                 if (!$manager->existsItem($itemid,0,0) ) {
741                         doError(_ERROR_NOSUCHITEM);
742                 }
743
744                 global $itemidprev, $itemidnext, $catid, $itemtitlenext, $itemtitleprev;
745
746                 // 1. get timestamp, blogid and catid for item
747                 $query = 'SELECT itime, iblog, icat FROM ' . sql_table('item') . ' WHERE inumber=' . intval($itemid);
748                 $res = sql_query($query);
749                 $obj = mysql_fetch_object($res);
750
751                 // if a different blog id has been set through the request or selectBlog(),
752                 // deny access
753 //              if ($blogid && (intval($blogid) != $obj->iblog) ) {
754 //                      doError(_ERROR_NOSUCHITEM);
755 //              }
756                 if ($blogid && (intval($blogid) != $obj->iblog)) {
757                         if (!headers_sent()) {
758                                 $b =& $manager->getBlog($obj->iblog);
759                                 $CONF['ItemURL'] = $b->getURL();
760                                 if ($CONF['URLMode'] == 'pathinfo' and substr($CONF['ItemURL'],-1) == '/')
761                                         $CONF['ItemURL'] = substr($CONF['ItemURL'], 0, -1);
762                                 $correctURL = createItemLink($itemid, '');
763                                 redirect($correctURL);
764                                 exit;
765                         } else {
766                                 doError(_ERROR_NOSUCHITEM);
767                         }
768                 }
769
770                 // if a category has been selected which doesn't match the item, ignore the
771                 // category. #85
772                 if (($catid != 0) && ($catid != $obj->icat) ) {
773                         $catid = 0;
774                 }
775
776                 $blogid = $obj->iblog;
777                 $timestamp = strtotime($obj->itime);
778
779                 $b =& $manager->getBlog($blogid);
780
781                 if ($b->isValidCategory($catid) ) {
782                         $catextra = ' and icat=' . $catid;
783                 }
784
785                 // get previous itemid and title
786                 $query = 'SELECT inumber, ititle FROM ' . sql_table('item') . ' WHERE itime<' . mysqldate($timestamp) . ' and idraft=0 and iblog=' . $blogid . $catextra . ' ORDER BY itime DESC LIMIT 1';
787                 $res = sql_query($query);
788
789                 $obj = mysql_fetch_object($res);
790
791                 if ($obj) {
792                         $itemidprev = $obj->inumber;
793                         $itemtitleprev = $obj->ititle;
794                 }
795
796                 // get next itemid and title
797                 $query = 'SELECT inumber, ititle FROM ' . sql_table('item') . ' WHERE itime>' . mysqldate($timestamp) . ' and itime <= ' . mysqldate($b->getCorrectTime()) . ' and idraft=0 and iblog=' . $blogid . $catextra . ' ORDER BY itime ASC LIMIT 1';
798                 $res = sql_query($query);
799
800                 $obj = mysql_fetch_object($res);
801
802                 if ($obj) {
803                         $itemidnext = $obj->inumber;
804                         $itemtitlenext = $obj->ititle;
805                 }
806
807         } elseif ($archive) {
808                 // show archive
809                 $type = 'archive';
810
811                 // get next and prev month links ...
812                 global $archivenext, $archiveprev, $archivetype, $archivenextexists, $archiveprevexists;
813
814                 // sql queries for the timestamp of the first and the last published item
815                 $query = "SELECT UNIX_TIMESTAMP(itime) as result FROM ".sql_table('item')." WHERE idraft=0 ORDER BY itime ASC";
816                 $first_timestamp=quickQuery ($query); 
817                 $query = "SELECT UNIX_TIMESTAMP(itime) as result FROM ".sql_table('item')." WHERE idraft=0 ORDER BY itime DESC";
818                 $last_timestamp=quickQuery ($query); 
819
820                 sscanf($archive, '%d-%d-%d', $y, $m, $d);
821
822                 if ($d != 0) {
823                         $archivetype = _ARCHIVETYPE_DAY;
824                         $t = mktime(0, 0, 0, $m, $d, $y);
825
826                         $archiveprev = strftime('%Y-%m-%d', $t - (24 * 60 * 60) );
827                         // check for published items                    
828                         if ($t > $first_timestamp) {
829                                 $archiveprevexists = true;
830                         }
831                         else {
832                                 $archiveprevexists = false;
833                         }
834
835                         // one day later
836 //                      $t += 86400; 
837 //                      $archivenext = strftime('%Y-%m-%d', $t);
838                         $archivenext = strftime('%Y-%m-%d', $t + (24 * 60 * 60) );
839                         if ($t + (24 * 60 * 60) < $last_timestamp) {
840                                 $archivenextexists = true;
841                         }
842                         else {
843                                 $archivenextexists = false;
844                         }
845
846                 } else {
847                         $archivetype = _ARCHIVETYPE_MONTH;
848                         $t = mktime(0, 0, 0, $m, 1, $y);
849
850                         $archiveprev = strftime('%Y-%m', $t - (1 * 24 * 60 * 60) );
851                         if ($t > $first_timestamp) {
852                                 $archiveprevexists = true;
853                         }
854                         else {
855                                 $archiveprevexists = false;
856                         }
857
858 //                      $archivenext = strftime('%Y-%m', $t + (32 * 24 * 60 * 60) );
859                         // timestamp for the next month                 
860                         $t = mktime(0, 0, 0, $m+1, 1, $y);
861                         $archivenext = strftime('%Y-%m', $t);
862                         if ($t < $last_timestamp) {
863                                 $archivenextexists = true;
864                         }
865                         else {
866                                 $archivenextexists = false;
867                         }
868                 }
869
870         } elseif ($archivelist) {
871                 $type = 'archivelist';
872
873                 if (is_numeric($archivelist)) {
874                         $blogid = intVal($archivelist);
875                 } else {
876                         $blogid = getBlogIDFromName($archivelist);
877                 }
878
879                 if (!$blogid) {
880                         doError(_ERROR_NOSUCHBLOG);
881                 }
882
883         } elseif ($query) {
884                 global $startpos;
885                 $type = 'search';
886                 $query = stripslashes($query);
887                 if(preg_match("/^(\xA1{2}|\xe3\x80{2}|\x20)+$/",$query)){
888                                         $type = 'index';
889                 }
890                 $order = (_CHARSET == 'EUC-JP') ? 'EUC-JP, UTF-8,' : 'UTF-8, EUC-JP,';
891                 $query = mb_convert_encoding($query, _CHARSET, $order.' JIS, SJIS, ASCII');
892                 if (is_numeric($blogid)) {
893                         $blogid = intVal($blogid);
894                 } else {
895                         $blogid = getBlogIDFromName($blogid);
896                 }
897
898                 if (!$blogid) {
899                         doError(_ERROR_NOSUCHBLOG);
900                 }
901
902         } elseif ($memberid) {
903                 $type = 'member';
904
905                 if (!MEMBER::existsID($memberid) ) {
906                         doError(_ERROR_NOSUCHMEMBER);
907                 }
908
909                 $memberinfo = $manager->getMember($memberid);
910
911         } elseif ($imagepopup) {
912                 // media object (images etc.)
913                 $type = 'imagepopup';
914
915                 // TODO: check if media-object exists
916                 // TODO: set some vars?
917         } else {
918                 // show regular index page
919                 global $startpos;
920                 $type = 'index';
921         }
922
923         // decide which blog should be displayed
924         if (!$blogid) {
925                 $blogid = $CONF['DefaultBlog'];
926         }
927
928         $b =& $manager->getBlog($blogid);
929         $blog = $b;     // references can't be placed in global variables?
930
931         if (!$blog->isValid) {
932                 doError(_ERROR_NOSUCHBLOG);
933         }
934
935         // set catid if necessary
936         if ($catid) {
937                 $blog->setSelectedCategory($catid);
938         }
939
940         // decide which skin should be used
941         if ($skinid != '' && ($skinid == 0) ) {
942                 selectSkin($skinid);
943         }
944
945         if (!$skinid) {
946                 $skinid = $blog->getDefaultSkin();
947         }
948
949         $special = requestVar('special');
950         if (!empty($special) && isValidShortName($special)) {
951                 $type = strtolower($special);
952         }
953
954         $skin = new SKIN($skinid);
955
956         if (!$skin->isValid) {
957                 doError(_ERROR_NOSUCHSKIN);
958         }
959
960         // parse the skin
961         $skin->parse($type);
962 }
963
964 /**
965   * Show error skin with given message. An optional skin-object to use can be given
966   */
967 function doError($msg, $skin = '') {
968         global $errormessage, $CONF, $skinid, $blogid, $manager;
969
970         if ($skin == '') {
971
972                 if (SKIN::existsID($skinid) ) {
973                         $skin = new SKIN($skinid);
974                 } elseif ($manager->existsBlogID($blogid) ) {
975                         $blog =& $manager->getBlog($blogid);
976                         $skin = new SKIN($blog->getDefaultSkin() );
977                 } elseif ($CONF['DefaultBlog']) {
978                         $blog =& $manager->getBlog($CONF['DefaultBlog']);
979                         $skin = new SKIN($blog->getDefaultSkin() );
980                 } else {
981                         // this statement should actually never be executed
982                         $skin = new SKIN($CONF['BaseSkin']);
983                 }
984
985         }
986
987         $errormessage = $msg;
988         $skin->parse('error');
989         exit;
990 }
991
992 function getConfig() {
993         global $CONF;
994
995         $query = 'SELECT * FROM ' . sql_table('config');
996         $res = sql_query($query);
997
998         while ($obj = mysql_fetch_object($res) ) {
999                 $CONF[$obj->name] = $obj->value;
1000         }
1001 }
1002
1003 // some checks for names of blogs, categories, templates, members, ...
1004 function isValidShortName($name) {
1005         return eregi('^[a-z0-9]+$', $name);
1006 }
1007
1008 function isValidDisplayName($name) {
1009         return eregi('^[a-z0-9]+[a-z0-9 ]*[a-z0-9]+$', $name);
1010 }
1011
1012 function isValidCategoryName($name) {
1013         return 1;
1014 }
1015
1016 function isValidTemplateName($name) {
1017         return eregi('^[a-z0-9/]+$', $name);
1018 }
1019
1020 function isValidSkinName($name) {
1021         return eregi('^[a-z0-9/]+$', $name);
1022 }
1023
1024 // add and remove linebreaks
1025 function addBreaks($var) {
1026         return nl2br($var);
1027 }
1028
1029 function removeBreaks($var) {
1030         return preg_replace("/<br \/>([\r\n])/", "$1", $var);
1031 }
1032
1033 // shortens a text string to maxlength ($toadd) is what needs to be added
1034 // at the end (end length is <= $maxlength)
1035 function shorten($text, $maxlength, $toadd) {
1036         // 1. remove entities...
1037 //      $trans = get_html_translation_table(HTML_ENTITIES);
1038         $trans = get_html_translation_table(HTML_SPECIALCHARS); // for Japanese
1039         $trans = array_flip($trans);
1040         $text = strtr($text, $trans);
1041
1042         // 2. the actual shortening
1043         if (strlen($text) > $maxlength)
1044                 $text = mb_strimwidth($text, 0, $maxlength, $toadd, _CHARSET);
1045         return $text;
1046 }
1047
1048 /**
1049   * Converts a unix timestamp to a mysql DATETIME format, and places
1050   * quotes around it.
1051   */
1052 function mysqldate($timestamp) {
1053         return '"' . date('Y-m-d H:i:s', $timestamp) . '"';
1054 }
1055
1056 /**
1057   * functions for use in index.php
1058   */
1059 function selectBlog($shortname) {
1060         global $blogid, $archivelist;
1061         $blogid = getBlogIDFromName($shortname);
1062
1063         // also force archivelist variable, if it is set
1064         if ($archivelist) {
1065                 $archivelist = $blogid;
1066         }
1067 }
1068
1069 function selectSkin($skinname) {
1070         global $skinid;
1071         $skinid = SKIN::getIdFromName($skinname);
1072 }
1073
1074 /**
1075  * Can take either a category ID or a category name (be aware that
1076  * multiple categories can have the same name)
1077  */
1078 function selectCategory($cat) {
1079         global $catid;
1080         if (is_numeric($cat) ) {
1081                 $catid = intval($cat);
1082         } else {
1083                 $catid = getCatIDFromName($cat);
1084         }
1085 }
1086
1087 function selectItem($id) {
1088         global $itemid;
1089         $itemid = intval($id);
1090 }
1091
1092 // force the use of a language file (warning: can cause warnings)
1093 function selectLanguage($language) {
1094         global $DIR_LANG;
1095         include($DIR_LANG . ereg_replace( '[\\|/]', '', $language) . '.php');
1096 }
1097
1098 function parseFile($filename, $includeMode = 'normal', $includePrefix = '') {
1099         $handler = new ACTIONS('fileparser');
1100         $parser = new PARSER(SKIN::getAllowedActionsForType('fileparser'), $handler);
1101         $handler->parser =& $parser;
1102
1103         // set IncludeMode properties of parser
1104         PARSER::setProperty('IncludeMode', $includeMode);
1105         PARSER::setProperty('IncludePrefix', $includePrefix);
1106
1107         if (!file_exists($filename) ) {
1108                 doError('A file is missing');
1109         }
1110
1111         $fsize = filesize($filename);
1112
1113         if ($fsize <= 0) {
1114                 return;
1115         }
1116
1117         // read file
1118         $fd = fopen ($filename, 'r');
1119         $contents = fread ($fd, $fsize);
1120         fclose ($fd);
1121
1122         // parse file contents
1123         $parser->parse($contents);
1124 }
1125
1126 /**
1127   * Outputs a debug message
1128   */
1129 function debug($msg) {
1130         echo '<p><b>' . $msg . "</b></p>\n";
1131 }
1132
1133 // shortcut
1134 function addToLog($level, $msg) {
1135         ACTIONLOG::add($level, $msg);
1136 }
1137
1138 // shows a link to help file
1139 function help($id) {
1140         echo helpHtml($id);
1141 }
1142
1143 function helpHtml($id) {
1144         return helplink($id) . '<img src="documentation/icon-help.gif" width="15" height="15" alt="' . _HELP_TT . '" /></a>';
1145 }
1146
1147 function helplink($id) {
1148         return '<a href="documentation/help.html#'. $id . '" onclick="if (event &amp;&amp; event.preventDefault) event.preventDefault(); return help(this.href);">';
1149 }
1150
1151 function getMailFooter() {
1152         $message = "\n\n-----------------------------";
1153         $message .=  "\n   Powered by Nucleus CMS";
1154         $message .=  "\n(http://www.nucleuscms.org/)";
1155         return $message;
1156 }
1157
1158 /**
1159   * Returns the name of the language to use
1160   * preference priority: member - site
1161   * defaults to english when no good language found
1162   *
1163   * checks if file exists, etc...
1164   */
1165 function getLanguageName() {
1166         global $CONF, $member;
1167
1168         if ($member && $member->isLoggedIn() ) {
1169                 // try to use members language
1170                 $memlang = $member->getLanguage();
1171
1172                 if (($memlang != '') && (checkLanguage($memlang) ) ) {
1173                         return $memlang;
1174                 }
1175         }
1176
1177         // use default language
1178         if (checkLanguage($CONF['Language']) ) {
1179                 return $CONF['Language'];
1180         } else {
1181                 return 'english';
1182         }
1183 }
1184
1185 /**
1186   * Includes a PHP file. This method can be called while parsing templates and skins
1187   */
1188 function includephp($filename) {
1189         // make predefined variables global, so most simple scripts can be used here
1190
1191         // apache (names taken from PHP doc)
1192         global $GATEWAY_INTERFACE, $SERVER_NAME, $SERVER_SOFTWARE, $SERVER_PROTOCOL;
1193         global $REQUEST_METHOD, $QUERY_STRING, $DOCUMENT_ROOT, $HTTP_ACCEPT;
1194         global $HTTP_ACCEPT_CHARSET, $HTTP_ACCEPT_ENCODING, $HTTP_ACCEPT_LANGUAGE;
1195         global $HTTP_CONNECTION, $HTTP_HOST, $HTTP_REFERER, $HTTP_USER_AGENT;
1196         global $REMOTE_ADDR, $REMOTE_PORT, $SCRIPT_FILENAME, $SERVER_ADMIN;
1197         global $SERVER_PORT, $SERVER_SIGNATURE, $PATH_TRANSLATED, $SCRIPT_NAME;
1198         global $REQUEST_URI;
1199
1200         // php (taken from PHP doc)
1201         global $argv, $argc, $PHP_SELF, $HTTP_COOKIE_VARS, $HTTP_GET_VARS, $HTTP_POST_VARS;
1202         global $HTTP_POST_FILES, $HTTP_ENV_VARS, $HTTP_SERVER_VARS, $HTTP_SESSION_VARS;
1203
1204         // other
1205         global $PATH_INFO, $HTTPS, $HTTP_RAW_POST_DATA, $HTTP_X_FORWARDED_FOR;
1206
1207         if (@file_exists($filename) ) {
1208                 include($filename);
1209         }
1210 }
1211
1212 /**
1213   * Checks if a certain language/plugin exists
1214   */
1215 function checkLanguage($lang) {
1216         global $DIR_LANG ;
1217         return file_exists($DIR_LANG . ereg_replace( '[\\|/]', '', $lang) . '.php');
1218 }
1219
1220 function checkPlugin($plug) {
1221         global $DIR_PLUGINS;
1222         return file_exists($DIR_PLUGINS . ereg_replace( '[\\|/]', '', $plug) . '.php');
1223 }
1224
1225 /**
1226   * Centralisation of the functions that generate links
1227   */
1228 function createItemLink($itemid, $extra = '') {
1229         return createLink('item', array('itemid' => $itemid, 'extra' => $extra) );
1230 }
1231
1232 function createMemberLink($memberid, $extra = '') {
1233         return createLink('member', array('memberid' => $memberid, 'extra' => $extra) );
1234 }
1235
1236 function createCategoryLink($catid, $extra = '') {
1237         return createLink('category', array('catid' => $catid, 'extra' => $extra) );
1238 }
1239
1240 function createArchiveListLink($blogid = '', $extra = '') {
1241         return createLink('archivelist', array('blogid' => $blogid, 'extra' => $extra) );
1242 }
1243
1244 function createArchiveLink($blogid, $archive, $extra = '') {
1245         return createLink('archive', array('blogid' => $blogid, 'archive' => $archive, 'extra' => $extra) );
1246 }
1247
1248 function createBlogidLink($blogid, $params = '') {
1249         return createLink('blog', array('blogid' => $blogid, 'extra' => $params) );
1250 }
1251
1252 function createLink($type, $params) {
1253         global $manager, $CONF;
1254
1255         $generatedURL = '';
1256         $usePathInfo = ($CONF['URLMode'] == 'pathinfo');
1257
1258         // ask plugins first
1259         $created = false;
1260
1261         if ($usePathInfo) {
1262                 $manager->notify(
1263                         'GenerateURL',
1264                         array(
1265                                 'type' => $type,
1266                                 'params' => $params,
1267                                 'completed' => &$created,
1268                                 'url' => &$url
1269                         )
1270                 );
1271         }
1272
1273         // if a plugin created the URL, return it
1274         if ($created) {
1275                 return $url;
1276         }
1277
1278         // default implementation
1279         switch ($type) {
1280                 case 'item':
1281                         if ($usePathInfo) {
1282                                 $url = $CONF['ItemURL'] . '/' . $CONF['ItemKey'] . '/' . $params['itemid'];
1283                         } else {
1284                                 $url = $CONF['ItemURL'] . '?itemid=' . $params['itemid'];
1285                         }
1286                         break;
1287
1288                 case 'member':
1289                         if ($usePathInfo) {
1290                                 $url = $CONF['MemberURL'] . '/' . $CONF['MemberKey'] . '/' . $params['memberid'];
1291                         } else {
1292                                 $url = $CONF['MemberURL'] . '?memberid=' . $params['memberid'];
1293                         }
1294                         break;
1295
1296                 case 'category':
1297                         if ($usePathInfo) {
1298                                 $url = $CONF['CategoryURL'] . '/' . $CONF['CategoryKey'] . '/' . $params['catid'];
1299                         } else {
1300                                 $url = $CONF['CategoryURL'] . '?catid=' . $params['catid'];
1301                         }
1302                         break;
1303
1304                 case 'archivelist':
1305                         if (!$params['blogid']) {
1306                                 $params['blogid'] = $CONF['DefaultBlog'];
1307                         }
1308
1309                         if ($usePathInfo) {
1310                                 $url = $CONF['ArchiveListURL'] . '/' . $CONF['ArchivesKey'] . '/' . $params['blogid'];
1311                         } else {
1312                                 $url = $CONF['ArchiveListURL'] . '?archivelist=' . $params['blogid'];
1313                         }
1314                         break;
1315
1316                 case 'archive':
1317                         if ($usePathInfo) {
1318                                 $url = $CONF['ArchiveURL'] . '/' . $CONF['ArchiveKey'] . '/'.$params['blogid'].'/' . $params['archive'];
1319                         } else {
1320                                 $url = $CONF['ArchiveURL'] . '?blogid='.$params['blogid'].'&amp;archive=' . $params['archive'];
1321                         }
1322                         break;
1323
1324                 case 'blog':
1325                         if ($usePathInfo) {
1326                                 $url = $CONF['BlogURL'] . '/' . $CONF['BlogKey'] . '/' . $params['blogid'];
1327                         } else {
1328                                 $url = $CONF['BlogURL'] . '?blogid=' . $params['blogid'];
1329                         }
1330                         break;
1331         }
1332
1333         return addLinkParams($url, (isset($params['extra'])? $params['extra'] : null));
1334 }
1335
1336 function createBlogLink($url, $params) {
1337     global $CONF;
1338     if ($CONF['URLMode'] == 'normal') {
1339         if (strpos($url, '?') === FALSE && is_array($params)) {
1340             $fParam = reset($params);
1341             $fKey   = key($params);
1342             array_shift($params);
1343             $url .= '?' . $fKey . '=' . $fParam;
1344         }
1345     } elseif ($CONF['URLMode'] == 'pathinfo' && substr($url, -1) == '/') {
1346         $url = substr($url, 0, -1);
1347     }
1348         return addLinkParams($url, $params);
1349 }
1350
1351 function addLinkParams($link, $params) {
1352         global $CONF;
1353
1354         if (is_array($params) ) {
1355
1356                 if ($CONF['URLMode'] == 'pathinfo')     {
1357
1358                         foreach ($params as $param => $value) {
1359                                 $link .= '/' . $param . '/' . urlencode($value);
1360                         }
1361
1362                 } else {
1363
1364                         foreach ($params as $param => $value) {
1365                                 $link .= '&amp;' . $param . '=' . urlencode($value);
1366                         }
1367
1368                 }
1369         }
1370
1371         return $link;
1372 }
1373
1374 /**
1375  * @param $querystr
1376  *              querystring to alter (e.g. foo=1&bar=2&x=y)
1377  * @param $param
1378  *              name of parameter to change (e.g. 'foo')
1379  * @param $value
1380  *              New value for that parameter (e.g. 3)
1381  * @result
1382  *              altered query string (for the examples above: foo=3&bar=2&x=y)
1383  */
1384 function alterQueryStr($querystr, $param, $value) {
1385         $vars = explode('&', $querystr);
1386         $set  = false;
1387
1388         for ($i = 0; $i < count($vars); $i++) {
1389                 $v = explode('=', $vars[$i]);
1390
1391                 if ($v[0] == $param) {
1392                         $v[1] = $value;
1393                         $vars[$i] = implode('=', $v);
1394                         $set = true;
1395                         break;
1396                 }
1397         }
1398
1399         if (!$set) {
1400                 $vars[] = $param . '=' . $value;
1401         }
1402
1403         return ltrim(implode('&', $vars), '&');
1404 }
1405
1406 // passes one variable as hidden input field (multiple fields for arrays)
1407 // @see passRequestVars in varsx.x.x.php
1408 function passVar($key, $value) {
1409         // array ?
1410         if (is_array($value) ) {
1411                 for ($i = 0; $i < sizeof($value); $i++) {
1412                         passVar($key . '[' . $i . ']', $value[$i]);
1413                 }
1414
1415                 return;
1416         }
1417
1418         // other values: do stripslashes if needed
1419         ?><input type="hidden" name="<?php echo htmlspecialchars($key)?>" value="<?php echo htmlspecialchars(undoMagic($value) )?>" /><?php
1420 }
1421
1422 /*
1423         Date format functions (to be used from [%date(..)%] skinvars
1424 */
1425 function formatDate($format, $timestamp, $defaultFormat, &$blog) {
1426         // apply blog offset (#42)
1427         $boffset = $blog ? $blog->getTimeOffset() * 3600 : 0;
1428         $offset = date('Z', $timestamp) + $boffset;
1429
1430         switch ($format) {
1431                 case 'rfc822':
1432                         if ($offset >= 0) {
1433                                 $tz = '+';
1434                         } else {
1435                                 $tz = '-';
1436                                 $offset = -$offset;
1437                         }
1438
1439                         $tz .= sprintf("%02d%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
1440                         return date('D, j M Y H:i:s ', $timestamp) . $tz;
1441
1442                 case 'rfc822GMT':
1443                         $timestamp -= $offset;
1444                         return date('D, j M Y H:i:s ', $timestamp) . 'GMT';
1445
1446                 case 'utc':
1447                         $timestamp -= $offset;
1448                         return date('Y-m-d\TH:i:s\Z', $timestamp);
1449
1450                 case 'iso8601':
1451                         if ($offset >= 0) {
1452                                 $tz = '+';
1453                         } else {
1454                                 $tz = '-';
1455                                 $offset = -$offset;
1456                         }
1457
1458                         $tz .= sprintf("%02d:%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
1459                         return date('Y-m-d\TH:i:s', $timestamp) . $tz;
1460
1461                 default :
1462                         return strftime($format ? $format : $defaultFormat, $timestamp);
1463         }
1464 }
1465
1466 function checkVars($aVars) {
1467         global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS, $HTTP_ENV_VARS, $HTTP_POST_FILES, $HTTP_SESSION_VARS;
1468
1469         foreach ($aVars as $varName) {
1470
1471                 if (phpversion() >= '4.1.0') {
1472
1473                         if (   isset($_GET[$varName])
1474                                 || isset($_POST[$varName])
1475                                 || isset($_COOKIE[$varName])
1476                                 || isset($_ENV[$varName])
1477                                 || isset($_SESSION[$varName])
1478                                 || isset($_FILES[$varName])
1479                         ) {
1480                                 die('Sorry. An error occurred.');
1481                         }
1482
1483                 } else {
1484
1485                         if (   isset($HTTP_GET_VARS[$varName])
1486                                 || isset($HTTP_POST_VARS[$varName])
1487                                 || isset($HTTP_COOKIE_VARS[$varName])
1488                                 || isset($HTTP_ENV_VARS[$varName])
1489                                 || isset($HTTP_SESSION_VARS[$varName])
1490                                 || isset($HTTP_POST_FILES[$varName])
1491                         ) {
1492                                 die('Sorry. An error occurred.');
1493                         }
1494
1495                 }
1496         }
1497 }
1498
1499
1500 /** 
1501  * Sanitize parameters such as $_GET and $_SERVER['REQUEST_URI'] etc.
1502  * to avoid XSS 
1503  */
1504 function sanitizeParams()
1505 {
1506         global $HTTP_SERVER_VARS;
1507         
1508         $array = array();
1509         $str = '';
1510         $frontParam = '';
1511         
1512         // REQUEST_URI of $HTTP_SERVER_VARS
1513         $str =& $HTTP_SERVER_VARS["REQUEST_URI"];
1514         serverStringToArray($str, $array, $frontParam);
1515         sanitizeArray($array);
1516         arrayToServerString($array, $frontParam, $str);
1517         
1518         // QUERY_STRING of $HTTP_SERVER_VARS
1519         $str =& $HTTP_SERVER_VARS["QUERY_STRING"];
1520         serverStringToArray($str, $array, $frontParam);
1521         sanitizeArray($array);
1522         arrayToServerString($array, $frontParam, $str);
1523         
1524         if (phpversion() >= '4.1.0') {
1525                 // REQUEST_URI of $_SERVER
1526                 $str =& $_SERVER["REQUEST_URI"];
1527                 serverStringToArray($str, $array, $frontParam);
1528                 sanitizeArray($array);
1529                 arrayToServerString($array, $frontParam, $str);
1530         
1531                 // QUERY_STRING of $_SERVER
1532                 $str =& $_SERVER["QUERY_STRING"];
1533                 serverStringToArray($str, $array, $frontParam);
1534                 sanitizeArray($array);
1535                 arrayToServerString($array, $frontParam, $str);
1536         }
1537         
1538         // $_GET
1539         convArrayForSanitizing($_GET, $array);
1540         sanitizeArray($array);
1541         revertArrayForSanitizing($array, $_GET);
1542         
1543         // $_REQUEST (only GET param)
1544         convArrayForSanitizing($_REQUEST, $array);
1545         sanitizeArray($array);
1546         revertArrayForSanitizing($array, $_REQUEST);
1547 }
1548
1549 /** 
1550  * Check ticket when not checked in plugin's admin page
1551  * to avoid CSRF.
1552  * Also avoid the access to plugin/index.php by guest user.
1553  */
1554 function ticketForPlugin(){
1555         global $CONF,$DIR_PLUGINS,$member,$ticketforplugin;
1556         
1557         /* initialize */
1558         $ticketforplugin=array();
1559         $ticketforplugin['ticket']=false;
1560         
1561         /* Check if using plugin's php file. */
1562         if ($p_translated=serverVar('PATH_TRANSLATED')) {
1563                 if (!file_exists($p_translated)) $p_translated='';
1564         }
1565         if (!$p_translated) {
1566                 $p_translated=serverVar('SCRIPT_FILENAME');
1567                 if (!file_exists($p_translated)) {
1568                         header("HTTP/1.0 404 Not Found");
1569                         exit('');
1570                 }
1571         }
1572         $p_translated=str_replace('\\','/',$p_translated);
1573         $d_plugins=str_replace('\\','/',$DIR_PLUGINS);
1574         if (strpos($p_translated,$d_plugins)!==0) return;// This isn't plugin php file.
1575         
1576         /* Solve the plugin php file or admin directory */
1577         $phppath=substr($p_translated,strlen($d_plugins));
1578         $phppath=preg_replace('!^/!','',$phppath);// Remove the first "/" if exists.
1579         $path=preg_replace('/^NP_(.*)\.php$/','$1',$phppath); // Remove the first "NP_" and the last ".php" if exists.
1580         $path=preg_replace('!^([^/]*)/(.*)$!','$1',$path); // Remove the "/" and beyond.
1581         
1582         /* Solve the plugin name. */
1583         $plugins=array();
1584         $query='SELECT pfile FROM '.sql_table('plugin');
1585         $res=sql_query($query);
1586         while($row=mysql_fetch_row($res)) {
1587                 $name=substr($row[0],3);
1588                 $plugins[strtolower($name)]=$name;
1589         }
1590         mysql_free_result($res);
1591         if ($plugins[$path]) $plugin_name=$plugins[$path];
1592         else if (in_array($path,$plugins)) $plugin_name=$path;
1593         else {
1594                 header("HTTP/1.0 404 Not Found");
1595                 exit('');
1596         }
1597         
1598         /* Return if not index.php */
1599         if ( $phppath!=strtolower($plugin_name).'/'
1600                 && $phppath!=strtolower($plugin_name).'/index.php' ) return;
1601         
1602         /* Exit if not logged in. */
1603         if ( !$member->isLoggedIn() ) exit("You aren't logged in.");
1604         
1605         global $manager,$DIR_LIBS,$DIR_LANG,$HTTP_GET_VARS,$HTTP_POST_VARS;
1606         
1607         /* Check if this feature is needed (ie, if "$manager->checkTicket()" is not included in the script). */
1608         if (!($p_translated=serverVar('PATH_TRANSLATED'))) $p_translated=serverVar('SCRIPT_FILENAME');
1609         if ($file=@file($p_translated)) {
1610                 $prevline='';
1611                 foreach($file as $line) {
1612                         if (preg_match('/[\$]manager([\s]*)[\-]>([\s]*)checkTicket([\s]*)[\(]/i',$prevline.$line)) return;
1613                         $prevline=$line;
1614                 }
1615         }
1616         
1617         /* Show a form if not valid ticket */
1618         if ( ( strstr(serverVar('REQUEST_URI'),'?') || serverVar('QUERY_STRING')
1619                         || strtoupper(serverVar('REQUEST_METHOD'))=='POST' )
1620                                 && (!$manager->checkTicket()) ){
1621
1622                 if (!class_exists('PluginAdmin')) {
1623                         $language = getLanguageName();
1624                         include($DIR_LANG . ereg_replace( '[\\|/]', '', $language) . '.php');
1625                         include($DIR_LIBS . 'PLUGINADMIN.php');
1626                 }
1627                 if (!(function_exists('mb_strimwidth') || extension_loaded('mbstring'))) {
1628                         if (file_exists($DIR_LIBS.'mb_emulator/mb-emulator.php')) {
1629                                 global $mbemu_internals;
1630                                 include_once($DIR_LIBS.'mb_emulator/mb-emulator.php');
1631                         }
1632                 }
1633                 $oPluginAdmin = new PluginAdmin($plugin_name);
1634                 $oPluginAdmin->start();
1635                 echo '<p>' . _ERROR_BADTICKET . "</p>\n";
1636                 
1637                 /* Show the form to confirm action */
1638                 // PHP 4.0.x support
1639                 $get=  (isset($_GET))  ? $_GET  : $HTTP_GET_VARS;
1640                 $post= (isset($_POST)) ? $_POST : $HTTP_POST_VARS;
1641                 // Resolve URI and QUERY_STRING
1642                 if ($uri=serverVar('REQUEST_URI')) {
1643                         list($uri,$qstring)=explode('?',$uri);
1644                 } else {
1645                         if ( !($uri=serverVar('PHP_SELF')) ) $uri=serverVar('SCRIPT_NAME');
1646                         $qstring=serverVar('QUERY_STRING');
1647                 }
1648                 if ($qstring) $qstring='?'.$qstring;
1649                 echo '<p>'._SETTINGS_UPDATE.' : '._QMENU_PLUGINS.' <span style="color:red;">'.
1650                         htmlspecialchars($plugin_name)."</span> ?</p>\n";
1651                 switch(strtoupper(serverVar('REQUEST_METHOD'))){
1652                 case 'POST':
1653                         echo '<form method="POST" action="'.htmlspecialchars($uri.$qstring).'">';
1654                         $manager->addTicketHidden();
1655                         _addInputTags($post);
1656                         break;
1657                 case 'GET':
1658                         echo '<form method="GET" action="'.htmlspecialchars($uri).'">';
1659                         $manager->addTicketHidden();
1660                         _addInputTags($get);
1661                 default:
1662                         break;
1663                 }
1664                 echo '<input type="submit" value="'._YES.'" />&nbsp;&nbsp;&nbsp;&nbsp;';
1665                 echo '<input type="button" value="'._NO.'" onclick="history.back(); return false;" />';
1666                 echo "</form>\n";
1667                 
1668                 $oPluginAdmin->end();
1669                 exit;
1670         }
1671         
1672         /* Create new ticket */
1673         $ticket=$manager->addTicketToUrl('');
1674         $ticketforplugin['ticket']=substr($ticket,strpos($ticket,'ticket=')+7);
1675 }
1676 function _addInputTags(&$keys,$prefix=''){
1677         foreach($keys as $key=>$value){
1678                 if ($prefix) $key=$prefix.'['.$key.']';
1679                 if (is_array($value)) _addInputTags($value,$key);
1680                 else {
1681                         if (get_magic_quotes_gpc()) $value=stripslashes($value);
1682                         if ($key=='ticket') continue;
1683                         echo '<input type="hidden" name="'.htmlspecialchars($key).
1684                                 '" value="'.htmlspecialchars($value).'" />'."\n";
1685                 }
1686         }
1687 }
1688
1689 /** 
1690  * Convert the server string such as $_SERVER['REQUEST_URI']
1691  * to arry like arry['blogid']=1 and array['page']=2 etc.
1692  */
1693 function serverStringToArray($str, &$array, &$frontParam)
1694 {
1695         // init param
1696         $array = array();
1697         $fronParam = "";
1698
1699         // split front param, e.g. /index.php, and others, e.g. blogid=1&page=2
1700         if (strstr($str, "?")){
1701                 list($frontParam, $args) = preg_split("/\?/", $str, 2);
1702         }
1703         else {
1704                 $args = $str;
1705                 $frontParam = "";
1706         }
1707         
1708         // If there is no args like blogid=1&page=2, return
1709         if (!strstr($str, "=") && !strlen($frontParam)) {
1710                 $frontParam = $str;
1711                 return;
1712         }
1713
1714         $array = explode("&", $args);
1715 }
1716
1717 /** 
1718  * Convert array like array['blogid'] to server string
1719  * such as $_SERVER['REQUEST_URI']
1720  */
1721 function arrayToServerString($array, $frontParam, &$str)
1722 {
1723         if (strstr($str, "?")) {
1724                 $str = $frontParam . "?";
1725         } else {
1726                 $str = $frontParam;
1727         }
1728         if (count($array)) {
1729                 $str .= implode("&", $array);
1730         }
1731 }
1732
1733 /** 
1734  * Sanitize array parameters.
1735  * This function checks both key and value.
1736  * - check key if it inclues " (double quote),  remove from array
1737  * - check value if it includes \ (escape sequece), remove remaining string
1738  */
1739 function sanitizeArray(&$array)
1740 {       
1741         $excludeListForSanitization = array('query');
1742 //      $excludeListForSanitization = array();
1743
1744         foreach ($array as $k => $v) {
1745
1746                 // split to key and value
1747                 list($key, $val) = preg_split("/=/", $v, 2);
1748                 if (!isset($val)) {
1749                         continue;
1750                 }
1751
1752                 // when magic quotes is on, need to use stripslashes,
1753                 // and then addslashes
1754                 if (get_magic_quotes_gpc()) {
1755                         $val = stripslashes($val);
1756                 }
1757                 $val = addslashes($val);
1758                 
1759                 // if $key is included in exclude list, skip this param
1760                 if (!in_array($key, $excludeListForSanitization)) {
1761                                 
1762                         // check value
1763                         list($val, $tmp) = explode('\\', $val);
1764                         
1765                         // remove control code etc.
1766                         $val = strtr($val, "\0\r\n<>'\"", "       ");
1767                                 
1768                         // check key
1769                         if (preg_match('/\"/i', $key)) {
1770                                 unset($array[$k]);
1771                                 continue;
1772                         }
1773                                 
1774                         // set sanitized info
1775                         $array[$k] = sprintf("%s=%s", $key, $val);
1776                 }
1777         }
1778 }
1779
1780 /**
1781  * Convert array for sanitizeArray function
1782  */
1783 function convArrayForSanitizing($src, &$array)
1784 {
1785         $array = array();
1786         foreach ($src as $key => $val) {
1787                 if (key_exists($key, $_GET)) {
1788                         array_push($array, sprintf("%s=%s", $key, $val));
1789                 }
1790         }
1791 }
1792
1793 /**
1794  * Revert array after sanitizeArray function
1795  */
1796 function revertArrayForSanitizing($array, &$dst)
1797 {
1798         foreach ($array as $v) {
1799                 list($key, $val) = preg_split("/=/", $v, 2);
1800                 $dst[$key] = $val;
1801         }
1802 }
1803
1804 /**
1805  * Stops processing the request and redirects to the given URL.
1806  * - no actual contents should have been sent to the output yet
1807  * - the URL will be stripped of illegal or dangerous characters
1808  */
1809 function redirect($url) {
1810         $url = preg_replace('|[^a-z0-9-~+_.?#=&;,/:@%]|i', '', $url);
1811         header('Location: ' . $url);
1812         exit;
1813 }
1814
1815 /**
1816  * Strip HTML tags from a string
1817  * This function is a bit more intelligent than a regular call to strip_tags(),
1818  * because it also deletes the contents of certain tags and cleans up any
1819  * unneeded whitespace.
1820  */
1821 function stringStripTags ($string) {
1822         $string = preg_replace("/<del[^>]*>.+<\/del[^>]*>/isU", '', $string);
1823         $string = preg_replace("/<script[^>]*>.+<\/script[^>]*>/isU", '', $string);
1824         $string = preg_replace("/<style[^>]*>.+<\/style[^>]*>/isU", '', $string);
1825         $string = str_replace('>', '> ', $string);
1826         $string = str_replace('<', ' <', $string);
1827         $string = strip_tags($string);
1828         $string = preg_replace("/\s+/", " ", $string);
1829         $string = trim($string);
1830         return $string;
1831 }
1832
1833 /**
1834  * Make a string containing HTML safe for use in a HTML attribute
1835  * Tags are stripped and entities are normalized
1836  */
1837 function stringToAttribute ($string) {
1838         $string = stringStripTags($string);
1839         $string = entity::named_to_numeric($string);
1840         $string = entity::normalize_numeric($string);
1841
1842         if (_CHARSET == 'UTF-8') {
1843                 $string = entity::numeric_to_utf8($string);
1844         }
1845
1846         $string = entity::specialchars($string, 'html');
1847         $string = entity::numeric_to_named($string);
1848         return $string;
1849 }
1850
1851 /**
1852  * Make a string containing HTML safe for use in a XML document
1853  * Tags are stripped, entities are normalized and named entities are
1854  * converted to numeric entities.
1855  */
1856 function stringToXML ($string) {
1857         $string = stringStripTags($string);
1858         $string = entity::named_to_numeric($string);
1859         $string = entity::normalize_numeric($string);
1860
1861         if (_CHARSET == 'UTF-8') {
1862                 $string = entity::numeric_to_utf8($string);
1863         }
1864
1865         $string = entity::specialchars($string, 'xml');
1866         return $string;
1867 }
1868
1869 // START: functions from the end of file BLOG.php
1870 // used for mail notification (html -> text)
1871 function toAscii($html) {
1872         // strip off most tags
1873         $html = strip_tags($html,'<a>');
1874         $to_replace = "/<a[^>]*href=[\"\']([^\"^']*)[\"\'][^>]*>([^<]*)<\/a>/i";
1875         _links_init();
1876         $ascii = preg_replace_callback ($to_replace, '_links_add', $html);
1877         $ascii .= "\n\n" . _links_list();
1878         return strip_tags($ascii);
1879 }
1880
1881 function _links_init() {
1882    global $tmp_links;
1883    $tmp_links = array();
1884 }
1885
1886 function _links_add($match) {
1887    global $tmp_links;
1888    array_push($tmp_links, $match[1]);
1889    return $match[2] . ' [' . sizeof($tmp_links) .']';
1890 }
1891
1892 function _links_list() {
1893    global $tmp_links;
1894    $output = '';
1895    $i = 1;
1896    foreach ($tmp_links as $current) {
1897           $output .= "[$i] $current\n";
1898           $i++;
1899    }
1900    return $output;
1901 }
1902 // END: functions from the end of file BLOG.php
1903
1904 // START: functions from the end of file ADMIN.php
1905 /**
1906  * @todo document this
1907  */
1908 function encode_desc(&$data)
1909     {   //_$to_entities = get_html_translation_table(HTML_ENTITIES);
1910         $to_entities = get_html_translation_table(HTML_SPECIALCHARS);
1911         $from_entities = array_flip($to_entities);
1912         $data = str_replace('<br />','\n',$data); //hack
1913         $data = strtr($data,$from_entities);
1914         $data = strtr($data,$to_entities);
1915         $data = str_replace('\n','<br />',$data); //hack
1916         return $data;
1917     }
1918  
1919 /**
1920  * Returns the Javascript code for a bookmarklet that works on most modern browsers
1921  *
1922  * @param blogid
1923  */
1924 function getBookmarklet($blogid) {
1925         global $CONF;
1926
1927         // normal
1928         $document = 'document';
1929         $bookmarkletline = "javascript:Q='';x=".$document.";y=window;if(x.selection){Q=x.selection.createRange().text;}else if(y.getSelection){Q=y.getSelection();}else if(x.getSelection){Q=x.getSelection();}wingm=window.open('";
1930         $bookmarkletline .= $CONF['AdminURL'] . "bookmarklet.php?blogid=$blogid";
1931         $bookmarkletline .="&logtext='+escape(Q)+'&loglink='+escape(x.location.href)+'&loglinktitle='+escape(x.title),'nucleusbm','scrollbars=yes,width=600,height=500,left=10,top=10,status=yes,resizable=yes');wingm.focus();";
1932
1933         return $bookmarkletline;
1934 }
1935 // END: functions from the end of file ADMIN.php
1936
1937 /**
1938  * Returns a variable or null if not set
1939  *
1940  * @param mixed Variable
1941  * @return mixed Variable
1942  */
1943 function ifset(&$var) {
1944         if (isset($var)) {
1945                 return $var;
1946         }
1947
1948         return null;
1949 }
1950
1951 ?>