includes/functions.php~
changeset 1 fe660c52c48f
equal deleted inserted replaced
0:902822492a68 1:fe660c52c48f
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
       
     5  * Version 1.0 (Banshee)
       
     6  * Copyright (C) 2006-2007 Dan Fuhry
       
     7  *
       
     8  * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
       
     9  * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
       
    12  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
       
    13  */
       
    14  
       
    15 function getConfig($n) {
       
    16   global $enano_config;
       
    17   if(isset($enano_config[$n])) return $enano_config[$n];
       
    18   else                         return false;
       
    19 }
       
    20 
       
    21 function setConfig($n, $v) {
       
    22   global $enano_config, $db;
       
    23   $enano_config[$n] = $v;
       
    24   $v = $db->escape($v);
       
    25   $e=$db->sql_query('DELETE FROM '.table_prefix.'config WHERE config_name=\''.$n.'\';');
       
    26   if(!$e) $db->_die('Error during generic setConfig() call row deletion.');
       
    27   $e=$db->sql_query('INSERT INTO '.table_prefix.'config(config_name, config_value) VALUES(\''.$n.'\', \''.$v.'\')');
       
    28   if(!$e) $db->_die('Error during generic setConfig() call row insertion.');
       
    29 }
       
    30 
       
    31 function makeUrl($t, $query = false, $escape = false)
       
    32 {
       
    33   global $db, $session, $paths, $template, $plugins; // Common objects
       
    34   $flags = '';
       
    35   $sep = urlSeparator;
       
    36   if(isset($_GET['printable'])) { $flags .= $sep.'printable'; $sep = '&'; }
       
    37   if(isset($_GET['theme'])) { $flags .= $sep.'theme='.$session->theme; $sep = '&'; }
       
    38   if(isset($_GET['style'])) { $flags .= $sep.'style='.$session->style; $sep = '&'; }
       
    39   $url = $session->append_sid(contentPath.$t.$flags);
       
    40   if($query)
       
    41   {
       
    42     $sep = strstr($url, '?') ? '&' : '?';
       
    43     $url = $url . $sep . $query;
       
    44   }
       
    45   return ($escape) ? htmlspecialchars($url) : $url;
       
    46 }
       
    47 
       
    48 function makeUrlNS($n, $t, $query = false, $escape = false)
       
    49 {
       
    50   global $db, $session, $paths, $template, $plugins; // Common objects
       
    51   $flags = '';
       
    52   if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $sep = urlSeparator;
       
    53   else $sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?';
       
    54   if(isset($_GET['printable'])) { $flags .= $sep.'printable';              $sep = '&'; }
       
    55   if(isset($_GET['theme']))     { $flags .= $sep.'theme='.$session->theme; $sep = '&'; }
       
    56   if(isset($_GET['style']))     { $flags .= $sep.'style='.$session->style; $sep = '&'; }
       
    57   
       
    58   if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
       
    59   {
       
    60     $url = contentPath.$paths->nslist[$n].$t.$flags;
       
    61   }
       
    62   else
       
    63   {
       
    64     $url = contentPath.$n.':'.$t.$flags;
       
    65   }
       
    66   
       
    67   if($query)
       
    68   {
       
    69     if(strstr($url, '?')) $sep =  '&';
       
    70     else $sep = '?';
       
    71     $url = $url . $sep . $query . $flags;
       
    72   }
       
    73   
       
    74   if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
       
    75   {
       
    76     $url = $session->append_sid($url);
       
    77   }
       
    78   
       
    79   return ($escape) ? htmlspecialchars($url) : $url;
       
    80 }
       
    81 
       
    82 function makeUrlComplete($n, $t, $query = false, $escape = false)
       
    83 {
       
    84   global $db, $session, $paths, $template, $plugins; // Common objects
       
    85   $flags = '';
       
    86   if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $sep = urlSeparator;
       
    87   else $sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?';
       
    88   if(isset($_GET['printable'])) { $flags .= $sep.'printable';              $sep = '&'; }
       
    89   if(isset($_GET['theme']))     { $flags .= $sep.'theme='.$session->theme; $sep = '&'; }
       
    90   if(isset($_GET['style']))     { $flags .= $sep.'style='.$session->style; $sep = '&'; }
       
    91   if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $url = $session->append_sid(contentPath.$paths->nslist[$n].$t.$flags);
       
    92   else $url = contentPath.$n.':'.$t.$flags;
       
    93   if($query)
       
    94   {
       
    95     if(strstr($url, '?')) $sep =  '&';
       
    96     else $sep = '?';
       
    97     $url = $url . $sep . $query . $flags;
       
    98   }
       
    99   $baseprot = 'http' . ( isset($_SERVER['HTTPS']) ? 's' : '' ) . '://' . $_SERVER['HTTP_HOST'];
       
   100   $url = $baseprot . $url;
       
   101   return ($escape) ? htmlspecialchars($url) : $url;
       
   102 }
       
   103 
       
   104 /**
       
   105  * Redirect the user to the specified URL.
       
   106  * @param string $url The URL, either relative or absolute.
       
   107  * @param string $title The title of the message
       
   108  * @param string $message A short message to show to the user
       
   109  * @param string $timeout Timeout, in seconds, to delay the redirect. Defaults to 3.
       
   110  */
       
   111  
       
   112 function redirect($url, $title = 'Redirecting...', $message = 'Please wait while you are redirected.', $timeout = 3)
       
   113 {
       
   114   global $db, $session, $paths, $template, $plugins; // Common objects
       
   115   
       
   116   if ( $timeout == 0 )
       
   117   {
       
   118     header('Location: ' . $url);
       
   119     header('HTTP/1.1 307 Temporary Redirect');
       
   120   }
       
   121   
       
   122   $template->add_header('<meta http-equiv="refresh" content="' . $timeout . '; url=' . str_replace('"', '\\"', $url) . '" />');
       
   123   $template->add_header('<script type="text/javascript">
       
   124       function __r() {
       
   125         // FUNCTION AUTOMATICALLY GENERATED
       
   126         window.location="' . str_replace('"', '\\"', $url) . '";
       
   127       }
       
   128       setTimeout(\'__r();\', ' . $timeout . '000);
       
   129     </script>
       
   130     ');
       
   131   
       
   132   $template->tpl_strings['PAGE_NAME'] = $title;
       
   133   $template->header(true);
       
   134   echo '<p>' . $message . '</p><p>If you are not redirected within ' . ( $timeout + 1 ) . ' seconds, <a href="' . str_replace('"', '\\"', $url) . '">please click here</a>.</p>';
       
   135   $template->footer(true);
       
   136   
       
   137   $db->close();
       
   138   exit(0);
       
   139   
       
   140 }
       
   141 
       
   142 // Removed wikiFormat() from here, replaced with RenderMan::render
       
   143 
       
   144 function isPage($p) {
       
   145   global $db, $session, $paths, $template, $plugins; // Common objects
       
   146   if(isset($paths->pages[$p])) return true;
       
   147   $d = RenderMan::strToPageID($p);
       
   148   if($d[1] != 'Special' && $d[1] != 'Template' && $d[1] != 'Admin') return false;
       
   149   $a = explode('/', $p);
       
   150   if(isset($paths->pages[$a[0]])) return true;
       
   151   else return false;
       
   152 }
       
   153 
       
   154 function arrayItemUp($arr, $keyname) {
       
   155   $keylist = array_keys($arr);
       
   156   $keyflop = array_flip($keylist);
       
   157   $idx = $keyflop[$keyname];
       
   158   $idxm = $idx - 1;
       
   159   $temp = $arr[$keylist[$idxm]];
       
   160   if($arr[$keylist[0]] == $arr[$keyname]) return $arr;
       
   161   $arr[$keylist[$idxm]] = $arr[$keylist[$idx]];
       
   162   $arr[$keylist[$idx]] = $temp;
       
   163   return $arr;
       
   164 }
       
   165 
       
   166 function arrayItemDown($arr, $keyname) {
       
   167   $keylist = array_keys($arr);
       
   168   $keyflop = array_flip($keylist);
       
   169   $idx = $keyflop[$keyname];
       
   170   $idxm = $idx + 1;
       
   171   $temp = $arr[$keylist[$idxm]];
       
   172   $sz = sizeof($arr); $sz--;
       
   173   if($arr[$keylist[$sz]] == $arr[$keyname]) return $arr;
       
   174   $arr[$keylist[$idxm]]  =  $arr[$keylist[$idx]];
       
   175   $arr[$keylist[$idx]]   =  $temp;
       
   176   return $arr;
       
   177 }
       
   178 
       
   179 function arrayItemTop($arr, $keyname) {
       
   180   $keylist = array_keys($arr);
       
   181   $keyflop = array_flip($keylist);
       
   182   $idx = $keyflop[$keyname];
       
   183   while( $orig != $arr[$keylist[0]] ) {
       
   184     // echo 'Keyname: '.$keylist[$idx] . '<br />'; flush(); ob_flush(); // Debugger
       
   185     if($idx < 0) return $arr;
       
   186     if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) {
       
   187       /* echo 'Infinite loop caught in arrayItemTop(<br /><pre>';
       
   188       print_r($arr);
       
   189       echo '</pre><br />, '.$keyname.');<br /><br />EnanoCMS: Critical error during function call, exiting to prevent excessive server load.';
       
   190       exit; */
       
   191       return $arr;
       
   192     }
       
   193     $arr = arrayItemUp($arr, $keylist[$idx]);
       
   194     $idx--;
       
   195   }
       
   196   return $arr;
       
   197 }
       
   198 
       
   199 function arrayItemBottom($arr, $keyname) {
       
   200   $keylist = array_keys($arr);
       
   201   $keyflop = array_flip($keylist);
       
   202   $idx = $keyflop[$keyname];
       
   203   $sz = sizeof($arr); $sz--;
       
   204   while( $orig != $arr[$keylist[$sz]] ) {
       
   205     // echo 'Keyname: '.$keylist[$idx] . '<br />'; flush(); ob_flush(); // Debugger
       
   206     if($idx > $sz) return $arr;
       
   207     if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) {
       
   208       echo 'Infinite loop caught in arrayItemBottom(<br /><pre>';
       
   209       print_r($arr);
       
   210       echo '</pre><br />, '.$keyname.');<br /><br />EnanoCMS: Critical error during function call, exiting to prevent excessive server load.';
       
   211       exit;
       
   212     }
       
   213     $arr = arrayItemDown($arr, $keylist[$idx]);
       
   214     $idx++;
       
   215   }
       
   216   return $arr;
       
   217 }
       
   218 
       
   219 // Convert IP address to hex string
       
   220 // Input:  127.0.0.1  (string)
       
   221 // Output: 0x7f000001 (string)
       
   222 // Updated 12/8/06 to work with PHP4 and not use eval() (blech)
       
   223 function ip2hex($ip) {
       
   224   if ( preg_match('/^([0-9a-f:]+)$/', $ip) )
       
   225   {
       
   226     // this is an ipv6 address
       
   227     return str_replace(':', '', $ip);
       
   228   }
       
   229   $nums = explode('.', $ip);
       
   230   if(sizeof($nums) != 4) return false;
       
   231   $str = '0x';
       
   232   foreach($nums as $n)
       
   233   {
       
   234     $str .= (string)dechex($n);
       
   235   }
       
   236   return $str;
       
   237 }
       
   238 
       
   239 // Convert DWord to IP address
       
   240 // Input:  0x7f000001
       
   241 // Output: 127.0.0.1
       
   242 // Updated 12/8/06 to work with PHP4 and not use eval() (blech)
       
   243 function hex2ip($in) {
       
   244   if(substr($in, 0, 2) == '0x') $ip = substr($in, 2, 8);
       
   245   else $ip = substr($in, 0, 8);
       
   246   $octets = enano_str_split($ip, 2);
       
   247   $str = '';
       
   248   $newoct = Array();
       
   249   foreach($octets as $o)
       
   250   {
       
   251     $o = (int)hexdec($o);
       
   252     $newoct[] = $o;
       
   253   }
       
   254   return implode('.', $newoct);
       
   255 }
       
   256 
       
   257 // Function strip_php moved to RenderMan class
       
   258 
       
   259 function die_semicritical($t, $p)
       
   260 {
       
   261   global $db, $session, $paths, $template, $plugins; // Common objects
       
   262   $db->close();
       
   263   
       
   264   if ( ob_get_status() )
       
   265     ob_end_clean();
       
   266   
       
   267   dc_here('functions: <span style="color: red">calling die_semicritical</span>');
       
   268   
       
   269   $tpl = new template_nodb();
       
   270   $tpl->load_theme('oxygen', 'bleu');
       
   271   $tpl->tpl_strings['SITE_NAME'] = getConfig('site_name');
       
   272   $tpl->tpl_strings['SITE_DESC'] = getConfig('site_desc');
       
   273   $tpl->tpl_strings['COPYRIGHT'] = getConfig('copyright_notice');
       
   274   $tpl->tpl_strings['PAGE_NAME'] = $t;
       
   275   $tpl->header();
       
   276   echo $p;
       
   277   $tpl->footer();
       
   278   
       
   279   exit;
       
   280 }
       
   281 
       
   282 function die_friendly($t, $p)
       
   283 {
       
   284   global $db, $session, $paths, $template, $plugins; // Common objects
       
   285   
       
   286   if ( ob_get_status() )
       
   287     ob_end_clean();
       
   288   
       
   289   dc_here('functions: <span style="color: red">calling die_friendly</span>');
       
   290   $paths->cpage['name'] = $t;
       
   291   $template->tpl_strings['PAGE_NAME'] = $t;
       
   292   $template->header();
       
   293   echo $p;
       
   294   $template->footer();
       
   295   $db->close();
       
   296   
       
   297   exit;
       
   298 }
       
   299 
       
   300 function grinding_halt($t, $p)
       
   301 {
       
   302   global $db, $session, $paths, $template, $plugins; // Common objects
       
   303   
       
   304   $db->close();
       
   305   
       
   306   if ( ob_get_status() )
       
   307     ob_end_clean();
       
   308   
       
   309   dc_here('functions: <span style="color: red">calling grinding_halt</span>');
       
   310   $tpl = new template_nodb();
       
   311   $tpl->load_theme('oxygen', 'bleu');
       
   312   $tpl->tpl_strings['SITE_NAME'] = 'Critical error';
       
   313   $tpl->tpl_strings['SITE_DESC'] = 'This website is experiencing a serious error and cannot load.';
       
   314   $tpl->tpl_strings['COPYRIGHT'] = 'Unable to retrieve copyright information';
       
   315   $tpl->tpl_strings['PAGE_NAME'] = $t;
       
   316   $tpl->header();
       
   317   echo $p;
       
   318   $tpl->footer();
       
   319   exit;
       
   320 }
       
   321 
       
   322 function show_category_info() {
       
   323   global $db, $session, $paths, $template, $plugins; // Common objects
       
   324   dc_here('functions: showing category info');
       
   325   if($template->no_headers && !strpos($_SERVER['REQUEST_URI'], 'ajax.php')) return '';
       
   326   if($paths->namespace=='Category')
       
   327   {
       
   328     $q = $db->sql_query('SELECT page_id,namespace FROM '.table_prefix.'categories WHERE category_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\'Category\' ORDER BY page_id;');
       
   329     if(!$q) $db->_die('The category information could not be selected.');
       
   330     $ticker = -1;
       
   331     echo '<h3>Subcategories</h3>';
       
   332     if($db->numrows() < 1) echo '<p>There are no subcategories in this category.</p>';
       
   333     echo '<table border="0" cellspacing="1" cellpadding="4">';
       
   334     while($row = $db->fetchrow())
       
   335     {
       
   336       $ticker++;if($ticker==3) $ticker=0;
       
   337       if($ticker==0) echo '<tr>';
       
   338       echo '<td style="width: 200px;"><a href="'.makeUrlNS($row['namespace'], $row['page_id']).'">'.$paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name'].'</a></td>';
       
   339       if($ticker==2) echo '</tr>';
       
   340     }
       
   341     $db->free_result();
       
   342     if($ticker) echo '</tr>';
       
   343     echo '</table>';
       
   344     
       
   345     $q = $db->sql_query('SELECT page_id,namespace FROM '.table_prefix.'categories WHERE category_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace!=\'Category\' ORDER BY page_id;');
       
   346     if(!$q) $db->_die('The category information could not be selected.');
       
   347     $ticker = -1;
       
   348     echo '<h3>Pages</h3>';
       
   349     if($db->numrows() < 1) echo '<p>There are no pages in this category.</p>';
       
   350     echo '<table border="0" cellspacing="1" cellpadding="4">';
       
   351     while($row = $db->fetchrow())
       
   352     {
       
   353       $ticker++;if($ticker==3) $ticker=0;
       
   354       if($ticker==0) echo '<tr>';
       
   355       echo '<td style="width: 200px;"><a href="'.makeUrlNS($row['namespace'], $row['page_id']).'">'.$paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name'].'</a></td>';
       
   356       if($ticker==2) echo '</tr>';
       
   357     }
       
   358     $db->free_result();
       
   359     if($ticker) echo '</tr>';
       
   360     echo '</table><br /><br />';
       
   361   }
       
   362   $q = $db->sql_query('SELECT category_id FROM '.table_prefix.'categories WHERE page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$paths->namespace.'\'');
       
   363   if(!$q) $db->_die('The error seems to have occurred during selection of category data.');
       
   364   if($db->numrows() > 0) {
       
   365     echo '<div class="mdg-comment" style="margin-left: 0;">Categories: ';
       
   366     $i=0;
       
   367     while($r = $db->fetchrow())
       
   368     {
       
   369       if($i>0) echo ', ';
       
   370       $i++;
       
   371       echo '<a href="'.makeUrlNS('Category', $r['category_id']).'">'.$paths->pages[$paths->nslist['Category'].$r['category_id']]['name'].'</a>';
       
   372     }
       
   373     if( ( $paths->wiki_mode && !$paths->page_protected ) || ( $session->get_permissions('edit_cat') && $session->get_permissions('even_when_protected') ) ) echo ' [ <a href="'.makeUrl($paths->page, 'do=catedit', true).'" onclick="ajaxCatEdit(); return false;">edit categorization</a> ]</div>';
       
   374   } else {
       
   375     echo '<div class="mdg-comment" style="margin-left: 0;">Categories: ';
       
   376     echo '(Uncategorized)';
       
   377     if( ( $paths->wiki_mode && !$paths->page_protected ) || ( $session->get_permissions('edit_cat') && $session->get_permissions('even_when_protected') ) ) echo ' [ <a href="'.makeUrl($paths->page, 'do=catedit', true).'" onclick="ajaxCatEdit(); return false;">edit categorization</a> ]</div>';
       
   378     else echo '</div>';
       
   379   }
       
   380   $db->free_result();
       
   381 }
       
   382 
       
   383 function show_file_info()
       
   384 {
       
   385   global $db, $session, $paths, $template, $plugins; // Common objects
       
   386   if($paths->namespace != 'File') return null; // Prevent unnecessary work
       
   387   $selfn = $paths->cpage['urlname_nons']; // substr($paths->page, strlen($paths->nslist['File']), strlen($paths->cpage));
       
   388   if(substr($paths->cpage['name'], 0, strlen($paths->nslist['File']))==$paths->nslist['File']) $selfn = substr($paths->cpage['urlname_nons'], strlen($paths->nslist['File']), strlen($paths->cpage['urlname_nons']));
       
   389   $q = $db->sql_query('SELECT mimetype,time_id,size FROM '.table_prefix.'files WHERE page_id=\''.$selfn.'\' ORDER BY time_id DESC;');
       
   390   if(!$q) $db->_die('The file type could not be fetched.');
       
   391   if($db->numrows() < 1) { echo '<div class="mdg-comment" style="margin-left: 0;"><h3>Uploaded file</h3><p>There are no files uploaded with this name yet. <a href="'.makeUrlNS('Special', 'UploadFile/'.$paths->cpage['urlname_nons']).'">Upload a file...</a></p></div><br />'; return; }
       
   392   $r = $db->fetchrow();
       
   393   $mimetype = $r['mimetype'];
       
   394   $datestring = date('F d, Y h:i a', (int)$r['time_id']);
       
   395   echo '<div class="mdg-comment" style="margin-left: 0;"><p><h3>Uploaded file</h3></p><p>Type: '.$r['mimetype'].'<br />Size: ';
       
   396   $fs = $r['size'];
       
   397   echo $fs.' bytes';
       
   398   $fs = (int)$fs;
       
   399   if($fs >= 1048576)
       
   400   {
       
   401     $fs = round($fs / 1048576, 1);
       
   402     echo ' ('.$fs.' MB)';
       
   403   } elseif($fs >= 1024) {
       
   404     $fs = round($fs / 1024, 1);
       
   405     echo ' ('.$fs.' KB)';
       
   406   }
       
   407   echo '<br />Uploaded: '.$datestring.'</p>';
       
   408   if(substr($mimetype, 0, 6)!='image/' && ( substr($mimetype, 0, 5) != 'text/' || $mimetype == 'text/html' || $mimetype == 'text/javascript' ))
       
   409   {
       
   410     echo '<div class="warning-box">This file type may contain viruses or other code that could harm your computer. You should exercise caution if you download it.</div>';
       
   411   }
       
   412   if(substr($mimetype, 0, 6)=='image/')
       
   413   {
       
   414     echo '<p><a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn).'"><img style="border: 0;" alt="'.$paths->page.'" src="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn.htmlspecialchars(urlSeparator).'preview').'" /></a></p>';
       
   415   }
       
   416   echo '<p><a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn.'/'.$r['time_id'].htmlspecialchars(urlSeparator).'download').'">Download this file</a>';
       
   417   if(!$paths->page_protected && ( $paths->wiki_mode || $session->get_permissions('upload_new_version') ))
       
   418   {
       
   419     echo '  |  <a href="'.makeUrlNS('Special', 'UploadFile'.'/'.$selfn).'">Upload new version</a>';
       
   420   }
       
   421   echo '</p>';
       
   422   if($db->numrows() > 1)
       
   423   {
       
   424     echo '<h3>File history</h3><p>';
       
   425     while($r = $db->fetchrow())
       
   426     {
       
   427       echo '(<a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn.'/'.$r['time_id'].htmlspecialchars(urlSeparator).'download').'">this ver</a>) ';
       
   428       if($session->get_permissions('history_rollback'))
       
   429         echo ' (<a href="#" onclick="ajaxRollback(\''.$r['time_id'].'\'); return false;">revert</a>) ';
       
   430       $mimetype = $r['mimetype'];
       
   431       $datestring = date('F d, Y h:i a', (int)$r['time_id']);
       
   432       echo $datestring.': '.$r['mimetype'].', ';
       
   433       $fs = $r['size'];
       
   434       $fs = (int)$fs;
       
   435       if($fs >= 1048576)
       
   436       {
       
   437         $fs = round($fs / 1048576, 1);
       
   438         echo ' '.$fs.' MB';
       
   439       } elseif($fs >= 1024) {
       
   440         $fs = round($fs / 1024, 1);
       
   441         echo ' '.$fs.' KB';
       
   442       } else {
       
   443         echo ' '.$fs.' bytes';
       
   444       }
       
   445       echo '<br />';
       
   446     }
       
   447     echo '</p>';
       
   448   }
       
   449   $db->free_result();
       
   450   echo '</div><br />';
       
   451 }
       
   452 
       
   453 function display_page_headers()
       
   454 {
       
   455   global $db, $session, $paths, $template, $plugins; // Common objects
       
   456   if($session->get_permissions('vote_reset') && $paths->cpage['delvotes'] > 0)
       
   457   {
       
   458     $hr = implode(', ', explode('|', $paths->cpage['delvote_ips']));
       
   459     $is = 'is';
       
   460     $s = '';
       
   461     $s2 = 's';
       
   462     if ( $paths->cpage['delvotes'] > 1)
       
   463     {
       
   464       $is = 'are';
       
   465       $s = 's';
       
   466       $s2 = '';
       
   467     }
       
   468     echo '<div class="info-box" style="margin-left: 0; margin-top: 5px;" id="mdgDeleteVoteNoticeBox">
       
   469             <b>Notice:</b> There '.$is.' '.$paths->cpage['delvotes'].' user'.$s.' that think'.$s2.' this page should be deleted.<br />
       
   470             <b>Users that voted:</b> ' . $hr . '<br />
       
   471             <a href="'.makeUrl($paths->page, 'do=deletepage').'" onclick="ajaxDeletePage(); return false;">Delete page</a>  |  <a href="'.makeUrl($paths->page, 'do=resetvotes').'" onclick="ajaxResetDelVotes(); return false;">Reset votes</a>
       
   472           </div>';
       
   473   }
       
   474 }
       
   475 
       
   476 function display_page_footers()
       
   477 {
       
   478   global $db, $session, $paths, $template, $plugins; // Common objects
       
   479   if(isset($_GET['nofooters'])) return;
       
   480   $code = $plugins->setHook('send_page_footers');
       
   481   foreach ( $code as $cmd )
       
   482   {
       
   483     eval($cmd);
       
   484   }
       
   485   show_file_info();
       
   486   show_category_info();
       
   487 }
       
   488 
       
   489 function password_prompt($id = false)
       
   490 {
       
   491   global $db, $session, $paths, $template, $plugins; // Common objects
       
   492   if(!$id) $id = $paths->page;
       
   493   if(isset($paths->pages[$id]['password']) && strlen($paths->pages[$id]['password']) == 40 && !isset($_REQUEST['pagepass']))
       
   494   {
       
   495     die_friendly('Password required', '<p>You must supply a password to access this page.</p><form action="'.makeUrl($paths->pages[$id]['urlname']).'" method="post"><p>Password: <input name="pagepass" type="password" /></p><p><input type="submit" value="Submit" /></p>');
       
   496   } elseif(isset($_REQUEST['pagepass'])) {
       
   497     $p = (preg_match('#^([a-f0-9]*){40}$#', $_REQUEST['pagepass'])) ? $_REQUEST['pagepass'] : sha1($_REQUEST['pagepass']);
       
   498     if($p != $paths->pages[$id]['password']) die_friendly('Password required', '<p style="color: red;">The password you entered is incorrect.</p><form action="'.makeUrl($paths->page).'" method="post"><p>Password: <input name="pagepass" type="password" /></p><p><input type="submit" value="Submit" /></p>');
       
   499   }
       
   500 }
       
   501 
       
   502 function str_hex($string){
       
   503     $hex='';
       
   504     for ($i=0; $i < strlen($string); $i++){
       
   505         $hex .= ' '.dechex(ord($string[$i]));
       
   506     }
       
   507     return substr($hex, 1, strlen($hex));
       
   508 }
       
   509 
       
   510 // Function pulled from phpBB's smtp.php
       
   511 function smtp_get_response($socket, $response, $line = __LINE__) 
       
   512 {
       
   513 	$server_response = '';
       
   514 	while (substr($server_response, 3, 1) != ' ') 
       
   515 	{
       
   516 		if (!($server_response = fgets($socket, 256))) 
       
   517 		{
       
   518       die_friendly('SMTP Error', "<p>Couldn't get mail server response codes</p>");
       
   519 		}
       
   520 	} 
       
   521 
       
   522 	if (!(substr($server_response, 0, 3) == $response)) 
       
   523 	{ 
       
   524     die_friendly('SMTP Error', "<p>Ran into problems sending mail. Response: $server_response</p>");
       
   525 	} 
       
   526 }
       
   527 
       
   528 function smtp_send_email($to, $subject, $message, $from)
       
   529 {
       
   530   return smtp_send_email_core($to, $subject, $message, "From: <$from>\n");
       
   531 }
       
   532 
       
   533 // Replacement or substitute for PHP's mail command
       
   534 // Ported from phpBB - copyright (C) phpBB group, GPL.
       
   535 function smtp_send_email_core($mail_to, $subject, $message, $headers = '')
       
   536 {
       
   537 	global $board_config;
       
   538 
       
   539 	// Fix any bare linefeeds in the message to make it RFC821 Compliant.
       
   540 	$message = preg_replace("#(?<!\r)\n#si", "\r\n", $message);
       
   541 
       
   542 	if ($headers != '')
       
   543 	{
       
   544 		if (is_array($headers))
       
   545 		{
       
   546 			if (sizeof($headers) > 1)
       
   547 			{
       
   548 				$headers = join("\n", $headers);
       
   549 			}
       
   550 			else
       
   551 			{
       
   552 				$headers = $headers[0];
       
   553 			}
       
   554 		}
       
   555 		$headers = chop($headers);
       
   556 
       
   557 		// Make sure there are no bare linefeeds in the headers
       
   558 		$headers = preg_replace('#(?<!\r)\n#si', "\r\n", $headers);
       
   559 
       
   560 		// Ok this is rather confusing all things considered,
       
   561 		// but we have to grab bcc and cc headers and treat them differently
       
   562 		// Something we really didn't take into consideration originally
       
   563 		$header_array = explode("\r\n", $headers);
       
   564 		@reset($header_array);
       
   565 
       
   566 		$headers = '';
       
   567 		while(list(, $header) = each($header_array))
       
   568 		{
       
   569 			if (preg_match('#^cc:#si', $header))
       
   570 			{
       
   571 				$cc = preg_replace('#^cc:(.*)#si', '\1', $header);
       
   572 			}
       
   573 			else if (preg_match('#^bcc:#si', $header))
       
   574 			{
       
   575 				$bcc = preg_replace('#^bcc:(.*)#si', '\1', $header);
       
   576 				$header = '';
       
   577 			}
       
   578 			$headers .= ($header != '') ? $header . "\r\n" : '';
       
   579 		}
       
   580 
       
   581 		$headers = chop($headers);
       
   582 		$cc = explode(', ', $cc);
       
   583 		$bcc = explode(', ', $bcc);
       
   584 	}
       
   585 
       
   586 	if (trim($subject) == '')
       
   587 	{
       
   588 		die_friendly(GENERAL_ERROR, "No email Subject specified");
       
   589 	}
       
   590 
       
   591 	if (trim($message) == '')
       
   592 	{
       
   593 		die_friendly(GENERAL_ERROR, "Email message was blank");
       
   594 	}
       
   595   
       
   596   // setup SMTP
       
   597   $host = getConfig('smtp_server');
       
   598   if ( empty($host) )
       
   599     return 'No smtp_host in config';
       
   600   if ( strstr($host, ':' ) )
       
   601   {
       
   602     $n = explode(':', $host);
       
   603     $smtp_host = $n[0];
       
   604     $port = intval($n[1]);
       
   605   }
       
   606   else
       
   607   {
       
   608     $smtp_host = $host;
       
   609     $port = 25;
       
   610   }
       
   611   
       
   612   $smtp_user = getConfig('smtp_user');
       
   613   $smtp_pass = getConfig('smtp_password');
       
   614   
       
   615 	// Ok we have error checked as much as we can to this point let's get on
       
   616 	// it already.
       
   617 	if( !$socket = @fsockopen($smtp_host, $port, $errno, $errstr, 20) )
       
   618 	{
       
   619 		die_friendly(GENERAL_ERROR, "Could not connect to smtp host : $errno : $errstr");
       
   620 	}
       
   621 
       
   622 	// Wait for reply
       
   623 	smtp_get_response($socket, "220", __LINE__);
       
   624 
       
   625 	// Do we want to use AUTH?, send RFC2554 EHLO, else send RFC821 HELO
       
   626 	// This improved as provided by SirSir to accomodate
       
   627 	if( !empty($smtp_user) && !empty($smtp_pass) )
       
   628 	{ 
       
   629 		enano_fputs($socket, "EHLO " . $smtp_host . "\r\n");
       
   630 		smtp_get_response($socket, "250", __LINE__);
       
   631 
       
   632 		enano_fputs($socket, "AUTH LOGIN\r\n");
       
   633 		smtp_get_response($socket, "334", __LINE__);
       
   634 
       
   635 		enano_fputs($socket, base64_encode($smtp_user) . "\r\n");
       
   636 		smtp_get_response($socket, "334", __LINE__);
       
   637 
       
   638 		enano_fputs($socket, base64_encode($smtp_pass) . "\r\n");
       
   639 		smtp_get_response($socket, "235", __LINE__);
       
   640 	}
       
   641 	else
       
   642 	{
       
   643 		enano_fputs($socket, "HELO " . $smtp_host . "\r\n");
       
   644 		smtp_get_response($socket, "250", __LINE__);
       
   645 	}
       
   646 
       
   647 	// From this point onward most server response codes should be 250
       
   648 	// Specify who the mail is from....
       
   649 	enano_fputs($socket, "MAIL FROM: <" . getConfig('contact_email') . ">\r\n");
       
   650 	smtp_get_response($socket, "250", __LINE__);
       
   651 
       
   652 	// Specify each user to send to and build to header.
       
   653 	$to_header = '';
       
   654 
       
   655 	// Add an additional bit of error checking to the To field.
       
   656 	$mail_to = (trim($mail_to) == '') ? 'Undisclosed-recipients:;' : trim($mail_to);
       
   657 	if (preg_match('#[^ ]+\@[^ ]+#', $mail_to))
       
   658 	{
       
   659 		enano_fputs($socket, "RCPT TO: <$mail_to>\r\n");
       
   660 		smtp_get_response($socket, "250", __LINE__);
       
   661 	}
       
   662 
       
   663 	// Ok now do the CC and BCC fields...
       
   664 	@reset($bcc);
       
   665 	while(list(, $bcc_address) = each($bcc))
       
   666 	{
       
   667 		// Add an additional bit of error checking to bcc header...
       
   668 		$bcc_address = trim($bcc_address);
       
   669 		if (preg_match('#[^ ]+\@[^ ]+#', $bcc_address))
       
   670 		{
       
   671 			enano_fputs($socket, "RCPT TO: <$bcc_address>\r\n");
       
   672 			smtp_get_response($socket, "250", __LINE__);
       
   673 		}
       
   674 	}
       
   675 
       
   676 	@reset($cc);
       
   677 	while(list(, $cc_address) = each($cc))
       
   678 	{
       
   679 		// Add an additional bit of error checking to cc header
       
   680 		$cc_address = trim($cc_address);
       
   681 		if (preg_match('#[^ ]+\@[^ ]+#', $cc_address))
       
   682 		{
       
   683 			enano_fputs($socket, "RCPT TO: <$cc_address>\r\n");
       
   684 			smtp_get_response($socket, "250", __LINE__);
       
   685 		}
       
   686 	}
       
   687 
       
   688 	// Ok now we tell the server we are ready to start sending data
       
   689 	enano_fputs($socket, "DATA\r\n");
       
   690 
       
   691 	// This is the last response code we look for until the end of the message.
       
   692 	smtp_get_response($socket, "354", __LINE__);
       
   693 
       
   694 	// Send the Subject Line...
       
   695 	enano_fputs($socket, "Subject: $subject\r\n");
       
   696 
       
   697 	// Now the To Header.
       
   698 	enano_fputs($socket, "To: $mail_to\r\n");
       
   699 
       
   700 	// Now any custom headers....
       
   701 	enano_fputs($socket, "$headers\r\n\r\n");
       
   702 
       
   703 	// Ok now we are ready for the message...
       
   704 	enano_fputs($socket, "$message\r\n");
       
   705 
       
   706 	// Ok the all the ingredients are mixed in let's cook this puppy...
       
   707 	enano_fputs($socket, ".\r\n");
       
   708 	smtp_get_response($socket, "250", __LINE__);
       
   709 
       
   710 	// Now tell the server we are done and close the socket...
       
   711 	enano_fputs($socket, "QUIT\r\n");
       
   712 	fclose($socket);
       
   713 
       
   714 	return TRUE;
       
   715 }
       
   716 
       
   717 /**
       
   718  * Tell which version of Enano we're running.
       
   719  * @param bool $long if true, uses English version names (e.g. alpha, beta, release candidate). If false (default) uses abbreviations (1.0a1, 1.0b3, 1.0RC2, etc.)
       
   720  * @return string
       
   721  */
       
   722 
       
   723 function enano_version($long = false, $no_nightly = false)
       
   724 {
       
   725   $r = getConfig('enano_version');
       
   726   $rc = ( $long ) ? ' release candidate ' : 'RC';
       
   727   $b = ( $long ) ? ' beta ' : 'b';
       
   728   $a = ( $long ) ? ' alpha ' : 'a';
       
   729   if($v = getConfig('enano_rc_version')) $r .= $rc.$v;
       
   730   if($v = getConfig('enano_beta_version')) $r .= $b.$v;
       
   731   if($v = getConfig('enano_alpha_version')) $r .= $a.$v;
       
   732   if ( defined('ENANO_NIGHTLY') && !$no_nightly )
       
   733   {
       
   734     $nightlytag  = ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR;
       
   735     $nightlylong = ' nightly; build date: ' . ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR;
       
   736     $r = ( $long ) ? $r . $nightlylong : $r . '-nightly-' . $nightlytag;
       
   737   }
       
   738   return $r;
       
   739 }
       
   740 
       
   741 function _dualurlenc($t) {
       
   742   return rawurlencode(rawurlencode($t));
       
   743 }
       
   744   
       
   745 function _die($t) {
       
   746   $_ob = 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\'' . rawurlencode('' . $t . '') . '\')';
       
   747   die($_ob);
       
   748 }
       
   749 
       
   750 function jsdie($text) {
       
   751   global $db, $session, $paths, $template, $plugins; // Common objects
       
   752   $text = rawurlencode($text . "\n\nSQL Backtrace:\n" . $db->sql_backtrace());
       
   753   echo 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.$text.'\');';
       
   754 }
       
   755 
       
   756 // HTML sanitizing function - written by Kallahar
       
   757 // Original function at: http://quickwired.com/kallahar/smallprojects/php_xss_filter_function.php
       
   758 
       
   759 // UNUSED - todo: remove this in gold or put it to use
       
   760 
       
   761 function RemoveXSS($val) {
       
   762   // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
       
   763   // this prevents some character re-spacing such as <java\0script>
       
   764   // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs
       
   765   $val = preg_replace('/([\x00-\x08][\x0b-\x0c][\x0e-\x20])/', '', $val);
       
   766   
       
   767   // straight replacements, the user should never need these since they're normal characters
       
   768   // this prevents like <IMG SRC=&#X40&#X61&#X76&#X61&#X73&#X63&#X72&#X69&#X70&#X74&#X3A&#X61&#X6C&#X65&#X72&#X74&#X28&#X27&#X58&#X53&#X53&#X27&#X29>
       
   769   $search  = 'abcdefghijklmnopqrstuvwxyz';
       
   770   $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
       
   771   $search .= '1234567890!@#$%^&*()';
       
   772   $search .= '~`";:?+/={}[]-_|\'\\';
       
   773   for ($i = 0; $i < strlen($search); $i++) {
       
   774     // ;? matches the ;, which is optional
       
   775     // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars
       
   776   
       
   777     // &#x0040 @ search for the hex values
       
   778     $val = preg_replace('/(&#[x|X]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
       
   779     // &#00064 @ 0{0,7} matches '0' zero to seven times
       
   780     $val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
       
   781   }
       
   782   
       
   783   // now the only remaining whitespace attacks are \t, \n, and \r
       
   784   $ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
       
   785   $ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
       
   786   $ra = array_merge($ra1, $ra2);
       
   787   
       
   788   $found = true; // keep replacing as long as the previous round replaced something
       
   789   while ($found == true) {
       
   790     $val_before = $val;
       
   791     for ($i = 0; $i < sizeof($ra); $i++) {
       
   792       $pattern = '/';
       
   793       for ($j = 0; $j < strlen($ra[$i]); $j++) {
       
   794         if ($j > 0) {
       
   795           $pattern .= '(';
       
   796           $pattern .= '(&#[x|X]0{0,8}([9][a][b]);?)?';
       
   797           $pattern .= '|(&#0{0,8}([9][10][13]);?)?';
       
   798           $pattern .= ')?';
       
   799         }
       
   800         $pattern .= $ra[$i][$j];
       
   801       }
       
   802       $pattern .= '/i';
       
   803       $replacement = substr($ra[$i], 0, 2).'<b></b>'.substr($ra[$i], 2); // add in <> to nerf the tag
       
   804       $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
       
   805       if ($val_before == $val) {
       
   806         // no replacements were made, so exit the loop
       
   807         $found = false;
       
   808       }
       
   809     }
       
   810   }
       
   811   return $val;
       
   812 }
       
   813 
       
   814 /**
       
   815  * Capitalizes the first letter of a string
       
   816  * @param $text string the text to be transformed
       
   817  * @return string
       
   818  */
       
   819  
       
   820 function capitalize_first_letter($text)
       
   821 {
       
   822   return strtoupper(substr($text, 0, 1)) . substr($text, 1);
       
   823 }
       
   824 
       
   825 /**
       
   826  * Checks if a value in a bitfield is on or off
       
   827  * @param $bitfield int the bit-field value
       
   828  * @param $value int the value to switch off
       
   829  * @return bool
       
   830  */
       
   831  
       
   832 function is_bit($bitfield, $value)
       
   833 {
       
   834   return ( $bitfield & $value ) ? true : false;
       
   835 }
       
   836 
       
   837 /**
       
   838  * Trims spaces/newlines from the beginning and end of a string
       
   839  * @param $text the text to process
       
   840  * @return string
       
   841  */
       
   842  
       
   843 function trim_spaces($text)
       
   844 {
       
   845   $d = true;
       
   846   while($d)
       
   847   {
       
   848     $c = substr($text, 0, 1);
       
   849     $a = substr($text, strlen($text)-1, strlen($text));
       
   850     if($c == "\n" || $c == "\r" || $c == "\t" || $c == ' ') $text = substr($text, 1, strlen($text));
       
   851     elseif($a == "\n" || $a == "\r" || $a == "\t" || $a == ' ') $text = substr($text, 0, strlen($text)-1);
       
   852     else $d = false;
       
   853   }
       
   854   return $text;
       
   855 }
       
   856 
       
   857 /**
       
   858  * Enano-ese equivalent of str_split() which is only found in PHP5
       
   859  * @param $text string the text to split
       
   860  * @param $inc int size of each block
       
   861  * @return array
       
   862  */
       
   863  
       
   864 function enano_str_split($text, $inc = 1)
       
   865 {
       
   866   if($inc < 1) return false;
       
   867   if($inc >= strlen($text)) return Array($text);
       
   868   $len = ceil(strlen($text) / $inc);
       
   869   $ret = Array();
       
   870   for($i=0;$i<strlen($text);$i=$i+$inc)
       
   871   {
       
   872     $ret[] = substr($text, $i, $inc);
       
   873   }
       
   874   return $ret;
       
   875 }
       
   876 
       
   877 /**
       
   878  * Converts a hexadecimal number to a binary string.
       
   879  * @param text string hexadecimal number
       
   880  * @return string
       
   881  */
       
   882 function hex2bin($text)
       
   883 {
       
   884   $arr = enano_str_split($text, 2);
       
   885   $ret = '';
       
   886   for ($i=0; $i<sizeof($arr); $i++)
       
   887   {
       
   888     $ret .= chr(hexdec($arr[$i]));
       
   889   }
       
   890   return $ret;
       
   891 }
       
   892 
       
   893 /**
       
   894  * Generates and/or prints a human-readable backtrace
       
   895  * @param bool $return - if true, this function returns a string, otherwise returns null
       
   896  * @return mixed
       
   897  */
       
   898  
       
   899 function enano_debug_print_backtrace($return = false)
       
   900 {
       
   901   ob_start();
       
   902   echo '<pre>';
       
   903   debug_print_backtrace();
       
   904   echo '</pre>';
       
   905   $c = ob_get_contents();
       
   906   ob_end_clean();
       
   907   if($return) return $c;
       
   908   else echo $c;
       
   909   return null;
       
   910 }
       
   911 
       
   912 /**
       
   913  * Like rawurlencode(), but encodes all characters
       
   914  * @param string $text the text to encode
       
   915  * @param optional string $prefix text before each hex character
       
   916  * @param optional string $suffix text after each hex character
       
   917  * @return string
       
   918  */
       
   919  
       
   920 function hexencode($text, $prefix = '%', $suffix = '')
       
   921 {
       
   922   $arr = enano_str_split($text);
       
   923   $r = '';
       
   924   foreach($arr as $a)
       
   925   {
       
   926     $nibble = (string)dechex(ord($a));
       
   927     if(strlen($nibble) == 1) $nibble = '0' . $nibble;
       
   928     $r .= $prefix . $nibble . $suffix;
       
   929   }
       
   930   return $r;
       
   931 }
       
   932 
       
   933 /**
       
   934  * Enano-ese equivalent of get_magic_quotes_gpc()
       
   935  * @return bool
       
   936  */
       
   937  
       
   938 function enano_get_magic_quotes_gpc()
       
   939 {
       
   940   if(function_exists('get_magic_quotes_gpc'))
       
   941   {
       
   942     return ( get_magic_quotes_gpc() == 1 );
       
   943   }
       
   944   else
       
   945   {
       
   946     return ( strtolower(@ini_get('magic_quotes_gpc')) == '1' );
       
   947   }
       
   948 }
       
   949 
       
   950 /**
       
   951  * Recursive stripslashes()
       
   952  * @param array
       
   953  * @return array
       
   954  */
       
   955  
       
   956 function stripslashes_recurse($arr)
       
   957 {
       
   958   foreach($arr as $k => $xxxx)
       
   959   {
       
   960     $val =& $arr[$k];
       
   961     if(is_string($val))
       
   962       $val = stripslashes($val);
       
   963     elseif(is_array($val))
       
   964       $val = stripslashes_recurse($val);
       
   965   }
       
   966   return $arr;
       
   967 }
       
   968 
       
   969 /**
       
   970  * If magic_quotes_gpc is on, calls stripslashes() on everything in $_GET/$_POST/$_COOKIE
       
   971  * @ignore - this doesn't work
       
   972  * @todo port version from the PHP manual
       
   973  * @return void
       
   974  */
       
   975 function strip_magic_quotes_gpc()
       
   976 {
       
   977   if(enano_get_magic_quotes_gpc())
       
   978   {
       
   979     $_POST   = stripslashes_recurse($_POST);
       
   980     $_GET    = stripslashes_recurse($_GET);
       
   981     $_COOKIE = stripslashes_recurse($_COOKIE);
       
   982   }
       
   983 }
       
   984 
       
   985 /**
       
   986  * A very basic single-character compression algorithm for binary strings/bitfields
       
   987  * @param string $bits the text to compress
       
   988  * @return string
       
   989  */
       
   990  
       
   991 function compress_bitfield($bits)
       
   992 {
       
   993   $crc32 = crc32($bits);
       
   994   $bits .= '0';
       
   995   $start_pos = 0;
       
   996   $current = substr($bits, 1, 1);
       
   997   $last    = substr($bits, 0, 1);
       
   998   $chunk_size = 1;
       
   999   $len = strlen($bits);
       
  1000   $crc = $len;
       
  1001   $crcval = 0;
       
  1002   for ( $i = 1; $i < $len; $i++ )
       
  1003   {
       
  1004     $current = substr($bits, $i, 1);
       
  1005     $last    = substr($bits, $i - 1, 1);
       
  1006     $next    = substr($bits, $i + 1, 1);
       
  1007     // Are we on the last character?
       
  1008     if($current == $last && $i+1 < $len)
       
  1009       $chunk_size++;
       
  1010     else
       
  1011     {
       
  1012       if($i+1 == $len && $current == $next)
       
  1013       {
       
  1014         // This character completes a chunk
       
  1015         $chunk_size++;
       
  1016         $i++;
       
  1017         $chunk = substr($bits, $start_pos, $chunk_size);
       
  1018         $chunklen = strlen($chunk);
       
  1019         $newchunk = $last . '[' . $chunklen . ']';
       
  1020         $newlen   = strlen($newchunk);
       
  1021         $bits = substr($bits, 0, $start_pos) . $newchunk . substr($bits, $i, $len);
       
  1022         $chunk_size = 1;
       
  1023         $i = $start_pos + $newlen;
       
  1024         $start_pos = $i;
       
  1025         $len = strlen($bits);
       
  1026         $crcval = $crcval + $chunklen;
       
  1027       }
       
  1028       else
       
  1029       {
       
  1030         // Last character completed a chunk
       
  1031         $chunk = substr($bits, $start_pos, $chunk_size);
       
  1032         $chunklen = strlen($chunk);
       
  1033         $newchunk = $last . '[' . $chunklen . '],';
       
  1034         $newlen   = strlen($newchunk);
       
  1035         $bits = substr($bits, 0, $start_pos) . $newchunk . substr($bits, $i, $len);
       
  1036         $chunk_size = 1;
       
  1037         $i = $start_pos + $newlen;
       
  1038         $start_pos = $i;
       
  1039         $len = strlen($bits);
       
  1040         $crcval = $crcval + $chunklen;
       
  1041       }
       
  1042     }
       
  1043   }
       
  1044   if($crc != $crcval)
       
  1045   {
       
  1046     echo __FUNCTION__.'(): ERROR: length check failed, this is a bug in the algorithm<br />Debug info: aiming for a CRC val of '.$crc.', got '.$crcval;
       
  1047     return false;
       
  1048   }
       
  1049   $compressed = 'cbf:len='.$crc.';crc='.dechex($crc32).';data='.$bits.'|end';
       
  1050   return $compressed;
       
  1051 }
       
  1052 
       
  1053 /**
       
  1054  * Uncompresses a bitfield compressed with compress_bitfield()
       
  1055  * @param string $bits the compressed bitfield
       
  1056  * @return string the uncompressed, original (we hope) bitfield OR bool false on error
       
  1057  */
       
  1058  
       
  1059 function uncompress_bitfield($bits)
       
  1060 {
       
  1061   if(substr($bits, 0, 4) != 'cbf:')
       
  1062   {
       
  1063     echo __FUNCTION__.'(): ERROR: Invalid stream';
       
  1064     return false;
       
  1065   }
       
  1066   $len = intval(substr($bits, strpos($bits, 'len=')+4, strpos($bits, ';')-strpos($bits, 'len=')-4));
       
  1067   $crc = substr($bits, strpos($bits, 'crc=')+4, 8);
       
  1068   $data = substr($bits, strpos($bits, 'data=')+5, strpos($bits, '|end')-strpos($bits, 'data=')-5);
       
  1069   $data = explode(',', $data);
       
  1070   foreach($data as $a => $b)
       
  1071   {
       
  1072     $d =& $data[$a];
       
  1073     $char = substr($d, 0, 1);
       
  1074     $dlen = intval(substr($d, 2, strlen($d)-1));
       
  1075     $s = '';
       
  1076     for($i=0;$i<$dlen;$i++,$s.=$char);
       
  1077     $d = $s;
       
  1078     unset($s, $dlen, $char);
       
  1079   }
       
  1080   $decompressed = implode('', $data);
       
  1081   $decompressed = substr($decompressed, 0, -1);
       
  1082   $dcrc = (string)dechex(crc32($decompressed));
       
  1083   if($dcrc != $crc)
       
  1084   {
       
  1085     echo __FUNCTION__.'(): ERROR: CRC check failed<br />debug info:<br />original crc: '.$crc.'<br />decomp\'ed crc: '.$dcrc.'<br />';
       
  1086     return false;
       
  1087   }
       
  1088   return $decompressed;
       
  1089 }
       
  1090 
       
  1091 /**
       
  1092  * Exports a MySQL table into a SQL string.
       
  1093  * @param string $table The name of the table to export
       
  1094  * @param bool $structure If true, include a CREATE TABLE command
       
  1095  * @param bool $data If true, include the contents of the table
       
  1096  * @param bool $compact If true, omits newlines between parts of SQL statements, use in Enano database exporter
       
  1097  * @return string
       
  1098  */
       
  1099 
       
  1100 function export_table($table, $structure = true, $data = true, $compact = false)
       
  1101 {
       
  1102   global $db, $session, $paths, $template, $plugins; // Common objects
       
  1103   $struct_keys = '';
       
  1104   $divider   = (!$compact) ? "\n" : "\n";
       
  1105   $spacer1   = (!$compact) ? "\n" : " ";
       
  1106   $spacer2   = (!$compact) ? "  " : " ";
       
  1107   $rowspacer = (!$compact) ? "\n  " : " ";
       
  1108   $index_list = Array();
       
  1109   $cols = $db->sql_query('SHOW COLUMNS IN '.$table.';');
       
  1110   if(!$cols)
       
  1111   {
       
  1112     echo 'export_table(): Error getting column list: '.$db->get_error_text().'<br />';
       
  1113     return false;
       
  1114   }
       
  1115   $col = Array();
       
  1116   $sqlcol = Array();
       
  1117   $collist = Array();
       
  1118   $pri_keys = Array();
       
  1119   // Using fetchrow_num() here to compensate for MySQL l10n
       
  1120   while( $row = $db->fetchrow_num() )
       
  1121   {
       
  1122     $field =& $row[0];
       
  1123     $type  =& $row[1];
       
  1124     $null  =& $row[2];
       
  1125     $key   =& $row[3];
       
  1126     $def   =& $row[4];
       
  1127     $extra =& $row[5];
       
  1128     $col[] = Array(
       
  1129       'name'=>$field,
       
  1130       'type'=>$type,
       
  1131       'null'=>$null,
       
  1132       'key'=>$key,
       
  1133       'default'=>$def,
       
  1134       'extra'=>$extra,
       
  1135       );
       
  1136     $collist[] = $field;
       
  1137   }
       
  1138   
       
  1139   if ( $structure )
       
  1140   {
       
  1141     $db->sql_query('SET SQL_QUOTE_SHOW_CREATE = 0;');
       
  1142     $struct = $db->sql_query('SHOW CREATE TABLE '.$table.';');
       
  1143     if ( !$struct )
       
  1144       $db->_die();
       
  1145     $row = $db->fetchrow_num();
       
  1146     $db->free_result();
       
  1147     $struct = $row[1];
       
  1148     $struct = preg_replace("/\n\) ENGINE=(.+)$/", "\n);", $struct);
       
  1149     unset($row);
       
  1150     if ( $compact )
       
  1151     {
       
  1152       $struct_arr = explode("\n", $struct);
       
  1153       foreach ( $struct_arr as $i => $leg )
       
  1154       {
       
  1155         if ( $i == 0 )
       
  1156           continue;
       
  1157         $test = trim($leg);
       
  1158         if ( empty($test) )
       
  1159         {
       
  1160           unset($struct_arr[$i]);
       
  1161           continue;
       
  1162         }
       
  1163         $struct_arr[$i] = preg_replace('/^([\s]*)/', ' ', $leg);
       
  1164       }
       
  1165       $struct = implode("", $struct_arr);
       
  1166     }
       
  1167   }
       
  1168   
       
  1169   // Structuring complete
       
  1170   if($data)
       
  1171   {
       
  1172     $datq = $db->sql_query('SELECT * FROM '.$table.';');
       
  1173     if(!$datq)
       
  1174     {
       
  1175       echo 'export_table(): Error getting column list: '.$db->get_error_text().'<br />';
       
  1176       return false;
       
  1177     }
       
  1178     if($db->numrows() < 1)
       
  1179     {
       
  1180       if($structure) return $struct;
       
  1181       else return '';
       
  1182     }
       
  1183     $rowdata = Array();
       
  1184     $dataqs = Array();
       
  1185     $insert_strings = Array();
       
  1186     $z = false;
       
  1187     while($row = $db->fetchrow_num())
       
  1188     {
       
  1189       $z = false;
       
  1190       foreach($row as $i => $cell)
       
  1191       {
       
  1192         $str = mysql_encode_column($cell, $col[$i]['type']);
       
  1193         $rowdata[] = $str;
       
  1194       }
       
  1195       $dataqs2 = implode(",$rowspacer", $dataqs) . ",$rowspacer" . '( ' . implode(', ', $rowdata) . ' )';
       
  1196       $ins = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . $dataqs2 . ";";
       
  1197       if ( strlen( $ins ) > MYSQL_MAX_PACKET_SIZE )
       
  1198       {
       
  1199         // We've exceeded the maximum allowed packet size for MySQL - separate this into a different query
       
  1200         $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";;
       
  1201         $dataqs = Array('( ' . implode(', ', $rowdata) . ' )');
       
  1202         $z = true;
       
  1203       }
       
  1204       else
       
  1205       {
       
  1206         $dataqs[] = '( ' . implode(', ', $rowdata) . ' )';
       
  1207       }
       
  1208       $rowdata = Array();
       
  1209     }
       
  1210     if ( !$z )
       
  1211     {
       
  1212       $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";;
       
  1213       $dataqs = Array();
       
  1214     }
       
  1215     $datstring = implode($divider, $insert_strings);
       
  1216   }
       
  1217   if($structure && !$data) return $struct;
       
  1218   elseif(!$structure && $data) return $datstring;
       
  1219   elseif($structure && $data) return $struct . $divider . $datstring;
       
  1220   elseif(!$structure && !$data) return '';
       
  1221 }
       
  1222 
       
  1223 /**
       
  1224  * Encodes a string value for use in an INSERT statement for given column type $type.
       
  1225  * @access private
       
  1226  */
       
  1227  
       
  1228 function mysql_encode_column($input, $type)
       
  1229 {
       
  1230   global $db, $session, $paths, $template, $plugins; // Common objects
       
  1231   // Decide whether to quote the string or not
       
  1232   if(substr($type, 0, 7) == 'varchar' || $type == 'datetime' || $type == 'text' || $type == 'tinytext' || $type == 'smalltext' || $type == 'longtext' || substr($type, 0, 4) == 'char')
       
  1233   {
       
  1234     $str = "'" . $db->escape($input) . "'";
       
  1235   }
       
  1236   elseif(in_array($type, Array('blob', 'longblob', 'mediumblob', 'smallblob')) || substr($type, 0, 6) == 'binary' || substr($type, 0, 9) == 'varbinary')
       
  1237   {
       
  1238     $str = '0x' . hexencode($input, '', '');
       
  1239   }
       
  1240   elseif(is_null($input))
       
  1241   {
       
  1242     $str = 'NULL';
       
  1243   }
       
  1244   else
       
  1245   {
       
  1246     $str = (string)$input;
       
  1247   }
       
  1248   return $str;
       
  1249 }
       
  1250 
       
  1251 /**
       
  1252  * Creates an associative array defining which file extensions are allowed and which ones aren't
       
  1253  * @return array keyname will be a file extension, value will be true or false
       
  1254  */
       
  1255 
       
  1256 function fetch_allowed_extensions()
       
  1257 {
       
  1258   global $mime_types;
       
  1259   $bits = getConfig('allowed_mime_types');
       
  1260   if(!$bits) return Array(false);
       
  1261   $bits = uncompress_bitfield($bits);
       
  1262   if(!$bits) return Array(false);
       
  1263   $bits = enano_str_split($bits, 1);
       
  1264   $ret = Array();
       
  1265   $mt = array_keys($mime_types);
       
  1266   foreach($bits as $i => $b)
       
  1267   {
       
  1268     $ret[$mt[$i]] = ( $b == '1' ) ? true : false;
       
  1269   }
       
  1270   return $ret;
       
  1271 }
       
  1272 
       
  1273 /**
       
  1274  * Generates a random key suitable for encryption
       
  1275  * @param int $len the length of the key
       
  1276  * @return string a BINARY key
       
  1277  */
       
  1278 
       
  1279 function randkey($len = 32)
       
  1280 {
       
  1281   $key = '';
       
  1282   for($i=0;$i<$len;$i++)
       
  1283   {
       
  1284     $key .= chr(mt_rand(0, 255));
       
  1285   }
       
  1286   return $key;
       
  1287 }
       
  1288 
       
  1289 /**
       
  1290  * Decodes a hex string.
       
  1291  * @param string $hex The hex code to decode
       
  1292  * @return string
       
  1293  */
       
  1294 
       
  1295 function hexdecode($hex)
       
  1296 {
       
  1297   $hex = enano_str_split($hex, 2);
       
  1298   $bin_key = '';
       
  1299   foreach($hex as $nibble)
       
  1300   {
       
  1301     $byte = chr(hexdec($nibble));
       
  1302     $bin_key .= $byte;
       
  1303   }
       
  1304   return $bin_key;
       
  1305 }
       
  1306 
       
  1307 /**
       
  1308  * Enano's own (almost) bulletproof HTML sanitizer.
       
  1309  * @param string $html The input HTML
       
  1310  * @return string cleaned HTML
       
  1311  */
       
  1312 
       
  1313 function sanitize_html($html, $filter_php = true)
       
  1314 {
       
  1315   
       
  1316   $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>(.*?)</\\1>#is', '&lt;\\1\\2\\3javascript:\\59&gt;\\60&lt;/\\1&gt;', $html);
       
  1317   $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>#is', '&lt;\\1\\2\\3javascript:\\59&gt;', $html);
       
  1318   
       
  1319   if($filter_php)
       
  1320     $html = str_replace(
       
  1321       Array('<?php',    '<?',    '<%',    '?>',    '%>'),
       
  1322       Array('&lt;?php', '&lt;?', '&lt;%', '?&gt;', '%&gt;'),
       
  1323       $html);
       
  1324   
       
  1325   $tag_whitelist = array_keys ( setupAttributeWhitelist() );
       
  1326   if ( !$filter_php )
       
  1327     $tag_whitelist[] = '?php';
       
  1328   $len = strlen($html);
       
  1329   $in_quote = false;
       
  1330   $quote_char = '';
       
  1331   $tag_start = 0;
       
  1332   $tag_name = '';
       
  1333   $in_tag = false;
       
  1334   $trk_name = false;
       
  1335   for ( $i = 0; $i < $len; $i++ )
       
  1336   {
       
  1337     $chr = $html{$i};
       
  1338     $prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
       
  1339     $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
       
  1340     if ( $in_quote && $in_tag )
       
  1341     {
       
  1342       if ( $quote_char == $chr && $prev != '\\' )
       
  1343         $in_quote = false;
       
  1344     }
       
  1345     elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag )
       
  1346     {
       
  1347       $in_quote = true;
       
  1348       $quote_char = $chr;
       
  1349     }
       
  1350     if ( $chr == '<' && !$in_tag && $next != '/' )
       
  1351     {                                          
       
  1352       // start of a tag
       
  1353       $tag_start = $i;
       
  1354       $in_tag = true;
       
  1355       $trk_name = true;
       
  1356     }
       
  1357     elseif ( !$in_quote && $in_tag && $chr == '>' )
       
  1358     {
       
  1359       $full_tag = substr($html, $tag_start, ( $i - $tag_start ) + 1 );
       
  1360       $l = strlen($tag_name) + 2;
       
  1361       $attribs_only = trim( substr($full_tag, $l, ( strlen($full_tag) - $l - 1 ) ) );
       
  1362       
       
  1363       // Debugging message
       
  1364       // echo htmlspecialchars($full_tag) . '<br />';
       
  1365       
       
  1366       if ( !in_array($tag_name, $tag_whitelist) )
       
  1367       {
       
  1368         // Illegal tag
       
  1369         //echo $tag_name . ' ';
       
  1370         
       
  1371         $s = ( empty($attribs_only) ) ? '' : ' ';
       
  1372         
       
  1373         $sanitized = '&lt;' . $tag_name . $s . $attribs_only . '&gt;';
       
  1374         
       
  1375         $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1);
       
  1376         $html = str_replace('</' . $tag_name . '>', '&lt;/' . $tag_name . '&gt;', $html);
       
  1377         $new_i = $tag_start + strlen($sanitized);
       
  1378         
       
  1379         $len = strlen($html);
       
  1380         $i = $new_i;
       
  1381         
       
  1382         $in_tag = false;
       
  1383         $tag_name = '';
       
  1384         continue;
       
  1385       }
       
  1386       else
       
  1387       {
       
  1388         if ( $tag_name == '?php' && !$filter_php )
       
  1389           continue;
       
  1390         $f = fixTagAttributes( $attribs_only, $tag_name );
       
  1391         $s = ( empty($f) ) ? '' : ' ';
       
  1392         
       
  1393         $sanitized = '<' . $tag_name . $f . '>';
       
  1394         $new_i = $tag_start + strlen($sanitized);
       
  1395         
       
  1396         $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1);
       
  1397         $len = strlen($html);
       
  1398         $i = $new_i;
       
  1399         
       
  1400         $in_tag = false;
       
  1401         $tag_name = '';
       
  1402         continue;
       
  1403       }
       
  1404     }
       
  1405     elseif ( $in_tag && $trk_name )
       
  1406     {
       
  1407       $is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' );
       
  1408       if ( $is_alphabetical )
       
  1409         $tag_name .= $chr;
       
  1410       else
       
  1411       {
       
  1412         $trk_name = false;
       
  1413       }
       
  1414     }
       
  1415     
       
  1416   }
       
  1417   
       
  1418   return $html;
       
  1419   
       
  1420 }
       
  1421 
       
  1422 function htmlalternatives($string)
       
  1423 {
       
  1424   $ret = '';
       
  1425   for ( $i = 0; $i < strlen($string); $i++ )
       
  1426   {
       
  1427     $chr = $string{$i};
       
  1428     $ch1 = ord($chr);
       
  1429     $ch2 = dechex($ch1);
       
  1430     $byte = '(&\\#([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch2 . ';|&\\#x([0]*){0,7}' . $ch2 . ';|%([0]*){0,7}' . $ch2 . '|' . preg_quote($chr) . ')';
       
  1431     $ret .= $byte;
       
  1432     $ret .= '([\s]){0,2}';
       
  1433   }
       
  1434   return $ret;
       
  1435 }
       
  1436 
       
  1437 /**
       
  1438  * Paginates (breaks into multiple pages) a MySQL result resource, which is treated as unbuffered.
       
  1439  * @param resource The MySQL result resource. This should preferably be an unbuffered query.
       
  1440  * @param string A template, with variables being named after the column name
       
  1441  * @param int The number of total results. This should be determined by a second query.
       
  1442  * @param string sprintf-style formatting string for URLs for result pages. First parameter will be start offset.
       
  1443  * @param int Optional. Start offset in individual results. Defaults to 0.
       
  1444  * @param int Optional. The number of results per page. Defualts to 10.
       
  1445  * @param int Optional. An associative array of functions to call, with key names being column names, and values being function names. Values can also be an array with key 0 being either an object or a string(class name) and key 1 being a [static] method.
       
  1446  * @param string Optional. The text to be sent before the result list, only if there are any results. Possibly the start of a table.
       
  1447  * @param string Optional. The text to be sent after the result list, only if there are any results. Possibly the end of a table.
       
  1448  * @return string
       
  1449  */
       
  1450 
       
  1451 function paginate($q, $tpl_text, $num_results, $result_url, $start = 0, $perpage = 10, $callers = Array(), $header = '', $footer = '')
       
  1452 {
       
  1453   global $db, $session, $paths, $template, $plugins; // Common objects
       
  1454   $parser = $template->makeParserText($tpl_text);
       
  1455   $num_pages = ceil ( $num_results / $perpage );
       
  1456   $out = '';
       
  1457   $i = 0;
       
  1458   $this_page = ceil ( $start / $perpage );
       
  1459   
       
  1460   // Build paginator
       
  1461   $begin = '<div class="tblholder" style="display: table; margin: 10px 0 0 auto;">
       
  1462     <table border="0" cellspacing="1" cellpadding="4">
       
  1463       <tr><th>Page:</th>';
       
  1464   $block = '<td class="row1" style="text-align: center;">{LINK}</td>';
       
  1465   $end = '</tr></table></div>';
       
  1466   $blk = $template->makeParserText($block);
       
  1467   $inner = '';
       
  1468   $cls = 'row2';
       
  1469   if ( $num_pages < 5 )
       
  1470   {
       
  1471     for ( $i = 0; $i < $num_pages; $i++ )
       
  1472     {
       
  1473       $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1474       $offset = strval($i * $perpage);
       
  1475       $url = sprintf($result_url, $offset);
       
  1476       $j = $i + 1;
       
  1477       $link = ( $offset == strval($start) ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
       
  1478       $blk->assign_vars(array(
       
  1479         'CLASS'=>$cls,
       
  1480         'LINK'=>$link
       
  1481         ));
       
  1482       $inner .= $blk->run();
       
  1483     }
       
  1484   }
       
  1485   else
       
  1486   {
       
  1487     if ( $this_page + 5 > $num_pages )
       
  1488     {
       
  1489       $list = Array();
       
  1490       $tp = $this_page;
       
  1491       if ( $this_page + 0 == $num_pages ) $tp = $tp - 3;
       
  1492       if ( $this_page + 1 == $num_pages ) $tp = $tp - 2;
       
  1493       if ( $this_page + 2 == $num_pages ) $tp = $tp - 1;
       
  1494       for ( $i = $tp - 1; $i <= $tp + 1; $i++ )
       
  1495       {
       
  1496         $list[] = $i;
       
  1497       }
       
  1498     }
       
  1499     else
       
  1500     {
       
  1501       $list = Array();
       
  1502       $current = $this_page;
       
  1503       $lower = ( $current < 3 ) ? 1 : $current - 1;
       
  1504       for ( $i = 0; $i < 3; $i++ )
       
  1505       {
       
  1506         $list[] = $lower + $i;
       
  1507       }
       
  1508     }
       
  1509     $url = sprintf($result_url, '0');
       
  1510     $link = ( 0 == $start ) ? "<b>First</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>&laquo; First</a>";
       
  1511     $blk->assign_vars(array(
       
  1512       'CLASS'=>$cls,
       
  1513       'LINK'=>$link
       
  1514       ));
       
  1515     $inner .= $blk->run();
       
  1516     
       
  1517     // if ( !in_array(1, $list) )
       
  1518     // {
       
  1519     //   $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1520     //   $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...'));
       
  1521     //   $inner .= $blk->run();
       
  1522     // }
       
  1523     
       
  1524     foreach ( $list as $i )
       
  1525     {
       
  1526       if ( $i == $num_pages )
       
  1527         break;
       
  1528       $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1529       $offset = strval($i * $perpage);
       
  1530       $url = sprintf($result_url, $offset);
       
  1531       $j = $i + 1;
       
  1532       $link = ( $offset == strval($start) ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
       
  1533       $blk->assign_vars(array(
       
  1534         'CLASS'=>$cls,
       
  1535         'LINK'=>$link
       
  1536         ));
       
  1537       $inner .= $blk->run();
       
  1538     }
       
  1539     
       
  1540     $total = $num_pages * $perpage - $perpage;
       
  1541     
       
  1542     if ( $this_page < $num_pages )
       
  1543     {
       
  1544       // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1545       // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...'));
       
  1546       // $inner .= $blk->run();
       
  1547     
       
  1548       $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1549       $offset = strval($total);
       
  1550       $url = sprintf($result_url, $offset);
       
  1551       $j = $i + 1;
       
  1552       $link = ( $offset == strval($start) ) ? "<b>Last</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>Last &raquo;</a>";
       
  1553       $blk->assign_vars(array(
       
  1554         'CLASS'=>$cls,
       
  1555         'LINK'=>$link
       
  1556         ));
       
  1557       $inner .= $blk->run();
       
  1558     }
       
  1559     
       
  1560   }
       
  1561   
       
  1562   $inner .= '<td class="row2" style="cursor: pointer;" onclick="paginator_goto(this, '.$this_page.', '.$num_pages.', '.$perpage.', unescape(\'' . rawurlencode($result_url) . '\'));">&darr;</td>';
       
  1563   
       
  1564   $paginator = "\n$begin$inner$end\n";
       
  1565   $out .= $paginator;
       
  1566   
       
  1567   $cls = 'row2';
       
  1568   
       
  1569   if ( $row = $db->fetchrow($q) )
       
  1570   {
       
  1571     $i = 0;
       
  1572     $out .= $header;
       
  1573     do {
       
  1574       $i++;
       
  1575       if ( $i <= $start )
       
  1576       {
       
  1577         continue;
       
  1578       }
       
  1579       if ( ( $i - $start ) > $perpage )
       
  1580       {
       
  1581         break;
       
  1582       }
       
  1583       $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1584       foreach ( $row as $j => $val )
       
  1585       {
       
  1586         if ( isset($callers[$j]) )
       
  1587         {
       
  1588           $tmp = ( is_callable($callers[$j]) ) ? @call_user_func($callers[$j], $val, $row) : $v;
       
  1589           
       
  1590           if ( $tmp )
       
  1591           {
       
  1592             $row[$j] = $tmp;
       
  1593           }
       
  1594         }
       
  1595       }
       
  1596       $parser->assign_vars($row);
       
  1597       $parser->assign_vars(array('_css_class' => $cls));
       
  1598       $out .= $parser->run();
       
  1599     } while ( $row = $db->fetchrow($q) );
       
  1600     $out .= $footer;
       
  1601   }
       
  1602   
       
  1603   $out .= $paginator;
       
  1604   
       
  1605   return $out;
       
  1606 }
       
  1607 
       
  1608 /**
       
  1609  * This is the same as paginate(), but it processes an array instead of a MySQL result resource.
       
  1610  * @param array The results. Each value is simply echoed.
       
  1611  * @param int The number of total results. This should be determined by a second query.
       
  1612  * @param string sprintf-style formatting string for URLs for result pages. First parameter will be start offset.
       
  1613  * @param int Optional. Start offset in individual results. Defaults to 0.
       
  1614  * @param int Optional. The number of results per page. Defualts to 10.
       
  1615  * @param string Optional. The text to be sent before the result list, only if there are any results. Possibly the start of a table.
       
  1616  * @param string Optional. The text to be sent after the result list, only if there are any results. Possibly the end of a table.
       
  1617  * @return string
       
  1618  */
       
  1619 
       
  1620 function paginate_array($q, $num_results, $result_url, $start = 0, $perpage = 10, $header = '', $footer = '')
       
  1621 {
       
  1622   global $db, $session, $paths, $template, $plugins; // Common objects
       
  1623   $parser = $template->makeParserText($tpl_text);
       
  1624   $num_pages = ceil ( $num_results / $perpage );
       
  1625   $out = '';
       
  1626   $i = 0;
       
  1627   $this_page = ceil ( $start / $perpage );
       
  1628   
       
  1629   // Build paginator
       
  1630   $begin = '<div class="tblholder" style="display: table; margin: 10px 0 0 auto;">
       
  1631     <table border="0" cellspacing="1" cellpadding="4">
       
  1632       <tr><th>Page:</th>';
       
  1633   $block = '<td class="row1" style="text-align: center;">{LINK}</td>';
       
  1634   $end = '</tr></table></div>';
       
  1635   $blk = $template->makeParserText($block);
       
  1636   $inner = '';
       
  1637   $cls = 'row2';
       
  1638   if ( $start > 0 )
       
  1639   {
       
  1640     $url = sprintf($result_url, abs($start - $perpage));
       
  1641     $link = "<a href=".'"'."$url".'"'." style='text-decoration: none;'>&laquo; Prev</a>";
       
  1642     $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1643     $blk->assign_vars(array(
       
  1644       'CLASS'=>$cls,
       
  1645       'LINK'=>$link
       
  1646       ));
       
  1647     $inner .= $blk->run();
       
  1648   }
       
  1649   if ( $num_pages < 5 )
       
  1650   {
       
  1651     for ( $i = 0; $i < $num_pages; $i++ )
       
  1652     {
       
  1653       $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1654       $offset = strval($i * $perpage);
       
  1655       $url = sprintf($result_url, $offset);
       
  1656       $j = $i + 1;
       
  1657       $link = ( $offset == strval($start) ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
       
  1658       $blk->assign_vars(array(
       
  1659         'CLASS'=>$cls,
       
  1660         'LINK'=>$link
       
  1661         ));
       
  1662       $inner .= $blk->run();
       
  1663     }
       
  1664   }
       
  1665   else
       
  1666   {
       
  1667     if ( $this_page + 5 > $num_pages )
       
  1668     {
       
  1669       $list = Array();
       
  1670       $tp = $this_page;
       
  1671       if ( $this_page + 0 == $num_pages ) $tp = $tp - 3;
       
  1672       if ( $this_page + 1 == $num_pages ) $tp = $tp - 2;
       
  1673       if ( $this_page + 2 == $num_pages ) $tp = $tp - 1;
       
  1674       for ( $i = $tp - 1; $i <= $tp + 1; $i++ )
       
  1675       {
       
  1676         $list[] = $i;
       
  1677       }
       
  1678     }
       
  1679     else
       
  1680     {
       
  1681       $list = Array();
       
  1682       $current = $this_page;
       
  1683       $lower = ( $current < 3 ) ? 1 : $current - 1;
       
  1684       for ( $i = 0; $i < 3; $i++ )
       
  1685       {
       
  1686         $list[] = $lower + $i;
       
  1687       }
       
  1688     }
       
  1689     $url = sprintf($result_url, '0');
       
  1690     $link = ( 0 == $start ) ? "<b>First</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>&laquo; First</a>";
       
  1691     $blk->assign_vars(array(
       
  1692       'CLASS'=>$cls,
       
  1693       'LINK'=>$link
       
  1694       ));
       
  1695     $inner .= $blk->run();
       
  1696     
       
  1697     // if ( !in_array(1, $list) )
       
  1698     // {
       
  1699     //   $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1700     //   $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...'));
       
  1701     //   $inner .= $blk->run();
       
  1702     // }
       
  1703     
       
  1704     foreach ( $list as $i )
       
  1705     {
       
  1706       if ( $i == $num_pages )
       
  1707         break;
       
  1708       $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1709       $offset = strval($i * $perpage);
       
  1710       $url = sprintf($result_url, $offset);
       
  1711       $j = $i + 1;
       
  1712       $link = ( $offset == strval($start) ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
       
  1713       $blk->assign_vars(array(
       
  1714         'CLASS'=>$cls,
       
  1715         'LINK'=>$link
       
  1716         ));
       
  1717       $inner .= $blk->run();
       
  1718     }
       
  1719     
       
  1720     $total = $num_pages * $perpage - $perpage;
       
  1721     
       
  1722     if ( $this_page < $num_pages )
       
  1723     {
       
  1724       // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1725       // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...'));
       
  1726       // $inner .= $blk->run();
       
  1727     
       
  1728       $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1729       $offset = strval($total);
       
  1730       $url = sprintf($result_url, $offset);
       
  1731       $j = $i + 1;
       
  1732       $link = ( $offset == strval($start) ) ? "<b>Last</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>Last &raquo;</a>";
       
  1733       $blk->assign_vars(array(
       
  1734         'CLASS'=>$cls,
       
  1735         'LINK'=>$link
       
  1736         ));
       
  1737       $inner .= $blk->run();
       
  1738     }
       
  1739     
       
  1740   }
       
  1741   
       
  1742   if ( $start < $total )
       
  1743   {
       
  1744     $url = sprintf($result_url, abs($start + $perpage));
       
  1745     $link = "<a href=".'"'."$url".'"'." style='text-decoration: none;'>Next &raquo;</a>";
       
  1746     $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
       
  1747     $blk->assign_vars(array(
       
  1748       'CLASS'=>$cls,
       
  1749       'LINK'=>$link
       
  1750       ));
       
  1751     $inner .= $blk->run();
       
  1752   }
       
  1753   
       
  1754   $inner .= '<td class="row2" style="cursor: pointer;" onclick="paginator_goto(this, '.$this_page.', '.$num_pages.', '.$perpage.', unescape(\'' . rawurlencode($result_url) . '\'));">&darr;</td>';
       
  1755   
       
  1756   $paginator = "\n$begin$inner$end\n";
       
  1757   if ( $total > 1 )
       
  1758     $out .= $paginator;
       
  1759   
       
  1760   $cls = 'row2';
       
  1761   
       
  1762   if ( sizeof($q) > 0 )
       
  1763   {
       
  1764     $i = 0;
       
  1765     $out .= $header;
       
  1766     foreach ( $q as $val ) {
       
  1767       $i++;
       
  1768       if ( $i <= $start )
       
  1769       {
       
  1770         continue;
       
  1771       }
       
  1772       if ( ( $i - $start ) > $perpage )
       
  1773       {
       
  1774         break;
       
  1775       }
       
  1776       $out .= $val;
       
  1777     }
       
  1778     $out .= $footer;
       
  1779   }
       
  1780   
       
  1781   if ( $total > 1 )
       
  1782     $out .= $paginator;
       
  1783   
       
  1784   return $out;
       
  1785 }
       
  1786 
       
  1787 /** 
       
  1788  * Enano version of fputs for debugging
       
  1789  */
       
  1790 
       
  1791 function enano_fputs($socket, $data)
       
  1792 {
       
  1793   // echo '<pre>' . htmlspecialchars($data) . '</pre>';
       
  1794   // flush();
       
  1795   // ob_flush();
       
  1796   // ob_end_flush();
       
  1797   return fputs($socket, $data);
       
  1798 }
       
  1799 
       
  1800 /**
       
  1801  * Sanitizes a page URL string so that it can safely be stored in the database.
       
  1802  * @param string Page ID to sanitize
       
  1803  * @return string Cleaned text
       
  1804  */
       
  1805 
       
  1806 function sanitize_page_id($page_id)
       
  1807 {
       
  1808   
       
  1809   // First, replace spaces with underscores  
       
  1810   $page_id = str_replace(' ', '_', $page_id);
       
  1811   
       
  1812   preg_match_all('/\.[A-Fa-f0-9][A-Fa-f0-9]/', $page_id, $matches);
       
  1813   
       
  1814   foreach ( $matches[0] as $id => $char )
       
  1815   {
       
  1816     $char = substr($char, 1);
       
  1817     $char = strtolower($char);
       
  1818     $char = intval(hexdec($char));
       
  1819     $char = chr($char);
       
  1820     $page_id = str_replace($matches[0][$id], $char, $page_id);
       
  1821   }
       
  1822   
       
  1823   $pid_clean = preg_replace('/[\w\/:;\(\)@\[\]_-]/', 'X', $page_id);
       
  1824   $pid_dirty = enano_str_split($pid_clean, 1);
       
  1825   
       
  1826   foreach ( $pid_dirty as $id => $char )
       
  1827   {
       
  1828     if ( $char == 'X' )
       
  1829       continue;
       
  1830     $cid = ord($char);
       
  1831     $cid = dechex($cid);
       
  1832     $cid = strval($cid);
       
  1833     if ( strlen($cid) < 2 )
       
  1834     {
       
  1835       $cid = strtoupper("0$cid");
       
  1836     }
       
  1837     $pid_dirty[$id] = ".$cid";
       
  1838   }
       
  1839   
       
  1840   $pid_chars = enano_str_split($page_id, 1);
       
  1841   $page_id_cleaned = '';
       
  1842   
       
  1843   foreach ( $pid_chars as $id => $char )
       
  1844   {
       
  1845     if ( $pid_dirty[$id] == 'X' )
       
  1846       $page_id_cleaned .= $char;
       
  1847     else
       
  1848       $page_id_cleaned .= $pid_dirty[$id];
       
  1849   }
       
  1850   
       
  1851   global $mime_types;
       
  1852           
       
  1853   $exts = array_keys($mime_types);
       
  1854   $exts = '(' . implode('|', $exts) . ')';
       
  1855   
       
  1856   $page_id_cleaned = preg_replace('/\.2e' . $exts . '$/', '.\\1', $page_id_cleaned);
       
  1857   
       
  1858   return $page_id_cleaned;
       
  1859 }
       
  1860 
       
  1861 //die('<pre>Original:  01010101010100101010100101010101011010'."\nProcessed: ".uncompress_bitfield(compress_bitfield('01010101010100101010100101010101011010')).'</pre>');
       
  1862 
       
  1863 ?>