예제 #1
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);
    }
}
예제 #2
0
    function xthreads_moderation_custom_do(&$tids, $editstr)
    {
        if (!$editstr) {
            return;
        }
        $edits = array();
        // caching stuff
        static $threadfields = null;
        if (!isset($threadfields)) {
            $threadfields = xthreads_gettfcache();
        }
        // grab all threadfields
        require_once MYBB_ROOT . 'inc/xthreads/xt_phptpl_lib.php';
        foreach (explode("\n", str_replace("{\n}", "\r", str_replace("\r", '', $editstr))) as $editline) {
            $editline = trim(str_replace("\r", "\n", $editline));
            list($n, $v) = explode('=', $editline, 2);
            if (!isset($v)) {
                continue;
            }
            // don't allow editing of file fields
            if (!isset($threadfields[$n]) || $threadfields[$n]['inputtype'] == XTHREADS_INPUT_FILE) {
                continue;
            }
            // we don't do much validation here as we trust admins, right?
            // this is just a prelim check (speed optimisation) - we'll need to check this again after evaluating conditionals
            $upperv = strtoupper($v);
            if (($upperv === '' || $upperv == 'NULL' || $upperv == 'NUL') && $threadfields[$n]['datatype'] != XTHREADS_DATATYPE_TEXT) {
                $edits[$n] = null;
            } else {
                $edits[$n] = $v;
                xthreads_sanitize_eval($edits[$n], array('VALUE' => null, 'TID' => null));
            }
        }
        if (empty($edits)) {
            return;
        }
        $modfields = array_keys($edits);
        global $db;
        $query = $db->query('
			SELECT t.tid, tfd.`' . implode('`, tfd.`', $modfields) . '`
			FROM ' . TABLE_PREFIX . 'threads t
			LEFT JOIN ' . TABLE_PREFIX . 'threadfields_data tfd ON t.tid=tfd.tid
			WHERE t.tid IN (' . implode(',', $tids) . ')
		');
        //$query = $db->simple_select('threadfields_data', 'tid,`'.implode('`,`', $modfields).'`', 'tid IN ('.implode(',', $tids).')');
        while ($thread = $db->fetch_array($query)) {
            $updates = array();
            foreach ($edits as $n => $v) {
                if ($v !== null) {
                    // TODO: allowing conditionals direct access to multivals?
                    $v = trim(eval_str($v, array('VALUE' => $thread[$n], 'TID' => $thread['tid'])));
                    if ($threadfields[$n]['datatype'] != XTHREADS_DATATYPE_TEXT) {
                        $upperv = strtoupper($v);
                        if ($upperv == '' || $upperv == 'NULL' || $upperv == 'NUL') {
                            $v = null;
                        }
                        // TODO: intval/floatval here?
                    }
                }
                if ($v !== $thread[$n]) {
                    // we'll do some basic validation for multival fields
                    if (!xthreads_empty($threadfields[$n]['multival'])) {
                        $d = "\n";
                        if ($threadfields[$n]['inputtype'] == XTHREADS_INPUT_TEXT) {
                            $d = ',';
                        }
                        $v = array_unique(array_map('trim', explode($d, str_replace("\r", '', $v))));
                        foreach ($v as $key => &$val) {
                            if (xthreads_empty($val)) {
                                unset($v[$key]);
                            }
                        }
                        $v = implode($d, $v);
                    }
                    $updates[$n] = $v;
                }
            }
            if (!empty($updates)) {
                xthreads_db_update_replace('threadfields_data', $updates, 'tid', $thread['tid']);
            }
        }
        $db->free_result($query);
    }