Exemplo n.º 1
0
function plugin_pcomment_action()
{
    global $post, $vars;
    // if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
    if (auth::check_role('readonly')) {
        die_message('PKWK_READONLY prohibits editing');
    }
    // Petit SPAM Check (Client(Browser)-Server Ticket Check)
    $b = FALSE;
    if (!isset($post['encode_hint']) && PKWK_ENCODING_HINT == '') {
        $b = TRUE;
    } elseif (isset($post['encode_hint']) && $post['encode_hint'] == PKWK_ENCODING_HINT) {
        $b = TRUE;
    }
    if ($b === FALSE) {
        honeypot_write();
        return array('msg' => '', 'body' => '');
        // Do nothing
    }
    if (!isset($vars['msg']) || $vars['msg'] == '') {
        return array();
    }
    // Validate
    if (is_spampost(array('msg'))) {
        honeypot_write();
        return array('msg' => '', 'body' => '');
        // Do nothing
    }
    $refer = isset($vars['refer']) ? $vars['refer'] : '';
    if (!is_page($refer) && auth::is_check_role(PKWK_CREATE_PAGE)) {
        die_message(_('PKWK_CREATE_PAGE prohibits editing'));
    }
    $retval = plugin_pcomment_insert();
    if ($retval['collided']) {
        $vars['page'] = $refer;
        return $retval;
    }
    pkwk_headers_sent();
    header('Location: ' . get_page_location_uri($refer));
    exit;
}
Exemplo n.º 2
0
function plugin_tracker_action()
{
    global $post, $vars, $now;
    //	if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
    if (auth::check_role('readonly')) {
        die_message(_('PKWK_READONLY prohibits editing'));
    }
    if (auth::is_check_role(PKWK_CREATE_PAGE)) {
        die_message(_('PKWK_CREATE_PAGE prohibits editing'));
    }
    $base = isset($post['_base']) ? $post['_base'] : '';
    $refer = isset($post['_refer']) ? $post['_refer'] : '';
    $createProxy = isset($post['_createProxy']) ? $post['_createProxy'] : '';
    // $page name to add will be decided here
    $num = 0;
    $name = isset($post['_name']) ? $post['_name'] : '';
    if (isset($post['_page'])) {
        $real = $page = $post['_page'];
    } else {
        $real = is_pagename($name) ? $name : ++$num;
        $page = get_fullname('./' . $real, $base);
    }
    if (!is_pagename($page)) {
        $page = $base;
    }
    while (is_page($page)) {
        $real = ++$num;
        $page = $base . '/' . $real;
    }
    $config = isset($post['_config']) ? $post['_config'] : '';
    $createProxy = isset($post['_createProxy']) ? $post['_createProxy'] : '';
    // Petit SPAM Check (Client(Browser)-Server Ticket Check)
    $spam = FALSE;
    if (function_exists('pkwk_session_start') && pkwk_session_start() != 0) {
        $s_tracker = md5(get_ticket() . $config_name);
        error_log("\$s_tracker: " . $s_tracker);
        error_log("\$_SESSION['tracker']: " . $_SESSION['tracker']);
        //		if ($_SESSION['tracker'] != $s_tracker) {
        //			$spam = TRUE;
        //		}
    } else {
        if (isset($post['encode_hint']) && $post['encode_hint'] != '') {
            if (PKWK_ENCODING_HINT != $post['encode_hint']) {
                $spam = TRUE;
            }
        } else {
            if (PKWK_ENCODING_HINT != '') {
                $spam = TRUE;
            }
        }
        if (is_spampost(array('body'), PLUGIN_TRACKER_REJECT_SPAMCOUNT)) {
            $spam = TRUE;
        }
    }
    if ($spam) {
        honeypot_write();
        return array('msg' => 'cannot write', 'body' => '<p>prohibits editing</p>');
    }
    // TODO: Why here
    // Default
    $_post = array_merge($post, $_FILES);
    $_post['_date'] = $now;
    $_post['_page'] = $page;
    $_post['_name'] = $name;
    $_post['_real'] = $real;
    // $_post['_refer'] = $_post['refer'];
    // TODO: Why here => See BugTrack/662
    // Creating an empty page, before attaching files
    pkwk_touch_file(get_filename($page));
    $from = $to = array();
    $tracker_form =& new Tracker_form();
    if (!$tracker_form->init($base, $refer, $config)) {
        return array('msg' => 'Cannot write', 'body' => htmlspecialchars($tracker_form->error));
    }
    // Load $template
    $template_page = $tracker_form->config->page . '/' . PLUGIN_TRACKER_DEFAULT_PAGE;
    $template = plugin_tracker_get_source($template_page);
    if ($template === FALSE || empty($template)) {
        return array('msg' => 'Cannot write', 'body' => 'Page template (' . htmlspecialchars($template_page) . ') not found');
    }
    if (!$tracker_form->initFields(plugin_tracker_field_pickup(implode('', $template)))) {
        return array('msg' => 'Cannot write', 'body' => htmlspecialchars($tracker_form->error));
    }
    $fields = $tracker_form->fields;
    unset($tracker_form);
    foreach (array_keys($fields) as $field) {
        $from[] = '[' . $field . ']';
        $to[] = isset($_post[$field]) ? $fields[$field]->format_value($_post[$field]) : '';
        unset($fields[$field]);
    }
    // Repalace every [$field]s (found inside $template) to real values
    $subject = $escape = array();
    foreach (array_keys($template) as $linenum) {
        if (trim($template[$linenum]) == '') {
            continue;
        }
        // Escape some TextFormattingRules
        $letter = $template[$linenum][0];
        if ($letter == '|' || $letter == ':') {
            $escape['|'][$linenum] = $template[$linenum];
        } else {
            if ($letter == ',') {
                $escape[','][$linenum] = $template[$linenum];
            } else {
                // TODO: Escape "\n" except multiline-allowed fields
                $subject[$linenum] = $template[$linenum];
            }
        }
    }
    foreach (str_replace($from, $to, $subject) as $linenum => $line) {
        $template[$linenum] = $line;
    }
    if ($escape) {
        // Escape for some TextFormattingRules
        foreach (array_keys($escape) as $hint) {
            $to_e = plugin_tracker_escape($to, $hint);
            foreach (str_replace($from, $to_e, $escape[$hint]) as $linenum => $line) {
                $template[$linenum] = $line;
            }
        }
        unset($to_e);
    }
    unset($from, $to);
    // Write $template, without touch
    page_write($page, join('', $template));
    // Create proxy page
    if ($createProxy && ($proxyPage = isset($_post[$createProxy]) ? $_post[$createProxy] : '')) {
        page_write($proxyPage, '#include(' . $page . ',notitle)');
    }
    pkwk_headers_sent();
    header('Location: ' . get_page_location_uri($page));
    exit;
}
Exemplo n.º 3
0
function plugin_approve_action()
{
    global $vars, $post;
    if (auth::check_role('readonly')) {
        die_message(_('PKWK_READONLY prohibits editing'));
    }
    if (auth::is_check_role(PKWK_CREATE_PAGE)) {
        die_message(_('PKWK_CREATE_PAGE prohibits editing'));
    }
    // Petit SPAM Check (Client(Browser)-Server Ticket Check)
    $spam = FALSE;
    if (function_exists('pkwk_session_start') && pkwk_session_start() != 0) {
        $s_tracker = md5(get_ticket() . 'Approve');
        error_log("\$s_tracker: " . $s_tracker);
        error_log("\$_SESSION['tracker']: " . $_SESSION['tracker']);
    } else {
        if (isset($post['encode_hint']) && $post['encode_hint'] != '') {
            error_log("\$post['encode_hint']: " . $post['encode_hint']);
            if (PKWK_ENCODING_HINT != $post['encode_hint']) {
                $spam = TRUE;
            }
        } else {
            error_log("PKWK_ENCODING_HINT: " . PKWK_ENCODING_HINT);
            if (PKWK_ENCODING_HINT != '') {
                $spam = TRUE;
            }
        }
        error_log("is_spampost: " . is_spampost(array('body'), PLUGIN_TRACKER_REJECT_SPAMCOUNT));
        if (is_spampost(array('body'), PLUGIN_TRACKER_REJECT_SPAMCOUNT)) {
            $spam = TRUE;
        }
    }
    error_log("isSpam: " . $spam);
    if ($spam) {
        honeypot_write();
        return array('msg' => 'cannot write', 'body' => '<p>prohibits editing</p>');
    }
    $name = isset($post['name']) ? $post['name'] : '';
    $page = isset($post['_page']) ? $post['_page'] : '';
    if ($name == '') {
        return '<p>approve(): empty name.</p>';
    }
    if ($page == '') {
        return '<p>approve(): empty page.</p>';
    }
    $config_path = PLUGIN_APPROVE_CONFIG_ROOT . $name;
    $config = new YamlConfig($config_path);
    if (!$config->read()) {
        return array('msg' => 'Approve', 'body' => '<p>approve(): failed to load config. "' . $config_path . '"</p>');
    }
    $pattern = $config[PLUGIN_APPROVE_KEY_PATTERN];
    $replace = $config[PLUGIN_APPROVE_KEY_REPLACE];
    $page_regex = $config[PLUGIN_APPROVE_KEY_PAGE_REGEX];
    if ($page == '') {
        return array('msg' => 'Approve', 'body' => '<p>approve(): empty page.</p>');
    }
    if ($pattern == '') {
        return array('msg' => 'Approve', 'body' => '<p>approve(): empty pattern.</p>');
    }
    if ($page_regex == '') {
        return array('msg' => 'Approve', 'body' => '<p>approve(): empty page_regex.</p>');
    }
    if (!preg_match($page_regex, $page)) {
        return array('msg' => 'Approve', 'body' => '<p>approve(): page not match.</p>');
    }
    if (PKWK_READONLY > 0 || is_freeze($vars['page']) || !plugin_approve_is_edit_authed($page)) {
        return array('msg' => 'Approve', 'body' => '<p>approve(): prohibit editing. "' . $page . '"</p>');
    }
    $source = get_source($page, TRUE, TRUE);
    if ($source === FALSE) {
        return array('msg' => 'Approve', 'body' => '<p>approve(): failed to load page. "' . $page . '"</p>');
    }
    if (strpos($source, $pattern) === FALSE) {
        return array('msg' => 'Approve', 'body' => '<p>approve(): pattern not match.</p>');
    }
    $source = str_replace($pattern, $replace, $source);
    //return array('msg'=>'Approve', 'body'=>$source);
    page_write($page, $source);
    pkwk_headers_sent();
    header('Location: ' . get_page_location_uri($page));
    exit;
}
Exemplo n.º 4
0
function plugin_commentx_honeypot()
{
    // Logging for SPAM Report
    honeypot_write();
    // Same as "Cancel" action
    return array('msg' => '', 'body' => '');
    // Do nothing
}
Exemplo n.º 5
0
function plugin_edit_honeypot()
{
    // SPAM Logging
    honeypot_write();
    // Same as "Cancel" action
    return plugin_edit_cancel();
}
Exemplo n.º 6
0
function plugin_tb_save($url, $tb_id)
{
    global $vars, $trackback, $use_spam_check;
    static $fields = array('url', 'title', 'excerpt', 'blog_name');
    $die = '';
    if (!$trackback) {
        $die .= 'TrackBack feature disabled. ';
    }
    if ($url == '') {
        $die .= 'URL parameter is not set. ';
    }
    if ($tb_id == '') {
        $die .= 'TrackBack Ping ID is not set. ';
    }
    if ($die != '') {
        plugin_tb_return(PLUGIN_TB_ERROR, $die);
    }
    if (!file_exists(TRACKBACK_DIR)) {
        plugin_tb_return(PLUGIN_TB_ERROR, 'No such directory: TRACKBACK_DIR');
    }
    if (!is_writable(TRACKBACK_DIR)) {
        plugin_tb_return(PLUGIN_TB_ERROR, 'Permission denied: TRACKBACK_DIR');
    }
    $page = tb_id2page($tb_id);
    if ($page === FALSE) {
        plugin_tb_return(PLUGIN_TB_ERROR, 'TrackBack ID is invalid.');
    }
    // URL validation (maybe worse of processing time limit)
    if (!is_url($url)) {
        plugin_tb_return(PLUGIN_TB_ERROR, 'URL is fictitious.');
    }
    if (PLUGIN_TB_SITE_CHECK === TRUE) {
        $result = http_request($url);
        if ($result['rc'] !== 200) {
            plugin_tb_return(PLUGIN_TB_ERROR, 'URL is fictitious.');
        }
        $urlbase = get_script_absuri();
        $matches = array();
        if (preg_match_all('#' . preg_quote($urlbase, '#') . '#i', $result['data'], $matches) == 0) {
            honeypot_write();
            if (PLUGIN_TB_HTTP_ERROR === TRUE && is_sapi_clicgi() === FALSE) {
                header('HTTP/1.0 403 Forbidden');
                exit;
            }
            plugin_tb_return(PLUGIN_TB_ERROR, 'Writing is prohibited.');
        }
    } else {
        $result = http_request($url, 'HEAD');
        if ($result['rc'] !== 200) {
            plugin_tb_return(PLUGIN_TB_ERROR, 'URL is fictitious.');
        }
    }
    // Update TrackBack Ping data
    $filename = tb_get_filename($page);
    $data = tb_get($filename);
    $matches = array();
    $items = array(UTIME);
    foreach ($fields as $key) {
        $value = isset($vars[$key]) ? $vars[$key] : '';
        if (preg_match('/[,"' . "\n\r" . ']/', $value)) {
            $value = '"' . str_replace('"', '""', $value) . '"';
        }
        $items[$key] = $value;
        // minimum checking from SPAM
        if (preg_match_all('/a\\s+href=/i', $items[$key], $matches) >= 1) {
            honeypot_write();
            if (PLUGIN_TB_HTTP_ERROR === TRUE && is_sapi_clicgi() === FALSE) {
                header('HTTP/1.0 400 Bad Request');
                exit;
            }
            plugin_tb_return(PLUGIN_TB_ERROR, 'Writing is prohibited.');
        }
    }
    // minimum checking from SPAM #2
    foreach (array('title', 'excerpt', 'blog_name') as $key) {
        if (preg_match_all('#http\\://#i', $items[$key], $matches) >= 1) {
            honeypot_write();
            if (PLUGIN_TB_HTTP_ERROR === TRUE && is_sapi_clicgi() === FALSE) {
                header('HTTP/1.0 400 Bad Request');
                exit;
            }
            plugin_tb_return(PLUGIN_TB_ERROR, 'Writing is prohibited.');
        }
    }
    // Blocking SPAM
    if ($use_spam_check['trackback'] && SpamCheck($items['url'])) {
        plugin_tb_return(1, 'Writing is prohibited.');
    }
    $data[rawurldecode($items['url'])] = $items;
    $fp = fopen($filename, 'w');
    set_file_buffer($fp, 0);
    flock($fp, LOCK_EX);
    rewind($fp);
    foreach ($data as $line) {
        $line = preg_replace('/[\\r\\n]/s', '', $line);
        // One line, one ping
        fwrite($fp, join(',', $line) . "\n");
    }
    flock($fp, LOCK_UN);
    fclose($fp);
    plugin_tb_return(PLUGIN_TB_OK);
    // Return OK
}
Exemplo n.º 7
0
function plugin_bugtrack_action()
{
    global $vars;
    global $_plugin_bugtrack, $_string;
    // if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
    if (Auth::check_role('readonly')) {
        die_message($_string['prohibit']);
    }
    if (Auth::is_check_role(PKWK_CREATE_PAGE)) {
        die_message(str_replace('PKWK_CREATE_PAGE', 'PKWK_READONLY', $_string['prohibit']));
    }
    if ($vars['mode'] != 'submit') {
        return FALSE;
    }
    // Vaildation foreign values(by miko)
    $spam = !in_array($vars['priority'], $_plugin_bugtrack['priority_list']) || !in_array($vars['state'], $_plugin_bugtrack['state_list']) ? TRUE : FALSE;
    if ($spam) {
        honeypot_write();
        return array('msg' => 'cannot write', 'body' => '<p>prohibits editing</p>');
    }
    $page = plugin_bugtrack_write($vars['base'], $vars['pagename'], $vars['summary'], $vars['name'], $vars['priority'], $vars['state'], $vars['category'], $vars['version'], $vars['body']);
    Utility::redirect(get_page_location_uri($page));
    exit;
}
Exemplo n.º 8
0
function pkwk_spamnotify($action, $page, $target = array('title' => ''), $progress = array(), $method = array())
{
    global $notify, $notify_spam, $notify_subject;
    if (!$notify) {
        return;
    }
    if (!$notify_spam) {
        honeypot_write();
        return;
    }
    $asap = isset($method['asap']);
    $summary['ACTION'] = 'Blocked by: ' . summarize_spam_progress($progress, TRUE);
    if (!$asap) {
        $summary['METRICS'] = summarize_spam_progress($progress);
    }
    $tmp = summarize_detail_badhost($progress);
    if ($tmp != '') {
        $summary['DETAIL_BADHOST'] = $tmp;
    }
    $tmp = summarize_detail_newtral($progress);
    if (!$asap && $tmp != '') {
        $summary['DETAIL_NEUTRAL_HOST'] = $tmp;
    }
    $summary['COMMENT'] = $action;
    $summary['PAGE'] = '[blocked] ' . (is_pagename($page) ? $page : '');
    $summary['URI'] = get_script_absuri() . '?' . rawurlencode($page);
    $summary['USER_AGENT'] = TRUE;
    $summary['REMOTE_ADDR'] = TRUE;
    pkwk_mail_notify($notify_subject, var_export($target, TRUE), $summary, TRUE);
}
Exemplo n.º 9
0
function plugin_bugtrack_action()
{
    global $post;
    global $_plugin_bugtrack;
    // if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
    if (auth::check_role('readonly')) {
        die_message('PKWK_READONLY prohibits editing');
    }
    if (auth::is_check_role(PKWK_CREATE_PAGE)) {
        die_message(_('PKWK_CREATE_PAGE prohibits editing'));
    }
    if ($post['mode'] != 'submit') {
        return FALSE;
    }
    // Petit SPAM Check (Client(Browser)-Server Ticket Check)
    $spam = FALSE;
    if (isset($post['encode_hint']) && $post['encode_hint'] != '') {
        if (PKWK_ENCODING_HINT != $post['encode_hint']) {
            $spam = TRUE;
        }
    } else {
        if (PKWK_ENCODING_HINT != '') {
            $spam = TRUE;
        }
    }
    // Vaildation foreign values(by miko)
    if (!in_array($post['priority'], $_plugin_bugtrack['priority_list'])) {
        $spam = TRUE;
    }
    if (!in_array($post['state'], $_plugin_bugtrack['state_list'])) {
        $spam = TRUE;
    }
    if ($spam) {
        honeypot_write();
        return array('msg' => 'cannot write', 'body' => '<p>prohibits editing</p>');
    }
    $page = plugin_bugtrack_write($post['base'], $post['pagename'], $post['summary'], $post['name'], $post['priority'], $post['state'], $post['category'], $post['version'], $post['body']);
    pkwk_headers_sent();
    header('Location: ' . get_page_location_uri($page));
    exit;
}
Exemplo n.º 10
0
function plugin_tracker_action()
{
    global $vars, $now, $config_name, $_string, $session;
    //	if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
    // Plus! code start
    if (Auth::check_role('readonly')) {
        die_message($_string['prohibit']);
    }
    if (Auth::is_check_role(PKWK_CREATE_PAGE)) {
        die_message(_('PKWK_CREATE_PAGE prohibits editing'));
    }
    $base = isset($vars['_base']) ? $vars['_base'] : null;
    $refer = isset($vars['_refer']) ? $vars['_refer'] : null;
    if (isset($vars['cancel'])) {
        Utility::redirect(Router::get_page_uri($refer));
    }
    $tracker_form = new Tracker_form();
    // Petit SPAM Check (Client(Browser)-Server Ticket Check)
    $config = $tracker_form->config_name;
    // Rescan
    if ($session->offsetGet('tracker') !== md5(get_ticket() . $config_name)) {
        honeypot_write();
        return array('msg' => 'Cannot write', 'body' => 'Prohibits editing');
    }
    // Plus! code end
    // $page name to add will be decided here
    $num = 0;
    $name = isset($vars['_name']) ? $vars['_name'] : null;
    if (isset($vars['_page'])) {
        $real = $page = $vars['_page'];
    } else {
        $real = is_pagename($name) ? $name : ++$num;
        $page = get_fullname('./' . $real, $base);
    }
    if (!is_pagename($page)) {
        $page = $base;
    }
    while (is_page($page)) {
        $real = ++$num;
        $page = $base . '/' . $real;
    }
    $config = isset($vars['_config']) ? $vars['_config'] : null;
    // TODO: Why here
    // Default
    if (isset($_FILES)) {
        $_post = array_merge($vars, $_FILES);
    }
    $_post['_date'] = $now;
    $_post['_page'] = $page;
    $_post['_name'] = $name;
    $_post['_real'] = $real;
    // $_post['_refer'] = $_post['refer'];
    // TODO: Why here => See BugTrack/662
    // Creating an empty page, before attaching files
    $from = $to = array();
    $tracker_form = new Tracker_form();
    if (!$tracker_form->init($base, $refer, $config)) {
        return array('msg' => 'Cannot write', 'body' => '<p class="alert alert-warning">' . Utility::htmlsc($tracker_form->error) . '</p>');
    }
    // Load $template
    $template_page = $tracker_form->config->page . '/' . PLUGIN_TRACKER_DEFAULT_PAGE;
    $template = plugin_tracker_get_source($template_page);
    if ($template === FALSE || empty($template)) {
        return array('msg' => 'Cannot write', 'body' => '<p class="alert alert-warning">Page template (' . Utility::htmlsc($template_page) . ') not found</p>');
    }
    if (!$tracker_form->initFields(plugin_tracker_field_pickup(implode(null, $template)))) {
        return array('msg' => 'Cannot write', 'body' => '<p class="alert alert-warning">' . Utility::htmlsc($tracker_form->error) . '</p>');
    }
    $fields = $tracker_form->fields;
    unset($tracker_form);
    foreach (array_keys($fields) as $field) {
        $from[] = '[' . $field . ']';
        $to[] = isset($_post[$field]) ? $fields[$field]->format_value($_post[$field]) : null;
        unset($fields[$field]);
    }
    // Repalace every [$field]s (found inside $template) to real values
    $subject = $escape = array();
    foreach (array_keys($template) as $linenum) {
        if (trim($template[$linenum]) == null) {
            continue;
        }
        // Escape some TextFormattingRules
        $letter = $template[$linenum][0];
        if ($letter == '|' || $letter == ':') {
            $escape['|'][$linenum] = $template[$linenum];
        } else {
            if ($letter == ',') {
                $escape[','][$linenum] = $template[$linenum];
            } else {
                // TODO: Escape "\n" except multiline-allowed fields
                $subject[$linenum] = $template[$linenum];
            }
        }
    }
    foreach (str_replace($from, $to, $subject) as $linenum => $line) {
        $template[$linenum] = $line;
    }
    if ($escape) {
        // Escape for some TextFormattingRules
        foreach (array_keys($escape) as $hint) {
            $to_e = plugin_tracker_escape($to, $hint);
            foreach (str_replace($from, $to_e, $escape[$hint]) as $linenum => $line) {
                $template[$linenum] = $line;
            }
        }
        unset($to_e);
    }
    unset($from, $to);
    if (isset($vars['preview'])) {
        global $_button;
        unset($vars['preview']);
        $form[] = '<p class="alert alert-success">' . T_('It will be sent with the contents of the following.') . '</p>';
        $form[] = '<form action="' . Router::get_script_uri() . '"enctype="multipart/form-data" method="post" class="form-horizontal plugin-tracker-form">';
        foreach ($vars as $key => $value) {
            $form[] = '<input type="hidden" name="' . $key . '" value="' . $value . '" />';
        }
        $form[] = '<button type="submit" class="btn btn-primary" name="write" accesskey="s"><span class="fa fa-check"></span>' . $_button['update'] . '</button>';
        $form[] = '<button type="submit" class="btn btn-warning" name="cancel" accesskey="c"><span class="fa fa-ban"></span>' . $_button['cancel'] . '</button>';
        $form[] = '</form>';
        $form[] = '<hr />';
        $form[] = RendererFactory::factory($template);
        return array('msg' => 'Preview', 'body' => join("\n", $form));
    } else {
        // Write $template, without touch
        $wiki = Factory::Wiki($page);
        $wiki->set($template);
        Utility::redirect($wiki->uri());
    }
    exit;
}