diff -r 902822492a68 -r fe660c52c48f includes/functions.php~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/functions.php~ Wed Jun 13 16:07:17 2007 -0400 @@ -0,0 +1,1863 @@ +escape($v); + $e=$db->sql_query('DELETE FROM '.table_prefix.'config WHERE config_name=\''.$n.'\';'); + if(!$e) $db->_die('Error during generic setConfig() call row deletion.'); + $e=$db->sql_query('INSERT INTO '.table_prefix.'config(config_name, config_value) VALUES(\''.$n.'\', \''.$v.'\')'); + if(!$e) $db->_die('Error during generic setConfig() call row insertion.'); +} + +function makeUrl($t, $query = false, $escape = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $flags = ''; + $sep = urlSeparator; + if(isset($_GET['printable'])) { $flags .= $sep.'printable'; $sep = '&'; } + if(isset($_GET['theme'])) { $flags .= $sep.'theme='.$session->theme; $sep = '&'; } + if(isset($_GET['style'])) { $flags .= $sep.'style='.$session->style; $sep = '&'; } + $url = $session->append_sid(contentPath.$t.$flags); + if($query) + { + $sep = strstr($url, '?') ? '&' : '?'; + $url = $url . $sep . $query; + } + return ($escape) ? htmlspecialchars($url) : $url; +} + +function makeUrlNS($n, $t, $query = false, $escape = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $flags = ''; + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $sep = urlSeparator; + else $sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?'; + if(isset($_GET['printable'])) { $flags .= $sep.'printable'; $sep = '&'; } + if(isset($_GET['theme'])) { $flags .= $sep.'theme='.$session->theme; $sep = '&'; } + if(isset($_GET['style'])) { $flags .= $sep.'style='.$session->style; $sep = '&'; } + + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) + { + $url = contentPath.$paths->nslist[$n].$t.$flags; + } + else + { + $url = contentPath.$n.':'.$t.$flags; + } + + if($query) + { + if(strstr($url, '?')) $sep = '&'; + else $sep = '?'; + $url = $url . $sep . $query . $flags; + } + + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) + { + $url = $session->append_sid($url); + } + + return ($escape) ? htmlspecialchars($url) : $url; +} + +function makeUrlComplete($n, $t, $query = false, $escape = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $flags = ''; + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $sep = urlSeparator; + else $sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?'; + if(isset($_GET['printable'])) { $flags .= $sep.'printable'; $sep = '&'; } + if(isset($_GET['theme'])) { $flags .= $sep.'theme='.$session->theme; $sep = '&'; } + if(isset($_GET['style'])) { $flags .= $sep.'style='.$session->style; $sep = '&'; } + if(defined('ENANO_BASE_CLASSES_INITIALIZED')) $url = $session->append_sid(contentPath.$paths->nslist[$n].$t.$flags); + else $url = contentPath.$n.':'.$t.$flags; + if($query) + { + if(strstr($url, '?')) $sep = '&'; + else $sep = '?'; + $url = $url . $sep . $query . $flags; + } + $baseprot = 'http' . ( isset($_SERVER['HTTPS']) ? 's' : '' ) . '://' . $_SERVER['HTTP_HOST']; + $url = $baseprot . $url; + return ($escape) ? htmlspecialchars($url) : $url; +} + +/** + * Redirect the user to the specified URL. + * @param string $url The URL, either relative or absolute. + * @param string $title The title of the message + * @param string $message A short message to show to the user + * @param string $timeout Timeout, in seconds, to delay the redirect. Defaults to 3. + */ + +function redirect($url, $title = 'Redirecting...', $message = 'Please wait while you are redirected.', $timeout = 3) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( $timeout == 0 ) + { + header('Location: ' . $url); + header('HTTP/1.1 307 Temporary Redirect'); + } + + $template->add_header(''); + $template->add_header(' + '); + + $template->tpl_strings['PAGE_NAME'] = $title; + $template->header(true); + echo '

' . $message . '

If you are not redirected within ' . ( $timeout + 1 ) . ' seconds, please click here.

'; + $template->footer(true); + + $db->close(); + exit(0); + +} + +// Removed wikiFormat() from here, replaced with RenderMan::render + +function isPage($p) { + global $db, $session, $paths, $template, $plugins; // Common objects + if(isset($paths->pages[$p])) return true; + $d = RenderMan::strToPageID($p); + if($d[1] != 'Special' && $d[1] != 'Template' && $d[1] != 'Admin') return false; + $a = explode('/', $p); + if(isset($paths->pages[$a[0]])) return true; + else return false; +} + +function arrayItemUp($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + $idxm = $idx - 1; + $temp = $arr[$keylist[$idxm]]; + if($arr[$keylist[0]] == $arr[$keyname]) return $arr; + $arr[$keylist[$idxm]] = $arr[$keylist[$idx]]; + $arr[$keylist[$idx]] = $temp; + return $arr; +} + +function arrayItemDown($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + $idxm = $idx + 1; + $temp = $arr[$keylist[$idxm]]; + $sz = sizeof($arr); $sz--; + if($arr[$keylist[$sz]] == $arr[$keyname]) return $arr; + $arr[$keylist[$idxm]] = $arr[$keylist[$idx]]; + $arr[$keylist[$idx]] = $temp; + return $arr; +} + +function arrayItemTop($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + while( $orig != $arr[$keylist[0]] ) { + // echo 'Keyname: '.$keylist[$idx] . '
'; flush(); ob_flush(); // Debugger + if($idx < 0) return $arr; + if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) { + /* echo 'Infinite loop caught in arrayItemTop(
';
+      print_r($arr);
+      echo '

, '.$keyname.');

EnanoCMS: Critical error during function call, exiting to prevent excessive server load.'; + exit; */ + return $arr; + } + $arr = arrayItemUp($arr, $keylist[$idx]); + $idx--; + } + return $arr; +} + +function arrayItemBottom($arr, $keyname) { + $keylist = array_keys($arr); + $keyflop = array_flip($keylist); + $idx = $keyflop[$keyname]; + $sz = sizeof($arr); $sz--; + while( $orig != $arr[$keylist[$sz]] ) { + // echo 'Keyname: '.$keylist[$idx] . '
'; flush(); ob_flush(); // Debugger + if($idx > $sz) return $arr; + if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) { + echo 'Infinite loop caught in arrayItemBottom(
';
+      print_r($arr);
+      echo '

, '.$keyname.');

EnanoCMS: Critical error during function call, exiting to prevent excessive server load.'; + exit; + } + $arr = arrayItemDown($arr, $keylist[$idx]); + $idx++; + } + return $arr; +} + +// Convert IP address to hex string +// Input: 127.0.0.1 (string) +// Output: 0x7f000001 (string) +// Updated 12/8/06 to work with PHP4 and not use eval() (blech) +function ip2hex($ip) { + if ( preg_match('/^([0-9a-f:]+)$/', $ip) ) + { + // this is an ipv6 address + return str_replace(':', '', $ip); + } + $nums = explode('.', $ip); + if(sizeof($nums) != 4) return false; + $str = '0x'; + foreach($nums as $n) + { + $str .= (string)dechex($n); + } + return $str; +} + +// Convert DWord to IP address +// Input: 0x7f000001 +// Output: 127.0.0.1 +// Updated 12/8/06 to work with PHP4 and not use eval() (blech) +function hex2ip($in) { + if(substr($in, 0, 2) == '0x') $ip = substr($in, 2, 8); + else $ip = substr($in, 0, 8); + $octets = enano_str_split($ip, 2); + $str = ''; + $newoct = Array(); + foreach($octets as $o) + { + $o = (int)hexdec($o); + $newoct[] = $o; + } + return implode('.', $newoct); +} + +// Function strip_php moved to RenderMan class + +function die_semicritical($t, $p) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $db->close(); + + if ( ob_get_status() ) + ob_end_clean(); + + dc_here('functions: calling die_semicritical'); + + $tpl = new template_nodb(); + $tpl->load_theme('oxygen', 'bleu'); + $tpl->tpl_strings['SITE_NAME'] = getConfig('site_name'); + $tpl->tpl_strings['SITE_DESC'] = getConfig('site_desc'); + $tpl->tpl_strings['COPYRIGHT'] = getConfig('copyright_notice'); + $tpl->tpl_strings['PAGE_NAME'] = $t; + $tpl->header(); + echo $p; + $tpl->footer(); + + exit; +} + +function die_friendly($t, $p) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( ob_get_status() ) + ob_end_clean(); + + dc_here('functions: calling die_friendly'); + $paths->cpage['name'] = $t; + $template->tpl_strings['PAGE_NAME'] = $t; + $template->header(); + echo $p; + $template->footer(); + $db->close(); + + exit; +} + +function grinding_halt($t, $p) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + $db->close(); + + if ( ob_get_status() ) + ob_end_clean(); + + dc_here('functions: calling grinding_halt'); + $tpl = new template_nodb(); + $tpl->load_theme('oxygen', 'bleu'); + $tpl->tpl_strings['SITE_NAME'] = 'Critical error'; + $tpl->tpl_strings['SITE_DESC'] = 'This website is experiencing a serious error and cannot load.'; + $tpl->tpl_strings['COPYRIGHT'] = 'Unable to retrieve copyright information'; + $tpl->tpl_strings['PAGE_NAME'] = $t; + $tpl->header(); + echo $p; + $tpl->footer(); + exit; +} + +function show_category_info() { + global $db, $session, $paths, $template, $plugins; // Common objects + dc_here('functions: showing category info'); + if($template->no_headers && !strpos($_SERVER['REQUEST_URI'], 'ajax.php')) return ''; + if($paths->namespace=='Category') + { + $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;'); + if(!$q) $db->_die('The category information could not be selected.'); + $ticker = -1; + echo '

Subcategories

'; + if($db->numrows() < 1) echo '

There are no subcategories in this category.

'; + echo ''; + while($row = $db->fetchrow()) + { + $ticker++;if($ticker==3) $ticker=0; + if($ticker==0) echo ''; + echo ''; + if($ticker==2) echo ''; + } + $db->free_result(); + if($ticker) echo ''; + echo '
'.$paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name'].'
'; + + $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;'); + if(!$q) $db->_die('The category information could not be selected.'); + $ticker = -1; + echo '

Pages

'; + if($db->numrows() < 1) echo '

There are no pages in this category.

'; + echo ''; + while($row = $db->fetchrow()) + { + $ticker++;if($ticker==3) $ticker=0; + if($ticker==0) echo ''; + echo ''; + if($ticker==2) echo ''; + } + $db->free_result(); + if($ticker) echo ''; + echo '
'.$paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name'].'


'; + } + $q = $db->sql_query('SELECT category_id FROM '.table_prefix.'categories WHERE page_id=\''.$paths->cpage['urlname_nons'].'\' AND namespace=\''.$paths->namespace.'\''); + if(!$q) $db->_die('The error seems to have occurred during selection of category data.'); + if($db->numrows() > 0) { + echo '
Categories: '; + $i=0; + while($r = $db->fetchrow()) + { + if($i>0) echo ', '; + $i++; + echo ''.$paths->pages[$paths->nslist['Category'].$r['category_id']]['name'].''; + } + if( ( $paths->wiki_mode && !$paths->page_protected ) || ( $session->get_permissions('edit_cat') && $session->get_permissions('even_when_protected') ) ) echo ' [ edit categorization ]
'; + } else { + echo '
Categories: '; + echo '(Uncategorized)'; + if( ( $paths->wiki_mode && !$paths->page_protected ) || ( $session->get_permissions('edit_cat') && $session->get_permissions('even_when_protected') ) ) echo ' [ edit categorization ]
'; + else echo ''; + } + $db->free_result(); +} + +function show_file_info() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if($paths->namespace != 'File') return null; // Prevent unnecessary work + $selfn = $paths->cpage['urlname_nons']; // substr($paths->page, strlen($paths->nslist['File']), strlen($paths->cpage)); + 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'])); + $q = $db->sql_query('SELECT mimetype,time_id,size FROM '.table_prefix.'files WHERE page_id=\''.$selfn.'\' ORDER BY time_id DESC;'); + if(!$q) $db->_die('The file type could not be fetched.'); + if($db->numrows() < 1) { echo '

Uploaded file

There are no files uploaded with this name yet. Upload a file...


'; return; } + $r = $db->fetchrow(); + $mimetype = $r['mimetype']; + $datestring = date('F d, Y h:i a', (int)$r['time_id']); + echo '

Uploaded file

Type: '.$r['mimetype'].'
Size: '; + $fs = $r['size']; + echo $fs.' bytes'; + $fs = (int)$fs; + if($fs >= 1048576) + { + $fs = round($fs / 1048576, 1); + echo ' ('.$fs.' MB)'; + } elseif($fs >= 1024) { + $fs = round($fs / 1024, 1); + echo ' ('.$fs.' KB)'; + } + echo '
Uploaded: '.$datestring.'

'; + if(substr($mimetype, 0, 6)!='image/' && ( substr($mimetype, 0, 5) != 'text/' || $mimetype == 'text/html' || $mimetype == 'text/javascript' )) + { + echo '
This file type may contain viruses or other code that could harm your computer. You should exercise caution if you download it.
'; + } + if(substr($mimetype, 0, 6)=='image/') + { + echo '

'.$paths->page.'

'; + } + echo '

Download this file'; + if(!$paths->page_protected && ( $paths->wiki_mode || $session->get_permissions('upload_new_version') )) + { + echo ' | Upload new version'; + } + echo '

'; + if($db->numrows() > 1) + { + echo '

File history

'; + while($r = $db->fetchrow()) + { + echo '(this ver) '; + if($session->get_permissions('history_rollback')) + echo ' (revert) '; + $mimetype = $r['mimetype']; + $datestring = date('F d, Y h:i a', (int)$r['time_id']); + echo $datestring.': '.$r['mimetype'].', '; + $fs = $r['size']; + $fs = (int)$fs; + if($fs >= 1048576) + { + $fs = round($fs / 1048576, 1); + echo ' '.$fs.' MB'; + } elseif($fs >= 1024) { + $fs = round($fs / 1024, 1); + echo ' '.$fs.' KB'; + } else { + echo ' '.$fs.' bytes'; + } + echo '
'; + } + echo '

'; + } + $db->free_result(); + echo '

'; +} + +function display_page_headers() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if($session->get_permissions('vote_reset') && $paths->cpage['delvotes'] > 0) + { + $hr = implode(', ', explode('|', $paths->cpage['delvote_ips'])); + $is = 'is'; + $s = ''; + $s2 = 's'; + if ( $paths->cpage['delvotes'] > 1) + { + $is = 'are'; + $s = 's'; + $s2 = ''; + } + echo '
+ Notice: There '.$is.' '.$paths->cpage['delvotes'].' user'.$s.' that think'.$s2.' this page should be deleted.
+ Users that voted: ' . $hr . '
+ Delete page | Reset votes +
'; + } +} + +function display_page_footers() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if(isset($_GET['nofooters'])) return; + $code = $plugins->setHook('send_page_footers'); + foreach ( $code as $cmd ) + { + eval($cmd); + } + show_file_info(); + show_category_info(); +} + +function password_prompt($id = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if(!$id) $id = $paths->page; + if(isset($paths->pages[$id]['password']) && strlen($paths->pages[$id]['password']) == 40 && !isset($_REQUEST['pagepass'])) + { + die_friendly('Password required', '

You must supply a password to access this page.

Password:

'); + } elseif(isset($_REQUEST['pagepass'])) { + $p = (preg_match('#^([a-f0-9]*){40}$#', $_REQUEST['pagepass'])) ? $_REQUEST['pagepass'] : sha1($_REQUEST['pagepass']); + if($p != $paths->pages[$id]['password']) die_friendly('Password required', '

The password you entered is incorrect.

Password:

'); + } +} + +function str_hex($string){ + $hex=''; + for ($i=0; $i < strlen($string); $i++){ + $hex .= ' '.dechex(ord($string[$i])); + } + return substr($hex, 1, strlen($hex)); +} + +// Function pulled from phpBB's smtp.php +function smtp_get_response($socket, $response, $line = __LINE__) +{ + $server_response = ''; + while (substr($server_response, 3, 1) != ' ') + { + if (!($server_response = fgets($socket, 256))) + { + die_friendly('SMTP Error', "

Couldn't get mail server response codes

"); + } + } + + if (!(substr($server_response, 0, 3) == $response)) + { + die_friendly('SMTP Error', "

Ran into problems sending mail. Response: $server_response

"); + } +} + +function smtp_send_email($to, $subject, $message, $from) +{ + return smtp_send_email_core($to, $subject, $message, "From: <$from>\n"); +} + +// Replacement or substitute for PHP's mail command +// Ported from phpBB - copyright (C) phpBB group, GPL. +function smtp_send_email_core($mail_to, $subject, $message, $headers = '') +{ + global $board_config; + + // Fix any bare linefeeds in the message to make it RFC821 Compliant. + $message = preg_replace("#(? 1) + { + $headers = join("\n", $headers); + } + else + { + $headers = $headers[0]; + } + } + $headers = chop($headers); + + // Make sure there are no bare linefeeds in the headers + $headers = preg_replace('#(?\r\n"); + smtp_get_response($socket, "250", __LINE__); + + // Specify each user to send to and build to header. + $to_header = ''; + + // Add an additional bit of error checking to the To field. + $mail_to = (trim($mail_to) == '') ? 'Undisclosed-recipients:;' : trim($mail_to); + if (preg_match('#[^ ]+\@[^ ]+#', $mail_to)) + { + enano_fputs($socket, "RCPT TO: <$mail_to>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } + + // Ok now do the CC and BCC fields... + @reset($bcc); + while(list(, $bcc_address) = each($bcc)) + { + // Add an additional bit of error checking to bcc header... + $bcc_address = trim($bcc_address); + if (preg_match('#[^ ]+\@[^ ]+#', $bcc_address)) + { + enano_fputs($socket, "RCPT TO: <$bcc_address>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } + } + + @reset($cc); + while(list(, $cc_address) = each($cc)) + { + // Add an additional bit of error checking to cc header + $cc_address = trim($cc_address); + if (preg_match('#[^ ]+\@[^ ]+#', $cc_address)) + { + enano_fputs($socket, "RCPT TO: <$cc_address>\r\n"); + smtp_get_response($socket, "250", __LINE__); + } + } + + // Ok now we tell the server we are ready to start sending data + enano_fputs($socket, "DATA\r\n"); + + // This is the last response code we look for until the end of the message. + smtp_get_response($socket, "354", __LINE__); + + // Send the Subject Line... + enano_fputs($socket, "Subject: $subject\r\n"); + + // Now the To Header. + enano_fputs($socket, "To: $mail_to\r\n"); + + // Now any custom headers.... + enano_fputs($socket, "$headers\r\n\r\n"); + + // Ok now we are ready for the message... + enano_fputs($socket, "$message\r\n"); + + // Ok the all the ingredients are mixed in let's cook this puppy... + enano_fputs($socket, ".\r\n"); + smtp_get_response($socket, "250", __LINE__); + + // Now tell the server we are done and close the socket... + enano_fputs($socket, "QUIT\r\n"); + fclose($socket); + + return TRUE; +} + +/** + * Tell which version of Enano we're running. + * @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.) + * @return string + */ + +function enano_version($long = false, $no_nightly = false) +{ + $r = getConfig('enano_version'); + $rc = ( $long ) ? ' release candidate ' : 'RC'; + $b = ( $long ) ? ' beta ' : 'b'; + $a = ( $long ) ? ' alpha ' : 'a'; + if($v = getConfig('enano_rc_version')) $r .= $rc.$v; + if($v = getConfig('enano_beta_version')) $r .= $b.$v; + if($v = getConfig('enano_alpha_version')) $r .= $a.$v; + if ( defined('ENANO_NIGHTLY') && !$no_nightly ) + { + $nightlytag = ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR; + $nightlylong = ' nightly; build date: ' . ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR; + $r = ( $long ) ? $r . $nightlylong : $r . '-nightly-' . $nightlytag; + } + return $r; +} + +function _dualurlenc($t) { + return rawurlencode(rawurlencode($t)); +} + +function _die($t) { + $_ob = 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\'' . rawurlencode('' . $t . '') . '\')'; + die($_ob); +} + +function jsdie($text) { + global $db, $session, $paths, $template, $plugins; // Common objects + $text = rawurlencode($text . "\n\nSQL Backtrace:\n" . $db->sql_backtrace()); + echo 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.$text.'\');'; +} + +// HTML sanitizing function - written by Kallahar +// Original function at: http://quickwired.com/kallahar/smallprojects/php_xss_filter_function.php + +// UNUSED - todo: remove this in gold or put it to use + +function RemoveXSS($val) { + // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed + // this prevents some character re-spacing such as + // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs + $val = preg_replace('/([\x00-\x08][\x0b-\x0c][\x0e-\x20])/', '', $val); + + // straight replacements, the user should never need these since they're normal characters + // this prevents like + $search = 'abcdefghijklmnopqrstuvwxyz'; + $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $search .= '1234567890!@#$%^&*()'; + $search .= '~`";:?+/={}[]-_|\'\\'; + for ($i = 0; $i < strlen($search); $i++) { + // ;? matches the ;, which is optional + // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars + + // @ @ search for the hex values + $val = preg_replace('/(&#[x|X]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ; + // @ @ 0{0,7} matches '0' zero to seven times + $val = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ; + } + + // now the only remaining whitespace attacks are \t, \n, and \r + $ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base'); + $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'); + $ra = array_merge($ra1, $ra2); + + $found = true; // keep replacing as long as the previous round replaced something + while ($found == true) { + $val_before = $val; + for ($i = 0; $i < sizeof($ra); $i++) { + $pattern = '/'; + for ($j = 0; $j < strlen($ra[$i]); $j++) { + if ($j > 0) { + $pattern .= '('; + $pattern .= '(&#[x|X]0{0,8}([9][a][b]);?)?'; + $pattern .= '|(�{0,8}([9][10][13]);?)?'; + $pattern .= ')?'; + } + $pattern .= $ra[$i][$j]; + } + $pattern .= '/i'; + $replacement = substr($ra[$i], 0, 2).''.substr($ra[$i], 2); // add in <> to nerf the tag + $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags + if ($val_before == $val) { + // no replacements were made, so exit the loop + $found = false; + } + } + } + return $val; +} + +/** + * Capitalizes the first letter of a string + * @param $text string the text to be transformed + * @return string + */ + +function capitalize_first_letter($text) +{ + return strtoupper(substr($text, 0, 1)) . substr($text, 1); +} + +/** + * Checks if a value in a bitfield is on or off + * @param $bitfield int the bit-field value + * @param $value int the value to switch off + * @return bool + */ + +function is_bit($bitfield, $value) +{ + return ( $bitfield & $value ) ? true : false; +} + +/** + * Trims spaces/newlines from the beginning and end of a string + * @param $text the text to process + * @return string + */ + +function trim_spaces($text) +{ + $d = true; + while($d) + { + $c = substr($text, 0, 1); + $a = substr($text, strlen($text)-1, strlen($text)); + if($c == "\n" || $c == "\r" || $c == "\t" || $c == ' ') $text = substr($text, 1, strlen($text)); + elseif($a == "\n" || $a == "\r" || $a == "\t" || $a == ' ') $text = substr($text, 0, strlen($text)-1); + else $d = false; + } + return $text; +} + +/** + * Enano-ese equivalent of str_split() which is only found in PHP5 + * @param $text string the text to split + * @param $inc int size of each block + * @return array + */ + +function enano_str_split($text, $inc = 1) +{ + if($inc < 1) return false; + if($inc >= strlen($text)) return Array($text); + $len = ceil(strlen($text) / $inc); + $ret = Array(); + for($i=0;$i'; + debug_print_backtrace(); + echo ''; + $c = ob_get_contents(); + ob_end_clean(); + if($return) return $c; + else echo $c; + return null; +} + +/** + * Like rawurlencode(), but encodes all characters + * @param string $text the text to encode + * @param optional string $prefix text before each hex character + * @param optional string $suffix text after each hex character + * @return string + */ + +function hexencode($text, $prefix = '%', $suffix = '') +{ + $arr = enano_str_split($text); + $r = ''; + foreach($arr as $a) + { + $nibble = (string)dechex(ord($a)); + if(strlen($nibble) == 1) $nibble = '0' . $nibble; + $r .= $prefix . $nibble . $suffix; + } + return $r; +} + +/** + * Enano-ese equivalent of get_magic_quotes_gpc() + * @return bool + */ + +function enano_get_magic_quotes_gpc() +{ + if(function_exists('get_magic_quotes_gpc')) + { + return ( get_magic_quotes_gpc() == 1 ); + } + else + { + return ( strtolower(@ini_get('magic_quotes_gpc')) == '1' ); + } +} + +/** + * Recursive stripslashes() + * @param array + * @return array + */ + +function stripslashes_recurse($arr) +{ + foreach($arr as $k => $xxxx) + { + $val =& $arr[$k]; + if(is_string($val)) + $val = stripslashes($val); + elseif(is_array($val)) + $val = stripslashes_recurse($val); + } + return $arr; +} + +/** + * If magic_quotes_gpc is on, calls stripslashes() on everything in $_GET/$_POST/$_COOKIE + * @ignore - this doesn't work + * @todo port version from the PHP manual + * @return void + */ +function strip_magic_quotes_gpc() +{ + if(enano_get_magic_quotes_gpc()) + { + $_POST = stripslashes_recurse($_POST); + $_GET = stripslashes_recurse($_GET); + $_COOKIE = stripslashes_recurse($_COOKIE); + } +} + +/** + * A very basic single-character compression algorithm for binary strings/bitfields + * @param string $bits the text to compress + * @return string + */ + +function compress_bitfield($bits) +{ + $crc32 = crc32($bits); + $bits .= '0'; + $start_pos = 0; + $current = substr($bits, 1, 1); + $last = substr($bits, 0, 1); + $chunk_size = 1; + $len = strlen($bits); + $crc = $len; + $crcval = 0; + for ( $i = 1; $i < $len; $i++ ) + { + $current = substr($bits, $i, 1); + $last = substr($bits, $i - 1, 1); + $next = substr($bits, $i + 1, 1); + // Are we on the last character? + if($current == $last && $i+1 < $len) + $chunk_size++; + else + { + if($i+1 == $len && $current == $next) + { + // This character completes a chunk + $chunk_size++; + $i++; + $chunk = substr($bits, $start_pos, $chunk_size); + $chunklen = strlen($chunk); + $newchunk = $last . '[' . $chunklen . ']'; + $newlen = strlen($newchunk); + $bits = substr($bits, 0, $start_pos) . $newchunk . substr($bits, $i, $len); + $chunk_size = 1; + $i = $start_pos + $newlen; + $start_pos = $i; + $len = strlen($bits); + $crcval = $crcval + $chunklen; + } + else + { + // Last character completed a chunk + $chunk = substr($bits, $start_pos, $chunk_size); + $chunklen = strlen($chunk); + $newchunk = $last . '[' . $chunklen . '],'; + $newlen = strlen($newchunk); + $bits = substr($bits, 0, $start_pos) . $newchunk . substr($bits, $i, $len); + $chunk_size = 1; + $i = $start_pos + $newlen; + $start_pos = $i; + $len = strlen($bits); + $crcval = $crcval + $chunklen; + } + } + } + if($crc != $crcval) + { + echo __FUNCTION__.'(): ERROR: length check failed, this is a bug in the algorithm
Debug info: aiming for a CRC val of '.$crc.', got '.$crcval; + return false; + } + $compressed = 'cbf:len='.$crc.';crc='.dechex($crc32).';data='.$bits.'|end'; + return $compressed; +} + +/** + * Uncompresses a bitfield compressed with compress_bitfield() + * @param string $bits the compressed bitfield + * @return string the uncompressed, original (we hope) bitfield OR bool false on error + */ + +function uncompress_bitfield($bits) +{ + if(substr($bits, 0, 4) != 'cbf:') + { + echo __FUNCTION__.'(): ERROR: Invalid stream'; + return false; + } + $len = intval(substr($bits, strpos($bits, 'len=')+4, strpos($bits, ';')-strpos($bits, 'len=')-4)); + $crc = substr($bits, strpos($bits, 'crc=')+4, 8); + $data = substr($bits, strpos($bits, 'data=')+5, strpos($bits, '|end')-strpos($bits, 'data=')-5); + $data = explode(',', $data); + foreach($data as $a => $b) + { + $d =& $data[$a]; + $char = substr($d, 0, 1); + $dlen = intval(substr($d, 2, strlen($d)-1)); + $s = ''; + for($i=0;$i<$dlen;$i++,$s.=$char); + $d = $s; + unset($s, $dlen, $char); + } + $decompressed = implode('', $data); + $decompressed = substr($decompressed, 0, -1); + $dcrc = (string)dechex(crc32($decompressed)); + if($dcrc != $crc) + { + echo __FUNCTION__.'(): ERROR: CRC check failed
debug info:
original crc: '.$crc.'
decomp\'ed crc: '.$dcrc.'
'; + return false; + } + return $decompressed; +} + +/** + * Exports a MySQL table into a SQL string. + * @param string $table The name of the table to export + * @param bool $structure If true, include a CREATE TABLE command + * @param bool $data If true, include the contents of the table + * @param bool $compact If true, omits newlines between parts of SQL statements, use in Enano database exporter + * @return string + */ + +function export_table($table, $structure = true, $data = true, $compact = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $struct_keys = ''; + $divider = (!$compact) ? "\n" : "\n"; + $spacer1 = (!$compact) ? "\n" : " "; + $spacer2 = (!$compact) ? " " : " "; + $rowspacer = (!$compact) ? "\n " : " "; + $index_list = Array(); + $cols = $db->sql_query('SHOW COLUMNS IN '.$table.';'); + if(!$cols) + { + echo 'export_table(): Error getting column list: '.$db->get_error_text().'
'; + return false; + } + $col = Array(); + $sqlcol = Array(); + $collist = Array(); + $pri_keys = Array(); + // Using fetchrow_num() here to compensate for MySQL l10n + while( $row = $db->fetchrow_num() ) + { + $field =& $row[0]; + $type =& $row[1]; + $null =& $row[2]; + $key =& $row[3]; + $def =& $row[4]; + $extra =& $row[5]; + $col[] = Array( + 'name'=>$field, + 'type'=>$type, + 'null'=>$null, + 'key'=>$key, + 'default'=>$def, + 'extra'=>$extra, + ); + $collist[] = $field; + } + + if ( $structure ) + { + $db->sql_query('SET SQL_QUOTE_SHOW_CREATE = 0;'); + $struct = $db->sql_query('SHOW CREATE TABLE '.$table.';'); + if ( !$struct ) + $db->_die(); + $row = $db->fetchrow_num(); + $db->free_result(); + $struct = $row[1]; + $struct = preg_replace("/\n\) ENGINE=(.+)$/", "\n);", $struct); + unset($row); + if ( $compact ) + { + $struct_arr = explode("\n", $struct); + foreach ( $struct_arr as $i => $leg ) + { + if ( $i == 0 ) + continue; + $test = trim($leg); + if ( empty($test) ) + { + unset($struct_arr[$i]); + continue; + } + $struct_arr[$i] = preg_replace('/^([\s]*)/', ' ', $leg); + } + $struct = implode("", $struct_arr); + } + } + + // Structuring complete + if($data) + { + $datq = $db->sql_query('SELECT * FROM '.$table.';'); + if(!$datq) + { + echo 'export_table(): Error getting column list: '.$db->get_error_text().'
'; + return false; + } + if($db->numrows() < 1) + { + if($structure) return $struct; + else return ''; + } + $rowdata = Array(); + $dataqs = Array(); + $insert_strings = Array(); + $z = false; + while($row = $db->fetchrow_num()) + { + $z = false; + foreach($row as $i => $cell) + { + $str = mysql_encode_column($cell, $col[$i]['type']); + $rowdata[] = $str; + } + $dataqs2 = implode(",$rowspacer", $dataqs) . ",$rowspacer" . '( ' . implode(', ', $rowdata) . ' )'; + $ins = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . $dataqs2 . ";"; + if ( strlen( $ins ) > MYSQL_MAX_PACKET_SIZE ) + { + // We've exceeded the maximum allowed packet size for MySQL - separate this into a different query + $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";; + $dataqs = Array('( ' . implode(', ', $rowdata) . ' )'); + $z = true; + } + else + { + $dataqs[] = '( ' . implode(', ', $rowdata) . ' )'; + } + $rowdata = Array(); + } + if ( !$z ) + { + $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";; + $dataqs = Array(); + } + $datstring = implode($divider, $insert_strings); + } + if($structure && !$data) return $struct; + elseif(!$structure && $data) return $datstring; + elseif($structure && $data) return $struct . $divider . $datstring; + elseif(!$structure && !$data) return ''; +} + +/** + * Encodes a string value for use in an INSERT statement for given column type $type. + * @access private + */ + +function mysql_encode_column($input, $type) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + // Decide whether to quote the string or not + if(substr($type, 0, 7) == 'varchar' || $type == 'datetime' || $type == 'text' || $type == 'tinytext' || $type == 'smalltext' || $type == 'longtext' || substr($type, 0, 4) == 'char') + { + $str = "'" . $db->escape($input) . "'"; + } + elseif(in_array($type, Array('blob', 'longblob', 'mediumblob', 'smallblob')) || substr($type, 0, 6) == 'binary' || substr($type, 0, 9) == 'varbinary') + { + $str = '0x' . hexencode($input, '', ''); + } + elseif(is_null($input)) + { + $str = 'NULL'; + } + else + { + $str = (string)$input; + } + return $str; +} + +/** + * Creates an associative array defining which file extensions are allowed and which ones aren't + * @return array keyname will be a file extension, value will be true or false + */ + +function fetch_allowed_extensions() +{ + global $mime_types; + $bits = getConfig('allowed_mime_types'); + if(!$bits) return Array(false); + $bits = uncompress_bitfield($bits); + if(!$bits) return Array(false); + $bits = enano_str_split($bits, 1); + $ret = Array(); + $mt = array_keys($mime_types); + foreach($bits as $i => $b) + { + $ret[$mt[$i]] = ( $b == '1' ) ? true : false; + } + return $ret; +} + +/** + * Generates a random key suitable for encryption + * @param int $len the length of the key + * @return string a BINARY key + */ + +function randkey($len = 32) +{ + $key = ''; + for($i=0;$i<$len;$i++) + { + $key .= chr(mt_rand(0, 255)); + } + return $key; +} + +/** + * Decodes a hex string. + * @param string $hex The hex code to decode + * @return string + */ + +function hexdecode($hex) +{ + $hex = enano_str_split($hex, 2); + $bin_key = ''; + foreach($hex as $nibble) + { + $byte = chr(hexdec($nibble)); + $bin_key .= $byte; + } + return $bin_key; +} + +/** + * Enano's own (almost) bulletproof HTML sanitizer. + * @param string $html The input HTML + * @return string cleaned HTML + */ + +function sanitize_html($html, $filter_php = true) +{ + + $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>(.*?)#is', '<\\1\\2\\3javascript:\\59>\\60</\\1>', $html); + $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>#is', '<\\1\\2\\3javascript:\\59>', $html); + + if($filter_php) + $html = str_replace( + Array('', '%>'), + Array('<?php', '<?', '<%', '?>', '%>'), + $html); + + $tag_whitelist = array_keys ( setupAttributeWhitelist() ); + if ( !$filter_php ) + $tag_whitelist[] = '?php'; + $len = strlen($html); + $in_quote = false; + $quote_char = ''; + $tag_start = 0; + $tag_name = ''; + $in_tag = false; + $trk_name = false; + for ( $i = 0; $i < $len; $i++ ) + { + $chr = $html{$i}; + $prev = ( $i == 0 ) ? '' : $html{ $i - 1 }; + $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 }; + if ( $in_quote && $in_tag ) + { + if ( $quote_char == $chr && $prev != '\\' ) + $in_quote = false; + } + elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag ) + { + $in_quote = true; + $quote_char = $chr; + } + if ( $chr == '<' && !$in_tag && $next != '/' ) + { + // start of a tag + $tag_start = $i; + $in_tag = true; + $trk_name = true; + } + elseif ( !$in_quote && $in_tag && $chr == '>' ) + { + $full_tag = substr($html, $tag_start, ( $i - $tag_start ) + 1 ); + $l = strlen($tag_name) + 2; + $attribs_only = trim( substr($full_tag, $l, ( strlen($full_tag) - $l - 1 ) ) ); + + // Debugging message + // echo htmlspecialchars($full_tag) . '
'; + + if ( !in_array($tag_name, $tag_whitelist) ) + { + // Illegal tag + //echo $tag_name . ' '; + + $s = ( empty($attribs_only) ) ? '' : ' '; + + $sanitized = '<' . $tag_name . $s . $attribs_only . '>'; + + $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1); + $html = str_replace('', '</' . $tag_name . '>', $html); + $new_i = $tag_start + strlen($sanitized); + + $len = strlen($html); + $i = $new_i; + + $in_tag = false; + $tag_name = ''; + continue; + } + else + { + if ( $tag_name == '?php' && !$filter_php ) + continue; + $f = fixTagAttributes( $attribs_only, $tag_name ); + $s = ( empty($f) ) ? '' : ' '; + + $sanitized = '<' . $tag_name . $f . '>'; + $new_i = $tag_start + strlen($sanitized); + + $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1); + $len = strlen($html); + $i = $new_i; + + $in_tag = false; + $tag_name = ''; + continue; + } + } + elseif ( $in_tag && $trk_name ) + { + $is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' ); + if ( $is_alphabetical ) + $tag_name .= $chr; + else + { + $trk_name = false; + } + } + + } + + return $html; + +} + +function htmlalternatives($string) +{ + $ret = ''; + for ( $i = 0; $i < strlen($string); $i++ ) + { + $chr = $string{$i}; + $ch1 = ord($chr); + $ch2 = dechex($ch1); + $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) . ')'; + $ret .= $byte; + $ret .= '([\s]){0,2}'; + } + return $ret; +} + +/** + * Paginates (breaks into multiple pages) a MySQL result resource, which is treated as unbuffered. + * @param resource The MySQL result resource. This should preferably be an unbuffered query. + * @param string A template, with variables being named after the column name + * @param int The number of total results. This should be determined by a second query. + * @param string sprintf-style formatting string for URLs for result pages. First parameter will be start offset. + * @param int Optional. Start offset in individual results. Defaults to 0. + * @param int Optional. The number of results per page. Defualts to 10. + * @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. + * @param string Optional. The text to be sent before the result list, only if there are any results. Possibly the start of a table. + * @param string Optional. The text to be sent after the result list, only if there are any results. Possibly the end of a table. + * @return string + */ + +function paginate($q, $tpl_text, $num_results, $result_url, $start = 0, $perpage = 10, $callers = Array(), $header = '', $footer = '') +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $parser = $template->makeParserText($tpl_text); + $num_pages = ceil ( $num_results / $perpage ); + $out = ''; + $i = 0; + $this_page = ceil ( $start / $perpage ); + + // Build paginator + $begin = '
+ + '; + $block = ''; + $end = '
Page:{LINK}
'; + $blk = $template->makeParserText($block); + $inner = ''; + $cls = 'row2'; + if ( $num_pages < 5 ) + { + for ( $i = 0; $i < $num_pages; $i++ ) + { + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + } + else + { + if ( $this_page + 5 > $num_pages ) + { + $list = Array(); + $tp = $this_page; + if ( $this_page + 0 == $num_pages ) $tp = $tp - 3; + if ( $this_page + 1 == $num_pages ) $tp = $tp - 2; + if ( $this_page + 2 == $num_pages ) $tp = $tp - 1; + for ( $i = $tp - 1; $i <= $tp + 1; $i++ ) + { + $list[] = $i; + } + } + else + { + $list = Array(); + $current = $this_page; + $lower = ( $current < 3 ) ? 1 : $current - 1; + for ( $i = 0; $i < 3; $i++ ) + { + $list[] = $lower + $i; + } + } + $url = sprintf($result_url, '0'); + $link = ( 0 == $start ) ? "First" : "« First"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + + // if ( !in_array(1, $list) ) + // { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + // } + + foreach ( $list as $i ) + { + if ( $i == $num_pages ) + break; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + $total = $num_pages * $perpage - $perpage; + + if ( $this_page < $num_pages ) + { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($total); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "Last" : "Last »"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + } + + $inner .= '↓'; + + $paginator = "\n$begin$inner$end\n"; + $out .= $paginator; + + $cls = 'row2'; + + if ( $row = $db->fetchrow($q) ) + { + $i = 0; + $out .= $header; + do { + $i++; + if ( $i <= $start ) + { + continue; + } + if ( ( $i - $start ) > $perpage ) + { + break; + } + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + foreach ( $row as $j => $val ) + { + if ( isset($callers[$j]) ) + { + $tmp = ( is_callable($callers[$j]) ) ? @call_user_func($callers[$j], $val, $row) : $v; + + if ( $tmp ) + { + $row[$j] = $tmp; + } + } + } + $parser->assign_vars($row); + $parser->assign_vars(array('_css_class' => $cls)); + $out .= $parser->run(); + } while ( $row = $db->fetchrow($q) ); + $out .= $footer; + } + + $out .= $paginator; + + return $out; +} + +/** + * This is the same as paginate(), but it processes an array instead of a MySQL result resource. + * @param array The results. Each value is simply echoed. + * @param int The number of total results. This should be determined by a second query. + * @param string sprintf-style formatting string for URLs for result pages. First parameter will be start offset. + * @param int Optional. Start offset in individual results. Defaults to 0. + * @param int Optional. The number of results per page. Defualts to 10. + * @param string Optional. The text to be sent before the result list, only if there are any results. Possibly the start of a table. + * @param string Optional. The text to be sent after the result list, only if there are any results. Possibly the end of a table. + * @return string + */ + +function paginate_array($q, $num_results, $result_url, $start = 0, $perpage = 10, $header = '', $footer = '') +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $parser = $template->makeParserText($tpl_text); + $num_pages = ceil ( $num_results / $perpage ); + $out = ''; + $i = 0; + $this_page = ceil ( $start / $perpage ); + + // Build paginator + $begin = '
+ + '; + $block = ''; + $end = '
Page:{LINK}
'; + $blk = $template->makeParserText($block); + $inner = ''; + $cls = 'row2'; + if ( $start > 0 ) + { + $url = sprintf($result_url, abs($start - $perpage)); + $link = "« Prev"; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + if ( $num_pages < 5 ) + { + for ( $i = 0; $i < $num_pages; $i++ ) + { + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + } + else + { + if ( $this_page + 5 > $num_pages ) + { + $list = Array(); + $tp = $this_page; + if ( $this_page + 0 == $num_pages ) $tp = $tp - 3; + if ( $this_page + 1 == $num_pages ) $tp = $tp - 2; + if ( $this_page + 2 == $num_pages ) $tp = $tp - 1; + for ( $i = $tp - 1; $i <= $tp + 1; $i++ ) + { + $list[] = $i; + } + } + else + { + $list = Array(); + $current = $this_page; + $lower = ( $current < 3 ) ? 1 : $current - 1; + for ( $i = 0; $i < 3; $i++ ) + { + $list[] = $lower + $i; + } + } + $url = sprintf($result_url, '0'); + $link = ( 0 == $start ) ? "First" : "« First"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + + // if ( !in_array(1, $list) ) + // { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + // } + + foreach ( $list as $i ) + { + if ( $i == $num_pages ) + break; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($i * $perpage); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "$j" : "$j"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + $total = $num_pages * $perpage - $perpage; + + if ( $this_page < $num_pages ) + { + // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...')); + // $inner .= $blk->run(); + + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $offset = strval($total); + $url = sprintf($result_url, $offset); + $j = $i + 1; + $link = ( $offset == strval($start) ) ? "Last" : "Last »"; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + } + + if ( $start < $total ) + { + $url = sprintf($result_url, abs($start + $perpage)); + $link = "Next »"; + $cls = ( $cls == 'row1' ) ? 'row2' : 'row1'; + $blk->assign_vars(array( + 'CLASS'=>$cls, + 'LINK'=>$link + )); + $inner .= $blk->run(); + } + + $inner .= '↓'; + + $paginator = "\n$begin$inner$end\n"; + if ( $total > 1 ) + $out .= $paginator; + + $cls = 'row2'; + + if ( sizeof($q) > 0 ) + { + $i = 0; + $out .= $header; + foreach ( $q as $val ) { + $i++; + if ( $i <= $start ) + { + continue; + } + if ( ( $i - $start ) > $perpage ) + { + break; + } + $out .= $val; + } + $out .= $footer; + } + + if ( $total > 1 ) + $out .= $paginator; + + return $out; +} + +/** + * Enano version of fputs for debugging + */ + +function enano_fputs($socket, $data) +{ + // echo '
' . htmlspecialchars($data) . '
'; + // flush(); + // ob_flush(); + // ob_end_flush(); + return fputs($socket, $data); +} + +/** + * Sanitizes a page URL string so that it can safely be stored in the database. + * @param string Page ID to sanitize + * @return string Cleaned text + */ + +function sanitize_page_id($page_id) +{ + + // First, replace spaces with underscores + $page_id = str_replace(' ', '_', $page_id); + + preg_match_all('/\.[A-Fa-f0-9][A-Fa-f0-9]/', $page_id, $matches); + + foreach ( $matches[0] as $id => $char ) + { + $char = substr($char, 1); + $char = strtolower($char); + $char = intval(hexdec($char)); + $char = chr($char); + $page_id = str_replace($matches[0][$id], $char, $page_id); + } + + $pid_clean = preg_replace('/[\w\/:;\(\)@\[\]_-]/', 'X', $page_id); + $pid_dirty = enano_str_split($pid_clean, 1); + + foreach ( $pid_dirty as $id => $char ) + { + if ( $char == 'X' ) + continue; + $cid = ord($char); + $cid = dechex($cid); + $cid = strval($cid); + if ( strlen($cid) < 2 ) + { + $cid = strtoupper("0$cid"); + } + $pid_dirty[$id] = ".$cid"; + } + + $pid_chars = enano_str_split($page_id, 1); + $page_id_cleaned = ''; + + foreach ( $pid_chars as $id => $char ) + { + if ( $pid_dirty[$id] == 'X' ) + $page_id_cleaned .= $char; + else + $page_id_cleaned .= $pid_dirty[$id]; + } + + global $mime_types; + + $exts = array_keys($mime_types); + $exts = '(' . implode('|', $exts) . ')'; + + $page_id_cleaned = preg_replace('/\.2e' . $exts . '$/', '.\\1', $page_id_cleaned); + + return $page_id_cleaned; +} + +//die('
Original:  01010101010100101010100101010101011010'."\nProcessed: ".uncompress_bitfield(compress_bitfield('01010101010100101010100101010101011010')).'
'); + +?>