Example #1
0
/**
 * 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;
}
Example #2
0
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;
}
Example #3
0
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('&quot;' => '')));
                $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' => "&lt;blockquote&gt;", '~</blockquote>~i' => "&lt;/blockquote&gt;", '~<ins(\\s(.)*?)*?' . '>~i' => "&lt;ins&gt;", '~</ins>~i' => "&lt;/ins&gt;");
    $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('~&#38;~i', '&#38;#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();
}
Example #5
0
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);
}
Example #6
0
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']);
}
Example #7
0
/**
 * 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();
}
Example #8
0
/**
 * 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);
}
Example #9
0
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;
}
Example #10
0
/**
 * 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);
}
Example #11
0
/**
 * 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']) ? '&amp;substep=' . $_GET['substep'] : '', isset($_GET['cstep']) ? '&amp;cstep=' . $_GET['cstep'] : '', '&amp;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 . '%;">&nbsp;</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']);
 }
Example #15
0
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();
    }
}
Example #17
0
/**
 * 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();
}
Example #18
0
/**
 * 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;
}
Example #19
0
/**
 * 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();
}
Example #20
0
/**
 * 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');
     }
 }
Example #22
0
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();
}
Example #23
0
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;
}
Example #24
0
/**
 * 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);
}
Example #25
0
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;
}
Example #26
0
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();
}
Example #27
0
/**
* 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();
}
Example #28
0
/**
 * 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']);
}
Example #30
0
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');
    }
}