OSDN Git Service

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