Beispiel #1
0
function xthreads_uninstall()
{
    global $db, $cache, $mybb, $plugins;
    if ($mybb->input['no']) {
        admin_redirect(xthreads_admin_url('config', 'plugins'));
        exit;
    }
    if (!$mybb->input['confirm_uninstall']) {
        $link = 'index.php?confirm_uninstall=1&' . htmlspecialchars($_SERVER['QUERY_STRING']);
        $GLOBALS['page']->output_confirm_action($link, $GLOBALS['lang']->xthreads_confirm_uninstall);
        exit;
    } else {
        unset($mybb->input['confirm_uninstall']);
    }
    $plugins->run_hooks('xthreads_uninstall_start');
    $query = $db->simple_select('adminoptions', 'uid,permissions');
    while ($adminopt = $db->fetch_array($query)) {
        $perms = @unserialize($adminopt['permissions']);
        if (empty($perms)) {
            continue;
        }
        // inherited or just messed up
        unset($perms['config']['threadfields']);
        $db->update_query('adminoptions', array('permissions' => $db->escape_string(serialize($perms))), 'uid=' . $adminopt['uid']);
    }
    $db->free_result($query);
    if ($db->table_exists('threadfields_data')) {
        $db->write_query('DROP TABLE ' . $db->table_prefix . 'threadfields_data');
    }
    if ($db->table_exists('threadfields')) {
        $db->write_query('DROP TABLE ' . $db->table_prefix . 'threadfields');
    }
    if ($db->table_exists('xtattachments')) {
        // remove attachments first
        require_once MYBB_ROOT . 'inc/xthreads/xt_updatehooks.php';
        $query = $db->simple_select('xtattachments', 'aid,indir,attachname');
        while ($xta = $db->fetch_array($query)) {
            xthreads_rm_attach_fs($xta);
        }
        $db->free_result($query);
        $db->write_query('DROP TABLE ' . $db->table_prefix . 'xtattachments');
    }
    // remove any indexes added on the threads table
    foreach (array('uid', 'lastposteruid', 'prefix', 'icon') as $afe) {
        if ($afe == 'uid') {
            continue;
        }
        // we won't remove this from the above array
        $db->write_query('ALTER TABLE `' . $db->table_prefix . 'threads` DROP KEY `xthreads_' . $afe . '`', true);
    }
    $fields = array('xthreads_grouping', 'xthreads_firstpostattop', 'xthreads_inlinesearch', 'xthreads_tplprefix', 'xthreads_langprefix', 'xthreads_allow_blankmsg', 'xthreads_nostatcount', 'xthreads_fdcolspan_offset', 'xthreads_settingoverrides', 'xthreads_postsperpage', 'xthreads_hideforum', 'xthreads_hidebreadcrumb', 'xthreads_defaultfilter', 'xthreads_addfiltenable', 'xthreads_wol_announcements', 'xthreads_wol_forumdisplay', 'xthreads_wol_newthread', 'xthreads_wol_attachment', 'xthreads_wol_newreply', 'xthreads_wol_showthread');
    foreach ($fields as $k => &$f) {
        if (!$db->field_exists($f, 'forums')) {
            unset($fields[$k]);
        }
    }
    if (!empty($fields)) {
        switch ($db->type) {
            case 'sqlite3':
            case 'sqlite2':
            case 'sqlite':
                $db->alter_table_parse($db->table_prefix . 'forums', 'DROP ' . implode(', DROP COLUMN ', $fields) . '');
                break;
            case 'pgsql':
                foreach ($fields as &$f) {
                    $db->write_query('ALTER TABLE ' . $db->table_prefix . 'forums
						DROP COLUMN ' . $f);
                }
                break;
            default:
                $db->write_query('ALTER TABLE ' . $db->table_prefix . 'forums
					DROP COLUMN ' . implode(', DROP COLUMN ', $fields));
        }
    }
    // remove any custom default sorts and reduce size of sorting column back to original
    $db->update_query('forums', array('defaultsortby' => ''), 'defaultsortby LIKE "tf_%" OR defaultsortby LIKE "tfa_%"');
    $db->write_query('ALTER TABLE `' . $db->table_prefix . 'forums` MODIFY `defaultsortby` varchar(10) NOT NULL default \'\'');
    $cache->update_forums();
    xthreads_delete_datacache('threadfields');
    @unlink(MYBB_ROOT . 'cache/xthreads.php');
    @unlink(MYBB_ROOT . 'cache/xthreads_evalcache.php');
    $db->delete_query('templates', 'title IN ("' . implode('","', array_keys(xthreads_new_templates())) . '") AND sid=-2');
    // revert QuickThread modification
    if (function_exists('quickthread_uninstall')) {
        $tpl = $db->fetch_array($db->simple_select('templates', 'tid,template', 'title="forumdisplay_quick_thread" AND sid=-1', array('limit' => 1)));
        if ($tpl && strpos($tpl['template'], '{$GLOBALS[\'extra_threadfields\']}') !== false) {
            $newtpl = preg_replace('~\\{\\$GLOBALS\\[\'extra_threadfields\'\\]\\}' . "\r?(\n\t{0,3})?" . '~is', '', $tpl['template'], 1);
            if ($newtpl != $tpl['template']) {
                $db->update_query('templates', array('template' => $db->escape_string($newtpl)), 'tid=' . $tpl['tid']);
            }
        }
    }
    // try to determine and remove stuff added to the custom moderation table
    $query = $db->simple_select('modtools', 'tid,threadoptions');
    while ($tool = $db->fetch_array($query)) {
        $opts = unserialize($tool['threadoptions']);
        if (isset($opts['edit_threadfields'])) {
            unset($opts['edit_threadfields']);
            $db->update_query('modtools', array('threadoptions' => $db->escape_string(serialize($opts))), 'tid=' . $tool['tid']);
        }
    }
    $plugins->run_hooks('xthreads_uninstall_end');
}
Beispiel #2
0
function xthreads_upload_attachments()
{
    global $xta_cache, $threadfield_cache, $mybb, $db, $lang, $fid;
    // only ever execute this function once per page
    static $done = false;
    if ($done) {
        return;
    }
    $done = true;
    if (!$fid) {
        if ($GLOBALS['forum']['fid']) {
            $fid = $GLOBALS['forum']['fid'];
        } elseif ($GLOBALS['foruminfo']['fid']) {
            $fid = $GLOBALS['foruminfo']['fid'];
        } elseif ($mybb->input['pid']) {
            // editpost - not good to trust user input, but should be fine
            $post = get_post((int) $mybb->input['pid']);
            if ($post['pid']) {
                $fid = $post['fid'];
            }
        } elseif ($mybb->input['fid']) {
            // newthread
            $fid = (int) $mybb->input['fid'];
        }
        // we _should_ now have an fid
    }
    if (!isset($threadfield_cache)) {
        $threadfield_cache = xthreads_gettfcache($fid);
    }
    // remove uneditable fields
    xthreads_filter_tfeditable($threadfield_cache, $fid);
    // NOTE: modifies the global tfcache!
    if (empty($threadfield_cache)) {
        return;
    }
    if (!is_array($xta_cache)) {
        $xta_cache = array();
    }
    // first, run through to see if we have already uploaded some attachments
    // this code totally relies on the posthash being unique...
    if ($GLOBALS['thread']['tid']) {
        $attachwhere = 'tid=' . (int) $GLOBALS['thread']['tid'];
    } else {
        $attachwhere = 'posthash="' . $db->escape_string($mybb->input['posthash']) . '"';
    }
    $query = $db->simple_select('xtattachments', '*', $attachwhere);
    $attach_fields = array();
    while ($attach = $db->fetch_array($query)) {
        $xta_cache[$attach['aid']] = $attach;
        $attach_fields[$attach['field']][] = $attach['aid'];
    }
    $db->free_result($query);
    @ignore_user_abort(true);
    $errors = array();
    $xta_remove = $threadfield_updates = array();
    foreach ($threadfield_cache as $k => &$v) {
        if ($v['inputtype'] != XTHREADS_INPUT_FILE && $v['inputtype'] != XTHREADS_INPUT_FILE_URL) {
            continue;
        }
        $aid =& $mybb->input['xthreads_' . $k];
        if ($v['inputtype'] != XTHREADS_INPUT_FILE_URL || is_numeric($mybb->input['xthreads_' . $k])) {
            $singleval = xthreads_empty($v['multival']);
            // now, we're ignoring what the user sends us, totally...
            if ($attach_fields[$k]) {
                if ($singleval) {
                    $aid = (int) reset($attach_fields[$k]);
                } else {
                    $aid = array_unique(array_map('intval', $attach_fields[$k]));
                    // re-ordering support
                    if (is_array($mybb->input['xtaorder'])) {
                        $aid_order = array_unique(array_map('intval', $mybb->input['xtaorder']));
                        if (count($aid) == count($aid_order) && $aid != $aid_order && !count(array_diff($aid, $aid_order))) {
                            $aid = $aid_order;
                            $threadfield_updates[$k] = implode(',', $aid);
                        }
                    }
                    $aid = array_combine($aid, $aid);
                }
            } else {
                $aid = 0;
            }
        }
        // handle file upload
        $ul = null;
        if ($singleval) {
            if (!empty($_FILES['xthreads_' . $k]) && !xthreads_empty($_FILES['xthreads_' . $k]['name']) && is_string($_FILES['xthreads_' . $k]['name'])) {
                $ul = $_FILES['xthreads_' . $k];
            } elseif ($v['inputtype'] == XTHREADS_INPUT_FILE && XTHREADS_ALLOW_URL_FETCH && !xthreads_empty($mybb->input['xtaurl_' . $k])) {
                // the preg_match is just a basic prelim check - the real URL checking is done later; we need this prelim check to stop it erroring out on the defalt "http://" string
                if (preg_match('~^[a-z0-9\\-]+\\://[^/?#]+(?:/.*)?$~', $mybb->input['xtaurl_' . $k])) {
                    $ul = $mybb->input['xtaurl_' . $k];
                }
            }
            !isset($ul) or $ul = array($ul);
        } else {
            $ul = array();
            if (is_array($mybb->input['xtaurl_' . $k])) {
                $input_urls = $mybb->input['xtaurl_' . $k];
                $input_key_match = true;
                // if URL input is an array, we'll match with equivalent file input keys
            } else {
                $input_urls = explode("\n", str_replace("\r", '', $mybb->input['xtaurl_' . $k]));
                $input_key_match = false;
            }
            if (!empty($_FILES['xthreads_' . $k]) && is_array($_FILES['xthreads_' . $k])) {
                foreach ($_FILES['xthreads_' . $k]['name'] as $file_k => $filename) {
                    if (!xthreads_empty($filename)) {
                        $file_v = array();
                        // why does PHP does this and make our life difficult?
                        foreach ($_FILES['xthreads_' . $k] as $fvkey => $fvval) {
                            $file_v[$fvkey] = $fvval[$file_k];
                        }
                        if ($input_key_match && is_numeric($file_k) || preg_match('~^aid\\d+$~', $file_k)) {
                            $ul[$file_k] = $file_v;
                        } else {
                            $ul[] = $file_v;
                        }
                    }
                }
            }
            if ($v['inputtype'] == XTHREADS_INPUT_FILE && XTHREADS_ALLOW_URL_FETCH && !empty($input_urls) && is_array($input_urls)) {
                foreach ($input_urls as $url_k => $url_v) {
                    $url_v = trim($url_v);
                    if (preg_match('~^[a-z0-9\\-]+\\://[a-z0-9_\\-@:.]+(?:/.*)?$~', $url_v)) {
                        if ($input_key_match && is_numeric($url_k) || preg_match('~^aid\\d+$~', $url_k)) {
                            isset($ul[$url_k]) or $ul[$url_k] = $url_v;
                        } else {
                            $ul[] = $url_v;
                        }
                    }
                }
            }
        }
        unset($mybb->input['xtaurl_' . $k], $_FILES['xthreads_' . $k]);
        // remove files from list first (so we can properly measure the correct final number of attachments when uploading)
        // fix the threadfield_updates array later on
        if ($singleval) {
            if (empty($ul) && $mybb->input['xtarm_' . $k] && $v['editable'] != XTHREADS_EDITABLE_REQ) {
                // user wants to remove attachment
                $xta_remove[$aid] = $aid;
                $aid = 0;
            }
        } elseif (!empty($mybb->input['xtarm_' . $k]) && is_array($mybb->input['xtarm_' . $k])) {
            foreach ($mybb->input['xtarm_' . $k] as $rm_aid => $rm_confirm) {
                if (!$rm_confirm) {
                    continue;
                }
                // double-check they really do want to remove this
                $xta_remove[$rm_aid] = $rm_aid;
                unset($aid[$rm_aid]);
            }
        }
        // upload new stuff
        if (!empty($ul)) {
            require_once MYBB_ROOT . 'inc/xthreads/xt_upload.php';
            $update_aid = is_array($aid) ? 0 : $aid;
            $failed_urls = array();
            // list of any URLs that failed to fetch
            foreach ($ul as $ul_key => $ul_file) {
                // hard limit number of files to at least 20
                if (!$singleval && is_array($aid)) {
                    // hard limit
                    if (strlen(implode(',', $aid)) >= 245) {
                        if (!$lang->xthreads_xtaerr_error_attachhardlimit) {
                            $lang->load('xthreads');
                        }
                        $errors[] = $lang->sprintf($lang->xthreads_xtaerr_error_attachhardlimit, htmlspecialchars_uni($v['title']));
                        break;
                    }
                    // admin defined limit
                    if ($v['multival_limit'] && count($aid) >= $v['multival_limit']) {
                        if (!$lang->xthreads_xtaerr_error_attachnumlimit) {
                            $lang->load('xthreads');
                        }
                        $errors[] = $lang->sprintf($lang->xthreads_xtaerr_error_attachnumlimit, $v['multival_limit'], htmlspecialchars_uni($v['title']));
                        break;
                    }
                }
                // allow updating a specific attachment in a multi-field thing
                $update_aid2 = $update_aid;
                if (!$update_aid2 && is_array($aid) && substr($ul_key, 0, 3) == 'aid') {
                    $update_aid2 = (int) substr($ul_key, 3);
                    if (!in_array($update_aid2, $aid)) {
                        $update_aid2 = 0;
                    }
                }
                $attachedfile = upload_xtattachment($ul_file, $v, $mybb->user['uid'], $update_aid2, $GLOBALS['thread']['tid']);
                if ($attachedfile['error']) {
                    if (!$lang->xthreads_threadfield_attacherror) {
                        $lang->load('xthreads');
                    }
                    $errors[] = $lang->sprintf($lang->xthreads_threadfield_attacherror, htmlspecialchars_uni($v['title']), $attachedfile['error']);
                    if (is_string($ul_file)) {
                        $failed_urls[] = $ul_file;
                    }
                } else {
                    //unset($attachedfile['posthash'], $attachedfile['tid'], $attachedfile['downloads']);
                    $xta_cache[$attachedfile['aid']] = $attachedfile;
                    if ($singleval) {
                        unset($mybb->input['xtarm_' . $k]);
                        // since successful upload, don't tick remove box
                        $aid = $attachedfile['aid'];
                    } else {
                        if (is_array($mybb->input['xtarm_' . $k])) {
                            unset($mybb->input['xtarm_' . $k][$attachedfile['aid']]);
                        }
                        is_array($aid) or $aid = array();
                        // if no aid already set, it will be 0, so turn into array if necessary
                        $aid[$attachedfile['aid']] = $attachedfile['aid'];
                    }
                    // if we were going to remove this file, don't
                    if (isset($xta_remove[$attachedfile['aid']])) {
                        unset($xta_remove[$attachedfile['aid']]);
                    }
                    if ($attachedfile['aid'] != $update_aid2) {
                        // adding a new attachment
                        $threadfield_updates[$k] = $singleval ? $aid : true;
                    }
                }
            }
            // list failed URLs in textboxes
            if (!empty($failed_urls)) {
                $mybb->input['xtaurl_' . $k] = implode("\n", $failed_urls);
                unset($failed_urls);
            }
        }
        // fix threadfield update if removing an item and not already done
        if (!empty($xta_remove) && !isset($threadfield_updates[$k])) {
            $threadfield_updates[$k] = $singleval ? 0 : true;
        }
        // fix placeholder value
        if ($threadfield_updates[$k] === true) {
            $threadfield_updates[$k] = implode(',', $aid);
        }
        unset($aid);
    }
    if (!empty($xta_remove)) {
        $db->delete_query('xtattachments', 'aid IN (' . implode(',', $xta_remove) . ')');
        foreach ($xta_remove as $aid) {
            xthreads_rm_attach_fs($xta_cache[$aid]);
        }
    }
    $is_editing = $GLOBALS['current_page'] == 'editpost.php';
    // if editing post, also commit change to thread field immediately (waiting for user to submit is unreliable)
    if (($is_editing || $GLOBALS['thread']['tid'] && $GLOBALS['current_page'] == 'newthread.php') && !empty($threadfield_updates)) {
        xthreads_db_update_replace('threadfields_data', $threadfield_updates, 'tid', $GLOBALS['thread']['tid']);
    }
    @ignore_user_abort(false);
    if (!empty($errors)) {
        global $plugins;
        $is_mybb_18 = $mybb->version_code >= 1700;
        // MyBB 1.4 - 1.5
        // and MyBB 1.6 is inconsistent (does different things on newthread/editpost)...
        if ($mybb->version_code < 1600 || $is_editing) {
            // can't find a better way to check other than to check version numbers
            global $theme, $templates;
            $errstr = '<li>' . implode('</li><li>', $errors) . '</li>';
            if ($is_editing && $is_mybb_18) {
                // special workaround for MyBB 1.8
                $GLOBALS['xt_attach_errors'] =& $errstr;
                $templates->cache['__xt_orig_error_attacherror'] = $templates->cache['error_attacherror'];
                function xthreads_upload_attachments_error()
                {
                    global $theme, $templates, $attacherror, $mybb, $lang, $fid;
                    if ($attacherror) {
                        return;
                    }
                    // already handled by template cache hack
                    $attachedfile = array('error' => '<ul>' . $GLOBALS['xt_attach_errors'] . '</ul>');
                    eval('$attacherror = "' . $templates->get('__xt_orig_error_attacherror') . '";');
                }
                $plugins->add_hook('editpost_action_start', 'xthreads_upload_attachments_error');
            } else {
                $attachedfile = array('error' => '<ul>' . $errstr . '</ul>');
                eval('$GLOBALS[\'attacherror\'] .= "' . $templates->get('error_attacherror') . '";');
            }
            // if there's going to be a MyBB attachment error, and it's not been evaluated yet, shove it in the template to force it through - safe since this function is guaranteed to run only once
            $templates->cache['error_attacherror'] = str_replace('{$attachedfile[\'error\']}', '<ul>' . strtr($errstr, array('\\' => '\\\\', '$' => '\\$', '{' => '\\{', '}' => '\\}')) . '<li>{$attachedfile[\'error\']}</li></ul>', $templates->cache['error_attacherror']);
        } elseif ($is_mybb_18) {
            // for MyBB 1.8
            $GLOBALS['xt_attach_errors'] =& $errors;
            function xthreads_upload_attachments_error()
            {
                if (empty($GLOBALS['errors'])) {
                    $GLOBALS['errors'] =& $GLOBALS['xt_attach_errors'];
                } else {
                    $GLOBALS['errors'] = array_merge($GLOBALS['errors'], $GLOBALS['xt_attach_errors']);
                }
            }
            $plugins->add_hook('newthread_start', 'xthreads_upload_attachments_error');
        } else {
            // for MyBB 1.6
            if (empty($GLOBALS['errors'])) {
                $GLOBALS['errors'] =& $errors;
            } else {
                $GLOBALS['errors'] = array_merge($GLOBALS['errors'], $errors);
            }
        }
        $mybb->input['action'] = $is_editing ? 'editpost' : 'newthread';
        // block the preview, since a failed upload can stuff it up
        // lower priority to go before inputdisp function (contention, the function checks for 'previewpost')
        $plugins->add_hook('newthread_start', 'xthreads_newthread_ulattach_blockpreview', 5);
        $plugins->add_hook('editpost_start', 'xthreads_editthread_ulattach_blockpreview', 5);
    }
}
Beispiel #3
0
function do_upload_xtattachment($attachment, &$tf, $update_attachment = 0, $tid = 0, $timestamp = TIME_NOW)
{
    global $db, $mybb, $lang;
    $posthash = $db->escape_string($mybb->input['posthash']);
    $tid = (int) $tid;
    // may be possible for this to be null, if so, change to 0
    $path = $mybb->settings['uploadspath'] . '/xthreads_ul/';
    if (!$lang->xthreads_threadfield_attacherror) {
        $lang->load('xthreads');
    }
    if (is_array($attachment)) {
        if (isset($attachment['error']) && $attachment['error']) {
            if ($attachment['error'] == 2) {
                return array('error' => $lang->sprintf($lang->xthreads_xtaerr_error_attachsize, get_friendly_size($tf['filemaxsize'])));
            } elseif ($attachment['error'] >= 1 && $attachment['error'] <= 7) {
                $langvar = 'error_uploadfailed_php' . $attachment['error'];
                $langstr = $lang->{$langvar};
            } else {
                $langstr = $lang->sprintf($lang->error_uploadfailed_phpx, $attachment['error']);
            }
            return array('error' => $lang->error_uploadfailed . $lang->error_uploadfailed_detail . $langstr);
        }
        if (!is_uploaded_file($attachment['tmp_name']) || empty($attachment['tmp_name'])) {
            return array('error' => $lang->error_uploadfailed . $lang->error_uploadfailed_php4);
        }
        $file_size = $attachment['size'];
        // @filesize($attachment['tmp_name'])
        $attachment['name'] = strtr($attachment['name'], array('/' => '', "" => ''));
        if ($error = xthreads_validate_attachment($attachment, $tf)) {
            @unlink($attachment['tmp_name']);
            return array('error' => $error);
        }
        $movefunc = 'move_uploaded_file';
    } elseif ($mybb->usergroup['cancp'] == 1 && substr($attachment, 0, 7) == 'file://') {
        // admin file move
        $filename = strtr(substr($attachment, 7), array('/' => '', DIRECTORY_SEPARATOR => '', "" => ''));
        $file = $path . 'admindrop/' . $filename;
        if (xthreads_empty($filename) || !file_exists($file)) {
            return array('error' => $lang->sprintf($lang->xthreads_xtaerr_admindrop_not_found, htmlspecialchars_uni($filename), htmlspecialchars_uni($file)));
        }
        if (!is_writable($file)) {
            return array('error' => $lang->sprintf($lang->xthreads_xtaerr_admindrop_file_unwritable, htmlspecialchars_uni($filename)));
        }
        if (strtolower($file) == 'index.html') {
            return array('error' => $lang->xthreads_xtaerr_admindrop_index_error);
        }
        $attachment = array('name' => $filename, 'tmp_name' => $file, 'size' => @filesize($file));
        unset($file, $filename);
        if ($error = xthreads_validate_attachment($attachment, $tf)) {
            return array('error' => $error);
        }
        $file_size = $attachment['size'];
        $movefunc = 'rename';
    } else {
        // fetch URL
        if (!empty($tf['filemagic'])) {
            $magic =& $tf['filemagic'];
        } else {
            $magic = array();
        }
        $attachment = xthreads_fetch_url($attachment, $tf['filemaxsize'], $tf['fileexts'], $magic);
        db_ping($db);
        if ($attachment['error']) {
            return array('error' => $attachment['error']);
        }
        $file_size = $attachment['size'];
        if (xthreads_empty($attachment['name']) || $file_size < 1) {
            return array('error' => $lang->error_uploadfailed);
        }
        $attachment['name'] = strtr($attachment['name'], array('/' => '', "" => ''));
        $movefunc = 'rename';
    }
    if ($tf['fileimage']) {
        $img_dimensions = @getimagesize($attachment['tmp_name']);
        if (empty($img_dimensions) || !in_array($img_dimensions[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG))) {
            @unlink($attachment['tmp_name']);
            return array('error' => $lang->error_attachtype);
        }
        if (preg_match('~^([0-9]+)x([0-9]+)(\\|([0-9]+)x([0-9]+))?$~', $tf['fileimage'], $match)) {
            // check if image exceeds max/min dimensions
            if ($img_dimensions[0] < $match[1] || $img_dimensions[1] < $match[2] || $match[3] && ($img_dimensions[0] > $match[4] || $img_dimensions[1] > $match[5])) {
                @unlink($attachment['tmp_name']);
                return array('error' => $lang->sprintf($lang->xthreads_xtaerr_error_imgdims, $img_dimensions[0], $img_dimensions[1]));
            }
        }
        /*
        // convert WBMP -> PNG (saves space, bandwidth and works with MyBB's thumbnail generator)
        // unfortunately, although this is nice, we have a problem of filetype checking etc...
        if($img_dimensions[2] == IMAGETYPE_WBMP) {
        	if(function_exists('imagecreatefromwbmp') && $img = @imagecreatefromwbmp($attachment['tmp_name'])) {
        		@unlink($attachment['tmp_name']);
        		@imagepng($img, $attachment['tmp_name'], 6); // use zlib's recommended compression level
        		imgdestroy($img);
        		unset($img);
        		// double check that we have a file
        		if(!file_exists($attachment['tmp_name']))
        			return array('error' => $lang->error_attachtype); // get user to upload a non-WBMP file, lol
        		// change extension + update filesize, do MIME as well
        		if(strtolower(substr($attachment['name'], -5)) == '.wbmp')
        			$attachment['name'] = substr($attachment['name'], 0, -5).'.png';
        		$file_size = @filesize($attachment['tmp_name']);
        		if(strtolower($attachment['type']) == 'image/wbmp')
        			$attachment['type'] = 'image/png';
        		// update type too
        		$img_dimensions[2] = IMAGETYPE_PNG;
        	}
        	else {
        		// can't do much, error out
        		@unlink($attachment['tmp_name']);
        		return array('error' => $lang->error_attachtype);
        	}
        }
        */
        // we won't actually bother checking MIME types - not a big issue anyway
    }
    if (!XTHREADS_UPLOAD_LARGEFILE_SIZE || $file_size < XTHREADS_UPLOAD_LARGEFILE_SIZE) {
        @set_time_limit(30);
        // as md5_file may take a while
        $md5_start = time();
        $file_md5 = @md5_file($attachment['tmp_name'], true);
        if (strlen($file_md5) == 32) {
            // perhaps not PHP5
            $file_md5 = pack('H*', $file_md5);
        }
        if (time() - $md5_start > 2) {
            // ping DB if process took longer than 2 secs
            db_ping($db);
        }
        unset($md5_start);
    }
    if ($update_attachment) {
        $prevattach = $db->fetch_array($db->simple_select('xtattachments', 'aid,attachname,indir,md5hash', 'aid=' . (int) $update_attachment));
        if (!$prevattach['aid']) {
            $update_attachment = false;
        }
    }
    /* else {
    		// Check if attachment already uploaded
    		// TODO: this is actually a little problematic - perhaps verify that this is attached to this field (or maybe rely on checks in xt_updatehooks file)
    		if(isset($file_md5))
    			$md5check = ' OR md5hash='.xthreads_db_escape_binary($file_md5);
    		else
    			$md5check = '';
    		$prevattach = $db->fetch_array($db->simple_select('xtattachments', 'aid', 'filename="'.$db->escape_string($attachment['name']).'" AND (md5hash IS NULL'.$md5check.') AND filesize='.$file_size.' AND (posthash="'.$posthash.'" OR (tid='.$tid.' AND tid!=0))'));
    		if($prevattach['aid']) {
    			@unlink($attachment['tmp_name']);
    			// TODO: maybe return aid instead?
    			return array('error' => $lang->error_alreadyuploaded);
    		}
    	} */
    // We won't use MyBB's nice monthly directories, instead, we'll use a more confusing system based on the timestamps
    // note, one month = 2592000 seconds, so if we split up by 1mil, it'll be approx 11.5 days
    // If safe_mode is enabled, don't attempt to use the monthly directories as it won't work
    if (ini_get('safe_mode') == 1 || strtolower(ini_get('safe_mode')) == 'on') {
        $month_dir = '';
    } else {
        $month_dir = 'ts_' . floor(TIME_NOW / 1000000) . '/';
        if (!@is_dir($path . $month_dir)) {
            @mkdir($path . $month_dir);
            // Still doesn't exist - oh well, throw it in the main directory
            if (@is_dir($path . $month_dir)) {
                // write index file
                if ($index = fopen($path . $month_dir . 'index.html', 'w')) {
                    fwrite($index, '<html><body></body></html>');
                    fclose($index);
                    @my_chmod($path . $month_dir . 'index.html', 0644);
                }
                @my_chmod($path . $month_dir, 0755);
            } else {
                $month_dir = '';
            }
        }
    }
    // All seems to be good, lets move the attachment!
    $basename = substr(md5(uniqid(mt_rand(), true) . substr($mybb->post_code, 16)), 12, 8) . '_' . preg_replace('~[^a-zA-Z0-9_\\-%]~', '', str_replace(array(' ', '.', '+'), '_', $attachment['name'])) . '.upload';
    $filename = 'file_' . ($prevattach['aid'] ? $prevattach['aid'] : 't' . TIME_NOW) . '_' . $basename;
    @ignore_user_abort(true);
    // don't let the user break this integrity between file system and DB
    if (isset($GLOBALS['xtfurl_tmpfiles'])) {
        // if using url fetch, remove this from list of temp files
        unset($GLOBALS['xtfurl_tmpfiles'][$attachment['tmp_name']]);
    }
    while (!@$movefunc($attachment['tmp_name'], $path . $month_dir . $filename)) {
        if ($month_dir) {
            // try doing it again without the month_dir
            $month_dir = '';
        } else {
            // failed
            @ignore_user_abort(false);
            return array('error' => $lang->error_uploadfailed . $lang->error_uploadfailed_detail . $lang->error_uploadfailed_movefailed);
        }
    }
    // Lets just double check that it exists
    if (!file_exists($path . $month_dir . $filename)) {
        @ignore_user_abort(false);
        return array('error' => $lang->error_uploadfailed . $lang->error_uploadfailed_detail . $lang->error_uploadfailed_lost);
    }
    // Generate the array for the insert_query
    $attacharray = array('posthash' => $posthash, 'tid' => $tid, 'uid' => (int) $mybb->user['uid'], 'field' => $tf['field'], 'filename' => strval($attachment['name']), 'uploadmime' => strval($attachment['type']), 'filesize' => $file_size, 'attachname' => $basename, 'indir' => $month_dir, 'downloads' => 0, 'uploadtime' => $timestamp, 'updatetime' => $timestamp);
    if (isset($file_md5)) {
        $attacharray['md5hash'] = new xthreads_db_binary_value($file_md5);
    } else {
        $attacharray['md5hash'] = null;
    }
    if (!empty($img_dimensions)) {
        $origdimarray = array('w' => $img_dimensions[0], 'h' => $img_dimensions[1], 'type' => $img_dimensions[2]);
        $attacharray['thumbs'] = serialize(array('orig' => $origdimarray));
    }
    if ($update_attachment) {
        unset($attacharray['downloads'], $attacharray['uploadtime']);
        //$attacharray['updatetime'] = TIME_NOW;
        xthreads_db_update('xtattachments', $attacharray, 'aid=' . $prevattach['aid']);
        $attacharray['aid'] = $prevattach['aid'];
        // and finally, delete old attachment
        xthreads_rm_attach_fs($prevattach);
        $new_file = $path . $month_dir . $filename;
    } else {
        $attacharray['aid'] = xthreads_db_insert('xtattachments', $attacharray);
        // now that we have the aid, move the file
        $new_file = $path . $month_dir . 'file_' . $attacharray['aid'] . '_' . $basename;
        @rename($path . $month_dir . $filename, $new_file);
        if (!file_exists($new_file)) {
            // oh dear, all our work for nothing...
            @unlink($path . $month_dir . $filename);
            $db->delete_query('xtattachments', 'aid=' . $attacharray['aid']);
            @ignore_user_abort(false);
            return array('error' => $lang->error_uploadfailed . $lang->error_uploadfailed_detail . $lang->error_uploadfailed_lost);
        }
    }
    @my_chmod($new_file, '0644');
    @ignore_user_abort(false);
    if (!empty($img_dimensions) && !empty($tf['fileimgthumbs'])) {
        // generate thumbnails
        $attacharray['thumbs'] = xthreads_build_thumbnail($tf['fileimgthumbs'], $attacharray['aid'], $tf['field'], $new_file, $path, $month_dir, $img_dimensions);
        $attacharray['thumbs']['orig'] = $origdimarray;
        $attacharray['thumbs'] = serialize($attacharray['thumbs']);
    }
    return $attacharray;
}
function xthreads_rm_attach_query($where)
{
    global $db;
    $has_attach = $successes = 0;
    $query = $db->simple_select('xtattachments', 'aid,indir,attachname', $where);
    $rmaid = '';
    while ($xta = $db->fetch_array($query)) {
        if (xthreads_rm_attach_fs($xta)) {
            if ($successes) {
                $rmaid .= ',';
            }
            $rmaid .= $xta['aid'];
            ++$successes;
        }
        ++$has_attach;
    }
    $db->free_result($query);
    if ($has_attach) {
        if ($has_attach == $successes) {
            $db->delete_query('xtattachments', $where);
        } elseif ($successes) {
            $db->delete_query('xtattachments', 'aid IN (' . $rmaid . ')');
        }
    }
    return $successes;
}