修正验证参数不严导致的可能泄漏路径
This commit is contained in:
@@ -68,7 +68,8 @@ if (!isset($_GET['finish']) && file_exists(__TYPECHO_ROOT_DIR__ . '/config.inc.p
|
||||
* @return string
|
||||
*/
|
||||
function _r($name, $default = NULL) {
|
||||
return isset($_REQUEST[$name]) ? $_REQUEST[$name] : $default;
|
||||
return isset($_REQUEST[$name]) ?
|
||||
(is_array($_REQUEST[$name]) ? $default : $_REQUEST[$name]) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +82,8 @@ function _rFrom() {
|
||||
$params = func_get_args();
|
||||
|
||||
foreach ($params as $param) {
|
||||
$result[$param] = isset($_REQUEST[$param]) ? $_REQUEST[$param] : NULL;
|
||||
$result[$param] = isset($_REQUEST[$param]) ?
|
||||
(is_array($_REQUEST[$param]) ? NULL : $_REQUEST[$param]) : NULL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
@@ -249,10 +249,36 @@ class Typecho_Request
|
||||
break;
|
||||
}
|
||||
|
||||
$value = is_array($value) || strlen($value) > 0 ? $value : $default;
|
||||
$value = !is_array($value) && strlen($value) > 0 ? $value : $default;
|
||||
return $this->_filter ? $this->_applyFilter($value) : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个数组
|
||||
*
|
||||
* @param $key
|
||||
* @return array
|
||||
*/
|
||||
public function getArray($key)
|
||||
{
|
||||
$result = array();
|
||||
|
||||
switch (true) {
|
||||
case isset($_GET[$key]):
|
||||
$result = $_GET[$key];
|
||||
break;
|
||||
case isset($_POST[$key]):
|
||||
$result = $_POST[$key];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$result = is_array($result) ? $result
|
||||
: (strlen($result) > 0 ? array($result) : array());
|
||||
return $this->_filter ? $this->_applyFilter($result) : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从参数列表指定的值中获取http传递参数
|
||||
*
|
||||
|
||||
@@ -60,18 +60,6 @@ class Widget_Comments_Edit extends Widget_Abstract_Comments implements Widget_In
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以数组形式获取coid
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function getCoidAsArray()
|
||||
{
|
||||
$coid = $this->request->filter('int')->coid;
|
||||
return $coid ? (is_array($coid) ? $coid : array($coid)) : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记为待审核
|
||||
*
|
||||
@@ -80,7 +68,7 @@ class Widget_Comments_Edit extends Widget_Abstract_Comments implements Widget_In
|
||||
*/
|
||||
public function waitingComment()
|
||||
{
|
||||
$comments = $this->getCoidAsArray();
|
||||
$comments = $this->request->filter('int')->getArray('coid');
|
||||
$updateRows = 0;
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
@@ -105,7 +93,7 @@ class Widget_Comments_Edit extends Widget_Abstract_Comments implements Widget_In
|
||||
*/
|
||||
public function spamComment()
|
||||
{
|
||||
$comments = $this->getCoidAsArray();
|
||||
$comments = $this->request->filter('int')->getArray('coid');
|
||||
$updateRows = 0;
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
@@ -130,7 +118,7 @@ class Widget_Comments_Edit extends Widget_Abstract_Comments implements Widget_In
|
||||
*/
|
||||
public function approvedComment()
|
||||
{
|
||||
$comments = $this->getCoidAsArray();
|
||||
$comments = $this->request->filter('int')->getArray('coid');
|
||||
$updateRows = 0;
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
@@ -155,7 +143,7 @@ class Widget_Comments_Edit extends Widget_Abstract_Comments implements Widget_In
|
||||
*/
|
||||
public function deleteComment()
|
||||
{
|
||||
$comments = $this->getCoidAsArray();
|
||||
$comments = $this->request->filter('int')->getArray('coid');
|
||||
$deleteRows = 0;
|
||||
|
||||
foreach ($comments as $coid) {
|
||||
|
||||
@@ -214,41 +214,34 @@ class Widget_Contents_Attachment_Edit extends Widget_Contents_Post_Edit implemen
|
||||
*/
|
||||
public function deleteAttachment()
|
||||
{
|
||||
$cid = $this->request->filter('int')->cid;
|
||||
$posts = $this->request->filter('int')->getArray('cid');
|
||||
$deleteCount = 0;
|
||||
$status = 'publish';
|
||||
|
||||
if ($cid) {
|
||||
/** 格式化文章主键 */
|
||||
$posts = is_array($cid) ? $cid : array($cid);
|
||||
foreach ($posts as $post) {
|
||||
// 删除插件接口
|
||||
$this->pluginHandle()->delete($post, $this);
|
||||
foreach ($posts as $post) {
|
||||
// 删除插件接口
|
||||
$this->pluginHandle()->delete($post, $this);
|
||||
|
||||
$condition = $this->db->sql()->where('cid = ?', $post);
|
||||
$row = $this->db->fetchRow($this->select()
|
||||
$condition = $this->db->sql()->where('cid = ?', $post);
|
||||
$row = $this->db->fetchRow($this->select()
|
||||
->where('table.contents.type = ?', 'attachment')
|
||||
->where('table.contents.cid = ?', $post)
|
||||
->limit(1), array($this, 'push'));
|
||||
|
||||
if ($this->isWriteable($condition) && $this->delete($condition)) {
|
||||
/** 删除文件 */
|
||||
Widget_Upload::deleteHandle($row);
|
||||
if ($this->isWriteable($condition) && $this->delete($condition)) {
|
||||
/** 删除文件 */
|
||||
Widget_Upload::deleteHandle($row);
|
||||
|
||||
/** 删除评论 */
|
||||
$this->db->query($this->db->delete('table.comments')
|
||||
/** 删除评论 */
|
||||
$this->db->query($this->db->delete('table.comments')
|
||||
->where('cid = ?', $post));
|
||||
|
||||
$status = $this->status;
|
||||
// 完成删除插件接口
|
||||
$this->pluginHandle()->finishDelete($post, $this);
|
||||
|
||||
// 完成删除插件接口
|
||||
$this->pluginHandle()->finishDelete($post, $this);
|
||||
|
||||
$deleteCount ++;
|
||||
}
|
||||
|
||||
unset($condition);
|
||||
$deleteCount ++;
|
||||
}
|
||||
|
||||
unset($condition);
|
||||
}
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
@@ -273,7 +266,8 @@ class Widget_Contents_Attachment_Edit extends Widget_Contents_Post_Edit implemen
|
||||
public function clearAttachment()
|
||||
{
|
||||
$page = 1;
|
||||
|
||||
$deleteCount = 0;
|
||||
|
||||
do {
|
||||
$posts = Typecho_Common::arrayFlatten($this->db->fetchAll($this->select('cid')
|
||||
->from('table.contents')
|
||||
|
||||
@@ -133,51 +133,47 @@ class Widget_Contents_Page_Edit extends Widget_Contents_Post_Edit implements Wid
|
||||
*/
|
||||
public function deletePage()
|
||||
{
|
||||
$cid = $this->request->filter('int')->cid;
|
||||
$pages = $this->request->filter('int')->getArray('cid');
|
||||
$deleteCount = 0;
|
||||
|
||||
if ($cid) {
|
||||
/** 格式化页面主键 */
|
||||
$pages = is_array($cid) ? $cid : array($cid);
|
||||
foreach ($pages as $page) {
|
||||
// 删除插件接口
|
||||
$this->pluginHandle()->delete($page, $this);
|
||||
foreach ($pages as $page) {
|
||||
// 删除插件接口
|
||||
$this->pluginHandle()->delete($page, $this);
|
||||
|
||||
if ($this->delete($this->db->sql()->where('cid = ?', $page))) {
|
||||
/** 删除评论 */
|
||||
$this->db->query($this->db->delete('table.comments')
|
||||
if ($this->delete($this->db->sql()->where('cid = ?', $page))) {
|
||||
/** 删除评论 */
|
||||
$this->db->query($this->db->delete('table.comments')
|
||||
->where('cid = ?', $page));
|
||||
|
||||
/** 解除附件关联 */
|
||||
$this->unAttach($page);
|
||||
/** 解除附件关联 */
|
||||
$this->unAttach($page);
|
||||
|
||||
/** 解除首页关联 */
|
||||
if ($this->options->frontPage == 'page:' . $page) {
|
||||
$this->db->query($this->db->update('table.options')
|
||||
/** 解除首页关联 */
|
||||
if ($this->options->frontPage == 'page:' . $page) {
|
||||
$this->db->query($this->db->update('table.options')
|
||||
->rows(array('value' => 'recent'))
|
||||
->where('name = ?', 'frontPage'));
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除草稿 */
|
||||
$draft = $this->db->fetchRow($this->db->select('cid')
|
||||
/** 删除草稿 */
|
||||
$draft = $this->db->fetchRow($this->db->select('cid')
|
||||
->from('table.contents')
|
||||
->where('table.contents.parent = ? AND table.contents.type = ?',
|
||||
$page, 'page_draft')
|
||||
->limit(1));
|
||||
|
||||
/** 删除自定义字段 */
|
||||
$this->deleteFields($page);
|
||||
/** 删除自定义字段 */
|
||||
$this->deleteFields($page);
|
||||
|
||||
if ($draft) {
|
||||
$this->deleteDraft($draft['cid']);
|
||||
$this->deleteFields($draft['cid']);
|
||||
}
|
||||
|
||||
// 完成删除插件接口
|
||||
$this->pluginHandle()->finishDelete($page, $this);
|
||||
|
||||
$deleteCount ++;
|
||||
if ($draft) {
|
||||
$this->deleteDraft($draft['cid']);
|
||||
$this->deleteFields($draft['cid']);
|
||||
}
|
||||
|
||||
// 完成删除插件接口
|
||||
$this->pluginHandle()->finishDelete($page, $this);
|
||||
|
||||
$deleteCount ++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,29 +193,24 @@ class Widget_Contents_Page_Edit extends Widget_Contents_Post_Edit implements Wid
|
||||
*/
|
||||
public function deletePageDraft()
|
||||
{
|
||||
$cid = $this->request->filter('int')->cid;
|
||||
$pages = $this->request->filter('int')->getArray('cid');
|
||||
$deleteCount = 0;
|
||||
|
||||
if ($cid) {
|
||||
/** 格式化文章主键 */
|
||||
$pages = is_array($cid) ? $cid : array($cid);
|
||||
|
||||
foreach ($pages as $page) {
|
||||
/** 删除草稿 */
|
||||
$draft = $this->db->fetchRow($this->db->select('cid')
|
||||
|
||||
foreach ($pages as $page) {
|
||||
/** 删除草稿 */
|
||||
$draft = $this->db->fetchRow($this->db->select('cid')
|
||||
->from('table.contents')
|
||||
->where('table.contents.parent = ? AND table.contents.type = ?',
|
||||
$page, 'page_draft')
|
||||
->limit(1));
|
||||
|
||||
if ($draft) {
|
||||
$this->deleteDraft($draft['cid']);
|
||||
$this->deleteFields($draft['cid']);
|
||||
$deleteCount ++;
|
||||
}
|
||||
if ($draft) {
|
||||
$this->deleteDraft($draft['cid']);
|
||||
$this->deleteFields($draft['cid']);
|
||||
$deleteCount ++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 设置提示信息 */
|
||||
$this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('草稿已经被删除') : _t('没有草稿被删除'),
|
||||
$deleteCount > 0 ? 'success' : 'notice');
|
||||
@@ -236,9 +227,9 @@ class Widget_Contents_Page_Edit extends Widget_Contents_Post_Edit implements Wid
|
||||
*/
|
||||
public function sortPage()
|
||||
{
|
||||
$pages = $this->request->filter('int')->cid;
|
||||
$pages = $this->request->filter('int')->getArray('cid');
|
||||
|
||||
if ($pages && is_array($pages)) {
|
||||
if ($pages) {
|
||||
foreach ($pages as $sort => $cid) {
|
||||
$this->db->query($this->db->update('table.contents')->rows(array('order' => $sort + 1))
|
||||
->where('cid = ?', $cid));
|
||||
|
||||
@@ -770,67 +770,63 @@ class Widget_Contents_Post_Edit extends Widget_Abstract_Contents implements Widg
|
||||
*/
|
||||
public function deletePost()
|
||||
{
|
||||
$cid = $this->request->filter('int')->cid;
|
||||
$posts = $this->request->filter('int')->getArray('cid');
|
||||
$deleteCount = 0;
|
||||
|
||||
if ($cid) {
|
||||
/** 格式化文章主键 */
|
||||
$posts = is_array($cid) ? $cid : array($cid);
|
||||
foreach ($posts as $post) {
|
||||
// 删除插件接口
|
||||
$this->pluginHandle()->delete($post, $this);
|
||||
foreach ($posts as $post) {
|
||||
// 删除插件接口
|
||||
$this->pluginHandle()->delete($post, $this);
|
||||
|
||||
$condition = $this->db->sql()->where('cid = ?', $post);
|
||||
$postObject = $this->db->fetchObject($this->db->select('status', 'type')
|
||||
->from('table.contents')->where('cid = ? AND type = ?', $post, 'post'));
|
||||
$condition = $this->db->sql()->where('cid = ?', $post);
|
||||
$postObject = $this->db->fetchObject($this->db->select('status', 'type')
|
||||
->from('table.contents')->where('cid = ? AND type = ?', $post, 'post'));
|
||||
|
||||
if ($this->isWriteable($condition) &&
|
||||
if ($this->isWriteable($condition) &&
|
||||
$postObject &&
|
||||
$this->delete($condition)) {
|
||||
|
||||
/** 删除分类 */
|
||||
$this->setCategories($post, array(), 'publish' == $postObject->status
|
||||
&& 'post' == $postObject->type);
|
||||
/** 删除分类 */
|
||||
$this->setCategories($post, array(), 'publish' == $postObject->status
|
||||
&& 'post' == $postObject->type);
|
||||
|
||||
/** 删除标签 */
|
||||
$this->setTags($post, NULL, 'publish' == $postObject->status
|
||||
&& 'post' == $postObject->type);
|
||||
/** 删除标签 */
|
||||
$this->setTags($post, NULL, 'publish' == $postObject->status
|
||||
&& 'post' == $postObject->type);
|
||||
|
||||
/** 删除评论 */
|
||||
$this->db->query($this->db->delete('table.comments')
|
||||
/** 删除评论 */
|
||||
$this->db->query($this->db->delete('table.comments')
|
||||
->where('cid = ?', $post));
|
||||
|
||||
/** 解除附件关联 */
|
||||
$this->unAttach($post);
|
||||
/** 解除附件关联 */
|
||||
$this->unAttach($post);
|
||||
|
||||
/** 删除草稿 */
|
||||
$draft = $this->db->fetchRow($this->db->select('cid')
|
||||
/** 删除草稿 */
|
||||
$draft = $this->db->fetchRow($this->db->select('cid')
|
||||
->from('table.contents')
|
||||
->where('table.contents.parent = ? AND table.contents.type = ?',
|
||||
$post, 'post_draft')
|
||||
->limit(1));
|
||||
|
||||
/** 删除自定义字段 */
|
||||
$this->deleteFields($post);
|
||||
/** 删除自定义字段 */
|
||||
$this->deleteFields($post);
|
||||
|
||||
if ($draft) {
|
||||
$this->deleteDraft($draft['cid']);
|
||||
$this->deleteFields($draft['cid']);
|
||||
}
|
||||
|
||||
// 完成删除插件接口
|
||||
$this->pluginHandle()->finishDelete($post, $this);
|
||||
|
||||
$deleteCount ++;
|
||||
if ($draft) {
|
||||
$this->deleteDraft($draft['cid']);
|
||||
$this->deleteFields($draft['cid']);
|
||||
}
|
||||
|
||||
unset($condition);
|
||||
// 完成删除插件接口
|
||||
$this->pluginHandle()->finishDelete($post, $this);
|
||||
|
||||
$deleteCount ++;
|
||||
}
|
||||
|
||||
// 清理标签
|
||||
if ($deleteCount > 0) {
|
||||
$this->widget('Widget_Abstract_Metas')->clearTags();
|
||||
}
|
||||
unset($condition);
|
||||
}
|
||||
|
||||
// 清理标签
|
||||
if ($deleteCount > 0) {
|
||||
$this->widget('Widget_Abstract_Metas')->clearTags();
|
||||
}
|
||||
|
||||
/** 设置提示信息 */
|
||||
@@ -849,29 +845,24 @@ class Widget_Contents_Post_Edit extends Widget_Abstract_Contents implements Widg
|
||||
*/
|
||||
public function deletePostDraft()
|
||||
{
|
||||
$cid = $this->request->filter('int')->cid;
|
||||
$posts = $this->request->filter('int')->getArray('cid');
|
||||
$deleteCount = 0;
|
||||
|
||||
if ($cid) {
|
||||
/** 格式化文章主键 */
|
||||
$posts = is_array($cid) ? $cid : array($cid);
|
||||
|
||||
foreach ($posts as $post) {
|
||||
/** 删除草稿 */
|
||||
$draft = $this->db->fetchRow($this->db->select('cid')
|
||||
|
||||
foreach ($posts as $post) {
|
||||
/** 删除草稿 */
|
||||
$draft = $this->db->fetchRow($this->db->select('cid')
|
||||
->from('table.contents')
|
||||
->where('table.contents.parent = ? AND table.contents.type = ?',
|
||||
$post, 'post_draft')
|
||||
->limit(1));
|
||||
|
||||
if ($draft) {
|
||||
$this->deleteDraft($draft['cid']);
|
||||
$this->deleteFields($draft['cid']);
|
||||
$deleteCount ++;
|
||||
}
|
||||
if ($draft) {
|
||||
$this->deleteDraft($draft['cid']);
|
||||
$this->deleteFields($draft['cid']);
|
||||
$deleteCount ++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 设置提示信息 */
|
||||
$this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('草稿已经被删除') : _t('没有草稿被删除'),
|
||||
$deleteCount > 0 ? 'success' : 'notice');
|
||||
|
||||
@@ -305,18 +305,16 @@ class Widget_Metas_Category_Edit extends Widget_Abstract_Metas implements Widget
|
||||
*/
|
||||
public function deleteCategory()
|
||||
{
|
||||
$categories = $this->request->filter('int')->mid;
|
||||
$categories = $this->request->filter('int')->getArray('mid');
|
||||
$deleteCount = 0;
|
||||
|
||||
if ($categories && is_array($categories)) {
|
||||
foreach ($categories as $category) {
|
||||
$parent = $this->db->fetchObject($this->select()->where('mid = ?', $category))->parent;
|
||||
foreach ($categories as $category) {
|
||||
$parent = $this->db->fetchObject($this->select()->where('mid = ?', $category))->parent;
|
||||
|
||||
if ($this->delete($this->db->sql()->where('mid = ?', $category))) {
|
||||
$this->db->query($this->db->delete('table.relationships')->where('mid = ?', $category));
|
||||
$this->update(array('parent' => $parent), $this->db->sql()->where('parent = ?', $category));
|
||||
$deleteCount ++;
|
||||
}
|
||||
if ($this->delete($this->db->sql()->where('mid = ?', $category))) {
|
||||
$this->db->query($this->db->delete('table.relationships')->where('mid = ?', $category));
|
||||
$this->update(array('parent' => $parent), $this->db->sql()->where('parent = ?', $category));
|
||||
$deleteCount ++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,9 +345,9 @@ class Widget_Metas_Category_Edit extends Widget_Abstract_Metas implements Widget
|
||||
}
|
||||
|
||||
$merge = $this->request->merge;
|
||||
$categories = $this->request->filter('int')->mid;
|
||||
$categories = $this->request->filter('int')->getArray('mid');
|
||||
|
||||
if ($categories && is_array($categories)) {
|
||||
if ($categories) {
|
||||
$this->merge($merge, 'category', $categories);
|
||||
|
||||
/** 提示信息 */
|
||||
@@ -370,8 +368,8 @@ class Widget_Metas_Category_Edit extends Widget_Abstract_Metas implements Widget
|
||||
*/
|
||||
public function sortCategory()
|
||||
{
|
||||
$categories = $this->request->filter('int')->mid;
|
||||
if ($categories && is_array($categories)) {
|
||||
$categories = $this->request->filter('int')->getArray('mid');
|
||||
if ($categories) {
|
||||
$this->sort($categories, 'category');
|
||||
}
|
||||
|
||||
@@ -391,8 +389,8 @@ class Widget_Metas_Category_Edit extends Widget_Abstract_Metas implements Widget
|
||||
*/
|
||||
public function refreshCategory()
|
||||
{
|
||||
$categories = $this->request->filter('int')->mid;
|
||||
if ($categories && is_array($categories)) {
|
||||
$categories = $this->request->filter('int')->getArray('mid');
|
||||
if ($categories) {
|
||||
foreach ($categories as $category) {
|
||||
$this->refreshCountByTypeAndStatus($category, 'post', 'publish');
|
||||
}
|
||||
@@ -445,8 +443,8 @@ class Widget_Metas_Category_Edit extends Widget_Abstract_Metas implements Widget
|
||||
/**
|
||||
* 获取菜单标题
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @throws Typecho_Widget_Exception
|
||||
*/
|
||||
public function getMenuTitle()
|
||||
{
|
||||
|
||||
@@ -306,9 +306,9 @@ class Widget_Metas_Tag_Edit extends Widget_Abstract_Metas implements Widget_Inte
|
||||
$this->response->goBack();
|
||||
}
|
||||
|
||||
$tags = $this->request->filter('int')->mid;
|
||||
$tags = $this->request->filter('int')->getArray('mid');
|
||||
|
||||
if ($tags && is_array($tags)) {
|
||||
if ($tags) {
|
||||
$this->merge($merge, 'tag', $tags);
|
||||
|
||||
/** 提示信息 */
|
||||
@@ -329,8 +329,8 @@ class Widget_Metas_Tag_Edit extends Widget_Abstract_Metas implements Widget_Inte
|
||||
*/
|
||||
public function refreshTag()
|
||||
{
|
||||
$tags = $this->request->filter('int')->mid;
|
||||
if ($tags && is_array($tags)) {
|
||||
$tags = $this->request->filter('int')->getArray('mid');
|
||||
if ($tags) {
|
||||
foreach ($tags as $tag) {
|
||||
$this->refreshCountByTypeAndStatus($tag, 'post', 'publish');
|
||||
}
|
||||
|
||||
@@ -268,18 +268,16 @@ class Widget_Users_Edit extends Widget_Abstract_Users implements Widget_Interfac
|
||||
*/
|
||||
public function deleteUser()
|
||||
{
|
||||
$users = $this->request->uid;
|
||||
$users = $this->request->filter('int')->getArray('uid');
|
||||
$deleteCount = 0;
|
||||
|
||||
if ($users && is_array($users)) {
|
||||
foreach ($users as $user) {
|
||||
if (1 == $user) {
|
||||
continue;
|
||||
}
|
||||
foreach ($users as $user) {
|
||||
if (1 == $user || $user == $this->user->id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->delete($this->db->sql()->where('uid = ?', $user))) {
|
||||
$deleteCount ++;
|
||||
}
|
||||
if ($this->delete($this->db->sql()->where('uid = ?', $user))) {
|
||||
$deleteCount ++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user