/** * Changes the filesystem permissions of a file or a folder recursively. Also prints out folder * names online on success / error and prints out filenames on error as well. * * @param string $filename absolute path to folder/file that should be chmod'ed * @param int $folderPermissions (octal) new permissions for folders * @param int $filePermissions (octal) new permissions for files * @param int $start unix timestamp of last webserver/php timeout counter-measure * @return null on success, int <> 0 on error */ function chmodRecursively($filename, $folderPermissions, $filePermissions, $start) { $filename = rtrim($filename, '\\/'); $error = 0; /* Try to prevent timeouts */ if (time() - $start > 55) { if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } @set_time_limit(600); $start = time(); } /* * Have to chmod first before the is_dir check because is_dir does a stat on the * file / dir which fails if the permissions are too tight. * Chmod to filepermissions since the majority of the chmod() calls will be for * files anyway and then change the permissions for folders with a second call. */ if (!@chmod($filename, $filePermissions)) { error("[ERROR]", $filename); $error = 1; } if (is_dir($filename)) { /* For folders, we change the permissions to the right ones with a second chmod call. */ if (!$error) { if (!@chmod($filename, $folderPermissions)) { error("[ERROR]", $filename); $error = 1; } else { status("[OK]", $filename); } } /* * Recurse into subdirectories: Open all files / sub-dirs and change the * permissions recursively. */ if ($fd = opendir($filename)) { while (($child = readdir($fd)) !== false) { if ($child == '.' || $child == '..') { continue; } $fullpath = "{$filename}/{$child}"; $ret = chmodRecursively($fullpath, $folderPermissions, $filePermissions, $start); $error |= $ret; } closedir($fd); } else { error("Cannot open directory", $filename); return 1; } } if ($error) { return 1; } return null; }
function smtp_mail($mail_to_array, $subject, $message, $headers) { global $modSettings, $webmaster_email, $txt; $modSettings['smtp_host'] = trim($modSettings['smtp_host']); // Try POP3 before SMTP? // !!! There's no interface for this yet. if ($modSettings['mail_type'] == 2 && $modSettings['smtp_username'] != '' && $modSettings['smtp_password'] != '') { $socket = fsockopen($modSettings['smtp_host'], 110, $errno, $errstr, 2); if (!$socket && (substr($modSettings['smtp_host'], 0, 5) == 'smtp.' || substr($modSettings['smtp_host'], 0, 11) == 'ssl://smtp.')) { $socket = fsockopen(strtr($modSettings['smtp_host'], array('smtp.' => 'pop.')), 110, $errno, $errstr, 2); } if ($socket) { fgets($socket, 256); fputs($socket, 'USER ' . $modSettings['smtp_username'] . "\r\n"); fgets($socket, 256); fputs($socket, 'PASS ' . base64_decode($modSettings['smtp_password']) . "\r\n"); fgets($socket, 256); fputs($socket, 'QUIT' . "\r\n"); fclose($socket); } } // Try to connect to the SMTP server... if it doesn't exist, only wait three seconds. if (!($socket = fsockopen($modSettings['smtp_host'], empty($modSettings['smtp_port']) ? 25 : $modSettings['smtp_port'], $errno, $errstr, 3))) { // Maybe we can still save this? The port might be wrong. if (substr($modSettings['smtp_host'], 0, 4) == 'ssl:' && (empty($modSettings['smtp_port']) || $modSettings['smtp_port'] == 25)) { if ($socket = fsockopen($modSettings['smtp_host'], 465, $errno, $errstr, 3)) { log_error($txt['smtp_port_ssl']); } } // Unable to connect! Don't show any error message, but just log one and try to continue anyway. if (!$socket) { log_error($txt['smtp_no_connect'] . ': ' . $errno . ' : ' . $errstr); return false; } } // Wait for a response of 220, without "-" continuer. if (!server_parse(null, $socket, '220')) { return false; } if ($modSettings['mail_type'] == 1 && $modSettings['smtp_username'] != '' && $modSettings['smtp_password'] != '') { // !!! These should send the CURRENT server's name, not the mail server's! // EHLO could be understood to mean encrypted hello... if (server_parse('EHLO ' . $modSettings['smtp_host'], $socket, null) == '250') { if (!server_parse('AUTH LOGIN', $socket, '334')) { return false; } // Send the username and password, encoded. if (!server_parse(base64_encode($modSettings['smtp_username']), $socket, '334')) { return false; } // The password is already encoded ;) if (!server_parse($modSettings['smtp_password'], $socket, '235')) { return false; } } elseif (!server_parse('HELO ' . $modSettings['smtp_host'], $socket, '250')) { return false; } } else { // Just say "helo". if (!server_parse('HELO ' . $modSettings['smtp_host'], $socket, '250')) { return false; } } // Fix the message for any lines beginning with a period! (the first is ignored, you see.) $message = strtr($message, array("\r\n." => "\r\n..")); // !! Theoretically, we should be able to just loop the RCPT TO. $mail_to_array = array_values($mail_to_array); foreach ($mail_to_array as $i => $mail_to) { // Reset the connection to send another email. if ($i != 0) { if (!server_parse('RSET', $socket, '250')) { return false; } } // From, to, and then start the data... if (!server_parse('MAIL FROM: <' . (empty($modSettings['mail_from']) ? $webmaster_email : $modSettings['mail_from']) . '>', $socket, '250')) { return false; } if (!server_parse('RCPT TO: <' . $mail_to . '>', $socket, '250')) { return false; } if (!server_parse('DATA', $socket, '354')) { return false; } fputs($socket, 'Subject: ' . $subject . "\r\n"); if (strlen($mail_to) > 0) { fputs($socket, 'To: <' . $mail_to . ">\r\n"); } fputs($socket, $headers . "\r\n\r\n"); fputs($socket, $message . "\r\n"); // Send a ., or in other words "end of data". if (!server_parse('.', $socket, '250')) { return false; } // Almost done, almost done... don't stop me just yet! @set_time_limit(300); if (function_exists('apache_reset_timeout')) { apache_reset_timeout(); } } fputs($socket, "QUIT\r\n"); fclose($socket); return true; }
function html_to_bbc($text) { global $modSettings, $smcFunc, $sourcedir, $scripturl, $context; // Replace newlines with spaces, as that's how browsers usually interpret them. $text = preg_replace("~\\s*[\r\n]+\\s*~", ' ', $text); // Though some of us love paragraphs, the parser will do better with breaks. $text = preg_replace('~</p>\\s*?<p~i', '</p><br /><p', $text); $text = preg_replace('~</p>\\s*(?!<)~i', '</p><br />', $text); // Safari/webkit wraps lines in Wysiwyg in <div>'s. if ($context['browser']['is_webkit']) { $text = preg_replace(array('~<div(?:\\s(?:[^<>]*?))?' . '>~i', '</div>'), array('<br />', ''), $text); } // If there's a trailing break get rid of it - Firefox tends to add one. $text = preg_replace('~<br\\s?/?' . '>$~i', '', $text); // Remove any formatting within code tags. if (strpos($text, '[code') !== false) { $text = preg_replace('~<br\\s?/?' . '>~i', '#smf_br_spec_grudge_cool!#', $text); $parts = preg_split('~(\\[/code\\]|\\[code(?:=[^\\]]+)?\\])~i', $text, -1, PREG_SPLIT_DELIM_CAPTURE); // Only mess with stuff outside [code] tags. for ($i = 0, $n = count($parts); $i < $n; $i++) { // Value of 2 means we're inside the tag. if ($i % 4 == 2) { $parts[$i] = strip_tags($parts[$i]); } } $text = strtr(implode('', $parts), array('#smf_br_spec_grudge_cool!#' => '<br />')); } // Remove scripts, style and comment blocks. $text = preg_replace('~<script[^>]*[^/]?' . '>.*?</script>~i', '', $text); $text = preg_replace('~<style[^>]*[^/]?' . '>.*?</style>~i', '', $text); $text = preg_replace('~\\<\\!--.*?-->~i', '', $text); $text = preg_replace('~\\<\\!\\[CDATA\\[.*?\\]\\]\\>~i', '', $text); // Do the smileys ultra first! preg_match_all('~<img\\s+[^<>]*?id="*smiley_\\d+_([^<>]+?)[\\s"/>]\\s*[^<>]*?/*>(?:\\s)?~i', $text, $matches); if (!empty($matches[0])) { // Easy if it's not custom. if (empty($modSettings['smiley_enable'])) { $smileysfrom = array('>:D', ':D', '::)', '>:(', ':)', ';)', ';D', ':(', ':o', '8)', ':P', '???', ':-[', ':-X', ':-*', ':\'(', ':-\\', '^-^', 'O0', 'C:-)', '0:)'); $smileysto = array('evil.gif', 'cheesy.gif', 'rolleyes.gif', 'angry.gif', 'smiley.gif', 'wink.gif', 'grin.gif', 'sad.gif', 'shocked.gif', 'cool.gif', 'tongue.gif', 'huh.gif', 'embarrassed.gif', 'lipsrsealed.gif', 'kiss.gif', 'cry.gif', 'undecided.gif', 'azn.gif', 'afro.gif', 'police.gif', 'angel.gif'); foreach ($matches[1] as $k => $file) { $found = array_search($file, $smileysto); // Note the weirdness here is to stop double spaces between smileys. if ($found) { $matches[1][$k] = '-[]-smf_smily_start#|#' . htmlspecialchars($smileysfrom[$found]) . '-[]-smf_smily_end#|#'; } else { $matches[1][$k] = ''; } } } else { // Load all the smileys. $names = array(); foreach ($matches[1] as $file) { $names[] = $file; } $names = array_unique($names); if (!empty($names)) { $request = smf_db_query(' SELECT code, filename FROM {db_prefix}smileys WHERE filename IN ({array_string:smiley_filenames})', array('smiley_filenames' => $names)); $mappings = array(); while ($row = mysql_fetch_assoc($request)) { $mappings[$row['filename']] = htmlspecialchars($row['code']); } mysql_free_result($request); foreach ($matches[1] as $k => $file) { if (isset($mappings[$file])) { $matches[1][$k] = '-[]-smf_smily_start#|#' . $mappings[$file] . '-[]-smf_smily_end#|#'; } } } } // Replace the tags! $text = str_replace($matches[0], $matches[1], $text); // Now sort out spaces $text = str_replace(array('-[]-smf_smily_end#|#-[]-smf_smily_start#|#', '-[]-smf_smily_end#|#', '-[]-smf_smily_start#|#'), ' ', $text); } // Only try to buy more time if the client didn't quit. if (connection_aborted() && $context['server']['is_apache']) { @apache_reset_timeout(); } $parts = preg_split('~(<[A-Za-z]+\\s*[^<>]*?style="?[^<>"]+"?[^<>]*?(?:/?)>|</[A-Za-z]+>)~', $text, -1, PREG_SPLIT_DELIM_CAPTURE); $replacement = ''; $stack = array(); foreach ($parts as $part) { if (preg_match('~(<([A-Za-z]+)\\s*[^<>]*?)style="?([^<>"]+)"?([^<>]*?(/?)>)~', $part, $matches) === 1) { // If it's being closed instantly, we can't deal with it...yet. if ($matches[5] === '/') { continue; } else { // Get an array of styles that apply to this element. (The strtr is there to combat HTML generated by Word.) $styles = explode(';', strtr($matches[3], array('"' => ''))); $curElement = $matches[2]; $precedingStyle = $matches[1]; $afterStyle = $matches[4]; $curCloseTags = ''; $extra_attr = ''; foreach ($styles as $type_value_pair) { // Remove spaces and convert uppercase letters. $clean_type_value_pair = strtolower(strtr(trim($type_value_pair), '=', ':')); // Something like 'font-weight: bold' is expected here. if (strpos($clean_type_value_pair, ':') === false) { continue; } // Capture the elements of a single style item (e.g. 'font-weight' and 'bold'). list($style_type, $style_value) = explode(':', $type_value_pair); $style_value = trim($style_value); switch (trim($style_type)) { case 'font-weight': if ($style_value === 'bold') { $curCloseTags .= '[/b]'; $replacement .= '[b]'; } break; case 'text-decoration': if ($style_value == 'underline') { $curCloseTags .= '[/u]'; $replacement .= '[u]'; } elseif ($style_value == 'line-through') { $curCloseTags .= '[/s]'; $replacement .= '[s]'; } break; case 'text-align': if ($style_value == 'left') { $curCloseTags .= '[/align]'; $replacement .= '[align=left]'; } elseif ($style_value == 'center') { $curCloseTags .= '[/align]'; $replacement .= '[align=center]'; } elseif ($style_value == 'right') { $curCloseTags .= '[/align]'; $replacement .= '[align=right]'; } break; case 'font-style': if ($style_value == 'italic') { $curCloseTags .= '[/i]'; $replacement .= '[i]'; } break; case 'color': $curCloseTags .= '[/color]'; $replacement .= '[color=' . $style_value . ']'; break; case 'font-size': // Sometimes people put decimals where decimals should not be. if (preg_match('~(\\d)+\\.\\d+(p[xt])~i', $style_value, $dec_matches) === 1) { $style_value = $dec_matches[1] . $dec_matches[2]; } $curCloseTags .= '[/size]'; $replacement .= '[size=' . $style_value . ']'; break; case 'font-family': // Only get the first freaking font if there's a list! if (strpos($style_value, ',') !== false) { $style_value = substr($style_value, 0, strpos($style_value, ',')); } $curCloseTags .= '[/font]'; $replacement .= '[font=' . strtr($style_value, array("'" => '')) . ']'; break; // This is a hack for images with dimensions embedded. // This is a hack for images with dimensions embedded. case 'width': case 'height': if (preg_match('~[1-9]\\d*~i', $style_value, $dimension) === 1) { $extra_attr .= ' ' . $style_type . '="' . $dimension[0] . '"'; } break; case 'list-style-type': if (preg_match('~none|disc|circle|square|decimal|decimal-leading-zero|lower-roman|upper-roman|lower-alpha|upper-alpha|lower-greek|lower-latin|upper-latin|hebrew|armenian|georgian|cjk-ideographic|hiragana|katakana|hiragana-iroha|katakana-iroha~i', $style_value, $listType) === 1) { $extra_attr .= ' listtype="' . $listType[0] . '"'; } break; } } // Preserve some tags stripping the styling. if (in_array($matches[2], array('a', 'font'))) { $replacement .= $precedingStyle . $afterStyle; $curCloseTags = '</' . $matches[2] . '>' . $curCloseTags; } // If there's something that still needs closing, push it to the stack. if (!empty($curCloseTags)) { array_push($stack, array('element' => strtolower($curElement), 'closeTags' => $curCloseTags)); } elseif (!empty($extra_attr)) { $replacement .= $precedingStyle . $extra_attr . $afterStyle; } } } elseif (preg_match('~</([A-Za-z]+)>~', $part, $matches) === 1) { // Is this the element that we've been waiting for to be closed? if (!empty($stack) && strtolower($matches[1]) === $stack[count($stack) - 1]['element']) { $byebyeTag = array_pop($stack); $replacement .= $byebyeTag['closeTags']; } else { $replacement .= $part; } } else { $replacement .= $part; } } // Now put back the replacement in the text. $text = $replacement; // We are not finished yet, request more time. if (connection_aborted() && $context['server']['is_apache']) { @apache_reset_timeout(); } // Let's pull out any legacy alignments. while (preg_match('~<([A-Za-z]+)\\s+[^<>]*?(align="*(left|center|right)"*)[^<>]*?(/?)>~i', $text, $matches) === 1) { // Find the position in the text of this tag over again. $start_pos = strpos($text, $matches[0]); if ($start_pos === false) { break; } // End tag? if ($matches[4] != '/' && strpos($text, '</' . $matches[1] . '>', $start_pos) !== false) { $end_length = strlen('</' . $matches[1] . '>'); $end_pos = strpos($text, '</' . $matches[1] . '>', $start_pos); // Remove the align from that tag so it's never checked again. $tag = substr($text, $start_pos, strlen($matches[0])); $content = substr($text, $start_pos + strlen($matches[0]), $end_pos - $start_pos - strlen($matches[0])); $tag = str_replace($matches[2], '', $tag); // Put the tags back into the body. $text = substr($text, 0, $start_pos) . $tag . '[' . $matches[3] . ']' . $content . '[/' . $matches[3] . ']' . substr($text, $end_pos); } else { // Just get rid of this evil tag. $text = substr($text, 0, $start_pos) . substr($text, $start_pos + strlen($matches[0])); } } // Let's do some special stuff for fonts - cause we all love fonts. while (preg_match('~<font\\s+([^<>]*)>~i', $text, $matches) === 1) { // Find the position of this again. $start_pos = strpos($text, $matches[0]); $end_pos = false; if ($start_pos === false) { break; } // This must have an end tag - and we must find the right one. $lower_text = strtolower($text); $start_pos_test = $start_pos + 4; // How many starting tags must we find closing ones for first? $start_font_tag_stack = 0; while ($start_pos_test < strlen($text)) { // Where is the next starting font? $next_start_pos = strpos($lower_text, '<font', $start_pos_test); $next_end_pos = strpos($lower_text, '</font>', $start_pos_test); // Did we past another starting tag before an end one? if ($next_start_pos !== false && $next_start_pos < $next_end_pos) { $start_font_tag_stack++; $start_pos_test = $next_start_pos + 4; } elseif ($start_font_tag_stack) { $start_font_tag_stack--; $start_pos_test = $next_end_pos + 4; } else { $end_pos = $next_end_pos; break; } } if ($end_pos === false) { break; } // Now work out what the attributes are. $attribs = fetchTagAttributes($matches[1]); $tags = array(); foreach ($attribs as $s => $v) { if ($s == 'size') { $tags[] = array('[size=' . (int) trim($v) . ']', '[/size]'); } elseif ($s == 'face') { $tags[] = array('[font=' . trim(strtolower($v)) . ']', '[/font]'); } elseif ($s == 'color') { $tags[] = array('[color=' . trim(strtolower($v)) . ']', '[/color]'); } } // As before add in our tags. $before = $after = ''; foreach ($tags as $tag) { $before .= $tag[0]; if (isset($tag[1])) { $after = $tag[1] . $after; } } // Remove the tag so it's never checked again. $content = substr($text, $start_pos + strlen($matches[0]), $end_pos - $start_pos - strlen($matches[0])); // Put the tags back into the body. $text = substr($text, 0, $start_pos) . $before . $content . $after . substr($text, $end_pos + 7); } // Almost there, just a little more time. if (connection_aborted() && $context['server']['is_apache']) { @apache_reset_timeout(); } if (count($parts = preg_split('~<(/?)(li|ol|ul)([^>]*)>~i', $text, null, PREG_SPLIT_DELIM_CAPTURE)) > 1) { // A toggle that dermines whether we're directly under a <ol> or <ul>. $inList = false; // Keep track of the number of nested list levels. $listDepth = 0; // Map what we can expect from the HTML to what is supported by SMF. $listTypeMapping = array('1' => 'decimal', 'A' => 'upper-alpha', 'a' => 'lower-alpha', 'I' => 'upper-roman', 'i' => 'lower-roman', 'disc' => 'disc', 'square' => 'square', 'circle' => 'circle'); // $i: text, $i + 1: '/', $i + 2: tag, $i + 3: tail. for ($i = 0, $numParts = count($parts) - 1; $i < $numParts; $i += 4) { $tag = strtolower($parts[$i + 2]); $isOpeningTag = $parts[$i + 1] === ''; if ($isOpeningTag) { switch ($tag) { case 'ol': case 'ul': // We have a problem, we're already in a list. if ($inList) { // Inject a list opener, we'll deal with the ol/ul next loop. array_splice($parts, $i, 0, array('', '', str_repeat("\t", $listDepth) . '[li]', '')); $numParts = count($parts) - 1; // The inlist status changes a bit. $inList = false; } else { $inList = true; if ($tag === 'ol') { $listType = 'decimal'; } elseif (preg_match('~type="?(' . implode('|', array_keys($listTypeMapping)) . ')"?~', $parts[$i + 3], $match) === 1) { $listType = $listTypeMapping[$match[1]]; } else { $listType = null; } $listDepth++; $parts[$i + 2] = '[list' . ($listType === null ? '' : ' type=' . $listType) . ']' . "\n"; $parts[$i + 3] = ''; } break; case 'li': // This is how it should be: a list item inside the list. if ($inList) { $parts[$i + 2] = str_repeat("\t", $listDepth) . '[li]'; $parts[$i + 3] = ''; // Within a list item, it's almost as if you're outside. $inList = false; } else { // We are apparently in a list item. if ($listDepth > 0) { $parts[$i + 2] = '[/li]' . "\n" . str_repeat("\t", $listDepth) . '[li]'; $parts[$i + 3] = ''; } else { // Quickly create a list with an item. $listDepth++; $parts[$i + 2] = '[list]' . "\n\t" . '[li]'; $parts[$i + 3] = ''; } } break; } } else { switch ($tag) { case 'ol': case 'ul': // As we expected it, closing the list while we're in it. if ($inList) { $inList = false; $listDepth--; $parts[$i + 1] = ''; $parts[$i + 2] = str_repeat("\t", $listDepth) . '[/list]'; $parts[$i + 3] = ''; } else { // We're in a list item. if ($listDepth > 0) { // Inject closure for this list item first. // The content of $parts[$i] is left as is! array_splice($parts, $i + 1, 0, array('', '[/li]' . "\n", '', '')); $numParts = count($parts) - 1; // Now that we've closed the li, we're in list space. $inList = true; } else { $parts[$i + 1] = ''; $parts[$i + 2] = ''; $parts[$i + 3] = ''; } } break; case 'li': if ($inList) { // There's no use for a </li> after <ol> or <ul>, ignore. $parts[$i + 1] = ''; $parts[$i + 2] = ''; $parts[$i + 3] = ''; } else { // Remove the trailing breaks from the list item. $parts[$i] = preg_replace('~\\s*<br\\s*' . '/?' . '>\\s*$~', '', $parts[$i]); $parts[$i + 1] = ''; $parts[$i + 2] = '[/li]' . "\n"; $parts[$i + 3] = ''; // And we're back in the [list] space. $inList = true; } break; } } // If we're in the [list] space, no content is allowed. if ($inList && trim(preg_replace('~\\s*<br\\s*' . '/?' . '>\\s*~', '', $parts[$i + 4])) !== '') { // Fix it by injecting an extra list item. array_splice($parts, $i + 4, 0, array('', '', 'li', '')); $numParts = count($parts) - 1; } } $text = implode('', $parts); if ($inList) { $listDepth--; $text .= str_repeat("\t", $listDepth) . '[/list]'; } for ($i = $listDepth; $i > 0; $i--) { $text .= '[/li]' . "\n" . str_repeat("\t", $i - 1) . '[/list]'; } } // I love my own image... while (preg_match('~<img\\s+([^<>]*)/*>~i', $text, $matches) === 1) { // Find the position of the image. $start_pos = strpos($text, $matches[0]); if ($start_pos === false) { break; } $end_pos = $start_pos + strlen($matches[0]); $params = ''; $had_params = array(); $src = ''; $attrs = fetchTagAttributes($matches[1]); foreach ($attrs as $attrib => $value) { if (in_array($attrib, array('width', 'height'))) { $params .= ' ' . $attrib . '=' . (int) $value; } elseif ($attrib == 'alt' && trim($value) != '') { $params .= ' alt=' . trim($value); } elseif ($attrib == 'src') { $src = trim($value); } } $tag = ''; if (!empty($src)) { // Attempt to fix the path in case it's not present. if (preg_match('~^https?://~i', $src) === 0 && is_array($parsedURL = parse_url($scripturl)) && isset($parsedURL['host'])) { $baseURL = (isset($parsedURL['scheme']) ? $parsedURL['scheme'] : 'http') . '://' . $parsedURL['host'] . (empty($parsedURL['port']) ? '' : ':' . $parsedURL['port']); if (substr($src, 0, 1) === '/') { $src = $baseURL . $src; } else { $src = $baseURL . (empty($parsedURL['path']) ? '/' : preg_replace('~/(?:index\\.php)?$~', '', $parsedURL['path'])) . '/' . $src; } } $tag = '[img' . $params . ']' . $src . '[/img]'; } // Replace the tag $text = substr($text, 0, $start_pos) . $tag . substr($text, $end_pos); } // The final bits are the easy ones - tags which map to tags which map to tags - etc etc. $tags = array('~<b(\\s(.)*?)*?' . '>~i' => '[b]', '~</b>~i' => '[/b]', '~<i(\\s(.)*?)*?' . '>~i' => '[i]', '~</i>~i' => '[/i]', '~<u(\\s(.)*?)*?' . '>~i' => '[u]', '~</u>~i' => '[/u]', '~<strong(\\s(.)*?)*?' . '>~i' => '[b]', '~</strong>~i' => '[/b]', '~<em(\\s(.)*?)*?' . '>~i' => '[i]', '~</em>~i' => '[/i]', '~<s(\\s(.)*?)*?' . '>~i' => "[s]", '~</s>~i' => "[/s]", '~<strike(\\s(.)*?)*?' . '>~i' => '[s]', '~</strike>~i' => '[/s]', '~<del(\\s(.)*?)*?' . '>~i' => '[s]', '~</del>~i' => '[/s]', '~<center(\\s(.)*?)*?' . '>~i' => '[center]', '~</center>~i' => '[/center]', '~<pre(\\s(.)*?)*?' . '>~i' => '[pre]', '~</pre>~i' => '[/pre]', '~<sub(\\s(.)*?)*?' . '>~i' => '[sub]', '~</sub>~i' => '[/sub]', '~<sup(\\s(.)*?)*?' . '>~i' => '[sup]', '~</sup>~i' => '[/sup]', '~<tt(\\s(.)*?)*?' . '>~i' => '[tt]', '~</tt>~i' => '[/tt]', '~<table(\\s(.)*?)*?' . '>~i' => '[table]', '~</table>~i' => '[/table]', '~<tr(\\s(.)*?)*?' . '>~i' => '[tr]', '~</tr>~i' => '[/tr]', '~<(td|th)\\s[^<>]*?colspan="?(\\d{1,2})"?.*?' . '>~ie' => 'str_repeat(\'[td][/td]\', $2 - 1) . \'[td]\'', '~<(td|th)(\\s(.)*?)*?' . '>~i' => '[td]', '~</(td|th)>~i' => '[/td]', '~<br(?:\\s[^<>]*?)?' . '>~i' => "\n", '~<hr[^<>]*>(\\n)?~i' => "[hr]\n\$1", '~(\\n)?\\[hr\\]~i' => "\n[hr]", '~^\\n\\[hr\\]~i' => "[hr]", '~<blockquote(\\s(.)*?)*?' . '>~i' => "<blockquote>", '~</blockquote>~i' => "</blockquote>", '~<ins(\\s(.)*?)*?' . '>~i' => "<ins>", '~</ins>~i' => "</ins>"); $text = preg_replace(array_keys($tags), array_values($tags), $text); // Please give us just a little more time. if (connection_aborted() && $context['server']['is_apache']) { @apache_reset_timeout(); } // What about URL's - the pain in the ass of the tag world. while (preg_match('~<a\\s+([^<>]*)>([^<>]*)</a>~i', $text, $matches) === 1) { // Find the position of the URL. $start_pos = strpos($text, $matches[0]); if ($start_pos === false) { break; } $end_pos = $start_pos + strlen($matches[0]); $tag_type = 'url'; $href = ''; $attrs = fetchTagAttributes($matches[1]); foreach ($attrs as $attrib => $value) { if ($attrib == 'href') { $href = trim($value); // Are we dealing with an FTP link? if (preg_match('~^ftps?://~', $href) === 1) { $tag_type = 'ftp'; } elseif (substr($href, 0, 7) == 'mailto:') { $tag_type = 'email'; $href = substr($href, 7); } elseif (preg_match('~^https?://~i', $href) === 0 && is_array($parsedURL = parse_url($scripturl)) && isset($parsedURL['host'])) { $baseURL = (isset($parsedURL['scheme']) ? $parsedURL['scheme'] : 'http') . '://' . $parsedURL['host'] . (empty($parsedURL['port']) ? '' : ':' . $parsedURL['port']); if (substr($href, 0, 1) === '/') { $href = $baseURL . $href; } else { $href = $baseURL . (empty($parsedURL['path']) ? '/' : preg_replace('~/(?:index\\.php)?$~', '', $parsedURL['path'])) . '/' . $href; } } } // External URL? if ($attrib == 'target' && $tag_type == 'url') { if (trim($value) == '_blank') { $tag_type == 'iurl'; } } } $tag = ''; if ($href != '') { if ($matches[2] == $href) { $tag = '[' . $tag_type . ']' . $href . '[/' . $tag_type . ']'; } else { $tag = '[' . $tag_type . '=' . $href . ']' . $matches[2] . '[/' . $tag_type . ']'; } } // Replace the tag $text = substr($text, 0, $start_pos) . $tag . substr($text, $end_pos); } $text = strip_tags($text); // Some tags often end up as just dummy tags - remove those. $text = preg_replace('~\\[[bisu]\\]\\s*\\[/[bisu]\\]~', '', $text); // Fix up entities. $text = preg_replace('~&~i', '&#38;', $text); $text = legalise_bbc($text); return $text; }
/** * Just pause the signature applying thing. * * @todo Move to subs file * @todo Merge with other pause functions? * pausePermsSave(), pausAttachmentMaintenance(), pauseRepairProcess() * * @param int $applied_sigs */ function pauseSignatureApplySettings($applied_sigs) { global $context, $txt, $sig_start; // Try get more time... @set_time_limit(600); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Have we exhausted all the time we allowed? if (time() - array_sum(explode(' ', $sig_start)) < 3) { return; } $context['continue_get_data'] = '?action=admin;area=featuresettings;sa=sig;apply;step=' . $applied_sigs . ';' . $context['session_var'] . '=' . $context['session_id']; $context['page_title'] = $txt['not_done_title']; $context['continue_post_data'] = ''; $context['continue_countdown'] = '2'; $context['sub_template'] = 'not_done'; // Specific stuff to not break this template! $context[$context['admin_menu_name']]['current_subsection'] = 'sig'; // Get the right percent. $context['continue_percent'] = round($applied_sigs / $context['max_member'] * 100); // Never more than 100%! $context['continue_percent'] = min($context['continue_percent'], 100); obExit(); }
function package_create_backup($id = 'backup') { global $sourcedir, $boarddir, $smcFunc; $files = array(); $base_files = array('index.php', 'SSI.php', 'agreement.txt', 'ssi_examples.php', 'ssi_examples.shtml'); foreach ($base_files as $file) { if (file_exists($boarddir . '/' . $file)) { $files[realpath($boarddir . '/' . $file)] = array(empty($_REQUEST['use_full_paths']) ? $file : $boarddir . '/' . $file, stat($boarddir . '/' . $file)); } } $dirs = array($sourcedir => empty($_REQUEST['use_full_paths']) ? 'Sources/' : strtr($sourcedir . '/', '\\', '/')); $request = smf_db_query(' SELECT value FROM {db_prefix}themes WHERE id_member = {int:no_member} AND variable = {string:theme_dir}', array('no_member' => 0, 'theme_dir' => 'theme_dir')); while ($row = mysql_fetch_assoc($request)) { $dirs[$row['value']] = empty($_REQUEST['use_full_paths']) ? 'Themes/' . basename($row['value']) . '/' : strtr($row['value'] . '/', '\\', '/'); } mysql_free_result($request); while (!empty($dirs)) { list($dir, $dest) = each($dirs); unset($dirs[$dir]); $listing = @dir($dir); if (!$listing) { continue; } while ($entry = $listing->read()) { if (preg_match('~^(\\.{1,2}|CVS|backup.*|help|images|.*\\~)$~', $entry) != 0) { continue; } $filepath = realpath($dir . '/' . $entry); if (isset($files[$filepath])) { continue; } $stat = stat($dir . '/' . $entry); if ($stat['mode'] & 040000) { $files[$filepath] = array($dest . $entry . '/', $stat); $dirs[$dir . '/' . $entry] = $dest . $entry . '/'; } else { $files[$filepath] = array($dest . $entry, $stat); } } $listing->close(); } if (!file_exists($boarddir . '/Packages/backups')) { mktree($boarddir . '/Packages/backups', 0777); } if (!is_writable($boarddir . '/Packages/backups')) { package_chmod($boarddir . '/Packages/backups'); } $output_file = $boarddir . '/Packages/backups/' . strftime('%Y-%m-%d_') . preg_replace('~[$\\\\/:<>|?*"\']~', '', $id); $output_ext = '.tar' . (function_exists('gzopen') ? '.gz' : ''); if (file_exists($output_file . $output_ext)) { $i = 2; while (file_exists($output_file . '_' . $i . $output_ext)) { $i++; } $output_file = $output_file . '_' . $i . $output_ext; } else { $output_file .= $output_ext; } @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } if (function_exists('gzopen')) { $fwrite = 'gzwrite'; $fclose = 'gzclose'; $output = gzopen($output_file, 'wb'); } else { $fwrite = 'fwrite'; $fclose = 'fclose'; $output = fopen($output_file, 'wb'); } foreach ($files as $real_file => $file) { if (!file_exists($real_file)) { continue; } $stat = $file[1]; if (substr($file[0], -1) == '/') { $stat['size'] = 0; } $current = pack('a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12', $file[0], decoct($stat['mode']), sprintf('%06d', decoct($stat['uid'])), sprintf('%06d', decoct($stat['gid'])), decoct($stat['size']), decoct($stat['mtime']), '', 0, '', '', '', '', '', '', '', '', ''); $checksum = 256; for ($i = 0; $i < 512; $i++) { $checksum += ord($current[$i]); } $fwrite($output, substr($current, 0, 148) . pack('a8', decoct($checksum)) . substr($current, 156, 511)); if ($stat['size'] == 0) { continue; } $fp = fopen($real_file, 'rb'); while (!feof($fp)) { $fwrite($output, fread($fp, 16384)); } fclose($fp); $fwrite($output, pack('a' . (512 - $stat['size'] % 512), '')); } $fwrite($output, pack('a1024', '')); $fclose($output); }
function ThemeInstall() { global $sourcedir, $boarddir, $boardurl, $txt, $context, $settings, $modSettings, $smcFunc; checkSession('request'); isAllowedTo('admin_forum'); checkSession('request'); require_once $sourcedir . '/Subs-Package.php'; loadTemplate('Themes'); if (isset($_GET['theme_id'])) { $result = $smcFunc['db_query']('', ' SELECT value FROM {db_prefix}themes WHERE id_theme = {int:current_theme} AND id_member = {int:no_member} AND variable = {string:name} LIMIT 1', array('current_theme' => (int) $_GET['theme_id'], 'no_member' => 0, 'name' => 'name')); list($theme_name) = $smcFunc['db_fetch_row']($result); $smcFunc['db_free_result']($result); $context['sub_template'] = 'installed'; $context['page_title'] = $txt['theme_installed']; $context['installed_theme'] = array('id' => (int) $_GET['theme_id'], 'name' => $theme_name); return; } if (!empty($_FILES['theme_gz']) && (!isset($_FILES['theme_gz']['error']) || $_FILES['theme_gz']['error'] != 4) || !empty($_REQUEST['theme_gz'])) { $method = 'upload'; } elseif (isset($_REQUEST['theme_dir']) && rtrim(realpath($_REQUEST['theme_dir']), '/\\') != realpath($boarddir . '/Themes') && file_exists($_REQUEST['theme_dir'])) { $method = 'path'; } else { $method = 'copy'; } if (!empty($_REQUEST['copy']) && $method == 'copy') { // Hopefully the themes directory is writable, or we might have a problem. if (!is_writable($boarddir . '/Themes')) { fatal_lang_error('theme_install_write_error', 'critical'); } $theme_dir = $boarddir . '/Themes/' . preg_replace('~[^A-Za-z0-9_\\- ]~', '', $_REQUEST['copy']); umask(0); mkdir($theme_dir, 0777); @set_time_limit(600); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Create subdirectories for css and javascript files. mkdir($theme_dir . '/css', 0777); mkdir($theme_dir . '/scripts', 0777); // Copy over the default non-theme files. $to_copy = array('/index.php', '/index.template.php', '/css/index.css', '/css/rtl.css', '/scripts/theme.js'); foreach ($to_copy as $file) { copy($settings['default_theme_dir'] . $file, $theme_dir . $file); @chmod($theme_dir . $file, 0777); } // And now the entire images directory! copytree($settings['default_theme_dir'] . '/images', $theme_dir . '/images'); package_flush_cache(); $theme_name = $_REQUEST['copy']; $images_url = $boardurl . '/Themes/' . basename($theme_dir) . '/images'; $theme_dir = realpath($theme_dir); // Lets get some data for the new theme. $request = $smcFunc['db_query']('', ' SELECT variable, value FROM {db_prefix}themes WHERE variable IN ({string:theme_templates}, {string:theme_layers}) AND id_member = {int:no_member} AND id_theme = {int:default_theme}', array('no_member' => 0, 'default_theme' => 1, 'theme_templates' => 'theme_templates', 'theme_layers' => 'theme_layers')); while ($row = $smcFunc['db_fetch_assoc']($request)) { if ($row['variable'] == 'theme_templates') { $theme_templates = $row['value']; } elseif ($row['variable'] == 'theme_layers') { $theme_layers = $row['value']; } else { continue; } } $smcFunc['db_free_result']($request); // Lets add a theme_info.xml to this theme. $xml_info = '<' . '?xml version="1.0"?' . '> <theme-info xmlns="http://www.simplemachines.org/xml/theme-info" xmlns:smf="http://www.simplemachines.org/"> <!-- For the id, always use something unique - put your name, a colon, and then the package name. --> <id>smf:' . $smcFunc['strtolower'](str_replace(array(' '), '_', $_REQUEST['copy'])) . '</id> <version>' . $modSettings['smfVersion'] . '</version> <!-- Theme name, used purely for aesthetics. --> <name>' . $_REQUEST['copy'] . '</name> <!-- Author: your email address or contact information. The name attribute is optional. --> <author name="Simple Machines">info@simplemachines.org</author> <!-- Website... where to get updates and more information. --> <website>http://www.simplemachines.org/</website> <!-- Template layers to use, defaults to "html,body". --> <layers>' . (empty($theme_layers) ? 'html,body' : $theme_layers) . '</layers> <!-- Templates to load on startup. Default is "index". --> <templates>' . (empty($theme_templates) ? 'index' : $theme_templates) . '</templates> <!-- Base this theme off another? Default is blank, or no. It could be "default". --> <based-on></based-on> </theme-info>'; // Now write it. $fp = @fopen($theme_dir . '/theme_info.xml', 'w+'); if ($fp) { fwrite($fp, $xml_info); fclose($fp); } } elseif (isset($_REQUEST['theme_dir']) && $method == 'path') { if (!is_dir($_REQUEST['theme_dir']) || !file_exists($_REQUEST['theme_dir'] . '/theme_info.xml')) { fatal_lang_error('theme_install_error', false); } $theme_name = basename($_REQUEST['theme_dir']); $theme_dir = $_REQUEST['theme_dir']; } elseif ($method = 'upload') { // Hopefully the themes directory is writable, or we might have a problem. if (!is_writable($boarddir . '/Themes')) { fatal_lang_error('theme_install_write_error', 'critical'); } require_once $sourcedir . '/Subs-Package.php'; // Set the default settings... $theme_name = strtok(basename(isset($_FILES['theme_gz']) ? $_FILES['theme_gz']['name'] : $_REQUEST['theme_gz']), '.'); $theme_name = preg_replace(array('/\\s/', '/\\.[\\.]+/', '/[^\\w_\\.\\-]/'), array('_', '.', ''), $theme_name); $theme_dir = $boarddir . '/Themes/' . $theme_name; if (isset($_FILES['theme_gz']) && is_uploaded_file($_FILES['theme_gz']['tmp_name']) && (@ini_get('open_basedir') != '' || file_exists($_FILES['theme_gz']['tmp_name']))) { $extracted = read_tgz_file($_FILES['theme_gz']['tmp_name'], $boarddir . '/Themes/' . $theme_name, false, true); } elseif (isset($_REQUEST['theme_gz'])) { // Check that the theme is from simplemachines.org, for now... maybe add mirroring later. if (preg_match('~^http://[\\w_\\-]+\\.simplemachines\\.org/~', $_REQUEST['theme_gz']) == 0 || strpos($_REQUEST['theme_gz'], 'dlattach') !== false) { fatal_lang_error('not_on_simplemachines'); } $extracted = read_tgz_file($_REQUEST['theme_gz'], $boarddir . '/Themes/' . $theme_name, false, true); } else { redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']); } } // Something go wrong? if ($theme_dir != '' && basename($theme_dir) != 'Themes') { // Defaults. $install_info = array('theme_url' => $boardurl . '/Themes/' . basename($theme_dir), 'images_url' => isset($images_url) ? $images_url : $boardurl . '/Themes/' . basename($theme_dir) . '/images', 'theme_dir' => $theme_dir, 'name' => $theme_name); if (file_exists($theme_dir . '/theme_info.xml')) { $theme_info = file_get_contents($theme_dir . '/theme_info.xml'); $xml_elements = array('name' => 'name', 'theme_layers' => 'layers', 'theme_templates' => 'templates', 'based_on' => 'based-on'); foreach ($xml_elements as $var => $name) { if (preg_match('~<' . $name . '>(?:<!\\[CDATA\\[)?(.+?)(?:\\]\\]>)?</' . $name . '>~', $theme_info, $match) == 1) { $install_info[$var] = $match[1]; } } if (preg_match('~<images>(?:<!\\[CDATA\\[)?(.+?)(?:\\]\\]>)?</images>~', $theme_info, $match) == 1) { $install_info['images_url'] = $install_info['theme_url'] . '/' . $match[1]; $explicit_images = true; } if (preg_match('~<extra>(?:<!\\[CDATA\\[)?(.+?)(?:\\]\\]>)?</extra>~', $theme_info, $match) == 1) { $install_info += unserialize($match[1]); } } if (isset($install_info['based_on'])) { if ($install_info['based_on'] == 'default') { $install_info['theme_url'] = $settings['default_theme_url']; $install_info['images_url'] = $settings['default_images_url']; } elseif ($install_info['based_on'] != '') { $install_info['based_on'] = preg_replace('~[^A-Za-z0-9\\-_ ]~', '', $install_info['based_on']); $request = $smcFunc['db_query']('', ' SELECT th.value AS base_theme_dir, th2.value AS base_theme_url' . (!empty($explicit_images) ? '' : ', th3.value AS images_url') . ' FROM {db_prefix}themes AS th INNER JOIN {db_prefix}themes AS th2 ON (th2.id_theme = th.id_theme AND th2.id_member = {int:no_member} AND th2.variable = {string:theme_url})' . (!empty($explicit_images) ? '' : ' INNER JOIN {db_prefix}themes AS th3 ON (th3.id_theme = th.id_theme AND th3.id_member = {int:no_member} AND th3.variable = {string:images_url})') . ' WHERE th.id_member = {int:no_member} AND (th.value LIKE {string:based_on} OR th.value LIKE {string:based_on_path}) AND th.variable = {string:theme_dir} LIMIT 1', array('no_member' => 0, 'theme_url' => 'theme_url', 'images_url' => 'images_url', 'theme_dir' => 'theme_dir', 'based_on' => '%/' . $install_info['based_on'], 'based_on_path' => '%' . "\\" . $install_info['based_on'])); $temp = $smcFunc['db_fetch_assoc']($request); $smcFunc['db_free_result']($request); // !!! An error otherwise? if (is_array($temp)) { $install_info = $temp + $install_info; if (empty($explicit_images) && !empty($install_info['base_theme_url'])) { $install_info['theme_url'] = $install_info['base_theme_url']; } } } unset($install_info['based_on']); } // Find the newest id_theme. $result = $smcFunc['db_query']('', ' SELECT MAX(id_theme) FROM {db_prefix}themes', array()); list($id_theme) = $smcFunc['db_fetch_row']($result); $smcFunc['db_free_result']($result); // This will be theme number... $id_theme++; $inserts = array(); foreach ($install_info as $var => $val) { $inserts[] = array($id_theme, $var, $val); } if (!empty($inserts)) { $smcFunc['db_insert']('insert', '{db_prefix}themes', array('id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), $inserts, array('id_theme', 'variable')); } updateSettings(array('knownThemes' => strtr($modSettings['knownThemes'] . ',' . $id_theme, array(',,' => ',')))); } redirectexit('action=admin;area=theme;sa=install;theme_id=' . $id_theme . ';' . $context['session_var'] . '=' . $context['session_id']); }
/** * Function called to briefly pause execution of directory/file chmod actions * * - Called by action_perms_save(). * * @package Packages */ function pausePermsSave() { global $context, $txt; // Try get more time... @set_time_limit(600); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Set up the items for the pause form $context['sub_template'] = 'pause_action_permissions'; $context['page_title'] = $txt['package_file_perms_applying']; // And how are we progressing with our directories $context['remaining_items'] = count($context['method'] == 'individual' ? $context['to_process'] : $context['directory_list']); $context['progress_message'] = sprintf($context['method'] == 'individual' ? $txt['package_file_perms_items_done'] : $txt['package_file_perms_dirs_done'], $context['total_items'] - $context['remaining_items'], $context['total_items']); $context['progress_percent'] = round(($context['total_items'] - $context['remaining_items']) / $context['total_items'] * 100, 1); // Never more than 100%! $context['progress_percent'] = min($context['progress_percent'], 100); // And how are we progressing with files within a directory if ($context['method'] != 'individual' && !empty($context['total_files'])) { $context['file_progress_message'] = sprintf($txt['package_file_perms_files_done'], $context['file_offset'], $context['total_files']); $context['file_progress_percent'] = round($context['file_offset'] / $context['total_files'] * 100, 1); // Never more than 100%! $context['file_progress_percent'] = min($context['file_progress_percent'], 100); } obExit(); }
/** * Creates a site backup before installing a package just in case things don't go * as planned. * * @package Packages * @param string $id */ function package_create_backup($id = 'backup') { $db = database(); $files = array(); // The files that reside outside of sources, in the base, we add manually $base_files = array('index.php', 'SSI.php', 'agreement.txt', 'ssi_examples.php', 'ssi_examples.shtml', 'subscriptions.php', 'email_imap_cron.php', 'emailpost.php', 'emailtopic.php'); foreach ($base_files as $file) { if (file_exists(BOARDDIR . '/' . $file)) { $files[realpath(BOARDDIR . '/' . $file)] = array(empty($_REQUEST['use_full_paths']) ? $file : BOARDDIR . '/' . $file, stat(BOARDDIR . '/' . $file)); } } // Root directory where most of our files reside $dirs = array(SOURCEDIR => empty($_REQUEST['use_full_paths']) ? 'sources/' : strtr(SOURCEDIR . '/', '\\', '/')); // Find all installed theme directories $request = $db->query('', ' SELECT value FROM {db_prefix}themes WHERE id_member = {int:no_member} AND variable = {string:theme_dir}', array('no_member' => 0, 'theme_dir' => 'theme_dir')); while ($row = $db->fetch_assoc($request)) { $dirs[$row['value']] = empty($_REQUEST['use_full_paths']) ? 'themes/' . basename($row['value']) . '/' : strtr($row['value'] . '/', '\\', '/'); } $db->free_result($request); // While we have directorys to check while (!empty($dirs)) { list($dir, $dest) = each($dirs); unset($dirs[$dir]); // Get the file listing for this directory $listing = @dir($dir); if (!$listing) { continue; } while ($entry = $listing->read()) { if (preg_match('~^(\\.{1,2}|CVS|backup.*|help|images|.*\\~)$~', $entry) != 0) { continue; } $filepath = realpath($dir . '/' . $entry); if (isset($files[$filepath])) { continue; } $stat = stat($dir . '/' . $entry); // If this is a directory, add it to the dir stack for processing if ($stat['mode'] & 040000) { $files[$filepath] = array($dest . $entry . '/', $stat); $dirs[$dir . '/' . $entry] = $dest . $entry . '/'; } else { $files[$filepath] = array($dest . $entry, $stat); } } $listing->close(); } // Make sure we have a backup directory and its writable if (!file_exists(BOARDDIR . '/packages/backups')) { mktree(BOARDDIR . '/packages/backups', 0777); } if (!is_writable(BOARDDIR . '/packages/backups')) { package_chmod(BOARDDIR . '/packages/backups'); } // Name the output file, yyyy-mm-dd_before_package_name.tar.gz $output_file = BOARDDIR . '/packages/backups/' . strftime('%Y-%m-%d_') . preg_replace('~[$\\\\/:<>|?*"\']~', '', $id); $output_ext = '.tar' . (function_exists('gzopen') ? '.gz' : ''); if (file_exists($output_file . $output_ext)) { $i = 2; while (file_exists($output_file . '_' . $i . $output_ext)) { $i++; } $output_file = $output_file . '_' . $i . $output_ext; } else { $output_file .= $output_ext; } // Buy some more time so we have enough to create this archive @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Set up the file output handle, try gzip first to save space if (function_exists('gzopen')) { $fwrite = 'gzwrite'; $fclose = 'gzclose'; $output = gzopen($output_file, 'wb'); } else { $fwrite = 'fwrite'; $fclose = 'fclose'; $output = fopen($output_file, 'wb'); } // For each file we found in the directory, we add them to a TAR archive foreach ($files as $real_file => $file) { if (!file_exists($real_file)) { continue; } // Check if its a directory $stat = $file[1]; if (substr($file[0], -1) == '/') { $stat['size'] = 0; } // Create a tar file header, pack the details in to the fields $current = pack('a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12', $file[0], decoct($stat['mode']), sprintf('%06d', decoct($stat['uid'])), sprintf('%06d', decoct($stat['gid'])), decoct($stat['size']), decoct($stat['mtime']), '', 0, '', '', '', '', '', '', '', '', ''); // Create the header checksum $checksum = 256; for ($i = 0; $i < 512; $i++) { $checksum += ord($current[$i]); } // Write out the file header (insert the checksum we just computed) $fwrite($output, substr($current, 0, 148) . pack('a8', decoct($checksum)) . substr($current, 156, 511)); // If this is a directory entry all thats needed is the header if ($stat['size'] == 0) { continue; } // Write the actual file contents to the backup file $fp = fopen($real_file, 'rb'); while (!feof($fp)) { $fwrite($output, fread($fp, 16384)); } fclose($fp); // Pad the output so its on 512 boundarys $fwrite($output, pack('a' . (512 - $stat['size'] % 512), '')); } $fwrite($output, pack('a1024', '')); $fclose($output); }
function DumpDatabase2() { global $db_name, $scripturl, $context, $modSettings, $crlf, $smcFunc, $db_prefix; // Administrators only! if (!allowedTo('admin_forum')) { fatal_lang_error('no_dump_database', 'critical'); } // You can't dump nothing! if (!isset($_REQUEST['struct']) && !isset($_REQUEST['data'])) { $_REQUEST['data'] = true; } checkSession('post'); // We will need this, badly! db_extend(); // Attempt to stop from dying... @set_time_limit(600); if (@ini_get('memory_limit') < 256) { @ini_set('memory_limit', '256M'); } // Start saving the output... (don't do it otherwise for memory reasons.) if (isset($_REQUEST['compress']) && function_exists('gzencode')) { // Make sure we're gzipping output, but then say we're not in the header ^_^. if (empty($modSettings['enableCompressedOutput'])) { @ob_start('ob_gzhandler'); } elseif (ob_get_length() != 0) { ob_end_clean(); @ob_start('ob_gzhandler'); } // Send faked headers so it will just save the compressed output as a gzip. header('Content-Type: application/x-gzip'); header('Accept-Ranges: bytes'); header('Content-Encoding: none'); // Gecko browsers... don't like this. (Mozilla, Firefox, etc.) if (!$context['browser']['is_gecko']) { header('Content-Transfer-Encoding: binary'); } // The file extension will include .gz... $extension = '.sql.gz'; } else { // Get rid of the gzipping alreading being done. if (!empty($modSettings['enableCompressedOutput'])) { @ob_end_clean(); } elseif (function_exists('ob_clean') && ob_get_length() != 0) { ob_clean(); } // Tell the client to save this file, even though it's text. header('Content-Type: ' . ($context['browser']['is_ie'] || $context['browser']['is_opera'] ? 'application/octetstream' : 'application/octet-stream')); header('Content-Encoding: none'); // This time the extension should just be .sql. $extension = '.sql'; } // This should turn off the session URL parser. $scripturl = ''; // If this database is flat file and has a handler function pass it to that. if (!empty($smcFunc['db_get_backup'])) { $smcFunc['db_get_backup'](); exit; } // Send the proper headers to let them download this file. header('Content-Disposition: filename="' . $db_name . '-' . (empty($_REQUEST['struct']) ? 'data' : (empty($_REQUEST['data']) ? 'structure' : 'complete')) . '_' . strftime('%Y-%m-%d') . $extension . '"'); header('Cache-Control: private'); header('Connection: close'); // This makes things simpler when using it so very very often. $crlf = "\r\n"; // SQL Dump Header. echo '-- ==========================================================', $crlf, '--', $crlf, '-- Database dump of tables in `', $db_name, '`', $crlf, '-- ', timeformat(time(), false), $crlf, '--', $crlf, '-- ==========================================================', $crlf, $crlf; // Get all tables in the database.... if (preg_match('~^`(.+?)`\\.(.+?)$~', $db_prefix, $match) != 0) { $db = strtr($match[1], array('`' => '')); $dbp = str_replace('_', '\\_', $match[2]); } else { $db = false; $dbp = $db_prefix; } // Dump each table. $tables = $smcFunc['db_list_tables'](false, $db_prefix . '%'); foreach ($tables as $tableName) { if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Are we dumping the structures? if (isset($_REQUEST['struct'])) { echo $crlf, '--', $crlf, '-- Table structure for table `', $tableName, '`', $crlf, '--', $crlf, $crlf, $smcFunc['db_table_sql']($tableName), ';', $crlf; } // How about the data? if (!isset($_REQUEST['data']) || substr($tableName, -10) == 'log_errors') { continue; } // Are there any rows in this table? $get_rows = $smcFunc['db_insert_sql']($tableName); // No rows to get - skip it. if (empty($get_rows)) { continue; } echo $crlf, '--', $crlf, '-- Dumping data in `', $tableName, '`', $crlf, '--', $crlf, $crlf, $get_rows, '-- --------------------------------------------------------', $crlf; } echo $crlf, '-- Done', $crlf; exit; }
/** * This function removes all the messages of a certain user that are *not* * first messages of a topic * * @param int $memID The member id */ function removeNonTopicMessages($memID) { $db = database(); $request = $db->query('', ' SELECT m.id_msg FROM {db_prefix}messages AS m INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic AND t.id_first_msg != m.id_msg) WHERE m.id_member = {int:selected_member}', array('selected_member' => $memID)); // This could take a while... but ya know it's gonna be worth it in the end. while ($row = $db->fetch_assoc($request)) { if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } removeMessage($row['id_msg']); } $db->free_result($request); }
/** * Removes the passed id_topic's. * Permissions are NOT checked here because the function is used in a scheduled task * * @param int[]|int $topics The topics to remove (can be an id or an array of ids). * @param bool $decreasePostCount if true users' post count will be reduced * @param bool $ignoreRecycling if true topics are not moved to the recycle board (if it exists). */ function removeTopics($topics, $decreasePostCount = true, $ignoreRecycling = false) { global $modSettings; $db = database(); // Nothing to do? if (empty($topics)) { return; } // Only a single topic. if (is_numeric($topics)) { $topics = array($topics); } // Decrease the post counts for members. if ($decreasePostCount) { $requestMembers = $db->query('', ' SELECT m.id_member, COUNT(*) AS posts FROM {db_prefix}messages AS m INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) WHERE m.id_topic IN ({array_int:topics}) AND m.icon != {string:recycled} AND b.count_posts = {int:do_count_posts} AND m.approved = {int:is_approved} GROUP BY m.id_member', array('do_count_posts' => 0, 'recycled' => 'recycled', 'topics' => $topics, 'is_approved' => 1)); if ($db->num_rows($requestMembers) > 0) { while ($rowMembers = $db->fetch_assoc($requestMembers)) { updateMemberData($rowMembers['id_member'], array('posts' => 'posts - ' . $rowMembers['posts'])); } } $db->free_result($requestMembers); } // Recycle topics that aren't in the recycle board... if (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 && !$ignoreRecycling) { $request = $db->query('', ' SELECT id_topic, id_board, unapproved_posts, approved FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics}) AND id_board != {int:recycle_board} LIMIT ' . count($topics), array('recycle_board' => $modSettings['recycle_board'], 'topics' => $topics)); if ($db->num_rows($request) > 0) { // Get topics that will be recycled. $recycleTopics = array(); while ($row = $db->fetch_assoc($request)) { if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } $recycleTopics[] = $row['id_topic']; // Set the id_previous_board for this topic - and make it not sticky. $db->query('', ' UPDATE {db_prefix}topics SET id_previous_board = {int:id_previous_board}, is_sticky = {int:not_sticky} WHERE id_topic = {int:id_topic}', array('id_previous_board' => $row['id_board'], 'id_topic' => $row['id_topic'], 'not_sticky' => 0)); } $db->free_result($request); // Mark recycled topics as recycled. $db->query('', ' UPDATE {db_prefix}messages SET icon = {string:recycled} WHERE id_topic IN ({array_int:recycle_topics})', array('recycle_topics' => $recycleTopics, 'recycled' => 'recycled')); // Move the topics to the recycle board. require_once SUBSDIR . '/Topic.subs.php'; moveTopics($recycleTopics, $modSettings['recycle_board']); // Close reports that are being recycled. require_once SUBSDIR . '/Moderation.subs.php'; $db->query('', ' UPDATE {db_prefix}log_reported SET closed = {int:is_closed} WHERE id_topic IN ({array_int:recycle_topics})', array('recycle_topics' => $recycleTopics, 'is_closed' => 1)); updateSettings(array('last_mod_report_action' => time())); recountOpenReports(); // Topics that were recycled don't need to be deleted, so subtract them. $topics = array_diff($topics, $recycleTopics); } else { $db->free_result($request); } } // Still topics left to delete? if (empty($topics)) { return; } $adjustBoards = array(); // Find out how many posts we are deleting. $request = $db->query('', ' SELECT id_board, approved, COUNT(*) AS num_topics, SUM(unapproved_posts) AS unapproved_posts, SUM(num_replies) AS num_replies FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics}) GROUP BY id_board, approved', array('topics' => $topics)); while ($row = $db->fetch_assoc($request)) { if (!isset($adjustBoards[$row['id_board']]['num_posts'])) { cache_put_data('board-' . $row['id_board'], null, 120); $adjustBoards[$row['id_board']] = array('num_posts' => 0, 'num_topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0, 'id_board' => $row['id_board']); } // Posts = (num_replies + 1) for each approved topic. $adjustBoards[$row['id_board']]['num_posts'] += $row['num_replies'] + ($row['approved'] ? $row['num_topics'] : 0); $adjustBoards[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts']; // Add the topics to the right type. if ($row['approved']) { $adjustBoards[$row['id_board']]['num_topics'] += $row['num_topics']; } else { $adjustBoards[$row['id_board']]['unapproved_topics'] += $row['num_topics']; } } $db->free_result($request); // Decrease number of posts and topics for each board. foreach ($adjustBoards as $stats) { if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } $db->query('', ' UPDATE {db_prefix}boards SET num_posts = CASE WHEN {int:num_posts} > num_posts THEN 0 ELSE num_posts - {int:num_posts} END, num_topics = CASE WHEN {int:num_topics} > num_topics THEN 0 ELSE num_topics - {int:num_topics} END, unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END, unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END WHERE id_board = {int:id_board}', array('id_board' => $stats['id_board'], 'num_posts' => $stats['num_posts'], 'num_topics' => $stats['num_topics'], 'unapproved_posts' => $stats['unapproved_posts'], 'unapproved_topics' => $stats['unapproved_topics'])); } // Remove polls for these topics. $request = $db->query('', ' SELECT id_poll FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics}) AND id_poll > {int:no_poll} LIMIT ' . count($topics), array('no_poll' => 0, 'topics' => $topics)); $polls = array(); while ($row = $db->fetch_assoc($request)) { $polls[] = $row['id_poll']; } $db->free_result($request); if (!empty($polls)) { $db->query('', ' DELETE FROM {db_prefix}polls WHERE id_poll IN ({array_int:polls})', array('polls' => $polls)); $db->query('', ' DELETE FROM {db_prefix}poll_choices WHERE id_poll IN ({array_int:polls})', array('polls' => $polls)); $db->query('', ' DELETE FROM {db_prefix}log_polls WHERE id_poll IN ({array_int:polls})', array('polls' => $polls)); } // Get rid of the attachment(s). require_once SUBSDIR . '/ManageAttachments.subs.php'; $attachmentQuery = array('attachment_type' => 0, 'id_topic' => $topics); removeAttachments($attachmentQuery, 'messages'); // Delete search index entries. if (!empty($modSettings['search_custom_index_config'])) { $customIndexSettings = unserialize($modSettings['search_custom_index_config']); $request = $db->query('', ' SELECT id_msg, body FROM {db_prefix}messages WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); $words = array(); $messages = array(); while ($row = $db->fetch_assoc($request)) { if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } $words = array_merge($words, text2words($row['body'], $customIndexSettings['bytes_per_word'], true)); $messages[] = $row['id_msg']; } $db->free_result($request); $words = array_unique($words); if (!empty($words) && !empty($messages)) { $db->query('', ' DELETE FROM {db_prefix}log_search_words WHERE id_word IN ({array_int:word_list}) AND id_msg IN ({array_int:message_list})', array('word_list' => $words, 'message_list' => $messages)); } } // Reuse the message array if available if (empty($messages)) { $messages = messagesInTopics($topics); } // If there are messages left in this topic if (!empty($messages)) { // Decrease / Update the member like counts require_once SUBSDIR . '/Likes.subs.php'; decreaseLikeCounts($messages); // Remove all likes now that the topic is gone $db->query('', ' DELETE FROM {db_prefix}message_likes WHERE id_msg IN ({array_int:messages})', array('messages' => $messages)); // Remove all mentions now that the topic is gone $db->query('', ' DELETE FROM {db_prefix}log_mentions WHERE id_msg IN ({array_int:messages})', array('messages' => $messages)); } // Delete messages in each topic. $db->query('', ' DELETE FROM {db_prefix}messages WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); // Remove linked calendar events. // @todo if unlinked events are enabled, wouldn't this be expected to keep them? $db->query('', ' DELETE FROM {db_prefix}calendar WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); // Delete log_topics data $db->query('', ' DELETE FROM {db_prefix}log_topics WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); // Delete notifications $db->query('', ' DELETE FROM {db_prefix}log_notify WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); // Delete the topics themselves $db->query('', ' DELETE FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); // Remove data from the subjects for search cache $db->query('', ' DELETE FROM {db_prefix}log_search_subjects WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); require_once SUBSDIR . '/FollowUps.subs.php'; removeFollowUpsByTopic($topics); foreach ($topics as $topic_id) { cache_put_data('topic_board-' . $topic_id, null, 120); } // Maybe there's an addon that wants to delete topic related data of its own call_integration_hook('integrate_remove_topics', array($topics)); // Update the totals... updateStats('message'); updateTopicStats(); updateSettings(array('calendar_updated' => time())); require_once SUBSDIR . '/Post.subs.php'; $updates = array(); foreach ($adjustBoards as $stats) { $updates[] = $stats['id_board']; } updateLastMessages($updates); }
function pastTime($substep = null, $force = false) { global $time_start, $command_line; if (isset($_GET['substep']) && $_GET['substep'] < $substep) { $_GET['substep'] = $substep; } if ($command_line) { if (time() - $time_start > 1) { print_line('.'); $time_start = time(); } return; } @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } if (time() - $time_start < 10 && !$force) { return; } echo ' <em>Incomplete.</em><br /> <h2 style="margin-top: 2ex;">Not quite done yet!</h2> <h3> This conversion has paused to avoid overloading your server, and hence not working properly.<br /> Don\'t worry though, <strong>nothing\'s wrong</strong> - simply click the <label for="continue">continue button</label> below to start the converter from where it left off. </h3> <form action="', $_SERVER['PHP_SELF'], '?step=', $_GET['step'], isset($_GET['substep']) ? '&substep=' . $_GET['substep'] : '', isset($_GET['cstep']) ? '&cstep=' . $_GET['cstep'] : '', '&start=', $_REQUEST['start'], '" method="post" name="autoSubmit"> <div class="righttext" style="margin: 1ex;"><input name="b" type="submit" value="Continue" class="button_submit" /></div> </form> <script type="text/javascript"><!-- // --><![CDATA[ window.onload = doAutoSubmit; var countdown = 3; function doAutoSubmit() { if (countdown == 0) document.autoSubmit.submit(); else if (countdown == -1) return; document.autoSubmit.b.value = "Continue (" + countdown + ")"; countdown--; setTimeout("doAutoSubmit();", 1000); } // ]]></script>'; template_convert_below(); exit; }
/** * Maintenance function to move attachments from one directory to another */ public function action_transfer() { global $modSettings, $txt; checkSession(); // We will need the functions from here require_once SUBSDIR . '/Attachments.subs.php'; require_once SUBSDIR . '/ManageAttachments.subs.php'; // The list(s) of directory's that are available. $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); if (!empty($modSettings['attachment_basedirectories'])) { $modSettings['attachment_basedirectories'] = unserialize($modSettings['attachment_basedirectories']); } else { $modSettings['basedirectory_for_attachments'] = array(); } // Clean the inputs $_POST['from'] = (int) $_POST['from']; $_POST['auto'] = !empty($_POST['auto']) ? (int) $_POST['auto'] : 0; $_POST['to'] = (int) $_POST['to']; $start = !empty($_POST['empty_it']) ? 0 : $modSettings['attachmentDirFileLimit']; $_SESSION['checked'] = !empty($_POST['empty_it']) ? true : false; // Prepare for the moving $limit = 501; $results = array(); $dir_files = 0; $current_progress = 0; $total_moved = 0; $total_not_moved = 0; // Need to know where we are moving things from if (empty($_POST['from']) || empty($_POST['auto']) && empty($_POST['to'])) { $results[] = $txt['attachment_transfer_no_dir']; } // Same location, that's easy if ($_POST['from'] == $_POST['to']) { $results[] = $txt['attachment_transfer_same_dir']; } // No errors so determine how many we may have to move if (empty($results)) { // Get the total file count for the progress bar. $total_progress = getFolderAttachmentCount($_POST['from']); $total_progress -= $start; if ($total_progress < 1) { $results[] = $txt['attachment_transfer_no_find']; } } // Nothing to move (no files in source or below the max limit) if (empty($results)) { // Moving them automaticaly? if (!empty($_POST['auto'])) { $modSettings['automanage_attachments'] = 1; // Create sub directroys off the root or from an attachment directory? $modSettings['use_subdirectories_for_attachments'] = $_POST['auto'] == -1 ? 0 : 1; $modSettings['basedirectory_for_attachments'] = $_POST['auto'] > 0 ? $modSettings['attachmentUploadDir'][$_POST['auto']] : $modSettings['basedirectory_for_attachments']; // Finaly, where do they need to go automanage_attachments_check_directory(); $new_dir = $modSettings['currentAttachmentUploadDir']; } else { $new_dir = $_POST['to']; } $modSettings['currentAttachmentUploadDir'] = $new_dir; $break = false; while ($break === false) { @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // If limits are set, get the file count and size for the destination folder if ($dir_files <= 0 && (!empty($modSettings['attachmentDirSizeLimit']) || !empty($modSettings['attachmentDirFileLimit']))) { $current_dir = attachDirProperties($new_dir); $dir_files = $current_dir['files']; $dir_size = $current_dir['size']; } // Find some attachments to move list($tomove_count, $tomove) = findAttachmentsToMove($_POST['from'], $start, $limit); // Nothing found to move if ($tomove_count === 0) { if (empty($current_progress)) { $results[] = $txt['attachment_transfer_no_find']; } break; } // No more to move after this batch then set the finished flag. if ($tomove_count < $limit) { $break = true; } // Move them $moved = array(); foreach ($tomove as $row) { $source = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']); $dest = $modSettings['attachmentUploadDir'][$new_dir] . '/' . basename($source); // Size and file count check if (!empty($modSettings['attachmentDirSizeLimit']) || !empty($modSettings['attachmentDirFileLimit'])) { $dir_files++; $dir_size += !empty($row['size']) ? $row['size'] : filesize($source); // If we've reached a directory limit. Do something if we are in auto mode, otherwise set an error. if (!empty($modSettings['attachmentDirSizeLimit']) && $dir_size > $modSettings['attachmentDirSizeLimit'] * 1024 || !empty($modSettings['attachmentDirFileLimit']) && $dir_files > $modSettings['attachmentDirFileLimit']) { // Since we're in auto mode. Create a new folder and reset the counters. if (!empty($_POST['auto'])) { automanage_attachments_by_space(); $results[] = sprintf($txt['attachments_transfered'], $total_moved, $modSettings['attachmentUploadDir'][$new_dir]); if (!empty($total_not_moved)) { $results[] = sprintf($txt['attachments_not_transfered'], $total_not_moved); } $dir_files = 0; $total_moved = 0; $total_not_moved = 0; $break = false; break; } else { $results[] = $txt['attachment_transfer_no_room']; $break = true; break; } } } // Actually move the file if (@rename($source, $dest)) { $total_moved++; $current_progress++; $moved[] = $row['id_attach']; } else { $total_not_moved++; } } // Update the database to reflect the new file location if (!empty($moved)) { moveAttachments($moved, $new_dir); } $new_dir = $modSettings['currentAttachmentUploadDir']; // Create / update the progress bar. // @todo why was this done this way? if (!$break) { $percent_done = min(round($current_progress / $total_progress * 100, 0), 100); $prog_bar = ' <div class="progress_bar"> <div class="full_bar">' . $percent_done . '%</div> <div class="green_percent" style="width: ' . $percent_done . '%;"> </div> </div>'; // Write it to a file so it can be displayed $fp = fopen(BOARDDIR . '/progress.php', 'w'); fwrite($fp, $prog_bar); fclose($fp); usleep(500000); } } $results[] = sprintf($txt['attachments_transfered'], $total_moved, $modSettings['attachmentUploadDir'][$new_dir]); if (!empty($total_not_moved)) { $results[] = sprintf($txt['attachments_not_transfered'], $total_not_moved); } } // All done, time to clean up $_SESSION['results'] = $results; if (file_exists(BOARDDIR . '/progress.php')) { unlink(BOARDDIR . '/progress.php'); } redirectexit('action=admin;area=manageattachments;sa=maintenance#transfer'); }
/** * Installs new themes, either from a gzip or copy of the default. * * What it does: * - Puts themes in $boardurl/themes. * - Assumes the gzip has a root directory in it. (ie default.) * - Requires admin_forum. * - Accessed with ?action=admin;area=theme;sa=install. * * @uses ManageThemes template */ public function action_install() { global $boardurl, $txt, $context, $settings, $modSettings; checkSession('request'); require_once SUBSDIR . '/Themes.subs.php'; require_once SUBSDIR . '/Package.subs.php'; loadTemplate('ManageThemes'); // Passed an ID, then the install is complete, lets redirect and show them if (isset($_GET['theme_id'])) { $_GET['theme_id'] = (int) $_GET['theme_id']; $context['sub_template'] = 'installed'; $context['page_title'] = $txt['theme_installed']; $context['installed_theme'] = array('id' => $_GET['theme_id'], 'name' => getThemeName($_GET['theme_id'])); return; } // How are we going to install this theme, from a dir, zip, copy of default? if (!empty($_FILES['theme_gz']) && (!isset($_FILES['theme_gz']['error']) || $_FILES['theme_gz']['error'] != 4) || !empty($_REQUEST['theme_gz'])) { $method = 'upload'; } elseif (isset($_REQUEST['theme_dir']) && rtrim(realpath($_REQUEST['theme_dir']), '/\\') != realpath(BOARDDIR . '/themes') && file_exists($_REQUEST['theme_dir'])) { $method = 'path'; } else { $method = 'copy'; } // Copy the default theme? if (!empty($_REQUEST['copy']) && $method == 'copy') { // Hopefully the themes directory is writable, or we might have a problem. if (!is_writable(BOARDDIR . '/themes')) { fatal_lang_error('theme_install_write_error', 'critical'); } // Make the new directory, standard characters only $theme_dir = BOARDDIR . '/themes/' . preg_replace('~[^A-Za-z0-9_\\- ]~', '', $_REQUEST['copy']); umask(0); mkdir($theme_dir, 0777); // Get some more time if we can @set_time_limit(600); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Create the subdirectories for css, javascript and font files. mkdir($theme_dir . '/css', 0777); mkdir($theme_dir . '/scripts', 0777); mkdir($theme_dir . '/webfonts', 0777); // Copy over the default non-theme files. $to_copy = array('/index.php', '/index.template.php', '/scripts/theme.js'); foreach ($to_copy as $file) { copy($settings['default_theme_dir'] . $file, $theme_dir . $file); @chmod($theme_dir . $file, 0777); } // And now the entire css, images and webfonts directories! copytree($settings['default_theme_dir'] . '/css', $theme_dir . '/css'); copytree($settings['default_theme_dir'] . '/images', $theme_dir . '/images'); copytree($settings['default_theme_dir'] . '/webfonts', $theme_dir . '/webfonts'); package_flush_cache(); $theme_name = $_REQUEST['copy']; $images_url = $boardurl . '/themes/' . basename($theme_dir) . '/images'; $theme_dir = realpath($theme_dir); // Lets get some data for the new theme (default theme (1), default settings (0)). $theme_values = loadThemeOptionsInto(1, 0, array(), array('theme_templates', 'theme_layers')); // Lets add a theme_info.xml to this theme. write_theme_info($_REQUEST['copy'], $modSettings['elkVersion'], $theme_dir, $theme_values); } elseif (isset($_REQUEST['theme_dir']) && $method == 'path') { if (!is_dir($_REQUEST['theme_dir']) || !file_exists($_REQUEST['theme_dir'] . '/theme_info.xml')) { fatal_lang_error('theme_install_error', false); } $theme_name = basename($_REQUEST['theme_dir']); $theme_dir = $_REQUEST['theme_dir']; } elseif ($method == 'upload') { // Hopefully the themes directory is writable, or we might have a problem. if (!is_writable(BOARDDIR . '/themes')) { fatal_lang_error('theme_install_write_error', 'critical'); } // This happens when the admin session is gone and the user has to login again if (empty($_FILES['theme_gz']) && empty($_REQUEST['theme_gz'])) { redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']); } // Set the default settings... $theme_name = strtok(basename(isset($_FILES['theme_gz']) ? $_FILES['theme_gz']['name'] : $_REQUEST['theme_gz']), '.'); $theme_name = preg_replace(array('/\\s/', '/\\.[\\.]+/', '/[^\\w_\\.\\-]/'), array('_', '.', ''), $theme_name); $theme_dir = BOARDDIR . '/themes/' . $theme_name; if (isset($_FILES['theme_gz']) && is_uploaded_file($_FILES['theme_gz']['tmp_name']) && (ini_get('open_basedir') != '' || file_exists($_FILES['theme_gz']['tmp_name']))) { read_tgz_file($_FILES['theme_gz']['tmp_name'], BOARDDIR . '/themes/' . $theme_name, false, true); } elseif (isset($_REQUEST['theme_gz'])) { if (!isAuthorizedServer($_REQUEST['theme_gz'])) { fatal_lang_error('not_valid_server'); } read_tgz_file($_REQUEST['theme_gz'], BOARDDIR . '/themes/' . $theme_name, false, true); } else { redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']); } } else { fatal_lang_error('theme_install_general', false); } // Something go wrong? if ($theme_dir != '' && basename($theme_dir) != 'themes') { // Defaults. $install_info = array('theme_url' => $boardurl . '/themes/' . basename($theme_dir), 'images_url' => isset($images_url) ? $images_url : $boardurl . '/themes/' . basename($theme_dir) . '/images', 'theme_dir' => $theme_dir, 'name' => $theme_name); $explicit_images = false; if (file_exists($theme_dir . '/theme_info.xml')) { $theme_info = file_get_contents($theme_dir . '/theme_info.xml'); // Parse theme-info.xml into an Xml_Array. require_once SUBSDIR . '/XmlArray.class.php'; $theme_info_xml = new Xml_Array($theme_info); // @todo Error message of some sort? if (!$theme_info_xml->exists('theme-info[0]')) { return 'package_get_error_packageinfo_corrupt'; } $theme_info_xml = $theme_info_xml->path('theme-info[0]'); $theme_info_xml = $theme_info_xml->to_array(); $xml_elements = array('name' => 'name', 'theme_layers' => 'layers', 'theme_templates' => 'templates', 'based_on' => 'based-on'); foreach ($xml_elements as $var => $name) { if (!empty($theme_info_xml[$name])) { $install_info[$var] = $theme_info_xml[$name]; } } if (!empty($theme_info_xml['images'])) { $install_info['images_url'] = $install_info['theme_url'] . '/' . $theme_info_xml['images']; $explicit_images = true; } if (!empty($theme_info_xml['extra'])) { $install_info += unserialize($theme_info_xml['extra']); } } if (isset($install_info['based_on'])) { if ($install_info['based_on'] == 'default') { $install_info['theme_url'] = $settings['default_theme_url']; $install_info['images_url'] = $settings['default_images_url']; } elseif ($install_info['based_on'] != '') { $install_info['based_on'] = preg_replace('~[^A-Za-z0-9\\-_ ]~', '', $install_info['based_on']); $temp = loadBasedOnTheme($install_info['based_on'], $explicit_images); // @todo An error otherwise? if (is_array($temp)) { $install_info = $temp + $install_info; if (empty($explicit_images) && !empty($install_info['base_theme_url'])) { $install_info['theme_url'] = $install_info['base_theme_url']; } } } unset($install_info['based_on']); } // Find the newest id_theme. $id_theme = nextTheme(); $inserts = array(); foreach ($install_info as $var => $val) { $inserts[] = array($id_theme, $var, $val); } if (!empty($inserts)) { addTheme($inserts); } updateSettings(array('knownThemes' => strtr($modSettings['knownThemes'] . ',' . $id_theme, array(',,' => ',')))); } redirectexit('action=admin;area=theme;sa=install;theme_id=' . $id_theme . ';' . $context['session_var'] . '=' . $context['session_id']); }
function pauseRepairProcess($to_fix, $max_substep = 0) { global $context, $txt, $time_start; // More time, I need more time! @set_time_limit(600); if (function_exists('apache_reset_timeout')) { apache_reset_timeout(); } // Errr, wait. How much time has this taken already? if (time() - array_sum(explode(' ', $time_start)) < 3) { return; } $context['continue_get_data'] = '?action=repairboards' . (isset($_GET['fixErrors']) ? ';fixErrors' : '') . ';step=' . $_GET['step'] . ';substep=' . $_GET['substep']; $context['page_title'] = $txt['not_done_title']; $context['continue_post_data'] = ''; $context['continue_countdown'] = '2'; $context['sub_template'] = 'not_done'; // Change these two if more steps are added! if (empty($max_substep)) { $context['continue_percent'] = round($_GET['step'] * 100 / 25); } else { $context['continue_percent'] = round(($_GET['step'] * 100 + $_GET['substep'] * 100 / $max_substep) / 25); } // Never more than 100%! $context['continue_percent'] = min($context['continue_percent'], 100); $_SESSION['repairboards_to_fix'] = $to_fix; $_SESSION['repairboards_to_fix2'] = $context['repair_errors']; obExit(); }
function deleteAccount2($profile_vars, $post_errors, $memID) { global $user_info, $sourcedir, $context, $cur_profile, $modSettings, $smcFunc; // Try get more time... @set_time_limit(600); // !!! Add a way to delete pms as well? if (!$context['user']['is_owner']) { isAllowedTo('profile_remove_any'); } elseif (!allowedTo('profile_remove_any')) { isAllowedTo('profile_remove_own'); } checkSession(); $old_profile =& $cur_profile; // Too often, people remove/delete their own only account. if (in_array(1, explode(',', $old_profile['additional_groups'])) || $old_profile['id_group'] == 1) { // Are you allowed to administrate the forum, as they are? isAllowedTo('admin_forum'); $request = $smcFunc['db_query']('', ' SELECT id_member FROM {db_prefix}members WHERE (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0) AND id_member != {int:selected_member} LIMIT 1', array('admin_group' => 1, 'selected_member' => $memID)); list($another) = $smcFunc['db_fetch_row']($request); $smcFunc['db_free_result']($request); if (empty($another)) { fatal_lang_error('at_least_one_admin', 'critical'); } } // This file is needed for the deleteMembers function. require_once $sourcedir . '/Subs-Members.php'; // Do you have permission to delete others profiles, or is that your profile you wanna delete? if ($memID != $user_info['id']) { isAllowedTo('profile_remove_any'); // Now, have you been naughty and need your posts deleting? // !!! Should this check board permissions? if ($_POST['remove_type'] != 'none' && allowedTo('moderate_forum')) { // Include RemoveTopics - essential for this type of work! require_once $sourcedir . '/RemoveTopic.php'; // First off we delete any topics the member has started - if they wanted topics being done. if ($_POST['remove_type'] == 'topics') { // Fetch all topics started by this user within the time period. $request = $smcFunc['db_query']('', ' SELECT t.id_topic FROM {db_prefix}topics AS t WHERE t.id_member_started = {int:selected_member}', array('selected_member' => $memID)); $topicIDs = array(); while ($row = $smcFunc['db_fetch_assoc']($request)) { $topicIDs[] = $row['id_topic']; } $smcFunc['db_free_result']($request); // Actually remove the topics. // !!! This needs to check permissions, but we'll let it slide for now because of moderate_forum already being had. removeTopics($topicIDs); } // Now delete the remaining messages. $request = $smcFunc['db_query']('', ' SELECT m.id_msg FROM {db_prefix}messages AS m INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic AND t.id_first_msg != m.id_msg) WHERE m.id_member = {int:selected_member}', array('selected_member' => $memID)); // This could take a while... but ya know it's gonna be worth it in the end. while ($row = $smcFunc['db_fetch_assoc']($request)) { if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } removeMessage($row['id_msg']); } $smcFunc['db_free_result']($request); } // Only delete this poor members account if they are actually being booted out of camp. if (isset($_POST['deleteAccount'])) { deleteMembers($memID); } } elseif (empty($post_errors) && !empty($modSettings['approveAccountDeletion']) && !allowedTo('moderate_forum')) { // Setup their account for deletion ;) updateMemberData($memID, array('is_activated' => 4)); // Another account needs approval... updateSettings(array('unapprovedMembers' => true), true); } elseif (empty($post_errors)) { deleteMembers($memID); require_once $sourcedir . '/LogInOut.php'; LogOut(true); redirectExit(); } }
/** * The next substep. * * @param int $substep */ function nextSubstep($substep) { global $start_time, $timeLimitThreshold, $command_line, $custom_warning; global $step_progress, $is_debug, $upcontext; if ($_GET['substep'] < $substep) { $_GET['substep'] = $substep; } if ($command_line) { if (time() - $start_time > 1 && empty($is_debug)) { echo '.'; $start_time = time(); } return; } @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } if (time() - $start_time <= $timeLimitThreshold) { return; } // Do we have some custom step progress stuff? if (!empty($step_progress)) { $upcontext['substep_progress'] = 0; $upcontext['substep_progress_name'] = isset($step_progress['name']) ? $step_progress['name'] : ''; if ($step_progress['current'] > $step_progress['total']) { $upcontext['substep_progress'] = 99.90000000000001; } else { $upcontext['substep_progress'] = $step_progress['current'] / $step_progress['total'] * 100; } // Make it nicely rounded. $upcontext['substep_progress'] = round($upcontext['substep_progress'], 1); } // If this is XML we just exit right away! if (isset($_GET['xml'])) { return upgradeExit(); } // We're going to pause after this! $upcontext['pause'] = true; $upcontext['query_string'] = ''; foreach ($_GET as $k => $v) { if ($k != 'data' && $k != 'substep' && $k != 'step') { $upcontext['query_string'] .= ';' . $k . '=' . $v; } } // Custom warning? if (!empty($custom_warning)) { $upcontext['custom_warning'] = $custom_warning; } upgradeExit(); }
/** * Sends a group of emails from the mail queue. * * - Allows a batch of emails to be released every 5 to 10 seconds (based on per period limits) * - If batch size is not set, will determine a size such that it sends in 1/2 the period (buffer) * * @package Mail * @param int|false $batch_size = false the number to send each loop * @param boolean $override_limit = false bypassing our limit flaf * @param boolean $force_send = false * @return boolean */ function reduceMailQueue($batch_size = false, $override_limit = false, $force_send = false) { global $modSettings, $context, $webmaster_email, $scripturl; // Do we have another script to send out the queue? if (!empty($modSettings['mail_queue_use_cron']) && empty($force_send)) { return false; } // How many emails can we send each time we are called in a period if (!$batch_size) { // Batch size has been set in the ACP, use it if (!empty($modSettings['mail_batch_size'])) { $batch_size = $modSettings['mail_batch_size']; } elseif (empty($modSettings['mail_period_limit'])) { $batch_size = 5; } else { // Based on the number of times we will potentially be called each minute $delay = !empty($modSettings['mail_queue_delay']) ? $modSettings['mail_queue_delay'] : (!empty($modSettings['mail_period_limit']) && $modSettings['mail_period_limit'] <= 5 ? 10 : 5); $batch_size = ceil($modSettings['mail_period_limit'] / ceil(60 / $delay)); $batch_size = $batch_size == 1 && $modSettings['mail_period_limit'] > 1 ? 2 : $batch_size; } } // If we came with a timestamp, and that doesn't match the next event, then someone else has beaten us. if (isset($_GET['ts']) && $_GET['ts'] != $modSettings['mail_next_send'] && empty($force_send)) { return false; } // Prepare to send each email, and log that for future proof. require_once SUBSDIR . '/Maillist.subs.php'; // Set the delay for the next sending if (!$override_limit) { // Update next send time for our mail queue, if there was something to update. Otherwise bail out :P $delay = updateNextSendTime(); if ($delay === false) { return false; } $modSettings['mail_next_send'] = time() + $delay; } // If we're not overriding, do we have quota left in this mail period limit? if (!$override_limit && !empty($modSettings['mail_period_limit'])) { // See if we have quota left to send another batch_size this minute or if we have to wait list($mail_time, $mail_number) = isset($modSettings['mail_recent']) ? explode('|', $modSettings['mail_recent']) : array(0, 0); // Nothing worth noting... if (empty($mail_number) || $mail_time < time() - 60) { $mail_time = time(); $mail_number = $batch_size; } elseif ($mail_number < $modSettings['mail_period_limit']) { // If this is likely one of the last cycles for this period, then send any remaining quota if ($mail_time - (time() - 60) < $delay * 2) { $batch_size = $modSettings['mail_period_limit'] - $mail_number; } elseif ($mail_number + $batch_size > $modSettings['mail_period_limit']) { $batch_size = $modSettings['mail_period_limit'] - $mail_number; } $mail_number += $batch_size; } else { return false; } // Reflect that we're about to send some, do it now to be safe. updateSettings(array('mail_recent' => $mail_time . '|' . $mail_number)); } // Now we know how many we're sending, let's send them. list($ids, $emails) = emailsInfo($batch_size); // Delete, delete, delete!!! if (!empty($ids)) { deleteMailQueueItems($ids); } // Don't believe we have any left after this batch? if (count($ids) < $batch_size) { resetNextSendTime(); } if (empty($ids)) { return false; } // We have some to send, lets send them! $sent = array(); $failed_emails = array(); // Use sendmail or SMTP $use_sendmail = empty($modSettings['mail_type']) || $modSettings['smtp_host'] == ''; // Line breaks need to be \r\n only in windows or for SMTP. $line_break = !empty($context['server']['is_windows']) || !$use_sendmail ? "\r\n" : "\n"; foreach ($emails as $key => $email) { // Use the right mail resource if ($use_sendmail) { $email['subject'] = strtr($email['subject'], array("\r" => '', "\n" => '')); if (!empty($modSettings['mail_strip_carriage'])) { $email['body'] = strtr($email['body'], array("\r" => '')); $email['headers'] = strtr($email['headers'], array("\r" => '')); } $need_break = substr($email['headers'], -1) === "\n" || substr($email['headers'], -1) === "\r" ? false : true; // Create our unique reply to email header if this message needs one $unq_id = ''; $unq_head = ''; if (!empty($modSettings['maillist_enabled']) && $email['message_id'] !== null && strpos($email['headers'], 'List-Id: <') !== false) { $unq_head = md5($scripturl . microtime() . rand()) . '-' . $email['message_id']; $encoded_unq_head = base64_encode($line_break . $line_break . '[' . $unq_head . ']' . $line_break); $unq_id = ($need_break ? $line_break : '') . 'Message-ID: <' . $unq_head . strstr(empty($modSettings['maillist_mail_from']) ? $webmaster_email : $modSettings['maillist_mail_from'], '@') . '>'; $email['body_fail'] = $email['body']; $email['body'] = mail_insert_key($email['body'], $unq_head, $encoded_unq_head, $line_break); } elseif ($email['message_id'] !== null && empty($modSettings['mail_no_message_id'])) { $unq_id = ($need_break ? $line_break : '') . 'Message-ID: <' . md5($scripturl . microtime()) . '-' . $email['message_id'] . strstr(empty($modSettings['maillist_mail_from']) ? $webmaster_email : $modSettings['maillist_mail_from'], '@') . '>'; } // No point logging a specific error here, as we have no language. PHP error is helpful anyway... $result = mail(strtr($email['to'], array("\r" => '', "\n" => '')), $email['subject'], $email['body'], $email['headers'] . $unq_id); // If it sent, keep a record so we can save it in our allowed to reply log if (!empty($unq_head) && $result) { $sent[] = array($unq_head, time(), $email['to']); } // Track total emails sent if ($result && !empty($modSettings['trackStats'])) { trackStats(array('email' => '+')); } // Try to stop a timeout, this would be bad... @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } } else { $result = smtp_mail(array($email['to']), $email['subject'], $email['body'], $email['send_html'] ? $email['headers'] : 'Mime-Version: 1.0' . "\r\n" . $email['headers'], $email['priority'], $email['message_id']); } // Hopefully it sent? if (!$result) { $failed_emails[] = array(time(), $email['to'], $email['body_fail'], $email['subject'], $email['headers'], $email['send_html'], $email['priority'], $email['private'], $email['message_id']); } } // Clear out the stat cache. trackStats(); // Log each of the sent emails. if (!empty($sent)) { log_email($sent); } // Any emails that didn't send? if (!empty($failed_emails)) { // If it failed, add it back to the queue updateFailedQueue($failed_emails); return false; } elseif (!empty($modSettings['mail_failed_attempts'])) { updateSuccessQueue(); } // Had something to send... return true; }
/** * Used for pausing the mail queue. */ function pauseMailQueueClear() { global $context, $txt, $time_start; // Try get more time... @set_time_limit(600); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Have we already used our maximum time? if (time() - array_sum(explode(' ', $time_start)) < 5) { return; } $context['continue_get_data'] = '?action=admin;area=mailqueue;sa=clear;te=' . $_GET['te'] . ';sent=' . $_GET['sent'] . ';' . $context['session_var'] . '=' . $context['session_id']; $context['page_title'] = $txt['not_done_title']; $context['continue_post_data'] = ''; $context['continue_countdown'] = '2'; $context['sub_template'] = 'not_done'; // Keep browse selected. $context['selected'] = 'browse'; // What percent through are we? $context['continue_percent'] = round($_GET['sent'] / $_GET['te'] * 100, 1); // Never more than 100%! $context['continue_percent'] = min($context['continue_percent'], 100); obExit(); }
/** * Dumps the database. * * What it does: * - It writes all of the database to standard output. * - It uses gzip compression if compress is set in the URL/post data. * - It may possibly time out, and mess up badly if you were relying on it. :P * - The data dumped depends on whether "struct" and "data" are passed. * - It is called from ManageMaintenance.controller.php. */ function DumpDatabase2() { global $db_name, $scripturl, $modSettings, $db_prefix, $db_show_debug; // We'll need a db to dump :P $database = database(); // We don't need debug when dumping the database $modSettings['disableQueryCheck'] = true; $db_show_debug = false; // You can't dump nothing! if (!isset($_REQUEST['struct']) && !isset($_REQUEST['data'])) { $_REQUEST['data'] = true; } // Attempt to stop from dying... @set_time_limit(600); $time_limit = ini_get('max_execution_time'); $start_time = time(); // @todo ... fail on not getting the requested memory? setMemoryLimit('256M'); $memory_limit = memoryReturnBytes(ini_get('memory_limit')) / 4; $current_used_memory = 0; $db_backup = ''; $output_function = 'un_compressed'; @ob_end_clean(); // Start saving the output... (don't do it otherwise for memory reasons.) if (isset($_REQUEST['compress']) && function_exists('gzencode')) { $output_function = 'gzencode'; // Send faked headers so it will just save the compressed output as a gzip. header('Content-Type: application/x-gzip'); header('Accept-Ranges: bytes'); header('Content-Encoding: none'); // Gecko browsers... don't like this. (Mozilla, Firefox, etc.) if (!isBrowser('gecko')) { header('Content-Transfer-Encoding: binary'); } // The file extension will include .gz... $extension = '.sql.gz'; } else { // Get rid of the gzipping alreading being done. if (!empty($modSettings['enableCompressedOutput'])) { @ob_end_clean(); } elseif (ob_get_length() != 0) { ob_clean(); } // Tell the client to save this file, even though it's text. header('Content-Type: ' . (isBrowser('ie') || isBrowser('opera') ? 'application/octetstream' : 'application/octet-stream')); header('Content-Encoding: none'); // This time the extension should just be .sql. $extension = '.sql'; } // This should turn off the session URL parser. $scripturl = ''; // Send the proper headers to let them download this file. header('Content-Disposition: attachment; filename="' . $db_name . '-' . (empty($_REQUEST['struct']) ? 'data' : (empty($_REQUEST['data']) ? 'structure' : 'complete')) . '_' . strftime('%Y-%m-%d') . $extension . '"'); header('Cache-Control: private'); header('Connection: close'); // This makes things simpler when using it so very very often. $crlf = "\r\n"; // SQL Dump Header. $db_chunks = '-- ==========================================================' . $crlf . '--' . $crlf . '-- Database dump of tables in `' . $db_name . '`' . $crlf . '-- ' . standardTime(time(), false) . $crlf . '--' . $crlf . '-- ==========================================================' . $crlf . $crlf; // Get all tables in the database....for our installation $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; $tables = $database->db_list_tables(false, $real_prefix . '%'); // Dump each table. foreach ($tables as $tableName) { // Are we dumping the structures? if (isset($_REQUEST['struct'])) { $db_chunks .= $crlf . '--' . $crlf . '-- Table structure for table `' . $tableName . '`' . $crlf . '--' . $crlf . $crlf . $database->db_table_sql($tableName) . ';' . $crlf; } else { // This is needed to speedup things later $database->db_table_sql($tableName); } // How about the data? if (!isset($_REQUEST['data']) || substr($tableName, -10) == 'log_errors') { continue; } $first_round = true; $close_table = false; // Are there any rows in this table? while ($get_rows = $database->insert_sql($tableName, $first_round)) { if (empty($get_rows)) { break; } // Time is what we need here! if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } elseif (!empty($time_limit) && $start_time + $time_limit - 20 > time()) { $start_time = time(); @set_time_limit(150); } // for the first pass, start the output with a custom line... if ($first_round) { $db_chunks .= $crlf . '--' . $crlf . '-- Dumping data in `' . $tableName . '`' . $crlf . '--' . $crlf . $crlf; $first_round = false; } $db_chunks .= $get_rows; $current_used_memory += Util::strlen($db_chunks); $db_backup .= $db_chunks; unset($db_chunks); $db_chunks = ''; if ($current_used_memory > $memory_limit) { echo $output_function($db_backup); $current_used_memory = 0; // This is probably redundant unset($db_backup); $db_backup = ''; } $close_table = true; } // No rows to get - skip it. if ($close_table) { $db_backup .= '-- --------------------------------------------------------' . $crlf; } } // write the last line $db_backup .= $crlf . '-- Done' . $crlf; echo $output_function($db_backup); exit; }
/** * Create a custom search index for the messages table. * * What it does: * - Called by ?action=admin;area=managesearch;sa=createmsgindex. * - Linked from the action_edit screen. * - Requires the admin_forum permission. * - Depending on the size of the message table, the process is divided in steps. * * @uses ManageSearch template, 'create_index', 'create_index_progress', and 'create_index_done' * sub-templates. */ public function action_create() { global $modSettings, $context, $txt, $db_show_debug; // Scotty, we need more time... @set_time_limit(600); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } $context[$context['admin_menu_name']]['current_subsection'] = 'method'; $context['page_title'] = $txt['search_index_custom']; $messages_per_batch = 50; $index_properties = array(2 => array('column_definition' => 'small', 'step_size' => 1000000), 4 => array('column_definition' => 'medium', 'step_size' => 1000000, 'max_size' => 16777215), 5 => array('column_definition' => 'large', 'step_size' => 100000000, 'max_size' => 2000000000)); // Resume building an index that was not completed if (isset($_REQUEST['resume']) && !empty($modSettings['search_custom_index_resume'])) { $context['index_settings'] = unserialize($modSettings['search_custom_index_resume']); $context['start'] = (int) $context['index_settings']['resume_at']; unset($context['index_settings']['resume_at']); $context['step'] = 1; } else { $context['index_settings'] = array('bytes_per_word' => isset($_REQUEST['bytes_per_word']) && isset($index_properties[$_REQUEST['bytes_per_word']]) ? (int) $_REQUEST['bytes_per_word'] : 2); $context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; $context['step'] = isset($_REQUEST['step']) ? (int) $_REQUEST['step'] : 0; // Admin timeouts are painful when building these long indexes if ($_SESSION['admin_time'] + 3300 < time() && $context['step'] >= 1) { $_SESSION['admin_time'] = time(); } } if ($context['step'] !== 0) { checkSession('request'); } // Step 0: let the user determine how they like their index. if ($context['step'] === 0) { $context['sub_template'] = 'create_index'; } require_once SUBSDIR . '/ManageSearch.subs.php'; // Logging may cause session issues with many queries $old_db_show_debug = $db_show_debug; $db_show_debug = false; // Step 1: insert all the words. if ($context['step'] === 1) { $context['sub_template'] = 'create_index_progress'; list($context['start'], $context['step'], $context['percentage']) = createSearchIndex($context['start'], $messages_per_batch, $index_properties[$context['index_settings']['bytes_per_word']]['column_definition'], $context['index_settings']); } elseif ($context['step'] === 2) { if ($context['index_settings']['bytes_per_word'] < 4) { $context['step'] = 3; } else { list($context['start'], $complete) = removeCommonWordsFromIndex($context['start'], $index_properties[$context['index_settings']['bytes_per_word']]); if ($complete) { $context['step'] = 3; } $context['sub_template'] = 'create_index_progress'; $context['percentage'] = 80 + round($context['start'] / $index_properties[$context['index_settings']['bytes_per_word']]['max_size'], 3) * 20; } } // Restore previous debug state $db_show_debug = $old_db_show_debug; // Step 3: everything done. if ($context['step'] === 3) { $context['sub_template'] = 'create_index_done'; updateSettings(array('search_index' => 'custom', 'search_custom_index_config' => serialize($context['index_settings']))); removeSettings('search_custom_index_resume'); } }
function pauseAttachmentMaintenance($to_fix, $max_substep = 0) { global $context, $txt, $time_start; // Try get more time... @set_time_limit(600); if (function_exists('apache_reset_timeout')) { apache_reset_timeout(); } // Have we already used our maximum time? if (time() - array_sum(explode(' ', $time_start)) < 3) { return; } // Specific stuff to not break this template! $context['page_title'] = $txt['not_done_title']; $context['sub_template'] = 'not_done'; $context['description'] = $txt['smf202']; $context['selected'] = 'maintenance'; $context['continue_get_data'] = '?action=manageattachments;sa=repair' . (isset($_GET['fixErrors']) ? ';fixErrors' : '') . ';step=' . $_GET['step'] . ';substep=' . $_GET['substep'] . ';sesc=' . $context['session_id']; $context['continue_post_data'] = ''; $context['continue_countdown'] = '2'; // Change these two if more steps are added! if (empty($max_substep)) { $context['continue_percent'] = round($_GET['step'] * 100 / 25); } else { $context['continue_percent'] = round(($_GET['step'] * 100 + $_GET['substep'] * 100 / $max_substep) / 25); } // Never more than 100%! $context['continue_percent'] = min($context['continue_percent'], 100); $_SESSION['attachments_to_fix'] = $to_fix; $_SESSION['attachments_to_fix2'] = $context['repair_errors']; obExit(); }
function scheduled_birthdayemails() { global $modSettings, $sourcedir, $mbname, $txt, $smcFunc, $birthdayEmails; // Need this in order to load the language files. loadEssentialThemeData(); // Going to need this to send the emails. require_once $sourcedir . '/lib/Subs-Post.php'; $greeting = isset($modSettings['birthday_email']) ? $modSettings['birthday_email'] : 'happy_birthday'; // Get the month and day of today. $month = date('n'); // Month without leading zeros. $day = date('j'); // Day without leading zeros. // So who are the lucky ones? Don't include those who are banned and those who don't want them. $result = smf_db_query(' SELECT id_member, real_name, lngfile, email_address FROM {db_prefix}members WHERE is_activated < 10 AND MONTH(birthdate) = {int:month} AND DAYOFMONTH(birthdate) = {int:day} AND notify_announcements = {int:notify_announcements} AND YEAR(birthdate) > {int:year}', array('notify_announcements' => 1, 'year' => 1, 'month' => $month, 'day' => $day)); // Group them by languages. $birthdays = array(); while ($row = mysql_fetch_assoc($result)) { if (!isset($birthdays[$row['lngfile']])) { $birthdays[$row['lngfile']] = array(); } $birthdays[$row['lngfile']][$row['id_member']] = array('name' => $row['real_name'], 'email' => $row['email_address']); } mysql_free_result($result); // Send out the greetings! foreach ($birthdays as $lang => $recps) { // We need to do some shuffling to make this work properly. loadLanguage('EmailTemplates', $lang); $txt['emails']['happy_birthday'] = $birthdayEmails[$greeting]; foreach ($recps as $recp) { $replacements = array('REALNAME' => $recp['name']); $emaildata = loadEmailTemplate('happy_birthday', $replacements, $lang, false); sendmail($recp['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 4); // Try to stop a timeout, this would be bad... @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } } } // Flush the mail queue, just in case. AddMailQueue(true); return true; }
/** * Removes the passed id_topic's. (permissions are NOT checked here!). * * @param array/int $topics The topics to remove (can be an id or an array of ids). * @param bool $decreasePostCount if true users' post count will be reduced * @param bool $ignoreRecycling if true topics are not moved to the recycle board (if it exists). */ function removeTopics($topics, $decreasePostCount = true, $ignoreRecycling = false) { global $sourcedir, $modSettings, $smcFunc; // Nothing to do? if (empty($topics)) { return; } // Only a single topic. if (is_numeric($topics)) { $topics = array($topics); } // Decrease the post counts. if ($decreasePostCount) { $requestMembers = $smcFunc['db_query']('', ' SELECT m.id_member, COUNT(*) AS posts FROM {db_prefix}messages AS m INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) WHERE m.id_topic IN ({array_int:topics}) AND m.icon != {string:recycled} AND b.count_posts = {int:do_count_posts} AND m.approved = {int:is_approved} GROUP BY m.id_member', array('do_count_posts' => 0, 'recycled' => 'recycled', 'topics' => $topics, 'is_approved' => 1)); if ($smcFunc['db_num_rows']($requestMembers) > 0) { while ($rowMembers = $smcFunc['db_fetch_assoc']($requestMembers)) { updateMemberData($rowMembers['id_member'], array('posts' => 'posts - ' . $rowMembers['posts'])); } } $smcFunc['db_free_result']($requestMembers); } // Recycle topics that aren't in the recycle board... if (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 && !$ignoreRecycling) { $request = $smcFunc['db_query']('', ' SELECT id_topic, id_board, unapproved_posts, approved FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics}) AND id_board != {int:recycle_board} LIMIT ' . count($topics), array('recycle_board' => $modSettings['recycle_board'], 'topics' => $topics)); if ($smcFunc['db_num_rows']($request) > 0) { // Get topics that will be recycled. $recycleTopics = array(); while ($row = $smcFunc['db_fetch_assoc']($request)) { if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } $recycleTopics[] = $row['id_topic']; // Set the id_previous_board for this topic - and make it not sticky. $smcFunc['db_query']('', ' UPDATE {db_prefix}topics SET id_previous_board = {int:id_previous_board}, is_sticky = {int:not_sticky} WHERE id_topic = {int:id_topic}', array('id_previous_board' => $row['id_board'], 'id_topic' => $row['id_topic'], 'not_sticky' => 0)); } $smcFunc['db_free_result']($request); // Mark recycled topics as recycled. $smcFunc['db_query']('', ' UPDATE {db_prefix}messages SET icon = {string:recycled} WHERE id_topic IN ({array_int:recycle_topics})', array('recycle_topics' => $recycleTopics, 'recycled' => 'recycled')); // Move the topics to the recycle board. require_once $sourcedir . '/MoveTopic.php'; moveTopics($recycleTopics, $modSettings['recycle_board']); // Close reports that are being recycled. require_once $sourcedir . '/ModerationCenter.php'; $smcFunc['db_query']('', ' UPDATE {db_prefix}log_reported SET closed = {int:is_closed} WHERE id_topic IN ({array_int:recycle_topics})', array('recycle_topics' => $recycleTopics, 'is_closed' => 1)); updateSettings(array('last_mod_report_action' => time())); recountOpenReports(); // Topics that were recycled don't need to be deleted, so subtract them. $topics = array_diff($topics, $recycleTopics); } else { $smcFunc['db_free_result']($request); } } // Still topics left to delete? if (empty($topics)) { return; } $adjustBoards = array(); // Find out how many posts we are deleting. $request = $smcFunc['db_query']('', ' SELECT id_board, approved, COUNT(*) AS num_topics, SUM(unapproved_posts) AS unapproved_posts, SUM(num_replies) AS num_replies FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics}) GROUP BY id_board, approved', array('topics' => $topics)); while ($row = $smcFunc['db_fetch_assoc']($request)) { if (!isset($adjustBoards[$row['id_board']]['num_posts'])) { $adjustBoards[$row['id_board']] = array('num_posts' => 0, 'num_topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0, 'id_board' => $row['id_board']); } // Posts = (num_replies + 1) for each approved topic. $adjustBoards[$row['id_board']]['num_posts'] += $row['num_replies'] + ($row['approved'] ? $row['num_topics'] : 0); $adjustBoards[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts']; // Add the topics to the right type. if ($row['approved']) { $adjustBoards[$row['id_board']]['num_topics'] += $row['num_topics']; } else { $adjustBoards[$row['id_board']]['unapproved_topics'] += $row['num_topics']; } } $smcFunc['db_free_result']($request); // Decrease the posts/topics... foreach ($adjustBoards as $stats) { if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } $smcFunc['db_query']('', ' UPDATE {db_prefix}boards SET num_posts = CASE WHEN {int:num_posts} > num_posts THEN 0 ELSE num_posts - {int:num_posts} END, num_topics = CASE WHEN {int:num_topics} > num_topics THEN 0 ELSE num_topics - {int:num_topics} END, unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END, unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END WHERE id_board = {int:id_board}', array('id_board' => $stats['id_board'], 'num_posts' => $stats['num_posts'], 'num_topics' => $stats['num_topics'], 'unapproved_posts' => $stats['unapproved_posts'], 'unapproved_topics' => $stats['unapproved_topics'])); } // Remove Polls. $request = $smcFunc['db_query']('', ' SELECT id_poll FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics}) AND id_poll > {int:no_poll} LIMIT ' . count($topics), array('no_poll' => 0, 'topics' => $topics)); $polls = array(); while ($row = $smcFunc['db_fetch_assoc']($request)) { $polls[] = $row['id_poll']; } $smcFunc['db_free_result']($request); if (!empty($polls)) { $smcFunc['db_query']('', ' DELETE FROM {db_prefix}polls WHERE id_poll IN ({array_int:polls})', array('polls' => $polls)); $smcFunc['db_query']('', ' DELETE FROM {db_prefix}poll_choices WHERE id_poll IN ({array_int:polls})', array('polls' => $polls)); $smcFunc['db_query']('', ' DELETE FROM {db_prefix}log_polls WHERE id_poll IN ({array_int:polls})', array('polls' => $polls)); } // Get rid of the attachment, if it exists. require_once $sourcedir . '/ManageAttachments.php'; $attachmentQuery = array('attachment_type' => 0, 'id_topic' => $topics); removeAttachments($attachmentQuery, 'messages'); // Delete possible search index entries. if (!empty($modSettings['search_custom_index_config'])) { $customIndexSettings = unserialize($modSettings['search_custom_index_config']); $words = array(); $messages = array(); $request = $smcFunc['db_query']('', ' SELECT id_msg, body FROM {db_prefix}messages WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); while ($row = $smcFunc['db_fetch_assoc']($request)) { if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } $words = array_merge($words, text2words($row['body'], $customIndexSettings['bytes_per_word'], true)); $messages[] = $row['id_msg']; } $smcFunc['db_free_result']($request); $words = array_unique($words); if (!empty($words) && !empty($messages)) { $smcFunc['db_query']('', ' DELETE FROM {db_prefix}log_search_words WHERE id_word IN ({array_int:word_list}) AND id_msg IN ({array_int:message_list})', array('word_list' => $words, 'message_list' => $messages)); } } // Delete anything related to the topic. $smcFunc['db_query']('', ' DELETE FROM {db_prefix}messages WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); $smcFunc['db_query']('', ' DELETE FROM {db_prefix}calendar WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); $smcFunc['db_query']('', ' DELETE FROM {db_prefix}log_topics WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); $smcFunc['db_query']('', ' DELETE FROM {db_prefix}log_notify WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); $smcFunc['db_query']('', ' DELETE FROM {db_prefix}topics WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); $smcFunc['db_query']('', ' DELETE FROM {db_prefix}log_search_subjects WHERE id_topic IN ({array_int:topics})', array('topics' => $topics)); // Maybe there's a mod that wants to delete topic related data of its own call_integration_hook('integrate_remove_topics', array($topics)); // Update the totals... updateStats('message'); updateStats('topic'); updateSettings(array('calendar_updated' => time())); require_once $sourcedir . '/Subs-Post.php'; $updates = array(); foreach ($adjustBoards as $stats) { $updates[] = $stats['id_board']; } updateLastMessages($updates); }
function ConvertEntities() { global $db_character_set, $modSettings, $context, $sourcedir, $smcFunc; isAllowedTo('admin_forum'); // Check to see if UTF-8 is currently the default character set. if ($modSettings['global_character_set'] !== 'UTF-8' || !isset($db_character_set) || $db_character_set !== 'utf8') { fatal_lang_error('entity_convert_only_utf8'); } // Some starting values. $context['table'] = empty($_REQUEST['table']) ? 0 : (int) $_REQUEST['table']; $context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start']; $context['start_time'] = time(); $context['first_step'] = !isset($_REQUEST[$context['session_var']]); $context['last_step'] = false; // The first step is just a text screen with some explanation. if ($context['first_step']) { $context['sub_template'] = 'convert_entities'; return; } // Otherwise use the generic "not done" template. $context['sub_template'] = 'not_done'; $context['continue_post_data'] = ''; $context['continue_countdown'] = 3; // Now we're actually going to convert... checkSession('request'); // A list of tables ready for conversion. $tables = array('ban_groups', 'ban_items', 'boards', 'calendar', 'calendar_holidays', 'categories', 'log_errors', 'log_search_subjects', 'membergroups', 'members', 'message_icons', 'messages', 'package_servers', 'personal_messages', 'pm_recipients', 'polls', 'poll_choices', 'smileys', 'themes'); $context['num_tables'] = count($tables); // This function will do the conversion later on. $entity_replace = create_function('$string', ' $num = substr($string, 0, 1) === \'x\' ? hexdec(substr($string, 1)) : (int) $string; return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) ? \'\' : ($num < 0x80 ? \'&#\' . $num . \';\' : ($num < 0x800 ? chr(192 | $num >> 6) . chr(128 | $num & 63) : ($num < 0x10000 ? chr(224 | $num >> 12) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63) : chr(240 | $num >> 18) . chr(128 | $num >> 12 & 63) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63))));'); // Loop through all tables that need converting. for (; $context['table'] < $context['num_tables']; $context['table']++) { $cur_table = $tables[$context['table']]; $primary_key = ''; // Make sure we keep stuff unique! $primary_keys = array(); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Get a list of text columns. $columns = array(); $request = $smcFunc['db_query']('', ' SHOW FULL COLUMNS FROM {db_prefix}' . $cur_table, array()); while ($column_info = $smcFunc['db_fetch_assoc']($request)) { if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false) { $columns[] = strtolower($column_info['Field']); } } // Get the column with the (first) primary key. $request = $smcFunc['db_query']('', ' SHOW KEYS FROM {db_prefix}' . $cur_table, array()); while ($row = $smcFunc['db_fetch_assoc']($request)) { if ($row['Key_name'] === 'PRIMARY') { if (empty($primary_key) || $row['Seq_in_index'] == 1 && !in_array(strtolower($row['Column_name']), $columns)) { $primary_key = $row['Column_name']; } $primary_keys[] = $row['Column_name']; } } $smcFunc['db_free_result']($request); // No primary key, no glory. // Same for columns. Just to be sure we've work to do! if (empty($primary_key) || empty($columns)) { continue; } // Get the maximum value for the primary key. $request = $smcFunc['db_query']('', ' SELECT MAX(' . $primary_key . ') FROM {db_prefix}' . $cur_table, array()); list($max_value) = $smcFunc['db_fetch_row']($request); $smcFunc['db_free_result']($request); if (empty($max_value)) { continue; } while ($context['start'] <= $max_value) { // Retrieve a list of rows that has at least one entity to convert. $request = $smcFunc['db_query']('', ' SELECT {raw:primary_keys}, {raw:columns} FROM {db_prefix}{raw:cur_table} WHERE {raw:primary_key} BETWEEN {int:start} AND {int:start} + 499 AND {raw:like_compare} LIMIT 500', array('primary_keys' => implode(', ', $primary_keys), 'columns' => implode(', ', $columns), 'cur_table' => $cur_table, 'primary_key' => $primary_key, 'start' => $context['start'], 'like_compare' => '(' . implode(' LIKE \'%&#%\' OR ', $columns) . ' LIKE \'%&#%\')')); while ($row = $smcFunc['db_fetch_assoc']($request)) { $insertion_variables = array(); $changes = array(); foreach ($row as $column_name => $column_value) { if ($column_name !== $primary_key && strpos($column_value, '&#') !== false) { $changes[] = $column_name . ' = {string:changes_' . $column_name . '}'; $insertion_variables['changes_' . $column_name] = preg_replace_callback('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', 'fixchar__callback', $column_value); } } $where = array(); foreach ($primary_keys as $key) { $where[] = $key . ' = {string:where_' . $key . '}'; $insertion_variables['where_' . $key] = $row[$key]; } // Update the row. if (!empty($changes)) { $smcFunc['db_query']('', ' UPDATE {db_prefix}' . $cur_table . ' SET ' . implode(', ', $changes) . ' WHERE ' . implode(' AND ', $where), $insertion_variables); } } $smcFunc['db_free_result']($request); $context['start'] += 500; // After ten seconds interrupt. if (time() - $context['start_time'] > 10) { // Calculate an approximation of the percentage done. $context['continue_percent'] = round(100 * ($context['table'] + $context['start'] / $max_value) / $context['num_tables'], 1); $context['continue_get_data'] = '?action=admin;area=maintain;sa=database;activity=convertentities;table=' . $context['table'] . ';start=' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; return; } } $context['start'] = 0; } // Make sure all serialized strings are all right. require_once $sourcedir . '/Subs-Charset.php'; fix_serialized_columns(); // If we're here, we must be done. $context['continue_percent'] = 100; $context['continue_get_data'] = '?action=admin;area=maintain;sa=database;done=convertentities'; $context['last_step'] = true; $context['continue_countdown'] = -1; }
function pauseRepairProcess($to_fix, $current_step_description, $max_substep = 0, $force = false) { global $context, $txt, $time_start, $db_temp_cache, $db_cache; // More time, I need more time! @set_time_limit(600); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Errr, wait. How much time has this taken already? if (!$force && time() - array_sum(explode(' ', $time_start)) < 3) { return; } // Restore the query cache if interested. if (!empty($db_temp_cache)) { $db_cache = $db_temp_cache; } $context['continue_get_data'] = '?action=admin;area=repairboards' . (isset($_GET['fixErrors']) ? ';fixErrors' : '') . ';step=' . $_GET['step'] . ';substep=' . $_GET['substep'] . ';' . $context['session_var'] . '=' . $context['session_id']; $context['page_title'] = $txt['not_done_title']; $context['continue_post_data'] = ''; $context['continue_countdown'] = '2'; $context['sub_template'] = 'not_done'; // Change these two if more steps are added! if (empty($max_substep)) { $context['continue_percent'] = round($_GET['step'] * 100 / $context['total_steps']); } else { $context['continue_percent'] = round(($_GET['step'] + $_GET['substep'] / $max_substep) * 100 / $context['total_steps']); } // Never more than 100%! $context['continue_percent'] = min($context['continue_percent'], 100); // What about substeps? $context['substep_enabled'] = $max_substep != 0; $context['substep_title'] = sprintf($txt['repair_currently_' . (isset($_GET['fixErrors']) ? 'fixing' : 'checking')], isset($txt['repair_operation_' . $current_step_description]) ? $txt['repair_operation_' . $current_step_description] : $current_step_description); $context['substep_continue_percent'] = $max_substep == 0 ? 0 : round($_GET['substep'] * 100 / $max_substep, 1); $_SESSION['repairboards_to_fix'] = $to_fix; $_SESSION['repairboards_to_fix2'] = $context['repair_errors']; obExit(); }
/** * nextStep() * * - called from function execute, uses template not_done to pause the loop * - sets $_SESSION vars as needed for the next loop * * @param mixed $name * @param integer $i * @return */ function nextStep($name, $i = 0) { global $context, $txt; // Try get more time... @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // set the session info for the step $_SESSION[$name]['done'] = $i; // progress bar $context['continue_percent'] = round((int) $_SESSION[$name]['done'] / $_SESSION[$name]['work'] * 100); $context['continue_percent'] = min($context['continue_percent'], 100); // set the context vars for display via the admin template 'not_done' $context['continue_get_data'] = '?action=execute'; $context['page_title'] = $txt['not_done_title']; $context['continue_post_data'] = ' <input type="hidden" name="' . (isset($context['session_var']) ? $context['session_var'] : 'sc') . '" value="' . $context['session_id'] . '" /> <input type="hidden" name="agree" value="' . $_POST['agree'] . '" /> <input type="hidden" name="submit_ok" value="' . $_POST['submit_ok'] . '" />'; $context['continue_countdown'] = '5'; $context['sub_template'] = 'not_done'; obExit(); }
/** * Create a custom search index for the messages table. * Called by ?action=admin;area=managesearch;sa=createmsgindex. * Linked from the EditSearchMethod screen. * Requires the admin_forum permission. * Depending on the size of the message table, the process is divided in steps. * * @uses ManageSearch template, 'create_index', 'create_index_progress', and 'create_index_done' * sub-templates. */ function CreateMessageIndex() { global $modSettings, $context, $smcFunc, $db_prefix, $txt; // Scotty, we need more time... @set_time_limit(600); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } $context[$context['admin_menu_name']]['current_subsection'] = 'method'; $context['page_title'] = $txt['search_index_custom']; $messages_per_batch = 50; $index_properties = array(2 => array('column_definition' => 'small', 'step_size' => 1000000), 4 => array('column_definition' => 'medium', 'step_size' => 1000000, 'max_size' => 16777215), 5 => array('column_definition' => 'large', 'step_size' => 100000000, 'max_size' => 2000000000)); if (isset($_REQUEST['resume']) && !empty($modSettings['search_custom_index_resume'])) { $context['index_settings'] = unserialize($modSettings['search_custom_index_resume']); $context['start'] = (int) $context['index_settings']['resume_at']; unset($context['index_settings']['resume_at']); $context['step'] = 1; } else { $context['index_settings'] = array('bytes_per_word' => isset($_REQUEST['bytes_per_word']) && isset($index_properties[$_REQUEST['bytes_per_word']]) ? (int) $_REQUEST['bytes_per_word'] : 2); $context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; $context['step'] = isset($_REQUEST['step']) ? (int) $_REQUEST['step'] : 0; // admin timeouts are painful when building these long indexes if ($_SESSION['admin_time'] + 3300 < time() && $context['step'] >= 1) { $_SESSION['admin_time'] = time(); } } if ($context['step'] !== 0) { checkSession('request'); } // Step 0: let the user determine how they like their index. if ($context['step'] === 0) { $context['sub_template'] = 'create_index'; } // Step 1: insert all the words. if ($context['step'] === 1) { $context['sub_template'] = 'create_index_progress'; if ($context['start'] === 0) { db_extend(); $tables = $smcFunc['db_list_tables'](false, $db_prefix . 'log_search_words'); if (!empty($tables)) { $smcFunc['db_search_query']('drop_words_table', ' DROP TABLE {db_prefix}log_search_words', array()); } $smcFunc['db_create_word_search']($index_properties[$context['index_settings']['bytes_per_word']]['column_definition']); // Temporarily switch back to not using a search index. if (!empty($modSettings['search_index']) && $modSettings['search_index'] == 'custom') { updateSettings(array('search_index' => '')); } // Don't let simultanious processes be updating the search index. if (!empty($modSettings['search_custom_index_config'])) { updateSettings(array('search_custom_index_config' => '')); } } $num_messages = array('done' => 0, 'todo' => 0); $request = $smcFunc['db_query']('', ' SELECT id_msg >= {int:starting_id} AS todo, COUNT(*) AS num_messages FROM {db_prefix}messages GROUP BY todo', array('starting_id' => $context['start'])); while ($row = $smcFunc['db_fetch_assoc']($request)) { $num_messages[empty($row['todo']) ? 'done' : 'todo'] = $row['num_messages']; } if (empty($num_messages['todo'])) { $context['step'] = 2; $context['percentage'] = 80; $context['start'] = 0; } else { // Number of seconds before the next step. $stop = time() + 3; while (time() < $stop) { $inserts = array(); $request = $smcFunc['db_query']('', ' SELECT id_msg, body FROM {db_prefix}messages WHERE id_msg BETWEEN {int:starting_id} AND {int:ending_id} LIMIT {int:limit}', array('starting_id' => $context['start'], 'ending_id' => $context['start'] + $messages_per_batch - 1, 'limit' => $messages_per_batch)); $forced_break = false; $number_processed = 0; while ($row = $smcFunc['db_fetch_assoc']($request)) { // In theory it's possible for one of these to take friggin ages so add more timeout protection. if ($stop < time()) { $forced_break = true; break; } $number_processed++; foreach (text2words($row['body'], $context['index_settings']['bytes_per_word'], true) as $id_word) { $inserts[] = array($id_word, $row['id_msg']); } } $num_messages['done'] += $number_processed; $num_messages['todo'] -= $number_processed; $smcFunc['db_free_result']($request); $context['start'] += $forced_break ? $number_processed : $messages_per_batch; if (!empty($inserts)) { $smcFunc['db_insert']('ignore', '{db_prefix}log_search_words', array('id_word' => 'int', 'id_msg' => 'int'), $inserts, array('id_word', 'id_msg')); } if ($num_messages['todo'] === 0) { $context['step'] = 2; $context['start'] = 0; break; } else { updateSettings(array('search_custom_index_resume' => serialize(array_merge($context['index_settings'], array('resume_at' => $context['start']))))); } } // Since there are still two steps to go, 80% is the maximum here. $context['percentage'] = round($num_messages['done'] / ($num_messages['done'] + $num_messages['todo']), 3) * 80; } } elseif ($context['step'] === 2) { if ($context['index_settings']['bytes_per_word'] < 4) { $context['step'] = 3; } else { $stop_words = $context['start'] === 0 || empty($modSettings['search_stopwords']) ? array() : explode(',', $modSettings['search_stopwords']); $stop = time() + 3; $context['sub_template'] = 'create_index_progress'; $max_messages = ceil(60 * $modSettings['totalMessages'] / 100); while (time() < $stop) { $request = $smcFunc['db_query']('', ' SELECT id_word, COUNT(id_word) AS num_words FROM {db_prefix}log_search_words WHERE id_word BETWEEN {int:starting_id} AND {int:ending_id} GROUP BY id_word HAVING COUNT(id_word) > {int:minimum_messages}', array('starting_id' => $context['start'], 'ending_id' => $context['start'] + $index_properties[$context['index_settings']['bytes_per_word']]['step_size'] - 1, 'minimum_messages' => $max_messages)); while ($row = $smcFunc['db_fetch_assoc']($request)) { $stop_words[] = $row['id_word']; } $smcFunc['db_free_result']($request); updateSettings(array('search_stopwords' => implode(',', $stop_words))); if (!empty($stop_words)) { $smcFunc['db_query']('', ' DELETE FROM {db_prefix}log_search_words WHERE id_word in ({array_int:stop_words})', array('stop_words' => $stop_words)); } $context['start'] += $index_properties[$context['index_settings']['bytes_per_word']]['step_size']; if ($context['start'] > $index_properties[$context['index_settings']['bytes_per_word']]['max_size']) { $context['step'] = 3; break; } } $context['percentage'] = 80 + round($context['start'] / $index_properties[$context['index_settings']['bytes_per_word']]['max_size'], 3) * 20; } } // Step 3: remove words not distinctive enough. if ($context['step'] === 3) { $context['sub_template'] = 'create_index_done'; updateSettings(array('search_index' => 'custom', 'search_custom_index_config' => serialize($context['index_settings']))); $smcFunc['db_query']('', ' DELETE FROM {db_prefix}settings WHERE variable = {string:search_custom_index_resume}', array('search_custom_index_resume' => 'search_custom_index_resume')); } }
/** * List all the scheduled task in place on the forum. * * @uses ManageScheduledTasks template, view_scheduled_tasks sub-template */ function ScheduledTasks() { global $context, $txt, $sourcedir, $smcFunc, $user_info, $modSettings, $scripturl; // Mama, setup the template first - cause it's like the most important bit, like pickle in a sandwich. // ... ironically I don't like pickle. </grudge> $context['sub_template'] = 'view_scheduled_tasks'; $context['page_title'] = $txt['maintain_tasks']; // Saving changes? if (isset($_REQUEST['save']) && isset($_POST['enable_task'])) { checkSession(); // We'll recalculate the dates at the end! require_once $sourcedir . '/ScheduledTasks.php'; // Enable and disable as required. $enablers = array(0); foreach ($_POST['enable_task'] as $id => $enabled) { if ($enabled) { $enablers[] = (int) $id; } } // Do the update! $smcFunc['db_query']('', ' UPDATE {db_prefix}scheduled_tasks SET disabled = CASE WHEN id_task IN ({array_int:id_task_enable}) THEN 0 ELSE 1 END', array('id_task_enable' => $enablers)); // Pop along... CalculateNextTrigger(); } // Want to run any of the tasks? if (isset($_REQUEST['run']) && isset($_POST['run_task'])) { // Lets figure out which ones they want to run. $tasks = array(); foreach ($_POST['run_task'] as $task => $dummy) { $tasks[] = (int) $task; } // Load up the tasks. $request = $smcFunc['db_query']('', ' SELECT id_task, task FROM {db_prefix}scheduled_tasks WHERE id_task IN ({array_int:tasks}) LIMIT ' . count($tasks), array('tasks' => $tasks)); // Lets get it on! require_once $sourcedir . '/ScheduledTasks.php'; ignore_user_abort(true); while ($row = $smcFunc['db_fetch_assoc']($request)) { $start_time = microtime(); // The functions got to exist for us to use it. if (!function_exists('scheduled_' . $row['task'])) { continue; } // Try to stop a timeout, this would be bad... @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Do the task... $completed = call_user_func('scheduled_' . $row['task']); // Log that we did it ;) if ($completed) { $total_time = round(array_sum(explode(' ', microtime())) - array_sum(explode(' ', $start_time)), 3); $smcFunc['db_insert']('', '{db_prefix}log_scheduled_tasks', array('id_task' => 'int', 'time_run' => 'int', 'time_taken' => 'float'), array($row['id_task'], time(), $total_time), array('id_task')); } } $smcFunc['db_free_result']($request); redirectexit('action=admin;area=scheduledtasks;done'); } $listOptions = array('id' => 'scheduled_tasks', 'title' => $txt['maintain_tasks'], 'base_href' => $scripturl . '?action=admin;area=scheduledtasks', 'get_items' => array('function' => 'list_getScheduledTasks'), 'columns' => array('name' => array('header' => array('value' => $txt['scheduled_tasks_name'], 'style' => 'width: 40%;'), 'data' => array('sprintf' => array('format' => ' <a href="' . $scripturl . '?action=admin;area=scheduledtasks;sa=taskedit;tid=%1$d">%2$s</a><br /><span class="smalltext">%3$s</span>', 'params' => array('id' => false, 'name' => false, 'desc' => false)))), 'next_due' => array('header' => array('value' => $txt['scheduled_tasks_next_time']), 'data' => array('db' => 'next_time', 'class' => 'smalltext')), 'regularity' => array('header' => array('value' => $txt['scheduled_tasks_regularity']), 'data' => array('db' => 'regularity', 'class' => 'smalltext')), 'enabled' => array('header' => array('value' => $txt['scheduled_tasks_enabled'], 'style' => 'width: 6%;'), 'data' => array('sprintf' => array('format' => '<input type="hidden" name="enable_task[%1$d]" id="task_%1$d" value="0" /><input type="checkbox" name="enable_task[%1$d]" id="task_check_%1$d" %2$s class="input_check" />', 'params' => array('id' => false, 'checked_state' => false)), 'style' => 'text-align: center;')), 'run_now' => array('header' => array('value' => $txt['scheduled_tasks_run_now'], 'style' => 'width: 12%;'), 'data' => array('sprintf' => array('format' => '<input type="checkbox" name="run_task[%1$d]" id="run_task_%1$d" class="input_check" />', 'params' => array('id' => false)), 'style' => 'text-align: center;'))), 'form' => array('href' => $scripturl . '?action=admin;area=scheduledtasks'), 'additional_rows' => array(array('position' => 'below_table_data', 'value' => ' <input type="submit" name="save" value="' . $txt['scheduled_tasks_save_changes'] . '" class="button_submit" /> <input type="submit" name="run" value="' . $txt['scheduled_tasks_run_now'] . '" class="button_submit" />', 'class' => 'floatright', 'style' => 'text-align: right;'), array('position' => 'after_title', 'value' => $txt['scheduled_tasks_time_offset'], 'class' => 'windowbg2'))); require_once $sourcedir . '/Subs-List.php'; createList($listOptions); $context['sub_template'] = 'view_scheduled_tasks'; $context['tasks_were_run'] = isset($_GET['done']); }
function shd_perma_delete() { global $smcFunc, $user_info, $context, $sourcedir; checkSession('get'); // This is heavy duty stuff. @set_time_limit(0); if (is_callable('apache_reset_timeout')) { apache_reset_timeout(); } // We have to have either a ticket or a reply to know what to delete (Or do you want me to drop your whole database? >:D) if (empty($context['ticket_id']) && empty($_REQUEST['reply'])) { fatal_lang_error('shd_no_ticket', false); } // If we're deleting a whole ticket... if (!empty($context['ticket_id']) && empty($_REQUEST['reply'])) { // Can we even see this ticket? $query_ticket = shd_db_query('', ' SELECT id_ticket, id_dept, subject, id_member_started, status FROM {db_prefix}helpdesk_tickets AS hdt WHERE {query_see_ticket} AND id_ticket = {int:ticket}', array('ticket' => $context['ticket_id'])); if ($smcFunc['db_num_rows']($query_ticket) == 0) { $smcFunc['db_free_result']($query_ticket); fatal_lang_error('shd_no_ticket', false); } else { $row = $smcFunc['db_fetch_assoc']($query_ticket); shd_is_allowed_to('shd_delete_recycling', $row['id_dept']); $smcFunc['db_free_result']($query_ticket); } if ($row['status'] != TICKET_STATUS_DELETED) { fatal_lang_error('shd_cannot_delete_ticket', false); } $subject = $row['subject']; // Expire the cache of count(active tickets) shd_clear_active_tickets($row['id_dept']); // The ticket ID is in $context['ticket_id']. Nothing else is needed, really. call_integration_hook('shd_hook_permadeleteticket'); // Start by getting all the messages in this ticket, we'll need those for custom fields values that need purging. $query = shd_db_query('', ' SELECT id_msg FROM {db_prefix}helpdesk_ticket_replies WHERE id_ticket = {int:current_ticket}', array('current_ticket' => $context['ticket_id'])); $msgs = array(); while ($row = $smcFunc['db_fetch_assoc']($query)) { $msgs[] = $row['id_msg']; } $smcFunc['db_free_result']($query); if (!empty($msgs)) { shd_db_query('', ' DELETE FROM {db_prefix}helpdesk_custom_fields_values WHERE post_type = {int:type_reply} AND id_post IN ({array_int:msgs})', array('type_reply' => CFIELD_REPLY, 'msgs' => $msgs)); } shd_db_query('', ' DELETE FROM {db_prefix}helpdesk_custom_fields_values WHERE post_type = {int:type_ticket} AND id_post = {int:ticket}', array('type_ticket' => CFIELD_TICKET, 'ticket' => $context['ticket_id'])); // Now deleting the actual ticket. shd_db_query('', ' DELETE FROM {db_prefix}helpdesk_tickets WHERE id_ticket = {int:current_ticket}', array('current_ticket' => $context['ticket_id'])); // Then remove any replies associated with it. shd_db_query('', ' DELETE FROM {db_prefix}helpdesk_ticket_replies WHERE id_ticket = {int:current_ticket}', array('current_ticket' => $context['ticket_id'])); // And search entries. shd_db_query('', ' DELETE FROM {db_prefix}helpdesk_search_ticket_words WHERE id_msg = ({array_int:msgs})', array('msgs' => $msgs)); shd_db_query('', ' DELETE FROM {db_prefix}helpdesk_search_subject_words WHERE id_ticket = {int:ticket}', array('current_ticket' => $context['ticket_id'])); // And attachments... work out which attachments that is $query = shd_db_query('', ' SELECT id_attach FROM {db_prefix}helpdesk_attachments WHERE id_ticket = {int:ticket}', array('ticket' => $context['ticket_id'])); $attachIDs = array(); while ($row = $smcFunc['db_fetch_row']($query)) { $attachIDs[] = $row[0]; } $smcFunc['db_free_result']($query); if (!empty($attachIDs)) { // OK, so we have some ids require_once $sourcedir . '/ManageAttachments.php'; $attachmentQuery = array('attachment_type' => 0, 'id_msg' => 0, 'id_attach' => $attachIDs); removeAttachments($attachmentQuery); } shd_log_action('permadelete', array('ticket' => $context['ticket_id'], 'subject' => $subject)); redirectexit('action=helpdesk;sa=recyclebin'); } elseif (!empty($_REQUEST['reply'])) { // Check we can actually see the ticket we're deleting, that this reply is in this ticket and that we can delete this reply $query_ticket = shd_db_query('', ' SELECT hdt.id_ticket, hdt.id_dept, hdtr.id_member, hdt.subject, hdt.id_member_started, hdt.status, hdtr.message_status FROM {db_prefix}helpdesk_tickets AS hdt INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdt.id_ticket = hdtr.id_ticket) WHERE {query_see_ticket} AND hdt.id_ticket = {int:ticket} AND hdtr.id_msg = {int:reply} AND hdt.id_first_msg != {int:reply2}', array('ticket' => $context['ticket_id'], 'reply' => $_REQUEST['reply'], 'reply2' => $_REQUEST['reply'])); if ($smcFunc['db_num_rows']($query_ticket) == 0) { $smcFunc['db_free_result']($query_ticket); fatal_lang_error('shd_no_ticket', false); } else { $row = $smcFunc['db_fetch_assoc']($query_ticket); shd_is_allowed_to('shd_delete_recycling', $row['id_dept']); $smcFunc['db_free_result']($query_ticket); } if ($row['status'] == TICKET_STATUS_DELETED || $row['message_status'] != MSG_STATUS_DELETED) { fatal_lang_error('shd_cannot_delete_reply', false); } $subject = $row['subject']; // Expire the cache of count(active tickets) shd_clear_active_tickets($row['id_dept']); // The message ID is in $_REQUEST['reply']. Nothing else is needed, really. call_integration_hook('shd_hook_permadeletereply'); // Just remove the reply. shd_db_query('', ' DELETE FROM {db_prefix}helpdesk_ticket_replies WHERE id_msg = {int:current_reply}', array('current_reply' => (int) $_REQUEST['reply'])); // Custom fields shd_db_query('', ' DELETE FROM {db_prefix}helpdesk_custom_fields_values WHERE id_post = {int:reply} AND post_type = {int:type_reply}', array('reply' => (int) $_REQUEST['reply'], 'type_reply' => CFIELD_REPLY)); // And search entries. shd_db_query('', ' DELETE FROM {db_prefix}helpdesk_search_ticket_words WHERE id_msg = {int:reply}', array('reply' => (int) $_REQUEST['reply'])); // Now to handle attachments $query = shd_db_query('', ' SELECT id_attach FROM {db_prefix}helpdesk_attachments WHERE id_msg = {int:msg}', array('msg' => (int) $_REQUEST['reply'])); $attachIDs = array(); while ($row = $smcFunc['db_fetch_row']($query)) { $attachIDs[] = $row[0]; } $smcFunc['db_free_result']($query); if (!empty($attachIDs)) { // OK, so we have some ids require_once $sourcedir . '/ManageAttachments.php'; $attachmentQuery = array('attachment_type' => 0, 'id_msg' => 0, 'id_attach' => $attachIDs); removeAttachments($attachmentQuery); } shd_log_action('permadelete_reply', array('ticket' => $context['ticket_id'], 'subject' => $subject)); list($starter, $replier, $num_replies) = shd_recalc_ids($context['ticket_id']); $query_reply = shd_db_query('', ' UPDATE {db_prefix}helpdesk_tickets SET status = {int:status} WHERE id_ticket = {int:ticket}', array('ticket' => $context['ticket_id'], 'status' => shd_determine_status('deletereply', $starter, $replier, $num_replies, $row['id_dept']))); redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']); } else { fatal_lang_error('shd_no_ticket'); } }