diff --git a/extend/.gitignore b/extend/.gitignore
deleted file mode 100755
index a3a0c8b..0000000
--- a/extend/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
\ No newline at end of file
diff --git a/extend/.htaccess b/extend/.htaccess
new file mode 100644
index 0000000..3418e55
--- /dev/null
+++ b/extend/.htaccess
@@ -0,0 +1 @@
+deny from all
\ No newline at end of file
diff --git a/extend/fast/Auth.php b/extend/fast/Auth.php
new file mode 100644
index 0000000..2d7d677
--- /dev/null
+++ b/extend/fast/Auth.php
@@ -0,0 +1,288 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2011 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: luofei614 <weibo.com/luofei614>
+// +----------------------------------------------------------------------
+// | 修改者: anuo (本权限类在原3.2.3的基础上修改过来的)
+// +----------------------------------------------------------------------
+
+namespace fast;
+
+use think\Db;
+use think\Config;
+use think\Session;
+use think\Request;
+use think\Loader;
+
+/**
+ * 权限认证类
+ * 功能特性:
+ * 1,是对规则进行认证,不是对节点进行认证。用户可以把节点当作规则名称实现对节点进行认证。
+ *      $auth=new Auth();  $auth->check('规则名称','用户id')
+ * 2,可以同时对多条规则进行认证,并设置多条规则的关系(or或者and)
+ *      $auth=new Auth();  $auth->check('规则1,规则2','用户id','and')
+ *      第三个参数为and时表示,用户需要同时具有规则1和规则2的权限。 当第三个参数为or时,表示用户值需要具备其中一个条件即可。默认为or
+ * 3,一个用户可以属于多个用户组(think_auth_group_access表 定义了用户所属用户组)。我们需要设置每个用户组拥有哪些规则(think_auth_group 定义了用户组权限)
+ * 4,支持规则表达式。
+ *      在think_auth_rule 表中定义一条规则,condition字段就可以定义规则表达式。 如定义{score}>5  and {score}<100
+ * 表示用户的分数在5-100之间时这条规则才会通过。
+ */
+class Auth
+{
+
+    /**
+     * @var object 对象实例
+     */
+    protected static $instance;
+
+    /**
+     * 当前请求实例
+     * @var Request
+     */
+    protected $request;
+    //默认配置
+    protected $config = [
+        'auth_on'           => 1, // 权限开关
+        'auth_type'         => 1, // 认证方式,1为实时认证;2为登录认证。
+        'auth_group'        => 'auth_group', // 用户组数据表名
+        'auth_group_access' => 'auth_group_access', // 用户-用户组关系表
+        'auth_rule'         => 'auth_rule', // 权限规则表
+        'auth_user'         => 'user', // 用户信息表
+    ];
+
+    /**
+     * 类架构函数
+     * Auth constructor.
+     */
+    public function __construct()
+    {
+        if ($auth = Config::get('auth'))
+        {
+            $this->config = array_merge($this->config, $auth);
+        }
+        // 初始化request
+        $this->request = Request::instance();
+    }
+
+    /**
+     * 初始化
+     * @access public
+     * @param array $options 参数
+     * @return Auth
+     */
+    public static function instance($options = [])
+    {
+        if (is_null(self::$instance))
+        {
+            self::$instance = new static($options);
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * 检查权限
+     * @param       $name   string|array    需要验证的规则列表,支持逗号分隔的权限规则或索引数组
+     * @param       $uid    int             认证用户的id
+     * @param       string  $relation       如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证
+     * @param       string  $mode           执行验证的模式,可分为url,normal
+     * @return bool               通过验证返回true;失败返回false
+     */
+    public function check($name, $uid, $relation = 'or', $mode = 'url')
+    {
+        if (!$this->config['auth_on'])
+        {
+            return true;
+        }
+        // 获取用户需要验证的所有有效规则列表
+        $rulelist = $this->getRuleList($uid);
+        if (in_array('*', $rulelist))
+            return true;
+
+        if (is_string($name))
+        {
+            $name = strtolower($name);
+            if (strpos($name, ',') !== false)
+            {
+                $name = explode(',', $name);
+            }
+            else
+            {
+                $name = [$name];
+            }
+        }
+        $list = []; //保存验证通过的规则名
+        if ('url' == $mode)
+        {
+            $REQUEST = unserialize(strtolower(serialize($this->request->param())));
+        }
+        foreach ($rulelist as $rule)
+        {
+            $query = preg_replace('/^.+\?/U', '', $rule);
+            if ('url' == $mode && $query != $rule)
+            {
+                parse_str($query, $param); //解析规则中的param
+                $intersect = array_intersect_assoc($REQUEST, $param);
+                $rule = preg_replace('/\?.*$/U', '', $rule);
+                if (in_array($rule, $name) && $intersect == $param)
+                {
+                    //如果节点相符且url参数满足
+                    $list[] = $rule;
+                }
+            }
+            else
+            {
+                if (in_array($rule, $name))
+                {
+                    $list[] = $rule;
+                }
+            }
+        }
+        if ('or' == $relation && !empty($list))
+        {
+            return true;
+        }
+        $diff = array_diff($name, $list);
+        if ('and' == $relation && empty($diff))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 根据用户id获取用户组,返回值为数组
+     * @param  $uid int     用户id
+     * @return array       用户所属的用户组 array(
+     *              array('uid'=>'用户id','group_id'=>'用户组id','name'=>'用户组名称','rules'=>'用户组拥有的规则id,多个,号隔开'),
+     *              ...)
+     */
+    public function getGroups($uid)
+    {
+        static $groups = [];
+        if (isset($groups[$uid]))
+        {
+            return $groups[$uid];
+        }
+        // 转换表名
+        $auth_group_access = Loader::parseName($this->config['auth_group_access'], 1);
+        $auth_group = Loader::parseName($this->config['auth_group'], 1);
+        // 执行查询
+        $user_groups = Db::view($auth_group_access, 'uid,group_id')
+                ->view($auth_group, 'id,pid,name,rules', "{$auth_group_access}.group_id={$auth_group}.id", 'LEFT')
+                ->where("{$auth_group_access}.uid='{$uid}' and {$auth_group}.status='normal'")
+                ->select();
+        $groups[$uid] = $user_groups ? : [];
+
+        return $groups[$uid];
+    }
+
+    /**
+     * 获得权限规则列表
+     * @param integer $uid 用户id
+     * @return array
+     */
+    public function getRuleList($uid)
+    {
+        static $_rulelist = []; //保存用户验证通过的权限列表
+        if (isset($_rulelist[$uid]))
+        {
+            return $_rulelist[$uid];
+        }
+        if (2 == $this->config['auth_type'] && Session::has('_rule_list_' . $uid))
+        {
+            return Session::get('_rule_list_' . $uid);
+        }
+
+        // 读取用户规则节点
+        $ids = $this->getRuleIds($uid);
+        if (empty($ids))
+        {
+            $_rulelist[$uid] = [];
+            return [];
+        }
+
+        // 筛选条件
+        $where = [
+            'status' => 'normal'
+        ];
+        if (!in_array('*', $ids))
+        {
+            $where['id'] = ['in', $ids];
+        }
+        //读取用户组所有权限规则
+        $rules = Db::name($this->config['auth_rule'])->where($where)->field('id,condition,name,ismenu')->select();
+        //循环规则,判断结果。
+        $rulelist = []; //
+        foreach ($rules as $rule)
+        {
+            //超级管理员无需验证condition
+            if (!empty($rule['condition']) && !in_array('*', $ids))
+            {
+                //根据condition进行验证
+                $user = $this->getUserInfo($uid); //获取用户信息,一维数组
+                $command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']);
+                @(eval('$condition=(' . $command . ');'));
+                if ($condition)
+                {
+                    $rulelist[$rule['id']] = strtolower($rule['name']);
+                }
+            }
+            else
+            {
+                //只要存在就记录
+                $rulelist[$rule['id']] = strtolower($rule['name']);
+            }
+        }
+        $_rulelist[$uid] = $rulelist;
+
+        //登录验证则需要保存规则列表
+        if (2 == $this->config['auth_type'])
+        {
+            //规则列表结果保存到session
+            Session::set('_rule_list_' . $uid, $rulelist);
+        }
+        return array_unique($rulelist);
+    }
+
+    public function getRuleIds($uid)
+    {
+        //读取用户所属用户组
+        $groups = $this->getGroups($uid);
+        $ids = []; //保存用户所属用户组设置的所有权限规则id
+        foreach ($groups as $g)
+        {
+            $ids = array_merge($ids, explode(',', trim($g['rules'], ',')));
+        }
+        $ids = array_unique($ids);
+        return $ids;
+    }
+
+    /**
+     * 获得用户资料
+     * @param $uid
+     * @return mixed
+     */
+    protected function getUserInfo($uid)
+    {
+        static $user_info = [];
+
+        $user = Db::name($this->config['auth_user']);
+        // 获取用户表主键
+        $_pk = is_string($user->getPk()) ? $user->getPk() : 'uid';
+        if (!isset($user_info[$uid]))
+        {
+            $user_info[$uid] = $user->where($_pk, $uid)->find();
+        }
+
+        return $user_info[$uid];
+    }
+
+}
diff --git a/extend/fast/Date.php b/extend/fast/Date.php
new file mode 100644
index 0000000..8e84d04
--- /dev/null
+++ b/extend/fast/Date.php
@@ -0,0 +1,287 @@
+<?php
+
+namespace fast;
+
+/**
+ * 日期时间处理类
+ */
+class Date
+{
+
+    const YEAR = 31536000;
+    const MONTH = 2592000;
+    const WEEK = 604800;
+    const DAY = 86400;
+    const HOUR = 3600;
+    const MINUTE = 60;
+
+    /**
+     * 计算两个时区间相差的时长,单位为秒
+     *
+     * $seconds = self::offset('America/Chicago', 'GMT');
+     *
+     * [!!] A list of time zones that PHP supports can be found at
+     * <http://php.net/timezones>.
+     *
+     * @param   string  $remote timezone that to find the offset of
+     * @param   string  $local  timezone used as the baseline
+     * @param   mixed   $now    UNIX timestamp or date string
+     * @return  integer
+     */
+    public static function offset($remote, $local = NULL, $now = NULL)
+    {
+        if ($local === NULL)
+        {
+            // Use the default timezone
+            $local = date_default_timezone_get();
+        }
+        if (is_int($now))
+        {
+            // Convert the timestamp into a string
+            $now = date(DateTime::RFC2822, $now);
+        }
+        // Create timezone objects
+        $zone_remote = new DateTimeZone($remote);
+        $zone_local = new DateTimeZone($local);
+        // Create date objects from timezones
+        $time_remote = new DateTime($now, $zone_remote);
+        $time_local = new DateTime($now, $zone_local);
+        // Find the offset
+        $offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
+        return $offset;
+    }
+
+    /**
+     * 计算两个时间戳之间相差的时间
+     *
+     * $span = self::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2)
+     * $span = self::span(60, 182, 'minutes'); // 2
+     *
+     * @param   int $remote timestamp to find the span of
+     * @param   int $local  timestamp to use as the baseline
+     * @param   string  $output formatting string
+     * @return  string   when only a single output is requested
+     * @return  array    associative list of all outputs requested
+     */
+    public static function span($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
+    {
+        // Normalize output
+        $output = trim(strtolower((string) $output));
+        if (!$output)
+        {
+            // Invalid output
+            return FALSE;
+        }
+        // Array with the output formats
+        $output = preg_split('/[^a-z]+/', $output);
+        // Convert the list of outputs to an associative array
+        $output = array_combine($output, array_fill(0, count($output), 0));
+        // Make the output values into keys
+        extract(array_flip($output), EXTR_SKIP);
+        if ($local === NULL)
+        {
+            // Calculate the span from the current time
+            $local = time();
+        }
+        // Calculate timespan (seconds)
+        $timespan = abs($remote - $local);
+        if (isset($output['years']))
+        {
+            $timespan -= self::YEAR * ($output['years'] = (int) floor($timespan / self::YEAR));
+        }
+        if (isset($output['months']))
+        {
+            $timespan -= self::MONTH * ($output['months'] = (int) floor($timespan / self::MONTH));
+        }
+        if (isset($output['weeks']))
+        {
+            $timespan -= self::WEEK * ($output['weeks'] = (int) floor($timespan / self::WEEK));
+        }
+        if (isset($output['days']))
+        {
+            $timespan -= self::DAY * ($output['days'] = (int) floor($timespan / self::DAY));
+        }
+        if (isset($output['hours']))
+        {
+            $timespan -= self::HOUR * ($output['hours'] = (int) floor($timespan / self::HOUR));
+        }
+        if (isset($output['minutes']))
+        {
+            $timespan -= self::MINUTE * ($output['minutes'] = (int) floor($timespan / self::MINUTE));
+        }
+        // Seconds ago, 1
+        if (isset($output['seconds']))
+        {
+            $output['seconds'] = $timespan;
+        }
+        if (count($output) === 1)
+        {
+            // Only a single output was requested, return it
+            return array_pop($output);
+        }
+        // Return array
+        return $output;
+    }
+
+    /**
+     * 格式化 UNIX 时间戳为人易读的字符串
+     *
+     * @param	int	Unix 时间戳
+     * @param	mixed	$local 本地时间
+     *
+     * @return	string	格式化的日期字符串
+     */
+    public static function human($remote, $local = null)
+    {
+        $timediff = (is_null($local) || $local ? time() : $local) - $remote;
+        $chunks = array(
+            array(60 * 60 * 24 * 365, 'year'),
+            array(60 * 60 * 24 * 30, 'month'),
+            array(60 * 60 * 24 * 7, 'week'),
+            array(60 * 60 * 24, 'day'),
+            array(60 * 60, 'hour'),
+            array(60, 'minute'),
+            array(1, 'second')
+        );
+
+        for ($i = 0, $j = count($chunks); $i < $j; $i++)
+        {
+            $seconds = $chunks[$i][0];
+            $name = $chunks[$i][1];
+            if (($count = floor($timediff / $seconds)) != 0)
+            {
+                break;
+            }
+        }
+        return __("%d {$name}%s ago", $count, ($count > 1 ? 's' : ''));
+    }
+
+    /**
+     * 判断Unix时间是否满足Cron指定的执行条件
+     *
+     * @param string $cron Crontab格式
+     * @param string $time 时间,默认为当前时间
+     * @return boolean
+     */
+    public static function cron($cron, $time = null)
+    {
+        $time = is_null($time) ? time() : $time;
+        $cron_parts = explode(' ', $cron);
+        if (count($cron_parts) != 5)
+        {
+            return false;
+        }
+        list($min, $hour, $day, $mon, $week) = explode(' ', $cron);
+        $to_check = array('min' => 'i', 'hour' => 'G', 'day' => 'j', 'mon' => 'n', 'week' => 'w');
+        $ranges = array(
+            'min'  => '0-59',
+            'hour' => '0-23',
+            'day'  => '1-31',
+            'mon'  => '1-12',
+            'week' => '0-6',
+        );
+
+        foreach ($to_check as $part => $c)
+        {
+            $val = $$part;
+            $values = [];
+            if (strpos($val, '/') !== false)
+            {
+                //Get the range and step
+                list($range, $steps) = explode('/', $val);
+                //Now get the start and stop
+                if ($range == '*')
+                {
+                    $range = $ranges[$part];
+                }
+                list($start, $stop) = explode('-', $range);
+                for ($i = $start; $i <= $stop; $i = $i + $steps)
+                {
+                    $values[] = $i;
+                }
+            }
+            else
+            {
+                $k = explode(',', $val);
+                foreach ($k as $v)
+                {
+                    if (strpos($v, '-') !== false)
+                    {
+                        list($start, $stop) = explode('-', $v);
+
+                        for ($i = $start; $i <= $stop; $i++)
+                        {
+                            $values[] = $i;
+                        }
+                    }
+                    else
+                    {
+                        $values[] = $v;
+                    }
+                }
+            }
+            if (!in_array(date($c, $time), $values) and ( strval($val) != '*'))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 获取一个基于时间偏移的Unix时间戳
+     *
+     * @param string $type 时间类型,默认为day,可选minute,hour,day,week,month,quarter,year
+     * @param int $offset 时间偏移量 默认为0,正数表示当前type之后,负数表示当前type之前
+     * @param string $position 时间的开始或结束,默认为begin,可选前(begin,start,first,front),end
+     * @param int $year 基准年,默认为null,即以当前年为基准
+     * @param int $month 基准月,默认为null,即以当前月为基准
+     * @param int $day 基准天,默认为null,即以当前天为基准
+     * @param int $hour 基准小时,默认为null,即以当前年小时基准
+     * @param int $minute 基准分钟,默认为null,即以当前分钟为基准
+     * @return int 处理后的Unix时间戳
+     */
+    public static function unixtime($type = 'day', $offset = 0, $position = 'begin', $year = null, $month = null, $day = null, $hour = null, $minute = null)
+    {
+        $year = is_null($year) ? date('Y') : $year;
+        $month = is_null($month) ? date('m') : $month;
+        $day = is_null($day) ? date('d') : $day;
+        $hour = is_null($hour) ? date('H') : $hour;
+        $minute = is_null($minute) ? date('i') : $minute;
+        $position = in_array($position, array('begin', 'start', 'first', 'front'));
+
+        switch ($type)
+        {
+            case 'minute':
+                $time = $position ? mktime($hour, $minute + $offset, 0, $month, $day, $year) : mktime($hour, $minute + $offset, 59, $month, $day, $year);
+                break;
+            case 'hour':
+                $time = $position ? mktime($hour + $offset, 0, 0, $month, $day, $year) : mktime($hour + $offset, 59, 59, $month, $day, $year);
+                break;
+            case 'day':
+                $time = $position ? mktime(0, 0, 0, $month, $day + $offset, $year) : mktime(23, 59, 59, $month, $day + $offset, $year);
+                break;
+            case 'week':
+                $time = $position ?
+                        mktime(0, 0, 0, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 1 - 7 * (-$offset), $year) :
+                        mktime(23, 59, 59, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 7 - 7 * (-$offset), $year);
+                break;
+            case 'month':
+                $time = $position ? mktime(0, 0, 0, $month + $offset, 1, $year) : mktime(23, 59, 59, $month + $offset, get_month_days($month + $offset, $year), $year);
+                break;
+            case 'quarter':
+                $time = $position ?
+                        mktime(0, 0, 0, 1 + ((ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) - 1) * 3, 1, $year) :
+                        mktime(23, 59, 59, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, get_month_days((ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, $year), $year);
+                break;
+            case 'year':
+                $time = $position ? mktime(0, 0, 0, 1, 1, $year + $offset) : mktime(23, 59, 59, 12, 31, $year + $offset);
+                break;
+            default:
+                $time = mktime($hour, $minute, 0, $month, $day, $year);
+                break;
+        }
+        return $time;
+    }
+
+}
diff --git a/extend/fast/Form.php b/extend/fast/Form.php
new file mode 100644
index 0000000..af877e2
--- /dev/null
+++ b/extend/fast/Form.php
@@ -0,0 +1,1201 @@
+<?php
+
+namespace fast;
+
+use ArrayAccess;
+
+/**
+ * @class Form
+ * @package fast
+ * @method mixed token() static token
+ * @method mixed close() static 关闭一个HTML表单
+ * @method mixed setModel(mixed $model) static 设置HTML表单模型
+ * @method mixed open(array $options) static 打开一个新的HTML表单
+ * @method mixed file(string $name, array $options = array()) static 表单file
+ * @method mixed reset(string $name, array $options = array()) static 表单reset
+ * @method mixed password(string $name, array $options = array()) static 表单password
+ * @method mixed button(string $value = null, array $options = array()) static 表单button
+ * @method mixed model(mixed $model ,array $options = array()) static 打开一个新的HTML表单模型
+ * @method mixed submit(string $value = null, array $options = array()) static 表单submit提交
+ * @method mixed text(string $name, string $value = null, array $options = []) static 表单text
+ * @method mixed label(string $name, string $value = null, array $options = []) static label标签
+ * @method mixed url(string $name, string $value = null, array $options = array()) static 表单url
+ * @method mixed email(string $name, string $value = null, array $options = array()) static 表单email
+ * @method mixed hidden(string $name, string $value = null, array $options = array()) static 表单hidden
+ * @method mixed image(string $url, string $name = null, array $attributes = array()) static 表单image提交
+ * @method mixed textarea(string $name, string $value = null, array $options = array()) static 表单textarea
+ * @method mixed checkbox(string $name, string $value = 1, string $checked = null, array $options = array()) static 表单checkbox
+ * @method mixed radio(string $name, string $value = null, string $checked = null, array $options = array())) static 表单radio
+ * @method mixed select(string $name, array $list = array(), string $selected = null, array $options = array()) static 表单textarea
+ * @method mixed selectYear(string $name ,string $begin,string $end, string $selected = null ,array $options = array()) static select年
+ * @method mixed selectMonth(string $name ,string $selected = null ,array $options = array() ,string $format = '%m') static select月
+ */
+class Form
+{
+
+    public function __construct()
+    {
+
+    }
+
+    public static function __callStatic($name, $arguments)
+    {
+        return call_user_func_array([FormBuilder::instance(), $name], $arguments);
+    }
+
+}
+
+class FormBuilder
+{
+
+    /**
+     * The CSRF token used by the form builder.
+     *
+     * @var string
+     */
+    protected $csrfToken = array('name' => '__token__');
+
+    /**
+     * The current model instance for the form.
+     *
+     * @var mixed
+     */
+    protected $model;
+
+    /**
+     * An array of label names we've created.
+     *
+     * @var array
+     */
+    protected $labels = array();
+
+    /**
+     * The reserved form open attributes.
+     *
+     * @var array
+     */
+    protected $reserved = array('method', 'url', 'route', 'action', 'files');
+
+    /**
+     * The form methods that should be spoofed, in uppercase.
+     *
+     * @var array
+     */
+    protected $spoofedMethods = array('DELETE', 'PATCH', 'PUT');
+
+    /**
+     * The types of inputs to not fill values on by default.
+     *
+     * @var array
+     */
+    protected $skipValueTypes = array('file', 'password', 'checkbox', 'radio');
+    protected static $instance;
+
+    /**
+     * Create a new form builder instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+
+    }
+
+    public static function instance($options = [])
+    {
+        if (is_null(self::$instance))
+        {
+            self::$instance = new static($options);
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * Open up a new HTML form.
+     *
+     * @param  array   $options
+     * @return string
+     */
+    public function open(array $options = array())
+    {
+        $method = array_get($options, 'method', 'post');
+
+        // We need to extract the proper method from the attributes. If the method is
+        // something other than GET or POST we'll use POST since we will spoof the
+        // actual method since forms don't support the reserved methods in HTML.
+        $attributes['method'] = $this->getMethod($method);
+
+        $attributes['action'] = array_get($options, 'action', '');
+
+        $attributes['accept-charset'] = 'UTF-8';
+
+        // If the method is PUT, PATCH or DELETE we will need to add a spoofer hidden
+        // field that will instruct the Symfony request to pretend the method is a
+        // different method than it actually is, for convenience from the forms.
+        $append = $this->getAppendage($method);
+
+        if (isset($options['files']) && $options['files'])
+        {
+            $options['enctype'] = 'multipart/form-data';
+        }
+
+        // Finally we're ready to create the final form HTML field. We will attribute
+        // format the array of attributes. We will also add on the appendage which
+        // is used to spoof requests for this PUT, PATCH, etc. methods on forms.
+        $attributes = array_merge(
+                $attributes, array_except($options, $this->reserved)
+        );
+        // Finally, we will concatenate all of the attributes into a single string so
+        // we can build out the final form open statement. We'll also append on an
+        // extra value for the hidden _method field if it's needed for the form.
+        $attributes = $this->attributes($attributes);
+
+        return '<form' . $attributes . '>' . $append;
+    }
+
+    /**
+     * Create a new model based form builder.
+     *
+     * @param  mixed  $model
+     * @param  array  $options
+     * @return string
+     */
+    public function model($model, array $options = array())
+    {
+        $this->model = $model;
+
+        return $this->open($options);
+    }
+
+    /**
+     * Set the model instance on the form builder.
+     *
+     * @param  mixed  $model
+     * @return void
+     */
+    public function setModel($model)
+    {
+        $this->model = $model;
+    }
+
+    /**
+     * Close the current form.
+     *
+     * @return string
+     */
+    public function close()
+    {
+        $this->labels = array();
+
+        $this->model = null;
+
+        return '</form>';
+    }
+
+    /**
+     * Generate a hidden field with the current CSRF token.
+     *
+     * @return string
+     */
+    public function token($name = '__token__', $type = 'md5')
+    {
+        if (function_exists('token'))
+        {
+            return token($name, $type);
+        }
+
+        return '';
+    }
+
+    /**
+     * Create a form label element.
+     *
+     * @param  string  $name
+     * @param  string  $value
+     * @param  array   $options
+     * @return string
+     */
+    public function label($name, $value = null, $options = array())
+    {
+        $this->labels[] = $name;
+
+        $options = $this->attributes($options);
+
+        $value = e($this->formatLabel($name, $value));
+
+        return '<label for="' . $name . '"' . $options . '>' . $value . '</label>';
+    }
+
+    /**
+     * Format the label value.
+     *
+     * @param  string  $name
+     * @param  string|null  $value
+     * @return string
+     */
+    protected function formatLabel($name, $value)
+    {
+        return $value ? : ucwords(str_replace('_', ' ', $name));
+    }
+
+    /**
+     * Create a form input field.
+     *
+     * @param  string  $type
+     * @param  string  $name
+     * @param  string  $value
+     * @param  array   $options
+     * @return string
+     */
+    public function input($type, $name, $value = null, $options = array())
+    {
+        if (!isset($options['name']))
+            $options['name'] = $name;
+
+        // We will get the appropriate value for the given field. We will look for the
+        // value in the session for the value in the old input data then we'll look
+        // in the model instance if one is set. Otherwise we will just use empty.
+        $id = $this->getIdAttribute($name, $options);
+
+        if (!in_array($type, $this->skipValueTypes))
+        {
+            $value = $this->getValueAttribute($name, $value);
+        }
+
+        // Once we have the type, value, and ID we can merge them into the rest of the
+        // attributes array so we can convert them into their HTML attribute format
+        // when creating the HTML element. Then, we will return the entire input.
+        $merge = compact('type', 'value', 'id');
+
+        $options = array_merge($options, $merge);
+
+        return '<input' . $this->attributes($options) . '>';
+    }
+
+    /**
+     * Create a text input field.
+     *
+     * @param  string  $name
+     * @param  string  $value
+     * @param  array   $options
+     * @return string
+     */
+    public function text($name, $value = null, $options = array())
+    {
+        return $this->input('text', $name, $value, $options);
+    }
+
+    /**
+     * Create a password input field.
+     *
+     * @param  string  $name
+     * @param  array   $options
+     * @return string
+     */
+    public function password($name, $options = array())
+    {
+        return $this->input('password', $name, '', $options);
+    }
+
+    /**
+     * Create a hidden input field.
+     *
+     * @param  string  $name
+     * @param  string  $value
+     * @param  array   $options
+     * @return string
+     */
+    public function hidden($name, $value = null, $options = array())
+    {
+        return $this->input('hidden', $name, $value, $options);
+    }
+
+    /**
+     * Create an e-mail input field.
+     *
+     * @param  string  $name
+     * @param  string  $value
+     * @param  array   $options
+     * @return string
+     */
+    public function email($name, $value = null, $options = array())
+    {
+        return $this->input('email', $name, $value, $options);
+    }
+
+    /**
+     * Create a url input field.
+     *
+     * @param  string  $name
+     * @param  string  $value
+     * @param  array   $options
+     * @return string
+     */
+    public function url($name, $value = null, $options = array())
+    {
+        return $this->input('url', $name, $value, $options);
+    }
+
+    /**
+     * Create a file input field.
+     *
+     * @param  string  $name
+     * @param  array   $options
+     * @return string
+     */
+    public function file($name, $options = array())
+    {
+        return $this->input('file', $name, null, $options);
+    }
+
+    /**
+     * Create a textarea input field.
+     *
+     * @param  string  $name
+     * @param  string  $value
+     * @param  array   $options
+     * @return string
+     */
+    public function textarea($name, $value = null, $options = array())
+    {
+        if (!isset($options['name']))
+            $options['name'] = $name;
+
+        // Next we will look for the rows and cols attributes, as each of these are put
+        // on the textarea element definition. If they are not present, we will just
+        // assume some sane default values for these attributes for the developer.
+        $options = $this->setTextAreaSize($options);
+
+        $options['id'] = $this->getIdAttribute($name, $options);
+
+        $value = (string) $this->getValueAttribute($name, $value);
+
+        unset($options['size']);
+
+        // Next we will convert the attributes into a string form. Also we have removed
+        // the size attribute, as it was merely a short-cut for the rows and cols on
+        // the element. Then we'll create the final textarea elements HTML for us.
+        $options = $this->attributes($options);
+
+        return '<textarea' . $options . '>' . e($value) . '</textarea>';
+    }
+
+    /**
+     * Set the text area size on the attributes.
+     *
+     * @param  array  $options
+     * @return array
+     */
+    protected function setTextAreaSize($options)
+    {
+        if (isset($options['size']))
+        {
+            return $this->setQuickTextAreaSize($options);
+        }
+
+        // If the "size" attribute was not specified, we will just look for the regular
+        // columns and rows attributes, using sane defaults if these do not exist on
+        // the attributes array. We'll then return this entire options array back.
+        $cols = array_get($options, 'cols', 50);
+
+        $rows = array_get($options, 'rows', 10);
+
+        return array_merge($options, compact('cols', 'rows'));
+    }
+
+    /**
+     * Set the text area size using the quick "size" attribute.
+     *
+     * @param  array  $options
+     * @return array
+     */
+    protected function setQuickTextAreaSize($options)
+    {
+        $segments = explode('x', $options['size']);
+
+        return array_merge($options, array('cols' => $segments[0], 'rows' => $segments[1]));
+    }
+
+    /**
+     * Create a select box field.
+     *
+     * @param  string  $name
+     * @param  array   $list
+     * @param  string  $selected
+     * @param  array   $options
+     * @return string
+     */
+    public function select($name, $list = array(), $selected = null, $options = array())
+    {
+        // When building a select box the "value" attribute is really the selected one
+        // so we will use that when checking the model or session for a value which
+        // should provide a convenient method of re-populating the forms on post.
+        $selected = $this->getValueAttribute($name, $selected);
+
+        $options['id'] = $this->getIdAttribute($name, $options);
+
+        if (!isset($options['name']))
+            $options['name'] = $name;
+
+        // We will simply loop through the options and build an HTML value for each of
+        // them until we have an array of HTML declarations. Then we will join them
+        // all together into one single HTML element that can be put on the form.
+        $html = array();
+
+        foreach ($list as $value => $display)
+        {
+            $html[] = $this->getSelectOption($display, $value, $selected);
+        }
+
+        // Once we have all of this HTML, we can join this into a single element after
+        // formatting the attributes into an HTML "attributes" string, then we will
+        // build out a final select statement, which will contain all the values.
+        $options = $this->attributes($options);
+
+        $list = implode('', $html);
+
+        return "<select{$options}>{$list}</select>";
+    }
+
+    /**
+     * Create a select range field.
+     *
+     * @param  string  $name
+     * @param  string  $begin
+     * @param  string  $end
+     * @param  string  $selected
+     * @param  array   $options
+     * @return string
+     */
+    public function selectRange($name, $begin, $end, $selected = null, $options = array())
+    {
+
+        $range = array_combine($range = range($begin, $end), $range);
+
+        return $this->select($name, $range, $selected, $options);
+    }
+
+    /**
+     * Create a select year field.
+     *
+     * @param  string  $name
+     * @param  string  $begin
+     * @param  string  $end
+     * @param  string  $selected
+     * @param  array   $options
+     * @return string
+     */
+    public function selectYear()
+    {
+        return call_user_func_array(array($this, 'selectRange'), func_get_args());
+    }
+
+    /**
+     * Create a select month field.
+     *
+     * @param  string  $name
+     * @param  string  $selected
+     * @param  array   $options
+     * @param  string  $format
+     * @return string
+     */
+    public function selectMonth($name, $selected = null, $options = array(), $format = '%m')
+    {
+        $months = array();
+
+        foreach (range(1, 12) as $month)
+        {
+            $months[$month] = strftime($format, mktime(0, 0, 0, $month, 1));
+        }
+
+        return $this->select($name, $months, $selected, $options);
+    }
+
+    /**
+     * Get the select option for the given value.
+     *
+     * @param  string  $display
+     * @param  string  $value
+     * @param  string  $selected
+     * @return string
+     */
+    public function getSelectOption($display, $value, $selected)
+    {
+        if (is_array($display))
+        {
+            return $this->optionGroup($display, $value, $selected);
+        }
+
+        return $this->option($display, $value, $selected);
+    }
+
+    /**
+     * Create an option group form element.
+     *
+     * @param  array   $list
+     * @param  string  $label
+     * @param  string  $selected
+     * @return string
+     */
+    protected function optionGroup($list, $label, $selected)
+    {
+        $html = array();
+
+        foreach ($list as $value => $display)
+        {
+            $html[] = $this->option($display, $value, $selected);
+        }
+
+        return '<optgroup label="' . e($label) . '">' . implode('', $html) . '</optgroup>';
+    }
+
+    /**
+     * Create a select element option.
+     *
+     * @param  string  $display
+     * @param  string  $value
+     * @param  string  $selected
+     * @return string
+     */
+    protected function option($display, $value, $selected)
+    {
+        $selected = $this->getSelectedValue($value, $selected);
+
+        $options = array('value' => e($value), 'selected' => $selected);
+
+        return '<option' . $this->attributes($options) . '>' . e($display) . '</option>';
+    }
+
+    /**
+     * Determine if the value is selected.
+     *
+     * @param  string  $value
+     * @param  string  $selected
+     * @return string
+     */
+    protected function getSelectedValue($value, $selected)
+    {
+        if (is_array($selected))
+        {
+            return in_array($value, $selected) ? 'selected' : null;
+        }
+
+        return ((string) $value == (string) $selected) ? 'selected' : null;
+    }
+
+    /**
+     * Create a checkbox input field.
+     *
+     * @param  string  $name
+     * @param  mixed   $value
+     * @param  bool    $checked
+     * @param  array   $options
+     * @return string
+     */
+    public function checkbox($name, $value = 1, $checked = null, $options = array())
+    {
+        return $this->checkable('checkbox', $name, $value, $checked, $options);
+    }
+
+    /**
+     * Create a radio button input field.
+     *
+     * @param  string  $name
+     * @param  mixed   $value
+     * @param  bool    $checked
+     * @param  array   $options
+     * @return string
+     */
+    public function radio($name, $value = null, $checked = null, $options = array())
+    {
+        if (is_null($value))
+            $value = $name;
+
+        return $this->checkable('radio', $name, $value, $checked, $options);
+    }
+
+    /**
+     * Create a checkable input field.
+     *
+     * @param  string  $type
+     * @param  string  $name
+     * @param  mixed   $value
+     * @param  bool    $checked
+     * @param  array   $options
+     * @return string
+     */
+    protected function checkable($type, $name, $value, $checked, $options)
+    {
+        $checked = $this->getCheckedState($type, $name, $value, $checked);
+
+        if ($checked)
+            $options['checked'] = 'checked';
+
+        return $this->input($type, $name, $value, $options);
+    }
+
+    /**
+     * Get the check state for a checkable input.
+     *
+     * @param  string  $type
+     * @param  string  $name
+     * @param  mixed   $value
+     * @param  bool    $checked
+     * @return bool
+     */
+    protected function getCheckedState($type, $name, $value, $checked)
+    {
+        switch ($type)
+        {
+            case 'checkbox':
+                return $this->getCheckboxCheckedState($name, $value, $checked);
+
+            case 'radio':
+                return $this->getRadioCheckedState($name, $value, $checked);
+
+            default:
+                return $this->getValueAttribute($name) == $value;
+        }
+    }
+
+    /**
+     * Get the check state for a checkbox input.
+     *
+     * @param  string  $name
+     * @param  mixed  $value
+     * @param  bool  $checked
+     * @return bool
+     */
+    protected function getCheckboxCheckedState($name, $value, $checked)
+    {
+        if (isset($this->session) && !$this->oldInputIsEmpty() && is_null($this->old($name)))
+            return false;
+
+        if ($this->missingOldAndModel($name))
+            return $checked;
+
+        $posted = $this->getValueAttribute($name);
+
+        return is_array($posted) ? in_array($value, $posted) : (bool) $posted;
+    }
+
+    /**
+     * Get the check state for a radio input.
+     *
+     * @param  string  $name
+     * @param  mixed  $value
+     * @param  bool  $checked
+     * @return bool
+     */
+    protected function getRadioCheckedState($name, $value, $checked)
+    {
+        if ($this->missingOldAndModel($name))
+            return $checked;
+
+        return $this->getValueAttribute($name) == $value;
+    }
+
+    /**
+     * Determine if old input or model input exists for a key.
+     *
+     * @param  string  $name
+     * @return bool
+     */
+    protected function missingOldAndModel($name)
+    {
+        return (is_null($this->old($name)) && is_null($this->getModelValueAttribute($name)));
+    }
+
+    /**
+     * Create a HTML reset input element.
+     *
+     * @param  string  $value
+     * @param  array   $attributes
+     * @return string
+     */
+    public function reset($value, $attributes = array())
+    {
+        return $this->input('reset', null, $value, $attributes);
+    }
+
+    /**
+     * Create a HTML image input element.
+     *
+     * @param  string  $url
+     * @param  string  $name
+     * @param  array   $attributes
+     * @return string
+     */
+    public function image($url, $name = null, $attributes = array())
+    {
+        $attributes['src'] = $url;
+
+        return $this->input('image', $name, null, $attributes);
+    }
+
+    /**
+     * Create a submit button element.
+     *
+     * @param  string  $value
+     * @param  array   $options
+     * @return string
+     */
+    public function submit($value = null, $options = array())
+    {
+        return $this->input('submit', null, $value, $options);
+    }
+
+    /**
+     * Create a button element.
+     *
+     * @param  string  $value
+     * @param  array   $options
+     * @return string
+     */
+    public function button($value = null, $options = array())
+    {
+        if (!array_key_exists('type', $options))
+        {
+            $options['type'] = 'button';
+        }
+
+        return '<button' . $this->attributes($options) . '>' . $value . '</button>';
+    }
+
+    /**
+     * Parse the form action method.
+     *
+     * @param  string  $method
+     * @return string
+     */
+    protected function getMethod($method)
+    {
+        $method = strtoupper($method);
+
+        return $method != 'GET' ? 'POST' : $method;
+    }
+
+    /**
+     * Get the form action from the options.
+     *
+     * @param  array   $options
+     * @return string
+     */
+    /* protected function getAction(array $options)
+      {
+      // We will also check for a "route" or "action" parameter on the array so that
+      // developers can easily specify a route or controller action when creating
+      // a form providing a convenient interface for creating the form actions.
+      if (isset($options['url']))
+      {
+      return $this->getUrlAction($options['url']);
+      }
+
+      if (isset($options['route']))
+      {
+      return $this->getRouteAction($options['route']);
+      }
+
+      // If an action is available, we are attempting to open a form to a controller
+      // action route. So, we will use the URL generator to get the path to these
+      // actions and return them from the method. Otherwise, we'll use current.
+      elseif (isset($options['action']))
+      {
+      return $this->getControllerAction($options['action']);
+      }
+
+      return $this->url->current();
+      } */
+
+    /**
+     * Get the action for a "url" option.
+     *
+     * @param  array|string  $options
+     * @return string
+     */
+    /* protected function getUrlAction($options)
+      {
+      if (is_array($options))
+      {
+      return $this->url->to($options[0], array_slice($options, 1));
+      }
+
+      return $this->url->to($options);
+      } */
+
+    /**
+     * Get the action for a "route" option.
+     *
+     * @param  array|string  $options
+     * @return string
+     */
+    /* protected function getRouteAction($options)
+      {
+      if (is_array($options))
+      {
+      return $this->url->route($options[0], array_slice($options, 1));
+      }
+
+      return $this->url->route($options);
+      } */
+
+    /**
+     * Get the action for an "action" option.
+     *
+     * @param  array|string  $options
+     * @return string
+     */
+    /* protected function getControllerAction($options)
+      {
+      if (is_array($options))
+      {
+      return $this->url->action($options[0], array_slice($options, 1));
+      }
+
+      return $this->url->action($options);
+      } */
+
+    /**
+     * Get the form appendage for the given method.
+     *
+     * @param  string  $method
+     * @return string
+     */
+    protected function getAppendage($method)
+    {
+        list($method, $appendage) = array(strtoupper($method), '');
+
+        // If the HTTP method is in this list of spoofed methods, we will attach the
+        // method spoofer hidden input to the form. This allows us to use regular
+        // form to initiate PUT and DELETE requests in addition to the typical.
+        if (in_array($method, $this->spoofedMethods))
+        {
+            $appendage .= $this->hidden('_method', $method);
+        }
+
+
+        // If the method is something other than GET we will go ahead and attach the
+        // CSRF token to the form, as this can't hurt and is convenient to simply
+        // always have available on every form the developers creates for them.
+        if ($method != 'GET')
+        {
+            $appendage .= $this->token(array_get($this->csrfToken, 'name'), array_get($this->csrfToken, 'type'));
+        }
+
+        return $appendage;
+    }
+
+    /**
+     * Get the ID attribute for a field name.
+     *
+     * @param  string  $name
+     * @param  array   $attributes
+     * @return string
+     */
+    public function getIdAttribute($name, $attributes)
+    {
+
+        if (array_key_exists('id', $attributes))
+        {
+            return $attributes['id'];
+        }
+
+        if (in_array($name, $this->labels))
+        {
+            return $name;
+        }
+    }
+
+    /**
+     * Get the value that should be assigned to the field.
+     *
+     * @param  string  $name
+     * @param  string  $value
+     * @return string
+     */
+    public function getValueAttribute($name, $value = null)
+    {
+        if (is_null($name))
+            return $value;
+
+        if (!is_null($this->old($name)))
+        {
+            return $this->old($name);
+        }
+
+        if (!is_null($value))
+            return $value;
+
+        if (isset($this->model))
+        {
+            return $this->getModelValueAttribute($name);
+        }
+    }
+
+    /**
+     * Get the model value that should be assigned to the field.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    protected function getModelValueAttribute($name)
+    {
+        if (is_object($this->model))
+        {
+            return object_get($this->model, $this->transformKey($name));
+        }
+        elseif (is_array($this->model))
+        {
+            return array_get($this->model, $this->transformKey($name));
+        }
+    }
+
+    /**
+     * Get a value from the session's old input.
+     *
+     * @param  string  $name
+     * @return string
+     */
+    public function old($name)
+    {
+        if (isset($this->session))
+        {
+            return $this->session->getOldInput($this->transformKey($name));
+        }
+    }
+
+    /**
+     * Determine if the old input is empty.
+     *
+     * @return bool
+     */
+    public function oldInputIsEmpty()
+    {
+        return (isset($this->session) && count($this->session->getOldInput()) == 0);
+    }
+
+    /**
+     * Transform key from array to dot syntax.
+     *
+     * @param  string  $key
+     * @return string
+     */
+    protected function transformKey($key)
+    {
+        return str_replace(array('.', '[]', '[', ']'), array('_', '', '.', ''), $key);
+    }
+
+    /**
+     * 数组转换成一个HTML属性字符串。
+     *
+     * @param  array  $attributes
+     * @return string
+     */
+    public function attributes($attributes)
+    {
+        $html = array();
+        // 假设我们的keys 和 value 是相同的,
+        // 拿HTML“required”属性来说,假设是['required']数组,
+        // 会已 required="required" 拼接起来,而不是用数字keys去拼接
+        foreach ((array) $attributes as $key => $value)
+        {
+            $element = $this->attributeElement($key, $value);
+            if (!is_null($element))
+                $html[] = $element;
+        }
+        return count($html) > 0 ? ' ' . implode(' ', $html) : '';
+    }
+
+    /**
+     * 拼接成一个属性。
+     *
+     * @param  string  $key
+     * @param  string  $value
+     * @return string
+     */
+    protected function attributeElement($key, $value)
+    {
+        if (is_numeric($key))
+            $key = $value;
+        if (!is_null($value))
+            return $key . '="' . $value . '"';
+    }
+
+}
+
+class Arr
+{
+
+    /**
+     * Determine whether the given value is array accessible.
+     *
+     * @param  mixed  $value
+     * @return bool
+     */
+    public static function accessible($value)
+    {
+        return is_array($value) || $value instanceof ArrayAccess;
+    }
+
+    /**
+     * Determine if the given key exists in the provided array.
+     *
+     * @param  \ArrayAccess|array  $array
+     * @param  string|int  $key
+     * @return bool
+     */
+    public static function exists($array, $key)
+    {
+        if ($array instanceof ArrayAccess)
+        {
+            return $array->offsetExists($key);
+        }
+        return array_key_exists($key, $array);
+    }
+
+    /**
+     * Get an item from an array using "dot" notation.
+     *
+     * @param  \ArrayAccess|array  $array
+     * @param  string  $key
+     * @param  mixed   $default
+     * @return mixed
+     */
+    public static function get($array, $key, $default = null)
+    {
+        if (!static::accessible($array))
+        {
+            return $default;
+        }
+        if (is_null($key))
+        {
+            return $array;
+        }
+        if (static::exists($array, $key))
+        {
+            return $array[$key];
+        }
+        foreach (explode('.', $key) as $segment)
+        {
+            if (static::accessible($array) && static::exists($array, $segment))
+            {
+                $array = $array[$segment];
+            }
+            else
+            {
+                return $default;
+            }
+        }
+        return $array;
+    }
+
+    /**
+     * Get all of the given array except for a specified array of items.
+     *
+     * @param  array  $array
+     * @param  array|string  $keys
+     * @return array
+     */
+    public static function except($array, $keys)
+    {
+        static::forget($array, $keys);
+        return $array;
+    }
+
+    /**
+     * Remove one or many array items from a given array using "dot" notation.
+     *
+     * @param  array  $array
+     * @param  array|string  $keys
+     * @return void
+     */
+    public static function forget(&$array, $keys)
+    {
+        $original = &$array;
+        $keys = (array) $keys;
+        if (count($keys) === 0)
+        {
+            return;
+        }
+        foreach ($keys as $key)
+        {
+            // if the exact key exists in the top-level, remove it
+            if (static::exists($array, $key))
+            {
+                unset($array[$key]);
+                continue;
+            }
+            $parts = explode('.', $key);
+            // clean up before each pass
+            $array = &$original;
+            while (count($parts) > 1)
+            {
+                $part = array_shift($parts);
+                if (isset($array[$part]) && is_array($array[$part]))
+                {
+                    $array = &$array[$part];
+                }
+                else
+                {
+                    continue 2;
+                }
+            }
+            unset($array[array_shift($parts)]);
+        }
+    }
+
+}
+
+if (!function_exists('array_get'))
+{
+
+    /**
+     * Get an item from an array using "dot" notation.
+     *
+     * @param  \ArrayAccess|array  $array
+     * @param  string  $key
+     * @param  mixed   $default
+     * @return mixed
+     */
+    function array_get($array, $key, $default = null)
+    {
+        return Arr::get($array, $key, $default);
+    }
+
+}
+if (!function_exists('e'))
+{
+
+    /**
+     * Escape HTML special characters in a string.
+     *
+     *
+     * @return string
+     */
+    function e($value)
+    {
+        if (is_array($value))
+        {
+            $value = json_encode($value, JSON_UNESCAPED_UNICODE);
+        }
+        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
+    }
+
+}
+if (!function_exists('array_except'))
+{
+
+    /**
+     * Get all of the given array except for a specified array of items.
+     *
+     * @param  array  $array
+     * @param  array|string  $keys
+     * @return array
+     */
+    function array_except($array, $keys)
+    {
+        return Arr::except($array, $keys);
+    }
+
+}
diff --git a/extend/fast/Http.php b/extend/fast/Http.php
new file mode 100644
index 0000000..2206bb5
--- /dev/null
+++ b/extend/fast/Http.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace fast;
+
+/**
+ * 字符串类
+ */
+class Http
+{
+
+    /**
+     * 发送一个POST请求
+     */
+    public static function post($url, $params = [], $options = [])
+    {
+        $req = self::sendRequest($url, $params, 'POST', $options);
+        return $req['ret'] ? $req['msg'] : '';
+    }
+
+    /**
+     * 发送一个GET请求
+     */
+    public static function get($url, $params = [], $options = [])
+    {
+        $req = self::sendRequest($url, $params, 'GET', $options);
+        return $req['ret'] ? $req['msg'] : '';
+    }
+
+    /**
+     * CURL发送Request请求,含POST和REQUEST
+     * @param string $url 请求的链接
+     * @param mixed $params 传递的参数
+     * @param string $method 请求的方法
+     * @param mixed $options CURL的参数
+     * @return array
+     */
+    public static function sendRequest($url, $params = [], $method = 'POST', $options = [])
+    {
+        $method = strtoupper($method);
+        $protocol = substr($url, 0, 5);
+        $query_string = is_array($params) ? http_build_query($params) : $params;
+
+        $ch = curl_init();
+        $defaults = [];
+        if ('GET' == $method)
+        {
+            $geturl = $query_string ? $url . (stripos($url, "?") !== FALSE ? "&" : "?") . $query_string : $url;
+            $defaults[CURLOPT_URL] = $geturl;
+        }
+        else
+        {
+            $defaults[CURLOPT_URL] = $url;
+            if ($method == 'POST')
+            {
+                $defaults[CURLOPT_POST] = 1;
+            }
+            else
+            {
+                $defaults[CURLOPT_CUSTOMREQUEST] = $method;
+            }
+            $defaults[CURLOPT_POSTFIELDS] = $query_string;
+        }
+
+        $defaults[CURLOPT_HEADER] = FALSE;
+        $defaults[CURLOPT_USERAGENT] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36";
+        $defaults[CURLOPT_FOLLOWLOCATION] = TRUE;
+        $defaults[CURLOPT_RETURNTRANSFER] = TRUE;
+        $defaults[CURLOPT_CONNECTTIMEOUT] = 3;
+        $defaults[CURLOPT_TIMEOUT] = 3;
+
+        // disable 100-continue
+        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
+
+        if ('https' == $protocol)
+        {
+            $defaults[CURLOPT_SSL_VERIFYPEER] = FALSE;
+            $defaults[CURLOPT_SSL_VERIFYHOST] = FALSE;
+        }
+
+        curl_setopt_array($ch, (array) $options + $defaults);
+
+        $ret = curl_exec($ch);
+        $err = curl_error($ch);
+
+        if (FALSE === $ret || !empty($err))
+        {
+            $errno = curl_errno($ch);
+            $info = curl_getinfo($ch);
+            curl_close($ch);
+            return [
+                'ret'   => FALSE,
+                'errno' => $errno,
+                'msg'   => $err,
+                'info'  => $info,
+            ];
+        }
+        curl_close($ch);
+        return [
+            'ret' => TRUE,
+            'msg' => $ret,
+        ];
+    }
+
+    /**
+     * 异步发送一个请求
+     * @param string $url 请求的链接
+     * @param mixed $params 请求的参数
+     * @param string $method 请求的方法
+     * @return boolean TRUE
+     */
+    public static function sendAsyncRequest($url, $params = [], $method = 'POST')
+    {
+        $method = strtoupper($method);
+        $method = $method == 'POST' ? 'POST' : 'GET';
+        //构造传递的参数
+        if (is_array($params))
+        {
+            $post_params = [];
+            foreach ($params as $k => &$v)
+            {
+                if (is_array($v))
+                    $v = implode(',', $v);
+                $post_params[] = $k . '=' . urlencode($v);
+            }
+            $post_string = implode('&', $post_params);
+        }else
+        {
+            $post_string = $params;
+        }
+        $parts = parse_url($url);
+        //构造查询的参数
+        if ($method == 'GET' && $post_string)
+        {
+            $parts['query'] = isset($parts['query']) ? $parts['query'] . '&' . $post_string : $post_string;
+            $post_string = '';
+        }
+        $parts['query'] = isset($parts['query']) && $parts['query'] ? '?' . $parts['query'] : '';
+        //发送socket请求,获得连接句柄
+        $fp = fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80, $errno, $errstr, 3);
+        if (!$fp)
+            return FALSE;
+        //设置超时时间
+        stream_set_timeout($fp, 3);
+        $out = "{$method} {$parts['path']}{$parts['query']} HTTP/1.1\r\n";
+        $out.= "Host: {$parts['host']}\r\n";
+        $out.= "Content-Type: application/x-www-form-urlencoded\r\n";
+        $out.= "Content-Length: " . strlen($post_string) . "\r\n";
+        $out.= "Connection: Close\r\n\r\n";
+        if ($post_string !== '')
+            $out .= $post_string;
+        fwrite($fp, $out);
+        //不用关心服务器返回结果
+        //echo fread($fp, 1024);
+        fclose($fp);
+        return TRUE;
+    }
+
+    /**
+     * 发送文件到客户端
+     * @param string $file
+     * @param bool $delaftersend
+     * @param bool $exitaftersend
+     */
+    public static function sendToBrowser($file, $delaftersend = true, $exitaftersend = true)
+    {
+        if (file_exists($file) && is_readable($file))
+        {
+            header('Content-Description: File Transfer');
+            header('Content-Type: application/octet-stream');
+            header('Content-Disposition: attachment;filename = ' . basename($file));
+            header('Content-Transfer-Encoding: binary');
+            header('Expires: 0');
+            header('Cache-Control: must-revalidate, post-check = 0, pre-check = 0');
+            header('Pragma: public');
+            header('Content-Length: ' . filesize($file));
+            ob_clean();
+            flush();
+            readfile($file);
+            if ($delaftersend)
+            {
+                unlink($file);
+            }
+            if ($exitaftersend)
+            {
+                exit;
+            }
+        }
+    }
+
+}
diff --git a/extend/fast/Menu.php b/extend/fast/Menu.php
new file mode 100644
index 0000000..6208171
--- /dev/null
+++ b/extend/fast/Menu.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace fast;
+
+use app\admin\library\Auth;
+use fast\Tree;
+use think\Config;
+use think\Db;
+
+/**
+ * 菜单
+ */
+class Menu
+{
+
+    protected static $instance;
+    //默认配置
+    protected $config = [];
+
+    public function __construct($options = [])
+    {
+        if ($config = Config::get('menu'))
+        {
+            $this->config = array_merge($this->config, $config);
+        }
+        $this->config = array_merge($this->config, is_array($options) ? $options : []);
+    }
+
+    /**
+     * 单例
+     * @param array $options 参数
+     * @return Menu
+     */
+    public static function instance($options = [])
+    {
+        if (is_null(self::$instance))
+        {
+            self::$instance = new static($options);
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * 导入节点数据
+     * @param array $treearr
+     * @param int $pid
+     * @param string $module
+     */
+    public function import($treearr, $pid = 0, $module = NULL)
+    {
+        $module = is_null($module) ? Config::user('request.modulename') : $module;
+        foreach ($treearr as $key => & $v)
+        {
+            $controller = strtolower($v['controller']) . ($v['childlist'] ? "_folder" : '');
+            $icon = isset($v['icon']) ? $v['icon'] : (stripos($v['name'], '_') !== FALSE ? $v['childlist'] ? 'folder' : 'circle-o' : strtolower($v['name']));
+            $actionkeys = array_keys($v['actionlist']);
+            $action = '-' . ($v['actionlist'] ? reset($actionkeys) : '');
+            $values = array(
+                'name'       => $v['name'],
+                'weigh'      => $key,
+                'pid'        => $pid,
+                'module'     => $module,
+                'controller' => $controller,
+                'action'     => $action,
+                'icon'       => $icon,
+                'type'       => 'menu',
+                'status'     => 'normal'
+            );
+            $id = Db::table("node")->data($values)->insert();
+            $v['id'] = $id;
+        }
+        unset($v);
+        foreach ($treearr as $k => $v)
+        {
+            if ($v['childlist'])
+            {
+                $this->import($v['childlist'], $v['id'], $module);
+            }
+            else
+            {
+                $i = 0;
+                foreach ($v['actionlist'] as $m => $n)
+                {
+                    $values = array(
+                        'name'       => $n ? $n : $m,
+                        'weigh'      => $i,
+                        'pid'        => $v['id'],
+                        'module'     => $module,
+                        'controller' => strtolower($v['controller']),
+                        'action'     => $m,
+                        'icon'       => 'circle-o',
+                        'type'       => 'file',
+                        'status'     => 'normal'
+                    );
+                    Db::table("node")->data($values)->insert();
+                    $i++;
+                }
+            }
+        }
+    }
+
+    /**
+     * 获取左侧菜单栏
+     *
+     * @param array $params URL对应的badge数据
+     * @return string
+     */
+    public function sidebar($params = [])
+    {
+        $colorArr = ['red', 'green', 'yellow', 'blue', 'teal', 'orange', 'purple'];
+        $colorNums = count($colorArr);
+        $badgeList = [];
+        // 生成菜单的badge
+        foreach ($params as $k => $v)
+        {
+            if (stripos($k, '/') === false)
+            {
+                $url = '/admin/' . $k;
+            }
+            else
+            {
+                $url = url($k);
+            }
+
+            if (is_array($v))
+            {
+                $nums = isset($v[0]) ? $v[0] : 0;
+                $color = isset($v[1]) ? $v[1] : $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
+                $class = isset($v[2]) ? $v[2] : 'label';
+            }
+            else
+            {
+                $nums = $v;
+                $color = $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
+                $class = 'label';
+            }
+            //必须nums大于0才显示
+            if ($nums)
+            {
+                $badgeList[$url] = '<small class="' . $class . ' pull-right bg-' . $color . '">' . $nums . '</small>';
+            }
+        }
+
+        // 读取管理员当前拥有的权限节点
+        $userRule = Auth::instance()->getRuleList();
+
+        $select_id = 0;
+        $dashboard = rtrim(url('dashboard/'), '/');
+        // 必须将结果集转换为数组
+        $ruleList = collection(model('AuthRule')->where('ismenu', 1)->order('weigh', 'desc')->cache("__menu__")->select())->toArray();
+        foreach ($ruleList as $k => &$v)
+        {
+            if (!in_array($v['name'], $userRule))
+                continue;
+            $select_id = $v['name'] == $dashboard ? $v['id'] : $select_id;
+            $v['url'] = $v['name'];
+            $v['badge'] = isset($badgeList[$v['name']]) ? $badgeList[$v['name']] : '';
+        }
+        // 构造菜单数据
+        Tree::instance()->init($ruleList);
+        $menu = Tree::instance()->getTreeMenu(0, '<li class="@class"><a href="@url" addtabs="@id" url="@url"><i class="@icon"></i> <span>@title</span> <span class="pull-right-container">@caret @badge</span></a> @childlist</li>', $select_id, '', 'ul', 'class="treeview-menu"');
+        return $menu;
+    }
+
+}
diff --git a/extend/fast/Pinyin.php b/extend/fast/Pinyin.php
new file mode 100644
index 0000000..be5f4ee
--- /dev/null
+++ b/extend/fast/Pinyin.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace fast;
+
+/**
+ * 中文转拼音类
+ */
+class Pinyin
+{
+
+    protected static $keys = "a|ai|an|ang|ao|ba|bai|ban|bang|bao|bei|ben|beng|bi|bian|biao|bie|bin|bing|bo|bu|ca|cai|can|cang|cao|ce|ceng|cha|chai|chan|chang|chao|che|chen|cheng|chi|chong|chou|chu|chuai|chuan|chuang|chui|chun|chuo|ci|cong|cou|cu|cuan|cui|cun|cuo|da|dai|dan|dang|dao|de|deng|di|dian|diao|die|ding|diu|dong|dou|du|duan|dui|dun|duo|e|en|er|fa|fan|fang|fei|fen|feng|fo|fou|fu|ga|gai|gan|gang|gao|ge|gei|gen|geng|gong|gou|gu|gua|guai|guan|guang|gui|gun|guo|ha|hai|han|hang|hao|he|hei|hen|heng|hong|hou|hu|hua|huai|huan|huang|hui|hun|huo|ji|jia|jian|jiang|jiao|jie|jin|jing|jiong|jiu|ju|juan|jue|jun|ka|kai|kan|kang|kao|ke|ken|keng|kong|kou|ku|kua|kuai|kuan|kuang|kui|kun|kuo|la|lai|lan|lang|lao|le|lei|leng|li|lia|lian|liang|liao|lie|lin|ling|liu|long|lou|lu|lv|luan|lue|lun|luo|ma|mai|man|mang|mao|me|mei|men|meng|mi|mian|miao|mie|min|ming|miu|mo|mou|mu|na|nai|nan|nang|nao|ne|nei|nen|neng|ni|nian|niang|niao|nie|nin|ning|niu|nong|nu|nv|nuan|nue|nuo|o|ou|pa|pai|pan|pang|pao|pei|pen|peng|pi|pian|piao|pie|pin|ping|po|pu|qi|qia|qian|qiang|qiao|qie|qin|qing|qiong|qiu|qu|quan|que|qun|ran|rang|rao|re|ren|reng|ri|rong|rou|ru|ruan|rui|run|ruo|sa|sai|san|sang|sao|se|sen|seng|sha|shai|shan|shang|shao|she|shen|sheng|shi|shou|shu|shua|shuai|shuan|shuang|shui|shun|shuo|si|song|sou|su|suan|sui|sun|suo|ta|tai|tan|tang|tao|te|teng|ti|tian|tiao|tie|ting|tong|tou|tu|tuan|tui|tun|tuo|wa|wai|wan|wang|wei|wen|weng|wo|wu|xi|xia|xian|xiang|xiao|xie|xin|xing|xiong|xiu|xu|xuan|xue|xun|ya|yan|yang|yao|ye|yi|yin|ying|yo|yong|you|yu|yuan|yue|yun|za|zai|zan|zang|zao|ze|zei|zen|zeng|zha|zhai|zhan|zhang|zhao|zhe|zhen|zheng|zhi|zhong|zhou|zhu|zhua|zhuai|zhuan|zhuang|zhui|zhun|zhuo|zi|zong|zou|zu|zuan|zui|zun|zuo";
+    protected static $values = "-20319|-20317|-20304|-20295|-20292|-20283|-20265|-20257|-20242|-20230|-20051|-20036|-20032|-20026|-20002|-19990|-19986|-19982|-19976|-19805|-19784|-19775|-19774|-19763|-19756|-19751|-19746|-19741|-19739|-19728|-19725|-19715|-19540|-19531|-19525|-19515|-19500|-19484|-19479|-19467|-19289|-19288|-19281|-19275|-19270|-19263|-19261|-19249|-19243|-19242|-19238|-19235|-19227|-19224|-19218|-19212|-19038|-19023|-19018|-19006|-19003|-18996|-18977|-18961|-18952|-18783|-18774|-18773|-18763|-18756|-18741|-18735|-18731|-18722|-18710|-18697|-18696|-18526|-18518|-18501|-18490|-18478|-18463|-18448|-18447|-18446|-18239|-18237|-18231|-18220|-18211|-18201|-18184|-18183|-18181|-18012|-17997|-17988|-17970|-17964|-17961|-17950|-17947|-17931|-17928|-17922|-17759|-17752|-17733|-17730|-17721|-17703|-17701|-17697|-17692|-17683|-17676|-17496|-17487|-17482|-17468|-17454|-17433|-17427|-17417|-17202|-17185|-16983|-16970|-16942|-16915|-16733|-16708|-16706|-16689|-16664|-16657|-16647|-16474|-16470|-16465|-16459|-16452|-16448|-16433|-16429|-16427|-16423|-16419|-16412|-16407|-16403|-16401|-16393|-16220|-16216|-16212|-16205|-16202|-16187|-16180|-16171|-16169|-16158|-16155|-15959|-15958|-15944|-15933|-15920|-15915|-15903|-15889|-15878|-15707|-15701|-15681|-15667|-15661|-15659|-15652|-15640|-15631|-15625|-15454|-15448|-15436|-15435|-15419|-15416|-15408|-15394|-15385|-15377|-15375|-15369|-15363|-15362|-15183|-15180|-15165|-15158|-15153|-15150|-15149|-15144|-15143|-15141|-15140|-15139|-15128|-15121|-15119|-15117|-15110|-15109|-14941|-14937|-14933|-14930|-14929|-14928|-14926|-14922|-14921|-14914|-14908|-14902|-14894|-14889|-14882|-14873|-14871|-14857|-14678|-14674|-14670|-14668|-14663|-14654|-14645|-14630|-14594|-14429|-14407|-14399|-14384|-14379|-14368|-14355|-14353|-14345|-14170|-14159|-14151|-14149|-14145|-14140|-14137|-14135|-14125|-14123|-14122|-14112|-14109|-14099|-14097|-14094|-14092|-14090|-14087|-14083|-13917|-13914|-13910|-13907|-13906|-13905|-13896|-13894|-13878|-13870|-13859|-13847|-13831|-13658|-13611|-13601|-13406|-13404|-13400|-13398|-13395|-13391|-13387|-13383|-13367|-13359|-13356|-13343|-13340|-13329|-13326|-13318|-13147|-13138|-13120|-13107|-13096|-13095|-13091|-13076|-13068|-13063|-13060|-12888|-12875|-12871|-12860|-12858|-12852|-12849|-12838|-12831|-12829|-12812|-12802|-12607|-12597|-12594|-12585|-12556|-12359|-12346|-12320|-12300|-12120|-12099|-12089|-12074|-12067|-12058|-12039|-11867|-11861|-11847|-11831|-11798|-11781|-11604|-11589|-11536|-11358|-11340|-11339|-11324|-11303|-11097|-11077|-11067|-11055|-11052|-11045|-11041|-11038|-11024|-11020|-11019|-11018|-11014|-10838|-10832|-10815|-10800|-10790|-10780|-10764|-10587|-10544|-10533|-10519|-10331|-10329|-10328|-10322|-10315|-10309|-10307|-10296|-10281|-10274|-10270|-10262|-10260|-10256|-10254";
+
+    /**
+     * 获取文字的拼音
+     * @param string $chinese 中文汉字
+     * @param boolean $ucfirst 是否首字母大写
+     * @param string $charset 文字编码
+     * @return string
+     */
+    public static function get($chinese, $ucfirst = false, $charset = 'utf-8')
+    {
+        $keys_a = explode('|', self::$keys);
+        $values_a = explode('|', self::$values);
+        $data = array_combine($keys_a, $values_a);
+        arsort($data);
+        reset($data);
+        if ($charset != 'gb2312')
+            $chinese = self::_u2_utf8_gb($chinese);
+        $result = '';
+        for ($i = 0; $i < strlen($chinese); $i++)
+        {
+            $_P = ord(substr($chinese, $i, 1));
+            if ($_P > 160)
+            {
+                $_Q = ord(substr($chinese, ++$i, 1));
+                $_P = $_P * 256 + $_Q - 65536;
+            }
+            $result .= ($ucfirst ? ucfirst(self::_pinyin($_P, $data)) : self::_pinyin($_P, $data));
+        }
+
+        return preg_replace("/[^a-z0-9_\-]*/i", '', $result);
+    }
+
+    private static function _pinyin($num, $data)
+    {
+        if ($num > 0 && $num < 160)
+            return chr($num);
+        elseif ($num < -20319 || $num > -10247)
+            return '';
+        else
+        {
+            foreach ($data as $k => $v)
+            {
+                if ($v <= $num)
+                    break;
+            }
+
+            return $k;
+        }
+    }
+
+    private static function _u2_utf8_gb($c)
+    {
+        $string = '';
+        if ($c < 0x80)
+            $string .= $c;
+        elseif ($c < 0x800)
+        {
+            $string .= chr(0xC0 | $c >> 6);
+            $string .= chr(0x80 | $c & 0x3F);
+        }
+        elseif ($c < 0x10000)
+        {
+            $string .= chr(0xE0 | $c >> 12);
+            $string .= chr(0x80 | $c >> 6 & 0x3F);
+            $string .= chr(0x80 | $c & 0x3F);
+        }
+        elseif ($c < 0x200000)
+        {
+            $string .= chr(0xF0 | $c >> 18);
+            $string .= chr(0x80 | $c >> 12 & 0x3F);
+            $string .= chr(0x80 | $c >> 6 & 0x3F);
+            $string .= chr(0x80 | $c & 0x3F);
+        }
+        return iconv('UTF-8', 'GB2312//IGNORE', $string);
+    }
+
+}
diff --git a/extend/fast/Random.php b/extend/fast/Random.php
new file mode 100644
index 0000000..8851488
--- /dev/null
+++ b/extend/fast/Random.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace fast;
+
+/**
+ * 随机生成类
+ */
+class Random
+{
+
+    /**
+     * 生成数字和字母
+     *
+     * @param int $len 长度
+     * @return string
+     */
+    public static function alnum($len = 6)
+    {
+        return self::build('alnum', $len);
+    }
+
+    /**
+     * 仅生成字符
+     *
+     * @param int $len 长度
+     * @return string
+     */
+    public static function alpha($len = 6)
+    {
+        return self::build('alpha', $len);
+    }
+
+    /**
+     * 生成指定长度的随机数字
+     *
+     * @param int $len 长度
+     * @return string
+     */
+    public static function numeric($len = 4)
+    {
+        return self::build('numeric', $len);
+    }
+
+    /**
+     * 数字和字母组合的随机字符串
+     *
+     * @param int $len 长度
+     * @return string
+     */
+    public static function nozero($len = 4)
+    {
+        return self::build('nozero', $len);
+    }
+
+    /**
+     * 能用的随机数生成
+     * @param string $type 类型 alpha/alnum/numeric/nozero/unique/md5/encrypt/sha1
+     * @param int $len 长度
+     * @return string
+     */
+    public static function build($type = 'alnum', $len = 8)
+    {
+        switch ($type)
+        {
+            case 'alpha':
+            case 'alnum':
+            case 'numeric':
+            case 'nozero':
+                switch ($type)
+                {
+                    case 'alpha':
+                        $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+                        break;
+                    case 'alnum':
+                        $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+                        break;
+                    case 'numeric':
+                        $pool = '0123456789';
+                        break;
+                    case 'nozero':
+                        $pool = '123456789';
+                        break;
+                }
+                return substr(str_shuffle(str_repeat($pool, ceil($len / strlen($pool)))), 0, $len);
+            case 'unique':
+            case 'md5':
+                return md5(uniqid(mt_rand()));
+            case 'encrypt':
+            case 'sha1':
+                return sha1(uniqid(mt_rand(), TRUE));
+        }
+    }
+
+    /**
+     * 根据数组元素的概率获得键名
+     *
+     * @param array $ps array('p1'=>20, 'p2'=>30, 'p3'=>50);
+     * @param array $num 默认为1,即随机出来的数量
+     * @param array $unique 默认为true,即当num>1时,随机出的数量是否唯一
+     * @return mixed 当num为1时返回键名,反之返回一维数组
+     */
+    public static function lottery($ps, $num = 1, $unique = true)
+    {
+        if (!$ps)
+        {
+            return $num == 1 ? '' : [];
+        }
+        if ($num >= count($ps) && $unique)
+        {
+            $res = array_keys($ps);
+            return $num == 1 ? $res[0] : $res;
+        }
+        $max_exp = 0;
+        $res = [];
+        foreach ($ps as $key => $value)
+        {
+            $value = substr($value, 0, stripos($value, ".") + 6);
+            $exp = strlen(strchr($value, '.')) - 1;
+            if ($exp > $max_exp)
+            {
+                $max_exp = $exp;
+            }
+        }
+        $pow_exp = pow(10, $max_exp);
+        if ($pow_exp > 1)
+        {
+            reset($ps);
+            foreach ($ps as $key => $value)
+            {
+                $ps[$key] = $value * $pow_exp;
+            }
+        }
+        $pro_sum = array_sum($ps);
+        if ($pro_sum < 1)
+        {
+            return $num == 1 ? '' : [];
+        }
+        for ($i = 0; $i < $num; $i++)
+        {
+            $rand_num = mt_rand(1, $pro_sum);
+            reset($ps);
+            foreach ($ps as $key => $value)
+            {
+                if ($rand_num <= $value)
+                {
+                    break;
+                }
+                else
+                {
+                    $rand_num -= $value;
+                }
+            }
+            if ($num == 1)
+            {
+                $res = $key;
+                break;
+            }
+            else
+            {
+                $res[$i] = $key;
+            }
+            if ($unique)
+            {
+                $pro_sum -= $value;
+                unset($ps[$key]);
+            }
+        }
+        return $res;
+    }
+
+    /**
+     * 获取全球唯一标识
+     * @return string
+     */
+    public static function uuid()
+    {
+        return sprintf(
+                '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
+        );
+    }
+
+}
diff --git a/extend/fast/Rsa.php b/extend/fast/Rsa.php
new file mode 100644
index 0000000..2fc1a8e
--- /dev/null
+++ b/extend/fast/Rsa.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace fast;
+
+/**
+ * RSA签名类
+ */
+class Rsa
+{
+
+    public $publicKey = '';
+    public $privateKey = '';
+    private $_privKey;
+
+    /**
+     * * private key
+     */
+    private $_pubKey;
+
+    /**
+     * * public key
+     */
+    private $_keyPath;
+
+    /**
+     * * the keys saving path
+     */
+
+    /**
+     * * the construtor,the param $path is the keys saving path
+     */
+    function __construct($publicKey = null, $privateKey = null)
+    {
+        $this->setKey($publicKey, $privateKey);
+    }
+
+    /**
+     * 设置公钥和私钥
+     * @param string $publicKey 公钥
+     * @param string $privateKey 私钥
+     */
+    public function setKey($publicKey = null, $privateKey = null)
+    {
+        if (!is_null($publicKey))
+            $this->publicKey = $publicKey;
+        if (!is_null($privateKey))
+            $this->privateKey = $privateKey;
+    }
+
+    /**
+     * * setup the private key
+     */
+    private function setupPrivKey()
+    {
+        if (is_resource($this->_privKey))
+        {
+            return true;
+        }
+        $pem = chunk_split($this->privateKey, 64, "\n");
+        $pem = "-----BEGIN PRIVATE KEY-----\n" . $pem . "-----END PRIVATE KEY-----\n";
+        $this->_privKey = openssl_pkey_get_private($pem);
+        return true;
+    }
+
+    /**
+     * * setup the public key
+     */
+    private function setupPubKey()
+    {
+        if (is_resource($this->_pubKey))
+        {
+            return true;
+        }
+        $pem = chunk_split($this->publicKey, 64, "\n");
+        $pem = "-----BEGIN PUBLIC KEY-----\n" . $pem . "-----END PUBLIC KEY-----\n";
+        $this->_pubKey = openssl_pkey_get_public($pem);
+        return true;
+    }
+
+    /**
+     * * encrypt with the private key
+     */
+    public function privEncrypt($data)
+    {
+        if (!is_string($data))
+        {
+            return null;
+        }
+        $this->setupPrivKey();
+        $r = openssl_private_encrypt($data, $encrypted, $this->_privKey);
+        if ($r)
+        {
+            return base64_encode($encrypted);
+        }
+        return null;
+    }
+
+    /**
+     * * decrypt with the private key
+     */
+    public function privDecrypt($encrypted)
+    {
+        if (!is_string($encrypted))
+        {
+            return null;
+        }
+        $this->setupPrivKey();
+        $encrypted = base64_decode($encrypted);
+        $r = openssl_private_decrypt($encrypted, $decrypted, $this->_privKey);
+        if ($r)
+        {
+            return $decrypted;
+        }
+        return null;
+    }
+
+    /**
+     * * encrypt with public key
+     */
+    public function pubEncrypt($data)
+    {
+        if (!is_string($data))
+        {
+            return null;
+        }
+        $this->setupPubKey();
+        $r = openssl_public_encrypt($data, $encrypted, $this->_pubKey);
+        if ($r)
+        {
+            return base64_encode($encrypted);
+        }
+        return null;
+    }
+
+    /**
+     * * decrypt with the public key
+     */
+    public function pubDecrypt($crypted)
+    {
+        if (!is_string($crypted))
+        {
+            return null;
+        }
+        $this->setupPubKey();
+        $crypted = base64_decode($crypted);
+        $r = openssl_public_decrypt($crypted, $decrypted, $this->_pubKey);
+        if ($r)
+        {
+            return $decrypted;
+        }
+        return null;
+    }
+
+    /**
+     * 构造签名
+     * @param string $dataString 被签名数据
+     * @return string
+     */
+    public function sign($dataString)
+    {
+        $this->setupPrivKey();
+        $signature = false;
+        openssl_sign($dataString, $signature, $this->_privKey);
+        return base64_encode($signature);
+    }
+
+    /**
+     * 验证签名
+     * @param string $dataString 被签名数据
+     * @param string $signString 已经签名的字符串
+     * @return number 1签名正确 0签名错误
+     */
+    public function verify($dataString, $signString)
+    {
+        $this->setupPubKey();
+        $signature = base64_decode($signString);
+        $flg = openssl_verify($dataString, $signature, $this->_pubKey);
+        return $flg;
+    }
+
+    public function __destruct()
+    {
+        is_resource($this->_privKey) && @openssl_free_key($this->_privKey);
+        is_resource($this->_pubKey) && @openssl_free_key($this->_pubKey);
+    }
+
+}
diff --git a/extend/fast/Tree.php b/extend/fast/Tree.php
new file mode 100644
index 0000000..2c201bb
--- /dev/null
+++ b/extend/fast/Tree.php
@@ -0,0 +1,480 @@
+<?php
+
+namespace fast;
+
+use think\Config;
+
+/**
+ * 通用的树型类
+ * @author XiaoYao <476552238li@gmail.com>
+ */
+class Tree
+{
+
+    protected static $instance;
+    //默认配置
+    protected $config = [];
+    public $options = [];
+
+    /**
+     * 生成树型结构所需要的2维数组
+     * @var array
+     */
+    public $arr = [];
+
+    /**
+     * 生成树型结构所需修饰符号,可以换成图片
+     * @var array
+     */
+    public $icon = array('│', '├', '└');
+    public $nbsp = "&nbsp;";
+    public $pidname = 'pid';
+
+    public function __construct($options = [])
+    {
+        if ($config = Config::get('tree'))
+        {
+            $this->options = array_merge($this->config, $config);
+        }
+        $this->options = array_merge($this->config, $options);
+    }
+
+    /**
+     * 初始化
+     * @access public
+     * @param array $options 参数
+     * @return Tree
+     */
+    public static function instance($options = [])
+    {
+        if (is_null(self::$instance))
+        {
+            self::$instance = new static($options);
+        }
+
+        return self::$instance;
+    }
+
+    /**
+
+     * 初始化方法
+
+     * @param array 2维数组,例如:
+     * array(
+     *      1 => array('id'=>'1','pid'=>0,'name'=>'一级栏目一'),
+     *      2 => array('id'=>'2','pid'=>0,'name'=>'一级栏目二'),
+     *      3 => array('id'=>'3','pid'=>1,'name'=>'二级栏目一'),
+     *      4 => array('id'=>'4','pid'=>1,'name'=>'二级栏目二'),
+     *      5 => array('id'=>'5','pid'=>2,'name'=>'二级栏目三'),
+     *      6 => array('id'=>'6','pid'=>3,'name'=>'三级栏目一'),
+     *      7 => array('id'=>'7','pid'=>3,'name'=>'三级栏目二')
+     *      )
+     */
+    public function init($arr = [], $pidname = NULL, $nbsp = NULL)
+    {
+        $this->arr = $arr;
+        if (!is_null($pidname))
+            $this->pidname = $pidname;
+        if (!is_null($nbsp))
+            $this->nbsp = $nbsp;
+        return $this;
+    }
+
+    /**
+     * 得到子级数组
+     * @param int
+     * @return array
+     */
+    public function getChild($myid)
+    {
+        $newarr = [];
+        foreach ($this->arr as $value)
+        {
+            if (!isset($value['id']))
+                continue;
+            if ($value[$this->pidname] == $myid)
+                $newarr[$value['id']] = $value;
+        }
+        return $newarr;
+    }
+
+    /**
+
+     * 读取指定节点的所有孩子节点
+     * @param int $myid 节点ID
+     * @param boolean $withself 是否包含自身
+     * @return array
+     */
+    public function getChildren($myid, $withself = FALSE)
+    {
+        $newarr = [];
+        foreach ($this->arr as $value)
+        {
+            if (!isset($value['id']))
+                continue;
+            if ($value[$this->pidname] == $myid)
+            {
+                $newarr[] = $value;
+                $newarr = array_merge($newarr, $this->getChildren($value['id']));
+            }
+            else if ($withself && $value['id'] == $myid)
+            {
+                $newarr[] = $value;
+            }
+        }
+        return $newarr;
+    }
+
+    /**
+
+     * 读取指定节点的所有孩子节点ID
+     * @param int $myid 节点ID
+     * @param boolean $withself 是否包含自身
+     * @return array
+     */
+    public function getChildrenIds($myid, $withself = FALSE)
+    {
+        $childrenlist = $this->getChildren($myid, $withself);
+        $childrenids = [];
+        foreach ($childrenlist as $k => $v)
+        {
+            $childrenids[] = $v['id'];
+        }
+        return $childrenids;
+    }
+
+    /**
+
+     * 得到当前位置父辈数组
+     * @param int
+     * @return array
+
+     */
+    public function getParent($myid)
+    {
+        $pid = 0;
+        $newarr = [];
+        foreach ($this->arr as $value)
+        {
+            if (!isset($value['id']))
+                continue;
+            if ($value['id'] == $myid)
+            {
+                $pid = $value[$this->pidname];
+                break;
+            }
+        }
+        if ($pid)
+        {
+            foreach ($this->arr as $value)
+            {
+                if ($value['id'] == $pid)
+                {
+                    $newarr[] = $value;
+                    break;
+                }
+            }
+        }
+        return $newarr;
+    }
+
+    /**
+
+     * 得到当前位置所有父辈数组
+     * @param int
+     * @return array
+
+     */
+    public function getParents($myid, $withself = FALSE)
+    {
+        $pid = 0;
+        $newarr = [];
+        foreach ($this->arr as $value)
+        {
+            if (!isset($value['id']))
+                continue;
+            if ($value['id'] == $myid)
+            {
+                if ($withself)
+                {
+                    $newarr[] = $value;
+                }
+                $pid = $value[$this->pidname];
+                break;
+            }
+        }
+        if ($pid)
+        {
+            $arr = $this->getParents($pid, TRUE);
+            $newarr = array_merge($arr, $newarr);
+        }
+        return $newarr;
+    }
+
+    /**
+     * 读取指定节点所有父类节点ID
+     * @param int $myid
+     * @param boolean $withself
+     * @return array
+     */
+    public function getParentsIds($myid, $withself = FALSE)
+    {
+        $parentlist = $this->getParents($myid, $withself);
+        $parentsids = [];
+        foreach ($parentlist as $k => $v)
+        {
+            $parentsids[] = $v['id'];
+        }
+        return $parentsids;
+    }
+
+    /**
+
+     * 树型结构Option
+     * @param int $myid 表示获得这个ID下的所有子级
+     * @param string $itemtpl 条目模板 如:"<option value=@id @selected @disabled>@spacer@name</option>"
+     * @param mixed $selectedids 被选中的ID,比如在做树型下拉框的时候需要用到
+     * @param mixed $disabledids 被禁用的ID,比如在做树型下拉框的时候需要用到
+     * @param string $itemprefix 每一项前缀
+     * @param string $toptpl 顶级栏目的模板
+     * @return string
+
+     */
+    public function getTree($myid, $itemtpl = "<option value=@id @selected @disabled>@spacer@name</option>", $selectedids = '', $disabledids = '', $itemprefix = '', $toptpl = '')
+    {
+        $ret = '';
+        $number = 1;
+        $childs = $this->getChild($myid);
+        if ($childs)
+        {
+            $total = count($childs);
+            foreach ($childs as $value)
+            {
+                $id = $value['id'];
+                $j = $k = '';
+                if ($number == $total)
+                {
+                    $j .= $this->icon[2];
+                    $k = $itemprefix ? $this->nbsp : '';
+                }
+                else
+                {
+                    $j .= $this->icon[1];
+                    $k = $itemprefix ? $this->icon[0] : '';
+                }
+                $spacer = $itemprefix ? $itemprefix . $j : '';
+                $selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
+                $disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
+                $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled, 'spacer' => $spacer));
+                $value = array_combine(array_map(function($k)
+                        {
+                            return '@' . $k;
+                        }, array_keys($value)), $value);
+                $nstr = strtr((($value["@{$this->pidname}"] == 0 || $this->getChild($id) ) && $toptpl ? $toptpl : $itemtpl), $value);
+                $ret .= $nstr;
+                $ret .= $this->getTree($id, $itemtpl, $selectedids, $disabledids, $itemprefix . $k . $this->nbsp, $toptpl);
+                $number++;
+            }
+        }
+        return $ret;
+    }
+
+    /**
+
+     * 树型结构UL
+     * @param int $myid 表示获得这个ID下的所有子级
+     * @param string $itemtpl 条目模板 如:"<li value=@id @selected @disabled>@name @childlist</li>"
+     * @param string $selectedids 选中的ID
+     * @param string $disabledids 禁用的ID
+     * @param string $wraptag 子列表包裹标签
+     * @return string
+
+     */
+    public function getTreeUl($myid, $itemtpl, $selectedids = '', $disabledids = '', $wraptag = 'ul', $wrapattr = '')
+    {
+        $str = '';
+        $childs = $this->getChild($myid);
+        if ($childs)
+        {
+            foreach ($childs as $value)
+            {
+                $id = $value['id'];
+                unset($value['child']);
+                $selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
+                $disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
+                $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled));
+                $value = array_combine(array_map(function($k)
+                        {
+                            return '@' . $k;
+                        }, array_keys($value)), $value);
+                $nstr = strtr($itemtpl, $value);
+                $childdata = $this->getTreeUl($id, $itemtpl, $selectedids, $disabledids, $wraptag, $wrapattr);
+                $childlist = $childdata ? "<{$wraptag} {$wrapattr}>" . $childdata . "</{$wraptag}>" : "";
+                $str .= strtr($nstr, array('@childlist' => $childlist));
+            }
+        }
+        return $str;
+    }
+
+    /**
+     * 菜单数据
+     * @param int $myid
+     * @param string $itemtpl
+     * @param mixed $selectedids
+     * @param mixed $disabledids
+     * @param string $wraptag
+     * @param string $wrapattr
+     * @param int $deeplevel
+     * @return string
+     */
+    public function getTreeMenu($myid, $itemtpl, $selectedids = '', $disabledids = '', $wraptag = 'ul', $wrapattr = '', $deeplevel = 0)
+    {
+        $str = '';
+        $childs = $this->getChild($myid);
+        if ($childs)
+        {
+            foreach ($childs as $value)
+            {
+                $id = $value['id'];
+                unset($value['child']);
+                $selected = in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
+                $disabled = in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
+                $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled));
+                $value = array_combine(array_map(function($k)
+                        {
+                            return '@' . $k;
+                        }, array_keys($value)), $value);
+                $bakvalue = array_intersect_key($value, array_flip(['@url', '@caret', '@class']));
+                $value = array_diff_key($value, $bakvalue);
+                $nstr = strtr($itemtpl, $value);
+                $value = array_merge($value, $bakvalue);
+                $childdata = $this->getTreeMenu($id, $itemtpl, $selectedids, $disabledids, $wraptag, $wrapattr, $deeplevel + 1);
+                $childlist = $childdata ? "<{$wraptag} {$wrapattr}>" . $childdata . "</{$wraptag}>" : "";
+                $childlist = strtr($childlist, array('@class' => $childdata ? 'last' : ''));
+                $value = array(
+                    '@childlist' => $childlist,
+                    '@url'       => $childdata || !isset($value['@url']) ? "javascript:;" : url($value['@url']),
+                    '@caret'     => ($childdata && !$value['@badge'] ? '<i class="fa fa-angle-left"></i>' : ''),
+                    '@badge'     => $value['@badge'],
+                    '@class'     => ($selected ? ' active' : '') . ($disabled ? ' disabled' : '') . ($childdata ? ' treeview' : ''),
+                );
+                $str .= strtr($nstr, $value);
+            }
+        }
+        return $str;
+    }
+
+    /**
+     * 特殊
+     * @param integer $myid 要查询的ID
+     * @param string $itemtpl1   第一种HTML代码方式
+     * @param string $itemtpl2  第二种HTML代码方式
+     * @param mixed $selectedids  默认选中
+     * @param mixed $disabledids  禁用
+     * @param integer $itemprefix 前缀
+     */
+    public function getTreeSpecial($myid, $itemtpl1, $itemtpl2, $selectedids = 0, $disabledids = 0, $itemprefix = '')
+    {
+        $ret = '';
+        $number = 1;
+        $childs = $this->getChild($myid);
+        if ($childs)
+        {
+            $total = count($childs);
+            foreach ($childs as $id => $value)
+            {
+                $j = $k = '';
+                if ($number == $total)
+                {
+                    $j .= $this->icon[2];
+                    $k = $itemprefix ? $this->nbsp : '';
+                }
+                else
+                {
+                    $j .= $this->icon[1];
+                    $k = $itemprefix ? $this->icon[0] : '';
+                }
+                $spacer = $itemprefix ? $itemprefix . $j : '';
+                $selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
+                $disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
+                $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled, 'spacer' => $spacer));
+                $value = array_combine(array_map(function($k)
+                        {
+                            return '@' . $k;
+                        }, array_keys($value)), $value);
+                $nstr = strtr(!isset($value['@disabled']) || !$value['@disabled'] ? $itemtpl1 : $itemtpl2, $value);
+
+                $ret .= $nstr;
+                $ret .= $this->getTreeSpecial($id, $itemtpl1, $itemtpl2, $selectedids, $disabledids, $itemprefix . $k . $this->nbsp);
+                $number++;
+            }
+        }
+        return $ret;
+    }
+
+    /**
+     *
+     * 获取树状数组
+     * @param string $myid 要查询的ID
+     * @param string $nametpl 名称条目模板
+     * @param string $itemprefix 前缀
+     * @return string
+     */
+    public function getTreeArray($myid, $itemprefix = '')
+    {
+        $childs = $this->getChild($myid);
+        $n = 0;
+        $data = [];
+        $number = 1;
+        if ($childs)
+        {
+            $total = count($childs);
+            foreach ($childs as $id => $value)
+            {
+                $j = $k = '';
+                if ($number == $total)
+                {
+                    $j .= $this->icon[2];
+                    $k = $itemprefix ? $this->nbsp : '';
+                }
+                else
+                {
+                    $j .= $this->icon[1];
+                    $k = $itemprefix ? $this->icon[0] : '';
+                }
+                $spacer = $itemprefix ? $itemprefix . $j : '';
+                $value['spacer'] = $spacer;
+                $data[$n] = $value;
+                $data[$n]['childlist'] = $this->getTreeArray($id, $itemprefix . $k . $this->nbsp);
+                $n++;
+                $number++;
+            }
+        }
+        return $data;
+    }
+
+    /**
+     * 将getTreeArray的结果返回为二维数组
+     * @param array $data
+     * @return array
+     */
+    public function getTreeList($data = [], $field = 'name')
+    {
+        $arr = [];
+        foreach ($data as $k => $v)
+        {
+            $childlist = isset($v['childlist']) ? $v['childlist'] : [];
+            unset($v['childlist']);
+            $v[$field] = $v['spacer'] . ' ' . $v[$field];
+            $v['haschild'] = $childlist ? 1 : 0;
+            if ($v['id'])
+                $arr[] = $v;
+            if ($childlist)
+            {
+                $arr = array_merge($arr, $this->getTreeList($childlist, $field));
+            }
+        }
+        return $arr;
+    }
+
+}
diff --git a/extend/fast/payment/Alipay.php b/extend/fast/payment/Alipay.php
new file mode 100644
index 0000000..97d4e52
--- /dev/null
+++ b/extend/fast/payment/Alipay.php
@@ -0,0 +1,447 @@
+<?php
+
+namespace fast\payment;
+
+use DOMDocument;
+use Exception;
+
+/**
+ * 支付宝
+ * @link https://github.com/mytharcher/alipay-php-sdk
+ */
+class Alipay
+{
+
+    const SERVICE = 'create_direct_pay_by_user';
+    const SERVICE_WAP = 'alipay.wap.trade.create.direct';
+    const SERVICE_WAP_AUTH = 'alipay.wap.auth.authAndExecute';
+    const SERVICE_APP = 'mobile.securitypay.pay';
+    const GATEWAY = 'https://mapi.alipay.com/gateway.do?';
+    const GATEWAY_MOBILE = 'http://wappaygw.alipay.com/service/rest.htm?';
+    const VERIFY_URL = 'http://notify.alipay.com/trade/notify_query.do?';
+    const VERIFY_URL_HTTPS = 'https://mapi.alipay.com/gateway.do?service=notify_verify&';
+
+    // 配置信息在实例化时传入,以下为范例
+    private $config = array(
+        // 即时到账方式
+        'payment_type'     => 1,
+        // 传输协议
+        'transport'        => 'http',
+        // 编码方式
+        'input_charset'    => 'utf-8',
+        // 签名方法
+        'sign_type'        => 'MD5',
+        // 证书路径
+        'cacert'           => './cacert.pem',
+        //验签公钥地址
+        'public_key_path'  => './alipay_public_key.pem',
+        'private_key_path' => '',
+        // 支付完成异步通知调用地址
+        // 'notify_url' => 'http://'.$_SERVER['HTTP_HOST'].'/order/callback_alipay/notify',
+        // 支付完成同步返回地址
+        // 'return_url' => 'http://'.$_SERVER['HTTP_HOST'].'/order/callback_alipay/return',
+        // 支付宝商家 ID
+        'partner'          => '2088xxxxxxxx',
+        // // 支付宝商家 KEY
+        'key'              => 'xxxxxxxxxxxx',
+        // // 支付宝商家注册邮箱
+        'seller_email'     => 'email@domain.com'
+    );
+    private $is_mobile = FALSE;
+    public $service = self::SERVICE;
+    public $gateway = self::GATEWAY;
+
+    /**
+     * 配置
+     * @param $options  array 配置信息
+     * @param null $type string 类型  wap app
+     */
+    public function __construct($options = [], $type = null)
+    {
+        if ($config = Config::get('payment.alipay'))
+        {
+            $this->config = array_merge($this->config, $config);
+        }
+        $this->config = array_merge($this->config, is_array($options) ? $options : []);
+        $this->is_mobile = (($type == 'wap' || $type === true) ? true : false);
+        if ($this->is_mobile)
+        {
+            $this->gateway = self::GATEWAY_MOBILE;
+        }
+
+        if ($type == 'wap' || $type === true)
+        {
+            $this->service = self::SERVICE_WAP;
+        }
+        elseif ($type == 'app')
+        {
+            $this->service = self::SERVICE_APP;
+        }
+    }
+
+    /**
+     * 生成请求参数的签名
+     *
+     * @param $params <Array>
+     * @return <String>
+     *
+     */
+    function signParameters($params)
+    {
+        // 支付宝的签名串必须是未经过 urlencode 的字符串
+        // 不清楚为何 PHP 5.5 里没有 http_build_str() 方法
+        $paramStr = urldecode(http_build_query($params));
+        switch (strtoupper(trim($this->config['sign_type'])))
+        {
+            case "MD5" :
+                $result = md5($paramStr . $this->config['key']);
+                break;
+            case "RSA" :
+            case "0001" :
+                $priKey = file_get_contents($this->config['private_key_path']);
+                $res = openssl_get_privatekey($priKey);
+                openssl_sign($paramStr, $sign, $res);
+                openssl_free_key($res);
+                //base64编码
+                $result = base64_encode($sign);
+                break;
+            default :
+                $result = "";
+        }
+        return $result;
+    }
+
+    /**
+     * 准备签名参数
+     *
+     * @param $params <Array>
+     *        $params['out_trade_no']     唯一订单编号
+     *        $params['subject']
+     *        $params['total_fee']
+     *        $params['body']
+     *        $params['show_url']
+     *        $params['anti_phishing_key']
+     *        $params['exter_invoke_ip']
+     *        $params['it_b_pay']
+     *        $params['_input_charset']
+     * @return <Array>
+     */
+    function prepareParameters($params)
+    {
+        $default = array(
+            'service'        => $this->service,
+            'partner'        => $this->config['partner'],
+            '_input_charset' => trim(strtolower($this->config['input_charset']))
+        );
+        if (!$this->is_mobile)
+        {
+            $default = array_merge($default, array(
+                'payment_type' => $this->config['payment_type'],
+                'seller_id'    => $this->config['partner'],
+                'notify_url'   => $this->config['notify_url'],
+            ));
+            if (isset($this->config['return_url']))
+            {
+                $default['return_url'] = $this->config['return_url'];
+            }
+        }
+
+        $params = $this->filterSignParameter(array_merge($default, (array) $params));
+        ksort($params);
+        reset($params);
+        return $params;
+    }
+
+    /**
+     * 生成签名后的请求参数
+     *
+     */
+    function buildSignedParameters($params)
+    {
+        $params = $this->prepareParameters($params);
+        $params['sign'] = $this->signParameters($params);
+        if ($params['service'] != self::SERVICE_WAP && $params['service'] != self::SERVICE_WAP_AUTH)
+        {
+            $params['sign_type'] = strtoupper(trim($this->config['sign_type']));
+        }
+        return $params;
+    }
+
+    /**
+     * https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.NgdeQA&treeId=59&articleId=103663&docType=1
+     * 服务端生成app支付使用的参数以及签名
+     * @param $params <Array>
+     * @return <Array>
+     */
+    function buildSignedParametersForApp($params)
+    {
+        $params = $this->prepareParameters($params);
+        $params['sign'] = urlencode($this->signParameters($params));
+        $params['sign_type'] = 'RSA';
+        $paramStr = [];
+        foreach ($params as $k => &$param)
+        {
+            $param = '"' . $param . '"';
+            $paramStr[] = $k . '=' . $param;
+        }
+
+        return implode('&', $paramStr);
+    }
+
+    /**
+     * 生成请求参数的发送表单HTML
+     *
+     * 其实这个函数没有必要,更应该使用签名后的参数自己组装,只不过有时候方便就从官方 SDK 里留下了。
+     *
+     * @param $params <Array> 请求参数(未签名的)
+     * @param $method <String> 请求方法,默认:post,可选 get
+     * @param $target <String> 提交目标,默认:_self
+     * @return <String>
+     *
+     */
+    function buildRequestFormHTML($params, $method = 'post', $target = '_self')
+    {
+        $params = $this->buildSignedParameters($params);
+        $html = '<meta charset="' . $this->config['input_charset'] . '" /><form id="alipaysubmit" name="alipaysubmit" action="' . $this->gateway . ' _input_charset="' . trim(strtolower($this->config['input_charset'])) . '" method="' . $method . ' target="$target">';
+        foreach ($params as $key => $value)
+        {
+            $html .= "<input type='hidden' name='$key' value='$value'/>";
+        }
+        $html .= "</form><script>document.forms['alipaysubmit'].submit();</script>";
+        return $html;
+    }
+
+    /**
+     * 准备移动网页支付的请求参数
+     *
+     * 移动网页支付接口不同,需要先服务器提交一次请求,拿到返回 token 再返回客户端发起真实支付请求。
+     * 该方法只完成第一次服务端请求,生成参数后需要客户端另行处理(可调用`buildRequestFormHTML`生成表单提交)。
+     *
+     * @param $params <Array>
+     *        $params['out_trade_no'] 订单唯一编号
+     *        $params['subject']      商品标题
+     *        $params['total_fee']    支付总费用
+     *        $params['merchant_url'] 商品链接地址
+     *        $params['req_id']       请求唯一 ID
+     *        $params['it_b_pay']     超期时间(秒)
+     * @return <Array>/<NULL>
+     */
+    function prepareMobileTradeData($params)
+    {
+        // 不要用 SimpleXML 来构建 xml 结构,因为有第一行文档申明支付宝验证不通过
+        $xml_str = '<direct_trade_create_req>' .
+                '<notify_url>' . $this->config['notify_url'] . '</notify_url>' .
+                '<call_back_url>' . $this->config['return_url'] . '</call_back_url>' .
+                '<seller_account_name>' . $this->config['seller_email'] . '</seller_account_name>' .
+                '<out_trade_no>' . $params['out_trade_no'] . '</out_trade_no>' .
+                '<subject>' . htmlspecialchars($params['subject'], ENT_XML1, 'UTF-8') . '</subject>' .
+                '<total_fee>' . $params['total_fee'] . '</total_fee>' .
+                '<merchant_url>' . $params['merchant_url'] . '</merchant_url>' .
+                (isset($params['it_b_pay']) ? '<pay_expire>' . $params['it_b_pay'] . '</pay_expire>' : '') .
+                '</direct_trade_create_req>';
+        $request_data = $this->buildSignedParameters(array(
+            'service'  => $this->service,
+            'partner'  => $this->config['partner'],
+            'sec_id'   => $this->config['sign_type'],
+            'format'   => 'xml',
+            'v'        => '2.0',
+            'req_id'   => $params['req_id'],
+            'req_data' => $xml_str
+        ));
+        $url = $this->gateway;
+        $input_charset = trim(strtolower($this->config['input_charset']));
+        if (trim($input_charset) != '')
+        {
+            $url = $url . "_input_charset=" . $input_charset;
+        }
+        $curl = curl_init($url);
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); //SSL证书认证
+        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); //严格认证
+        curl_setopt($curl, CURLOPT_CAINFO, $this->config['cacert']); //证书地址
+        curl_setopt($curl, CURLOPT_HEADER, 0); // 过滤HTTP头
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 显示输出结果
+        curl_setopt($curl, CURLOPT_POST, true); // post传输数据
+        curl_setopt($curl, CURLOPT_POSTFIELDS, $request_data); // post传输数据
+        $responseText = curl_exec($curl);
+        //var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容
+        curl_close($curl);
+        if (empty($responseText))
+        {
+            return NULL;
+        }
+        parse_str($responseText, $responseData);
+        if (empty($responseData['res_data']))
+        {
+            return NULL;
+        }
+        if ($this->config['sign_type'] == '0001')
+        {
+            $responseData['res_data'] = $this->rsaDecrypt($responseData['res_data'], $this->config['private_key_path']);
+        }
+        //token从res_data中解析出来(也就是说res_data中已经包含token的内容)
+        $doc = new DOMDocument();
+        $doc->loadXML($responseData['res_data']);
+        $responseData['request_token'] = $doc->getElementsByTagName("request_token")->item(0)->nodeValue;
+        $xml_str = '<auth_and_execute_req>' .
+                '<request_token>' . $responseData['request_token'] . '</request_token>' .
+                '</auth_and_execute_req>';
+        return array(
+            'service'  => self::SERVICE_WAP_AUTH,
+            'partner'  => $this->config['partner'],
+            'sec_id'   => $this->config['sign_type'],
+            'format'   => 'xml',
+            'v'        => '2.0',
+            'req_data' => $xml_str
+        );
+    }
+
+    /**
+     * 支付完成验证返回参数(包含同步和异步)
+     *
+     * @return <Boolean>
+     */
+    function verifyCallback()
+    {
+        $async = empty($_GET);
+        $data = $async ? $_POST : $_GET;
+        if (empty($data))
+        {
+            return FALSE;
+        }
+        $signValid = $this->verifyParameters($data, $data["sign"]);
+        $notify_id = isset($data['notify_id']) ? $data['notify_id'] : NULL;
+        if ($async && $this->is_mobile)
+        {
+            //对notify_data解密
+            if ($this->config['sign_type'] == '0001')
+            {
+                $data['notify_data'] = $this->rsaDecrypt($data['notify_data'], $this->config['private_key_path']);
+            }
+            //notify_id从decrypt_post_para中解析出来(也就是说decrypt_post_para中已经包含notify_id的内容)
+            $doc = new DOMDocument();
+            $doc->loadXML($data['notify_data']);
+            $notify_id = $doc->getElementsByTagName('notify_id')->item(0)->nodeValue;
+        }
+        //获取支付宝远程服务器ATN结果(验证是否是支付宝发来的消息)
+        $responseTxt = 'true';
+        if (!empty($notify_id))
+        {
+            $responseTxt = $this->verifyFromServer($notify_id);
+        }
+        //验证
+        //$signValid的结果不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
+        //$responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
+        return $signValid && preg_match("/true$/i", $responseTxt);
+    }
+
+    function verifyParameters($params, $sign)
+    {
+        $params = $this->filterSignParameter($params);
+        if (isset($params['notify_data']))
+        {
+            $params = array(
+                'service'     => $params['service'],
+                'v'           => $params['v'],
+                'sec_id'      => $params['sec_id'],
+                'notify_data' => $params['notify_data']
+            );
+        }
+        else
+        {
+            ksort($params);
+            reset($params);
+        }
+        $content = urldecode(http_build_query($params));
+        switch (strtoupper(trim($this->config['sign_type'])))
+        {
+            case "MD5" :
+                return md5($content . $this->config['key']) == $sign;
+            case "RSA" :
+            case "0001" :
+                return $this->rsaVerify($content, $this->config['public_key_path'], $sign);
+            default :
+                return FALSE;
+        }
+    }
+
+    /**
+     * 过滤参数,去除sign/sign_type参数
+     * @param $params
+     * @return <Array>
+     */
+    function filterSignParameter($params)
+    {
+        $result = array();
+        foreach ($params as $key => $value)
+        {
+            if ($key != 'sign' && $key != 'sign_type' && $value)
+            {
+                $result[$key] = $value;
+            }
+        }
+        return $result;
+    }
+
+    function verifyFromServer($notify_id)
+    {
+        $transport = strtolower(trim($this->config['transport']));
+        $partner = trim($this->config['partner']);
+        $veryfy_url = ($transport == 'https' ? self::VERIFY_URL_HTTPS : self::VERIFY_URL) . "partner=$partner&notify_id=$notify_id";
+        $curl = curl_init($veryfy_url);
+        curl_setopt($curl, CURLOPT_HEADER, 0); // 过滤HTTP头
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); //SSL证书认证
+        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); //严格认证
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 显示输出结果
+        curl_setopt($curl, CURLOPT_CAINFO, $this->config['cacert']); //证书地址
+        $responseText = curl_exec($curl);
+        // var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容
+        curl_close($curl);
+        return $responseText;
+    }
+
+    /**
+     * RSA验签,注意验签的公钥是支付宝的公钥,不是自己生成的rsa公钥,可以在淘宝的demo中获得
+     * @param $data string 待签名数据
+     * @param $ali_public_key_path string 支付宝的公钥文件路径
+     * @param $sign string 要校对的的签名结果
+     * @return <Boolean> 验证结果
+     * @throws Exception
+     */
+    function rsaVerify($data, $ali_public_key_path, $sign)
+    {
+        $pubKey = file_get_contents($ali_public_key_path);
+        $res = openssl_get_publickey($pubKey);
+        if (!$res)
+        {
+            throw new Exception('公钥格式错误');
+        }
+        $result = (bool) openssl_verify($data, base64_decode($sign), $res);
+        openssl_free_key($res);
+        return $result;
+    }
+
+    /**
+     * RSA解密
+     * @param $content string 需要解密的内容,密文
+     * @param $private_key_path string 商户私钥文件路径
+     * @return string 解密后内容,明文
+     */
+    function rsaDecrypt($content, $private_key_path)
+    {
+        $priKey = file_get_contents($private_key_path);
+        $res = openssl_get_privatekey($priKey);
+        //用base64将内容还原成二进制
+        $content = base64_decode($content);
+        //把需要解密的内容,按128位拆开解密
+        $result = '';
+        for ($i = 0; $i < strlen($content) / 128; $i++)
+        {
+            $data = substr($content, $i * 128, 128);
+            openssl_private_decrypt($data, $decrypt, $res);
+            $result .= $decrypt;
+        }
+        openssl_free_key($res);
+        return $result;
+    }
+
+}
diff --git a/extend/fast/payment/Wechat.php b/extend/fast/payment/Wechat.php
new file mode 100644
index 0000000..fc02c94
--- /dev/null
+++ b/extend/fast/payment/Wechat.php
@@ -0,0 +1,440 @@
+<?php
+
+namespace fast\payment;
+
+use Exception;
+use think\Config;
+
+/**
+ * @link https://github.com/zhangv/wechat-pay
+ */
+class Wechat
+{
+
+    const TRADETYPE_JSAPI = 'JSAPI', TRADETYPE_NATIVE = 'NATIVE', TRADETYPE_APP = 'APP';
+    const URL_UNIFIEDORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
+    const URL_ORDERQUERY = "https://api.mch.weixin.qq.com/pay/orderquery";
+    const URL_CLOSEORDER = 'https://api.mch.weixin.qq.com/pay/closeorder';
+    const URL_REFUND = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
+    const URL_REFUNDQUERY = 'https://api.mch.weixin.qq.com/pay/refundquery';
+    const URL_DOWNLOADBILL = 'https://api.mch.weixin.qq.com/pay/downloadbill';
+    const URL_REPORT = 'https://api.mch.weixin.qq.com/payitil/report';
+    const URL_SHORTURL = 'https://api.mch.weixin.qq.com/tools/shorturl';
+    const URL_MICROPAY = 'https://api.mch.weixin.qq.com/pay/micropay';
+
+    /**
+     * 错误信息
+     */
+    public $error = null;
+
+    /**
+     * 错误信息XML
+     */
+    public $errorXML = null;
+
+    /**
+     * 微信支付配置数组
+     * appid        公众账号appid
+     * mch_id       商户号
+     * apikey       加密key
+     * appsecret    公众号appsecret
+     * sslcertPath  证书路径(apiclient_cert.pem)
+     * sslkeyPath   密钥路径(apiclient_key.pem)
+     */
+    private $_config;
+
+    /**
+     * @param $options 微信支付配置数组
+     */
+    public function __construct($options = [])
+    {
+        if ($config = Config::get('payment.wechat'))
+        {
+            $this->config = array_merge($this->config, $config);
+        }
+        $this->_config = array_merge($this->_config, is_array($options) ? $options : []);
+    }
+
+    /**
+     * JSAPI获取prepay_id
+     *
+     * @param string $body
+     * @param string $out_trade_no
+     * @param int $total_fee
+     * @param string $openid
+     * @param array $ext
+     * @return string
+     */
+    public function getPrepayId($body, $out_trade_no, $total_fee, $openid, $ext = null)
+    {
+        $data = $ext? : [];
+        $data["nonce_str"] = $this->getNonceStr();
+        $data["body"] = $body;
+        $data["out_trade_no"] = $out_trade_no;
+        $data["total_fee"] = $total_fee;
+        $data["spbill_create_ip"] = $_SERVER["REMOTE_ADDR"];
+        $data["trade_type"] = self::TRADETYPE_JSAPI;
+        $data["openid"] = $openid;
+        $result = $this->unifiedOrder($data);
+        if ($result["return_code"] == "SUCCESS" && $result["result_code"] == "SUCCESS")
+        {
+            return $result["prepay_id"];
+        }
+        else
+        {
+            $this->error = $result["return_code"] == "SUCCESS" ? $result["err_code_des"] : $result["return_msg"];
+            $this->errorXML = $this->array2xml($result);
+            return null;
+        }
+    }
+
+    private function getNonceStr()
+    {
+        return substr(str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789"), 0, 32);
+    }
+
+    /**
+     * 统一下单接口
+     */
+    public function unifiedOrder($params)
+    {
+        $data = array();
+        $data["appid"] = $this->_config["appid"];
+        $data["mch_id"] = $this->_config["mch_id"];
+        $data["device_info"] = (isset($params['device_info']) && trim($params['device_info']) != '') ? $params['device_info'] : null;
+        $data["nonce_str"] = $this->getNonceStr();
+        $data["body"] = $params['body'];
+        $data["detail"] = isset($params['detail']) ? $params['detail'] : null; //optional
+        $data["attach"] = isset($params['attach']) ? $params['attach'] : null; //optional
+        $data["out_trade_no"] = isset($params['out_trade_no']) ? $params['out_trade_no'] : null;
+        $data["fee_type"] = isset($params['fee_type']) ? $params['fee_type'] : 'CNY';
+        $data["total_fee"] = $params['total_fee'];
+        $data["spbill_create_ip"] = $params['spbill_create_ip'];
+        $data["time_start"] = isset($params['time_start']) ? $params['time_start'] : null; //optional
+        $data["time_expire"] = isset($params['time_expire']) ? $params['time_expire'] : null; //optional
+        $data["goods_tag"] = isset($params['goods_tag']) ? $params['goods_tag'] : null;
+        $data["notify_url"] = isset($params['notify_url']) ? $params['notify_url'] : $this->_config['notify_url'];
+        $data["trade_type"] = $params['trade_type'];
+        $data["product_id"] = isset($params['product_id']) ? $params['product_id'] : null; //required when trade_type = NATIVE
+        $data["openid"] = isset($params['openid']) ? $params['openid'] : null; //required when trade_type = JSAPI
+        $result = $this->post(self::URL_UNIFIEDORDER, $data);
+        return $result;
+    }
+
+    private function post($url, $data, $cert = false)
+    {
+        if (!isset($data['sign']))
+            $data["sign"] = $this->sign($data);
+        $xml = $this->array2xml($data);
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_URL, $url);
+        if ($cert == true)
+        {
+            //使用证书:cert 与 key 分别属于两个.pem文件
+            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
+            curl_setopt($ch, CURLOPT_SSLCERT, $this->_config['sslcertPath']);
+            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
+            curl_setopt($ch, CURLOPT_SSLKEY, $this->_config['sslkeyPath']);
+        }
+        $content = curl_exec($ch);
+        $array = $this->xml2array($content);
+        return $array;
+    }
+
+    /**
+     * 扫码支付(模式二)获取支付二维码
+     *
+     * @param string $body
+     * @param string $out_trade_no
+     * @param int $total_fee
+     * @param string $product_id
+     * @param array $ext
+     * @return string
+     */
+    public function getCodeUrl($body, $out_trade_no, $total_fee, $product_id, $ext = null)
+    {
+        $data = $ext ? $ext : [];
+        $data["nonce_str"] = $this->getNonceStr();
+        $data["body"] = $body;
+        $data["out_trade_no"] = $out_trade_no;
+        $data["total_fee"] = $total_fee;
+        $data["spbill_create_ip"] = $_SERVER["SERVER_ADDR"];
+        $data["trade_type"] = self::TRADETYPE_NATIVE;
+        $data["product_id"] = $product_id;
+        $result = $this->unifiedOrder($data);
+        if ($result["return_code"] == "SUCCESS" && $result["result_code"] == "SUCCESS")
+        {
+            return $result["code_url"];
+        }
+        else
+        {
+            $this->error = $result["return_code"] == "SUCCESS" ? $result["err_code_des"] : $result["return_msg"];
+            return null;
+        }
+    }
+
+    /**
+     * 查询订单
+     * @param $transaction_id
+     * @param $out_trade_no
+     * @return array
+     */
+    public function orderQuery($transaction_id, $out_trade_no)
+    {
+        $data = array();
+        $data["appid"] = $this->_config["appid"];
+        $data["mch_id"] = $this->_config["mch_id"];
+        $data["transaction_id"] = $transaction_id;
+        $data["out_trade_no"] = $out_trade_no;
+        $data["nonce_str"] = $this->getNonceStr();
+        $result = $this->post(self::URL_ORDERQUERY, $data);
+        return $result;
+    }
+
+    /**
+     * 关闭订单
+     * @param $out_trade_no
+     * @return array
+     */
+    public function closeOrder($out_trade_no)
+    {
+        $data = array();
+        $data["appid"] = $this->_config["appid"];
+        $data["mch_id"] = $this->_config["mch_id"];
+        $data["out_trade_no"] = $out_trade_no;
+        $data["nonce_str"] = $this->getNonceStr();
+        $result = $this->post(self::URL_CLOSEORDER, $data);
+        return $result;
+    }
+
+    /**
+     * 申请退款 - 使用商户订单号
+     * @param $out_trade_no 商户订单号
+     * @param $out_refund_no 退款单号
+     * @param $total_fee 总金额(单位:分)
+     * @param $refund_fee 退款金额(单位:分)
+     * @param $op_user_id 操作员账号
+     * @return array
+     */
+    public function refund($out_trade_no, $out_refund_no, $total_fee, $refund_fee, $op_user_id)
+    {
+        $data = array();
+        $data["appid"] = $this->_config["appid"];
+        $data["mch_id"] = $this->_config["mch_id"];
+        $data["nonce_str"] = $this->getNonceStr();
+        $data["out_trade_no"] = $out_trade_no;
+        $data["out_refund_no"] = $out_refund_no;
+        $data["total_fee"] = $total_fee;
+        $data["refund_fee"] = $refund_fee;
+        $data["op_user_id"] = $op_user_id;
+        $result = $this->post(self::URL_REFUND, $data, true);
+        return $result;
+    }
+
+    /**
+     * 申请退款 - 使用微信订单号
+     * @param $transaction_id 微信订单号
+     * @param $out_refund_no 退款单号
+     * @param $total_fee 总金额(单位:分)
+     * @param $refund_fee 退款金额(单位:分)
+     * @param $op_user_id 操作员账号
+     * @return array
+     */
+    public function refundByTransId($transaction_id, $out_refund_no, $total_fee, $refund_fee, $op_user_id)
+    {
+        $data = array();
+        $data["appid"] = $this->_config["appid"];
+        $data["mch_id"] = $this->_config["mch_id"];
+        $data["nonce_str"] = $this->getNonceStr();
+        $data["transaction_id"] = $transaction_id;
+        $data["out_refund_no"] = $out_refund_no;
+        $data["total_fee"] = $total_fee;
+        $data["refund_fee"] = $refund_fee;
+        $data["op_user_id"] = $op_user_id;
+        $result = $this->post(self::URL_REFUND, $data, true);
+        return $result;
+    }
+
+    /**
+     * 下载对账单
+     * @param $bill_date 下载对账单的日期,格式:20140603
+     * @param string $bill_type 类型
+     * @return array
+     */
+    public function downloadBill($bill_date, $bill_type = 'ALL')
+    {
+        $data = array();
+        $data["appid"] = $this->_config["appid"];
+        $data["mch_id"] = $this->_config["mch_id"];
+        $data["bill_date"] = $bill_date;
+        $data["bill_type"] = $bill_type;
+        $data["nonce_str"] = $this->getNonceStr();
+        $result = $this->post(self::URL_DOWNLOADBILL, $data);
+        return $result;
+    }
+
+    /**
+     * 扫码原生支付模式一中的二维码链接转成短链接
+     * @param $long_url 需要转换的URL,签名用原串,传输需URLencode
+     * @return array
+     */
+    public function shortUrl($long_url)
+    {
+        $data = array();
+        $data["appid"] = $this->_config["appid"];
+        $data["mch_id"] = $this->_config["mch_id"];
+        $data["long_url"] = $long_url;
+        $data["nonce_str"] = $this->getNonceStr();
+        $data["sign"] = $this->sign($data);
+        $data["long_url"] = urlencode($long_url);
+        $result = $this->post(self::URL_SHORTURL, $data);
+        return $result;
+    }
+
+    /**
+     * 获取jsapi支付所需参数
+     *
+     * @param string $prepay_id
+     * @return array
+     */
+    public function getPackageData($prepay_id)
+    {
+        $data = array();
+        $data["appId"] = $this->_config["appid"];
+        //解决微信支付调用JSAPI缺少参数:timeStamp
+        $data["timeStamp"] = time();
+        $data["nonceStr"] = $this->getNonceStr();
+        $data["package"] = "prepay_id=$prepay_id";
+        $data["signType"] = "MD5";
+        $data["paySign"] = $this->sign($data);
+        return $data;
+    }
+
+    /**
+     * 获取发送到通知地址的数据(在通知地址内使用)
+     * @return string 结果数组,如果不是微信服务器发送的数据返回null
+     *          appid
+     *          bank_type
+     *          cash_fee
+     *          fee_type
+     *          is_subscribe
+     *          mch_id
+     *          nonce_str
+     *          openid
+     *          out_trade_no    商户订单号
+     *          result_code
+     *          return_code
+     *          sign
+     *          time_end
+     *          total_fee       总金额
+     *          trade_type
+     *          transaction_id  微信支付订单号
+     */
+    public function getNotifyData()
+    {
+        $xml = file_get_contents("php://input");
+        $data = $this->xml2array($xml);
+        if ($this->validate($data))
+        {
+            return $data;
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+    /**
+     * 验证数据签名
+     * @param $data 数据数组
+     * @return 数据校验结果
+     */
+    public function validate($data)
+    {
+        if (!isset($data["sign"]))
+        {
+            return false;
+        }
+        $sign = $data["sign"];
+        unset($data["sign"]);
+        return $this->sign($data) == $sign;
+    }
+
+    /**
+     * 响应微信支付后台通知
+     * @param string $return_code 返回状态码 SUCCESS/FAIL
+     * @param $return_msg  返回信息
+     */
+    public function response_back($return_code = "SUCCESS", $return_msg = null)
+    {
+        $data = array();
+        $data["return_code"] = $return_code;
+        if ($return_msg)
+        {
+            $data["return_msg"] = $return_msg;
+        }
+        $xml = $this->array2xml($data);
+        print $xml;
+    }
+
+    /**
+     * 数据签名
+     * @param $data
+     * @return string
+     */
+    private function sign($data)
+    {
+        ksort($data);
+        $string1 = "";
+        foreach ($data as $k => $v)
+        {
+            if ($v && trim($v) != '')
+            {
+                $string1 .= "$k=$v&";
+            }
+        }
+        $stringSignTemp = $string1 . "key=" . $this->_config["apikey"];
+        $sign = strtoupper(md5($stringSignTemp));
+        return $sign;
+    }
+
+    private function array2xml($array)
+    {
+        $xml = "<xml>" . PHP_EOL;
+        foreach ($array as $k => $v)
+        {
+            if ($v && trim($v) != '')
+                $xml .= "<$k><![CDATA[$v]]></$k>" . PHP_EOL;
+        }
+        $xml .= "</xml>";
+        return $xml;
+    }
+
+    private function xml2array($xml)
+    {
+        $array = array();
+        $tmp = null;
+        try
+        {
+            $tmp = (array) simplexml_load_string($xml);
+        }
+        catch (Exception $e)
+        {
+
+        }
+        if ($tmp && is_array($tmp))
+        {
+            foreach ($tmp as $k => $v)
+            {
+                $array[$k] = (string) $v;
+            }
+        }
+        return $array;
+    }
+
+}
diff --git a/extend/fast/service/Alisms.php b/extend/fast/service/Alisms.php
new file mode 100644
index 0000000..92d87cf
--- /dev/null
+++ b/extend/fast/service/Alisms.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace fast\service;
+
+use think\Config;
+
+/**
+ * 阿里大于SMS短信发送
+ */
+class Alisms
+{
+
+    private $_params = [];
+    public $error = [];
+    protected $config = [];
+
+    public function __construct($options = [])
+    {
+        if ($config = Config::get('service.alisms'))
+        {
+            $this->config = array_merge($this->config, $config);
+        }
+        $this->config = array_merge($this->config, is_array($options) ? $options : []);
+    }
+
+    /**
+     * 单例
+     * @param array $options 参数
+     * @return Alisms
+     */
+    public static function instance($options = [])
+    {
+        if (is_null(self::$instance))
+        {
+            self::$instance = new static($options);
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * 设置签名
+     * @param string $sign
+     * @return Alisms
+     */
+    public function sign($sign = '')
+    {
+        $this->_params['sms_free_sign_name'] = $sign;
+        return $this;
+    }
+
+    /**
+     * 设置参数
+     * @param array $param
+     * @return Alisms
+     */
+    public function param(array $param = [])
+    {
+        foreach ($param as $k => &$v)
+        {
+            $v = (string) $v;
+        }
+        unset($v);
+        $this->_params['sms_param'] = json_encode($param);
+        return $this;
+    }
+
+    /**
+     * 设置模板
+     * @param string $code 短信模板
+     * @return Alisms
+     */
+    public function template($code = '')
+    {
+        $this->_params['sms_template_code'] = $code;
+        return $this;
+    }
+
+    /**
+     * 接收手机
+     * @param string $mobile 手机号码
+     * @return Alisms
+     */
+    public function mobile($mobile = '')
+    {
+        $this->_params['rec_num'] = $mobile;
+        return $this;
+    }
+
+    /**
+     * 立即发送
+     * @return boolean
+     */
+    public function send()
+    {
+        $this->error = [];
+        $params = $this->_params();
+        $params['sign'] = $this->_signed($params);
+        $reponse = $this->_curl($params);
+        if ($reponse !== FALSE)
+        {
+            $res = json_decode($reponse, TRUE);
+            $res = array_pop($res);
+            if (isset($res['result']))
+                return TRUE;
+            $this->error = $res;
+        }
+        else
+        {
+            $this->error = array('code' => 0, 'msg' => 'HTTP_RESPONSE_NOT_WELL_FORMED');
+        }
+        return FALSE;
+    }
+
+    /**
+     * 获取错误信息
+     * @return array
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    private function _params()
+    {
+        return array_merge([
+            'app_key'     => $this->config['key'],
+            'format'      => 'json',
+            'method'      => 'alibaba.aliqin.fc.sms.num.send',
+            'v'           => '2.0',
+            'timestamp'   => date('Y-m-d H:i:s'),
+            'sign_method' => 'md5',
+            'sms_type'    => 'normal'
+                ], $this->_params);
+    }
+
+    private function _signed($params)
+    {
+        ksort($params);
+        $sign = $this->config['secret'];
+        foreach ($params as $k => $v)
+        {
+            if (is_string($v) && '@' != substr($v, 0, 1))
+                $sign .= $k . $v;
+        }
+        $sign .= $this->config['secret'];
+        return strtoupper(md5($sign));
+    }
+
+    private function _curl($params)
+    {
+        $uri = 'https://eco.taobao.com/router/rest?' . http_build_query($params);
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
+        curl_setopt($ch, CURLOPT_URL, $uri);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
+        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
+        curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36");
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
+        $reponse = curl_exec($ch);
+        curl_close($ch);
+        return $reponse;
+    }
+
+}
diff --git a/extend/fast/service/Easemob.php b/extend/fast/service/Easemob.php
new file mode 100644
index 0000000..c1a9da5
--- /dev/null
+++ b/extend/fast/service/Easemob.php
@@ -0,0 +1,307 @@
+<?php
+
+namespace fast\service;
+
+use fast\Http;
+use think\Cache;
+use think\Config;
+use think\Log;
+
+/**
+ * 环信消息类
+ */
+class Easemob
+{
+
+    const URL = 'https://a1.easemob.com';
+
+    static $_config = null;
+
+    /**
+     * 创建用户
+     *
+     * @param string $username 用户名
+     * @param string $password 密码
+     * @param string $nickname 昵称
+     * @param boolean $token 是否认证模式
+     * @return array
+     */
+    public static function register($username, $password, $nickname = '', $token = TRUE)
+    {
+        $params = ['username' => $username, 'password' => $password];
+        if ($nickname)
+        {
+            $params['nickname'] = $nickname;
+        }
+        return self::api('users', $params, $token);
+    }
+
+    /**
+     * 创建聊天室
+     *
+     * @param string $name 名称
+     * @param string $description 描述
+     * @param string $owner 创建人
+     * @param int $maxusers 最多参与者
+     * @param array $members 成员
+     * @return array
+     */
+    public static function createRoom($name, $description, $owner, $maxusers = 5000, $members = [])
+    {
+        $owner = (string) $owner;
+        $params = [
+            "name"        => $name, //聊天室名称,此属性为必须的
+            "description" => $description, //聊天室描述,此属性为必须的
+            "maxusers"    => $maxusers, //聊天室成员最大数(包括群主),值为数值类型,默认值200,最大值5000,此属性为可选的
+            "owner"       => $owner, //聊天室的管理员,此属性为必须的
+        ];
+        if ($members)
+        {
+            if (!in_array($owner, $members))
+            {
+                $members[] = $owner;
+            }
+            $params['members'] = $members;
+        }
+        return self::api('chatrooms', $params, TRUE);
+    }
+
+    /**
+     * 创建群组
+     *
+     * @param string $name 名称
+     * @param string $description 描述
+     * @param string $owner 管理员
+     * @param int $maxusers 最大成员数量
+     * @param array $members 成员列表
+     * @param boolean $public 是否公开
+     * @param boolean $approval 加入是否审核
+     * @return array
+     */
+    public static function createGroup($name, $description, $owner, $maxusers = 2000, $members = [], $public = TRUE, $approval = FALSE)
+    {
+        $owner = (string) $owner;
+        $params = [
+            "groupname" => $name,
+            "desc"      => $description,
+            "public"    => (bool) $public,
+            "maxusers"  => $maxusers,
+            "approval"  => (bool) $approval,
+            "owner"     => $owner,
+        ];
+        if ($members)
+        {
+            if (!in_array($owner, $members))
+            {
+                $members[] = $owner;
+            }
+            $params['members'] = $members;
+        }
+        return self::api('chatgroups', $params, TRUE);
+    }
+
+    /**
+     * 修改群组信息
+     *
+     * @param int $group_id
+     * @param string $name
+     * @param string $description
+     * @param int $maxusers
+     * @return array
+     */
+    public static function editGroup($group_id, $name, $description, $maxusers)
+    {
+        $params = [
+            "groupname" => $name,
+            "desc"      => $description,
+            "maxusers"  => $maxusers,
+        ];
+        return self::api('chatgroups/' . $group_id, $params, TRUE, 'PUT');
+    }
+
+    /**
+     * 获取好友列表
+     */
+    public static function getFiends($owner_username)
+    {
+        return self::api("users/{$owner_username}/contacts/users", [], TRUE, 'GET');
+    }
+
+    /**
+     * 删除群组
+     *
+     * @param int $group_id
+     * @return array
+     */
+    public static function deleteGroup($group_id)
+    {
+        $params = [];
+        return self::api('chatgroups/' . $group_id, $params, TRUE, 'DELETE');
+    }
+
+    /**
+     * 发送消息
+     *
+     * @param string $from 发件人
+     * @param string $to 收件人
+     * @param mixed $msg 消息内容
+     * @param string $target_type 消息类型 users/chatgroups/chatrooms
+     * @return array
+     */
+    public static function sendMessage($from, $to, $msg, $target_type = 'users')
+    {
+        if (!is_array($msg))
+        {
+            $msg = [
+                'type' => 'txt',
+                'msg'  => $msg
+            ];
+        }
+        $data = [
+            'target_type' => $target_type,
+            'target'      => is_array($to) ? $to : [$to],
+            'from'        => $from,
+        ];
+        if (isset($msg['ext']))
+        {
+            $data['ext'] = $msg['ext'];
+        }
+        unset($msg['ext']);
+        $data['msg'] = $msg;
+        return self::api('messages', $data);
+    }
+
+    /**
+     * 获取离线消息记录条数
+     * @param string $owner_username
+     * @return array
+     */
+    public static function getOfflineMsgCount($owner_username)
+    {
+        return self::api("users/{$owner_username}/offline_msg_count", [], TRUE, 'GET');
+    }
+
+    /**
+     * 群组添加成员
+     * @param int $chatroom_id
+     * @param array $usernames
+     * @param array
+     */
+    public static function addChatRoomMembers($chatroom_id, $usernames)
+    {
+        return self::api("chatgroups/{$chatroom_id}/users", $usernames, TRUE);
+    }
+
+    /**
+     * 添加单个成员POST
+     */
+    public static function addOneChatRoomMember($chatroom_id, $username)
+    {
+        //return $chatroom_id;
+        return self::api("chatgroups/{$chatroom_id}/users/{$username}", [], TRUE);
+    }
+
+    /**
+     * 群组删除成员
+     * @param int $chatroom_id
+     * @param string $usernames
+     * @return array
+     */
+    public static function minusChatRoomMembers($chatroom_id, $usernames)
+    {
+        return self::api("chatgroups/{$chatroom_id}/users/{$usernames}", [], TRUE, 'DELETE');
+    }
+
+    /**
+     * 添加好友
+     */
+    public static function addFriends($owner_username, $friend_username)
+    {
+        return self::api("users/{$owner_username}/contacts/users/{$friend_username}", [], TRUE);
+    }
+
+    /**
+     * 删除好友
+     */
+    public static function minusFriends($owner_username, $friend_username)
+    {
+        return self::api("users/{$owner_username}/contacts/users/{$friend_username}", [], TRUE, 'DELETE');
+    }
+
+    /**
+     * 查看用户参与的所有群组
+     * @param type $owner_username
+     * @return type
+     */
+    public static function joinedChatgroups($owner_username)
+    {
+        return self::api("users/{$owner_username}/joined_chatgroups", [], TRUE, 'GET');
+    }
+
+    /**
+     * 调用API接口
+     *
+     * @param string $api 接口
+     * @param array $params request head参数
+     * @param boolean $token 是否认证模式
+     * @param string $method 请求方法 POST/GET
+     * @param array $options 扩展配置
+     * @return array
+     */
+    public static function api($api, $params = [], $token = TRUE, $method = 'POST', $options = [])
+    {
+        $header = ['Content-Type:application/json'];
+        if ($token)
+        {
+            $header[] = self::getNewToken();
+        }
+        $config = Config::get('service.easemob');
+        $options[CURLOPT_HTTPHEADER] = $header;
+
+        $url = self::URL . '/' . $config['org_name'] . '/' . $config['app_name'] . '/' . $api;
+        //return $url;
+        $ret = Http::sendRequest($url, json_encode($params), $method, $options);
+        if ($ret['ret'] && $ret['msg'])
+        {
+            $msg = json_decode($ret['msg'], TRUE);
+            if (isset($msg['error']))
+            {
+                Log::error($ret['msg']);
+            }
+            return isset($msg['error']) ? [] : $msg;
+        }
+        else
+        {
+            return [];
+        }
+    }
+
+    private static function getToken()
+    {
+        $tokendata = Cache::get('easemobtoken');
+        if ($tokendata && $tokendata['expiretime'] > time())
+        {
+            return $tokendata['access_token'];
+        }
+        else
+        {
+            $config = Config::get('service.easemob');
+            $data = self::api('token', [
+                        'grant_type'    => 'client_credentials',
+                        'client_id'     => $config['client_id'],
+                        'client_secret' => $config['client_secret'],
+                            ], FALSE, 'POST');
+            if ($data)
+            {
+                $data['expiretime'] = time() + $data['expires_in'];
+                Cache::set('easemobtoken', $data, $data['expires_in'] - 10);
+                return $data['access_token'];
+            }
+            else
+            {
+                return '';
+            }
+        }
+    }
+
+}
diff --git a/extend/fast/service/Qiniu.php b/extend/fast/service/Qiniu.php
new file mode 100644
index 0000000..c240bd5
--- /dev/null
+++ b/extend/fast/service/Qiniu.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace fast\service;
+
+use think\Config;
+use think\Log;
+
+/**
+ * 七牛上传文件管理
+ */
+class Qiniu
+{
+
+    const QINIU_RS = 'http://rs.qbox.me';
+
+    static $_config = null;
+
+    /**
+     * 获取文件
+     *
+     * @param string $domain   域名
+     * @param string $name     文件名
+     * @param string $param    附加参数
+     * @return string
+     */
+    public static function download($domain, $name, $param = [])
+    {
+        $url = $domain . $name . '?' . http_build_query($param);
+        $token = self::sign($url);
+        return $url . '&token=' . $token;
+    }
+
+    /**
+     * 重命名【移动】
+     *
+     * @param string $from 来源位置
+     * @param string $to   目标位置
+     * return bool
+     */
+    public static function move($from, $to)
+    {
+        // $bucket = $this->_config['bucket'];
+        $op = '/move/' . self::qiniuEncode($from) . '/' . self::qiniuEncode($to);
+        return self::opration($op);
+    }
+
+    /**
+     * 复制文件
+     *
+     * @param string $from      来源文件
+     * @param string $saveas    目标文件
+     * @return bool
+     */
+    public static function copy($from, $saveas)
+    {
+        // $bucket = $this->_config['bucket'];
+        $op = '/copy/' . self::qiniuEncode($from) . '/' . self::qiniuEncode($saveas);
+        return self::opration($op);
+    }
+
+    /**
+     * 获取token
+     *
+     * @param string $bucket    空间名
+     * @param string $key       密钥
+     * @param int    $max       文件大小限制
+     * @param int    $timeout   超时时间
+     * @return string
+     */
+    public static function getToken($bucket, $key, $max = 10485760, $timeout = 600)
+    {
+        $setting = array(
+            'scope'      => $bucket,
+            'saveKey'    => $key,
+            'deadline'   => $timeout + $_SERVER['REQUEST_TIME'],
+            'fsizeLimit' => intval($max),
+        );
+        $setting = self::qiniuEncode(json_encode($setting));
+        return self::sign($setting) . ':' . $setting;
+    }
+
+    /**
+     * 删除
+     *
+     * @param string $uri   文件路径
+     * @return boolean
+     */
+    public static function delete($uri)
+    {
+        $file = self::qiniuEncode($uri);
+        return self::opration('/delete/' . $file);
+    }
+
+    /**
+     * 判断文件是否存在
+     *
+     * @param string $uri
+     * @return boolean
+     */
+    public static function has($uri)
+    {
+        $op = '/stat/' . self::qiniuEncode($uri);
+        return self::opration($op);
+    }
+
+    /**
+     * 转pdf
+     *
+     * @param string $bucket
+     * @param string $key
+     * @param string $saveas
+     * @return boolean
+     */
+    public static function toPdf($bucket, $key, $saveas)
+    {
+        $API = 'http://api.qiniu.com';
+        $op = '/pfop/';
+        $data = 'bucket=' . $bucket . '&key=' . $key . '&fops=yifangyun_preview|saveas/' . self::qiniuEncode($saveas);
+        return self::opration($op, $data, $API);
+    }
+
+    /**
+     * 七牛操作
+     *
+     * @param string $op 操作命令
+     * @param string $data 操作结果
+     * @param string $host
+     * @return boolean
+     */
+    private static function opration($op, $data = null, $host = self::QINIU_RS)
+    {
+        $token = self::sign(is_string($data) ? $op . "\n" . $data : $op . "\n");
+        $url = $host . $op;
+        $header = array('Authorization: QBox ' . $token);
+
+        if ($ch = curl_init($url))
+        {
+            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
+            if ($data)
+            {
+                curl_setopt($ch, CURLOPT_POST, true);
+                curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+            }
+            curl_setopt($ch, CURLOPT_HEADER, 1);
+            $response = curl_exec($ch);
+            $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+            curl_close($ch);
+
+            if ($status == 200)
+            {
+                return true;
+            }
+            // elseif (\Config::get('debug'))
+            // {
+            // 	/*操作出错*/
+            // 	\Log::debug($response, '七牛请求出错');
+            // }
+        }
+        Log::error('[QINIU]七牛错误' . $url . ':' . ($response ? : '请求失败'), 'ERROR');
+        return false;
+    }
+
+    /**
+     * 获取url签名
+     *
+     * @param string $url
+     * @return string
+     */
+    private static function sign($url)
+    {
+        $config = self::$_config ? : (self::$_config = Config::get('service.qiniu'));
+        $sign = hash_hmac('sha1', $url, $config['secretkey'], true);
+        $ak = $config['accesskey'];
+        return $ak . ':' . self::qiniuEncode($sign);
+    }
+
+    /**
+     * 七牛安全编码
+     */
+    private static function qiniuEncode($str)
+    {
+        return strtr(base64_encode($str), ['+' => '-', '/' => '_']);
+    }
+
+}
diff --git a/extend/fast/service/Upyun.php b/extend/fast/service/Upyun.php
new file mode 100644
index 0000000..2155aee
--- /dev/null
+++ b/extend/fast/service/Upyun.php
@@ -0,0 +1,572 @@
+<?php
+
+namespace fast\service;
+
+use Exception;
+use think\Config;
+
+/**
+ * 又拍云上传管理
+ */
+class Upyun
+{
+
+    const VERSION = '2.0';
+
+    /* {{{ */
+    const ED_AUTO = 'v0.api.upyun.com';
+    const ED_TELECOM = 'v1.api.upyun.com';
+    const ED_CNC = 'v2.api.upyun.com';
+    const ED_CTT = 'v3.api.upyun.com';
+    const CONTENT_TYPE = 'Content-Type';
+    const CONTENT_MD5 = 'Content-MD5';
+    const CONTENT_SECRET = 'Content-Secret';
+    // 缩略图
+    const X_GMKERL_THUMBNAIL = 'x-gmkerl-thumbnail';
+    const X_GMKERL_TYPE = 'x-gmkerl-type';
+    const X_GMKERL_VALUE = 'x-gmkerl-value';
+    const X_GMKERL_QUALITY = 'x­gmkerl-quality';
+    const X_GMKERL_UNSHARP = 'x­gmkerl-unsharp';
+
+    private static $_instance;
+
+    /* }}} */
+
+    /**
+     * @deprecated
+     */
+    private $_content_md5 = NULL;
+
+    /**
+     * @deprecated
+     */
+    private $_file_secret = NULL;
+
+    /**
+     * @deprecated
+     */
+    private $_file_infos = NULL;
+
+    /**
+     * @var string: UPYUN 请求唯一id, 出现错误时, 可以将该id报告给 UPYUN,进行调试
+     */
+    private $x_request_id;
+
+    /**
+     * 初始化 UpYun 存储接口
+     */
+    public function __construct($options = [])
+    {
+        if ($config = Config::get('service.upyun'))
+        {
+            $this->config = array_merge($this->config, $config);
+        }
+        $this->config = array_merge($this->config, is_array($options) ? $options : []);
+    }
+
+    /* }}} */
+
+    /**
+     * 获取当前SDK版本号
+     */
+    public function version()
+    {
+        return self::VERSION;
+    }
+
+    /**
+     * 创建目录
+     * @param $path 路径
+     * @param $auto_mkdir 是否自动创建父级目录,最多10层次
+     *
+     * @return void
+     */
+    public function makeDir($path, $auto_mkdir = false)
+    {/* {{{ */
+        $headers = array('Folder' => 'true');
+        if ($auto_mkdir)
+            $headers['Mkdir'] = 'true';
+        return $this->_do_request('PUT', $path, $headers);
+    }
+
+    /* }}} */
+
+    /**
+     * 删除目录和文件
+     * @param string $path 路径
+     *
+     * @return boolean
+     */
+    public function delete($path)
+    {/* {{{ */
+        return $this->_do_request('DELETE', $path);
+    }
+
+    /* }}} */
+
+    /**
+     * 上传文件
+     * @param string $path 存储路径
+     * @param mixed $file 需要上传的文件,可以是文件流或者文件内容
+     * @param boolean $auto_mkdir 自动创建目录
+     * @param array $opts 可选参数
+     */
+    public function upload($path, $file = NULL, $auto_mkdir = True, $opts = NULL)
+    {
+        return $this->writeFile($path, $file = NULL, $auto_mkdir, $opts);
+    }
+
+    public function writeFile($path, $file = NULL, $auto_mkdir = True, $opts = NULL)
+    {/* {{{ */
+        if (is_null($file))
+            $file = ROOT_PATH . 'public/' . $path;
+        if (is_null($opts))
+            $opts = array();
+        if (!is_null($this->_content_md5) || !is_null($this->_file_secret))
+        {
+            //if (!is_null($this->_content_md5)) array_push($opts, self::CONTENT_MD5 . ": {$this->_content_md5}");
+            //if (!is_null($this->_file_secret)) array_push($opts, self::CONTENT_SECRET . ": {$this->_file_secret}");
+            if (!is_null($this->_content_md5))
+                $opts[self::CONTENT_MD5] = $this->_content_md5;
+            if (!is_null($this->_file_secret))
+                $opts[self::CONTENT_SECRET] = $this->_file_secret;
+        }
+
+        // 如果设置了缩略版本或者缩略图类型,则添加默认压缩质量和锐化参数
+        //if (isset($opts[self::X_GMKERL_THUMBNAIL]) || isset($opts[self::X_GMKERL_TYPE])) {
+        //    if (!isset($opts[self::X_GMKERL_QUALITY])) $opts[self::X_GMKERL_QUALITY] = 95;
+        //    if (!isset($opts[self::X_GMKERL_UNSHARP])) $opts[self::X_GMKERL_UNSHARP] = 'true';
+        //}
+
+        if ($auto_mkdir === True)
+            $opts['Mkdir'] = 'true';
+
+        $this->_file_infos = $this->_do_request('PUT', $path, $opts, $file);
+
+        return $this->_file_infos;
+    }
+
+    /* }}} */
+
+    /**
+     * 下载文件
+     * @param string $path 文件路径
+     * @param mixed $file_handle
+     *
+     * @return mixed
+     */
+    public function readFile($path, $file_handle = NULL)
+    {/* {{{ */
+        return $this->_do_request('GET', $path, NULL, NULL, $file_handle);
+    }
+
+    /* }}} */
+
+    /**
+     * 获取目录文件列表
+     *
+     * @param string $path 查询路径
+     *
+     * @return mixed
+     */
+    public function getList($path = '/')
+    {/* {{{ */
+        $rsp = $this->_do_request('GET', $path);
+
+        $list = array();
+        if ($rsp)
+        {
+            $rsp = explode("\n", $rsp);
+            foreach ($rsp as $item)
+            {
+                @list($name, $type, $size, $time) = explode("\t", trim($item));
+                if (!empty($time))
+                {
+                    $type = $type == 'N' ? 'file' : 'folder';
+                }
+
+                $item = array(
+                    'name' => $name,
+                    'type' => $type,
+                    'size' => intval($size),
+                    'time' => intval($time),
+                );
+                array_push($list, $item);
+            }
+        }
+
+        return $list;
+    }
+
+    /* }}} */
+
+    /**
+     * @deprecated
+     * @param string $path 目录路径
+     * @return mixed
+     */
+    public function getFolderUsage($path = '/')
+    {/* {{{ */
+        $rsp = $this->_do_request('GET', '/?usage');
+        return floatval($rsp);
+    }
+
+    /* }}} */
+
+    /**
+     * 获取文件、目录信息
+     *
+     * @param string $path 路径
+     *
+     * @return mixed
+     */
+    public function getFileInfo($path)
+    {/* {{{ */
+        $rsp = $this->_do_request('HEAD', $path);
+
+        return $rsp;
+    }
+
+    /* }}} */
+
+    /**
+     * 连接签名方法
+     * @param $method 请求方式 {GET, POST, PUT, DELETE}
+     * return 签名字符串
+     */
+    private function sign($method, $uri, $date, $length)
+    {/* {{{ */
+        //$uri = urlencode($uri);
+        $sign = "{$method}&{$uri}&{$date}&{$length}&{$this->config['password']}";
+        return 'UpYun ' . $this->config['username'] . ':' . md5($sign);
+    }
+
+    /* }}} */
+
+    /**
+     * HTTP REQUEST 封装
+     * @param string $method HTTP REQUEST方法,包括PUT、POST、GET、OPTIONS、DELETE
+     * @param string $path 除Bucketname之外的请求路径,包括get参数
+     * @param array $headers 请求需要的特殊HTTP HEADERS
+     * @param array $body 需要POST发送的数据
+     *
+     * @return mixed
+     */
+    protected function _do_request($method, $path, $headers = NULL, $body = NULL, $file_handle = NULL)
+    {/* {{{ */
+        $uri = "/{$this->config['bucket']}{$path}";
+        $ch = curl_init("http://{$this->config['endpoint']}{$uri}");
+
+        $_headers = array('Expect:');
+        if (!is_null($headers) && is_array($headers))
+        {
+            foreach ($headers as $k => $v)
+            {
+                array_push($_headers, "{$k}: {$v}");
+            }
+        }
+
+        $length = 0;
+        $date = gmdate('D, d M Y H:i:s \G\M\T');
+
+        if (!is_null($body))
+        {
+            if (!is_resource($body) && file_exists($body))
+            {
+                $body = fopen($body, "rb");
+            }
+            if (is_resource($body))
+            {
+                fseek($body, 0, SEEK_END);
+                $length = ftell($body);
+                fseek($body, 0);
+
+                array_push($_headers, "Content-Length: {$length}");
+                curl_setopt($ch, CURLOPT_INFILE, $body);
+                curl_setopt($ch, CURLOPT_INFILESIZE, $length);
+            }
+            else
+            {
+                $length = @strlen($body);
+                array_push($_headers, "Content-Length: {$length}");
+                curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+            }
+        }
+        else
+        {
+            array_push($_headers, "Content-Length: {$length}");
+        }
+
+        array_push($_headers, "Authorization: {$this->sign($method, $uri, $date, $length)}");
+        array_push($_headers, "Date: {$date}");
+
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $_headers);
+        curl_setopt($ch, CURLOPT_TIMEOUT, $this->config['timeout']);
+        curl_setopt($ch, CURLOPT_HEADER, 1);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        //curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
+        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
+        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
+
+        if ($method == 'PUT' || $method == 'POST')
+        {
+            curl_setopt($ch, CURLOPT_POST, 1);
+        }
+        else
+        {
+            curl_setopt($ch, CURLOPT_POST, 0);
+        }
+
+        if ($method == 'GET' && is_resource($file_handle))
+        {
+            curl_setopt($ch, CURLOPT_HEADER, 0);
+            curl_setopt($ch, CURLOPT_FILE, $file_handle);
+        }
+
+        if ($method == 'HEAD')
+        {
+            curl_setopt($ch, CURLOPT_NOBODY, true);
+        }
+        $response = curl_exec($ch);
+        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+        if ($http_code == 0)
+            throw new Exception('Connection Failed', $http_code);
+
+        curl_close($ch);
+
+        $header_string = '';
+        $body = '';
+
+        if ($method == 'GET' && is_resource($file_handle))
+        {
+            $header_string = '';
+            $body = $response;
+        }
+        else
+        {
+            list($header_string, $body) = explode("\r\n\r\n", $response, 2);
+        }
+        $this->setXRequestId($header_string);
+        if ($http_code == 200)
+        {
+            if ($method == 'GET' && is_null($file_handle))
+            {
+                return $body;
+            }
+            else
+            {
+                $data = $this->_getHeadersData($header_string);
+                return count($data) > 0 ? $data : true;
+            }
+        }
+        else
+        {
+            $message = $this->_getErrorMessage($header_string);
+            if (is_null($message) && $method == 'GET' && is_resource($file_handle))
+            {
+                $message = 'File Not Found';
+            }
+            switch ($http_code)
+            {
+                case 401:
+                    throw new Exception($message, $http_code);
+                    break;
+                case 403:
+                    throw new Exception($message, $http_code);
+                    break;
+                case 404:
+                    throw new Exception($message, $http_code);
+                    break;
+                case 406:
+                    throw new Exception($message, $http_code);
+                    break;
+                case 503:
+                    throw new Exception($message, $http_code);
+                    break;
+                default:
+                    throw new Exception($message, $http_code);
+            }
+        }
+    }
+
+    /* }}} */
+
+    /**
+     * 处理HTTP HEADERS中返回的自定义数据
+     *
+     * @param string $text header字符串
+     *
+     * @return array
+     */
+    private function _getHeadersData($text)
+    {/* {{{ */
+        $headers = explode("\r\n", $text);
+        $items = array();
+        foreach ($headers as $header)
+        {
+            $header = trim($header);
+            if (stripos($header, 'x-upyun') !== False)
+            {
+                list($k, $v) = explode(':', $header);
+                $items[trim($k)] = in_array(substr($k, 8, 5), array('width', 'heigh', 'frame')) ? intval($v) : trim($v);
+            }
+        }
+        return $items;
+    }
+
+    /* }}} */
+
+    /**
+     * 获取返回的错误信息
+     *
+     * @param string $header_string
+     *
+     * @return mixed
+     */
+    private function _getErrorMessage($header_string)
+    {
+        list($status, $stash) = explode("\r\n", $header_string, 2);
+        list($v, $code, $message) = explode(" ", $status, 3);
+        return $message . " X-Request-Id: " . $this->getXRequestId();
+    }
+
+    private function setXRequestId($header_string)
+    {
+        preg_match('~^X-Request-Id: ([0-9a-zA-Z]{32})~ism', $header_string, $result);
+        $this->x_request_id = isset($result[1]) ? $result[1] : '';
+    }
+
+    public function getXRequestId()
+    {
+        return $this->x_request_id;
+    }
+
+    /**
+     * 删除目录
+     * @deprecated
+     * @param $path 路径
+     *
+     * @return void
+     */
+    public function rmDir($path)
+    {/* {{{ */
+        $this->_do_request('DELETE', $path);
+    }
+
+    /* }}} */
+
+    /**
+     * 删除文件
+     *
+     * @deprecated
+     * @param string $path 要删除的文件路径
+     *
+     * @return boolean
+     */
+    public function deleteFile($path)
+    {/* {{{ */
+        $rsp = $this->_do_request('DELETE', $path);
+    }
+
+    /* }}} */
+
+    /**
+     * 获取目录文件列表
+     * @deprecated
+     *
+     * @param string $path 要获取列表的目录
+     *
+     * @return array
+     */
+    public function readDir($path)
+    {/* {{{ */
+        return $this->getList($path);
+    }
+
+    /* }}} */
+
+    /**
+     * 获取空间使用情况
+     *
+     * @deprecated 推荐直接使用 getFolderUsage('/')来获取
+     * @return mixed
+     */
+    public function getBucketUsage()
+    {/* {{{ */
+        return $this->getFolderUsage('/');
+    }
+
+    /* }}} */
+
+    /**
+     * 获取文件信息
+     *
+     * #deprecated
+     * @param $file 文件路径(包含文件名)
+     * return array('type'=> file | folder, 'size'=> file size, 'date'=> unix time) 或 null
+     */
+    //public function getFileInfo($file){/*{{{*/
+    //    $result = $this->head($file);
+    //	if(is_null($r))return null;
+    //	return array('type'=> $this->tmp_infos['x-upyun-file-type'], 'size'=> @intval($this->tmp_infos['x-upyun-file-size']), 'date'=> @intval($this->tmp_infos['x-upyun-file-date']));
+    //}/*}}}*/
+
+    /**
+     * 切换 API 接口的域名
+     *
+     * @deprecated
+     * @param $domain {默然 v0.api.upyun.com 自动识别, v1.api.upyun.com 电信, v2.api.upyun.com 联通, v3.api.upyun.com 移动}
+     * return null;
+     */
+    public function setApiDomain($domain)
+    {/* {{{ */
+        $this->config['endpoint'] = $domain;
+    }
+
+    /* }}} */
+
+    /**
+     * 设置待上传文件的 Content-MD5 值(如又拍云服务端收到的文件MD5值与用户设置的不一致,将回报 406 Not Acceptable 错误)
+     *
+     * @deprecated
+     * @param $str (文件 MD5 校验码)
+     * return null;
+     */
+    public function setContentMD5($str)
+    {/* {{{ */
+        $this->_content_md5 = $str;
+    }
+
+    /* }}} */
+
+    /**
+     * 设置待上传文件的 访问密钥(注意:仅支持图片空!,设置密钥后,无法根据原文件URL直接访问,需带 URL 后面加上 (缩略图间隔标志符+密钥) 进行访问)
+     * 如缩略图间隔标志符为 ! ,密钥为 bac,上传文件路径为 /folder/test.jpg ,那么该图片的对外访问地址为: http://空间域名/folder/test.jpg!bac
+     *
+     * @deprecated
+     * @param $str (文件 MD5 校验码)
+     * return null;
+     */
+    public function setFileSecret($str)
+    {/* {{{ */
+        $this->_file_secret = $str;
+    }
+
+    /* }}} */
+
+    /**
+     * @deprecated
+     * 获取上传文件后的信息(仅图片空间有返回数据)
+     * @param $key 信息字段名(x-upyun-width、x-upyun-height、x-upyun-frames、x-upyun-file-type)
+     * return value or NULL
+     */
+    public function getWritedFileInfo($key)
+    {/* {{{ */
+        if (!isset($this->_file_infos))
+            return NULL;
+        return $this->_file_infos[$key];
+    }
+
+    /* }}} */
+}
diff --git a/extend/fast/service/Wechat.php b/extend/fast/service/Wechat.php
new file mode 100644
index 0000000..f64438b
--- /dev/null
+++ b/extend/fast/service/Wechat.php
@@ -0,0 +1,213 @@
+<?php
+
+namespace fast\service;
+
+use app\common\model\Page;
+use app\common\model\User;
+use app\common\model\UserSignin;
+use app\common\model\UserThird;
+use EasyWeChat\Message\News;
+use EasyWeChat\Message\Transfer;
+use fast\Date;
+
+/**
+ * Wechat服务类
+ */
+class Wechat
+{
+
+    public function __construct()
+    {
+
+    }
+
+    public static function appConfig()
+    {
+        return array(
+            'signin'  => array(
+                'name'   => '签到送积分',
+                'config' => array(
+                )
+            ),
+            'article' => array(
+                'name'   => '关联文章',
+                'config' => array(
+                    array(
+                        'type'    => 'text',
+                        'caption' => '文章ID',
+                        'field'   => 'id',
+                        'options' => ''
+                    )
+                )
+            ),
+            'page'    => array(
+                'name'   => '关联单页',
+                'config' => array(
+                    array(
+                        'type'    => 'text',
+                        'caption' => '单页ID',
+                        'field'   => 'id',
+                        'options' => ''
+                    )
+                )
+            ),
+            'service' => array(
+                'name'   => '在线客服',
+                'config' => array(
+                )
+            ),
+        );
+    }
+
+    // 微信输入交互内容指令
+    public function command($obj, $openid, $content, $context)
+    {
+        $content = [];
+        $response = FALSE;
+        if (isset($content['app']))
+        {
+            switch ($content['app'])
+            {
+                case 'signin':
+                case 'article':
+                case 'page':
+                    break;
+                case 'service':
+                    $service = configvalue('service');
+                    list($begintime, $endtime) = explode('-', $service['onlinetime']);
+                    $session = $obj->app->staff_session;
+                    $staff = $obj->app->staff;
+
+                    $kf_account = $session->get($openid)->kf_account;
+                    $time = time();
+                    if (!$kf_account && ($time < strtotime(date("Y-m-d {$begintime}")) || $time > strtotime(date("Y-m-d {$endtime}"))))
+                    {
+                        return $service['offlinemsg'];
+                    }
+                    if (!$kf_account)
+                    {
+                        $kf_list = $staff->onlines()->kf_online_list;
+                        if ($kf_list)
+                        {
+                            $kfarr = [];
+                            foreach ($kf_list as $k => $v)
+                            {
+                                $kfarr[$v['kf_account']] = $v['accepted_case'];
+                            }
+                            $kfkeys = array_keys($kfarr, min($kfarr));
+                            $kf_account = reset($kfkeys);
+                            $session->create($kf_account, $openid);
+                            $response = $service['waitformsg'];
+                        }
+                        else
+                        {
+                            $response = $service['nosessionmsg'];
+                        }
+                    }
+                    else
+                    {
+                        $server = $obj->app->server;
+                        $server->setMessageHandler(function($message)
+                        {
+                            return new Transfer();
+                        });
+                        $response = $server->serve();
+                        $response->send();
+                        exit;
+                    }
+
+                    break;
+                default:
+                    break;
+            }
+        }
+        else
+        {
+            $response = isset($content['content']) ? $content['content'] : $response;
+        }
+        return $response;
+    }
+
+    // 微信点击菜单event指令
+    public function response($obj, $openid, $content, $context)
+    {
+        $content = [];
+        $response = FALSE;
+        if (isset($content['app']))
+        {
+            switch ($content['app'])
+            {
+                case 'signin':
+                    $thirdinfo = UserThird::get(['platform' => 'wechat', 'openid' => $openid]);
+                    if (!$thirdinfo)
+                    {
+                        $response = '您还没有<a href="' . url('user/profile', 1) . '">绑定用户</a>还不能签到!';
+                    }
+                    else
+                    {
+                        $user_id = $thirdinfo->user_id;
+                        $usersign = new UserSignin;
+                        $signdata = $usersign->get([['user_id', '=', $user_id], ['createtime', '>=', Date::unixtime()]]);
+                        if ($signdata)
+                        {
+                            $response = '今天已签到,请明天再来!';
+                        }
+                        else
+                        {
+                            $signdata = configvalue('signin');
+
+                            $lastdata = $usersign->where('user_id', $user_id)->order('id', 'desc')->limit(1)->get();
+                            $successions = $lastdata && $lastdata['createtime'] > Date::unixtime('day', -1) ? $lastdata['successions'] + 1 : 1;
+                            $usersign->save(['user_id' => $thirdinfo['user_id'], 'successions' => $successions, 'createtime' => time()]);
+                            $score = isset($signdata['s' . $successions]) ? $signdata['s' . $successions] : $signdata['sn'];
+                            User::where(['id' => $user_id])->setInc('score', $score);
+                            $response = '签到成功!连续签到' . $successions . '天!获得' . $score . '积分';
+                        }
+                    }
+
+                    break;
+                case 'article':
+                    $id = explode(',', $content['id']);
+                    $pagelist = Page::all($id);
+                    $response = [];
+                    foreach ($pagelist as $k => $pageinfo)
+                    {
+                        if ($pageinfo)
+                        {
+                            $news = new News();
+                            $news->title = $pageinfo['title'];
+                            $news->url = $pageinfo['outlink'] ? $pageinfo['outlink'] : url('page/show/' . $pageinfo['id'], 1);
+                            $news->image = cdn($pageinfo['image']);
+                            $news->description = $pageinfo['description'];
+                            $response[] = $news;
+                        }
+                    }
+
+                case 'page':
+                    $id = isset($content['id']) ? $content['id'] : 0;
+                    $pageinfo = Page::get($id);
+                    if ($pageinfo)
+                    {
+                        $news = new News();
+                        $news->title = $pageinfo['title'];
+                        $news->url = $pageinfo['outlink'] ? $pageinfo['outlink'] : url('page/show/' . $pageinfo['id'], 1);
+                        $news->image = cdn($pageinfo['image']);
+                        $news->description = $pageinfo['description'];
+                        return $news;
+                    }
+                    break;
+                case 'service':
+                    $response = $this->command($obj, $openid, $content, $context);
+                    break;
+                default:
+                    break;
+            }
+        }
+        else
+        {
+            $response = isset($content['content']) ? $content['content'] : $response;
+        }
+        return $response;
+    }
+
+}
diff --git a/extend/fast/third/Application.php b/extend/fast/third/Application.php
new file mode 100644
index 0000000..de16a3a
--- /dev/null
+++ b/extend/fast/third/Application.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace fast\third;
+
+use think\Config;
+
+class Application
+{
+
+    /**
+     * 配置信息
+     * @var array
+     */
+    private $config = [];
+
+    /**
+     * 服务提供者
+     * @var array
+     */
+    private $providers = [
+        'qq'     => 'Qq',
+        'weibo'  => 'Weibo',
+        'wechat' => 'Wechat',
+    ];
+
+    /**
+     * 服务对象信息
+     * @var array
+     */
+    protected $services = [];
+
+    public function __construct($options = [])
+    {
+        if ($config = Config::get('third'))
+        {
+            $this->config = array_merge($this->config, $config);
+        }
+        $this->config = array_merge($this->config, is_array($options) ? $options : []);
+
+        //注册服务器提供者
+        $this->registerProviders();
+    }
+
+    /**
+     * 注册服务提供者
+     */
+    private function registerProviders()
+    {
+        foreach ($this->providers as $k => $v)
+        {
+            $this->services[$k] = function() use ($k, $v)
+            {
+                $options = $this->config[$k];
+                $options['app_id'] = isset($options['app_id']) ? $options['app_id'] : '';
+                $options['app_secret'] = isset($options['app_secret']) ? $options['app_secret'] : '';
+                // 如果未定义回调地址则自动生成
+                $options['callback'] = isset($options['callback']) && $options['callback'] ? $options['callback'] : url('user/third?action=callback&platform=' . $k, [], false, true);
+                // 如果非http地址则转换一下
+                $options['callback'] = substr($options['callback'], 0, 7) == 'http://' || substr($options['callback'], 0, 8) == 'https://' ? $options['callback'] : url($options['callback'], '', false, true);
+                $objname = __NAMESPACE__ . "\\{$v}";
+                return new $objname($options);
+            };
+        }
+    }
+
+    public function __set($key, $value)
+    {
+        $this->services[$key] = $value;
+    }
+
+    public function __get($key)
+    {
+        return $this->services[$key]($this);
+    }
+
+}
diff --git a/extend/fast/third/Qq.php b/extend/fast/third/Qq.php
new file mode 100644
index 0000000..f7a2e65
--- /dev/null
+++ b/extend/fast/third/Qq.php
@@ -0,0 +1,145 @@
+<?php
+
+namespace fast\third;
+
+use fast\Http;
+use think\Config;
+use think\Session;
+
+/**
+ * QQ
+ */
+class Qq
+{
+
+    const GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize";
+    const GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token";
+    const GET_USERINFO_URL = "https://graph.qq.com/user/get_user_info";
+    const GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me";
+
+    /**
+     * 配置信息
+     * @var array
+     */
+    private $config = [];
+
+    public function __construct($options = [])
+    {
+        if ($config = Config::get('third.qq'))
+        {
+            $this->config = array_merge($this->config, $config);
+        }
+        $this->config = array_merge($this->config, is_array($options) ? $options : []);
+    }
+
+    /**
+     * 登陆
+     */
+    public function login()
+    {
+        header("Location:" . $this->getAuthorizeUrl());
+    }
+
+    /**
+     * 获取authorize_url
+     */
+    public function getAuthorizeUrl()
+    {
+        $state = md5(uniqid(rand(), TRUE));
+        Session::set('state', $state);
+        $queryarr = array(
+            "response_type" => "code",
+            "client_id"     => $this->config['app_id'],
+            "redirect_uri"  => $this->config['callback'],
+            "scope"         => $this->config['scope'],
+            "state"         => $state,
+        );
+        request()->isMobile() && $queryarr['display'] = 'mobile';
+        $url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr);
+        return $url;
+    }
+
+    /**
+     * 获取用户信息
+     * @param array $params
+     * @return array
+     */
+    public function getUserInfo($params = [])
+    {
+        $params = $params ? $params : $_GET;
+        if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code'])))
+        {
+            //获取access_token
+            $data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params;
+            $access_token = isset($data['access_token']) ? $data['access_token'] : '';
+            $refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : '';
+            $expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0;
+            if ($access_token)
+            {
+                $openid = $this->getOpenId($access_token);
+                //获取用户信息
+                $queryarr = [
+                    "access_token"       => $access_token,
+                    "oauth_consumer_key" => $this->config['app_id'],
+                    "openid"             => $openid,
+                ];
+                $ret = Http::get(self::GET_USERINFO_URL, $queryarr);
+                $userinfo = json_decode($ret, TRUE);
+                if (!$userinfo || !isset($userinfo['ret']) || $userinfo['ret'] !== 0)
+                    return [];
+                $userinfo = $userinfo ? $userinfo : [];
+                $userinfo['avatar'] = isset($userinfo['figureurl_qq_2']) ? $userinfo['figureurl_qq_2'] : '';
+                $data = [
+                    'access_token'  => $access_token,
+                    'refresh_token' => $refresh_token,
+                    'expires_in'    => $expires_in,
+                    'openid'        => $openid,
+                    'userinfo'      => $userinfo
+                ];
+                return $data;
+            }
+        }
+        return [];
+    }
+
+    /**
+     * 获取access_token
+     * @param string $code
+     * @return array
+     */
+    private function getAccessToken($code = '')
+    {
+        if (!$code)
+            return '';
+        $queryarr = array(
+            "grant_type"    => "authorization_code",
+            "client_id"     => $this->config['app_id'],
+            "client_secret" => $this->config['app_secret'],
+            "redirect_uri"  => $this->config['callback'],
+            "code"          => $code,
+        );
+        $ret = Http::get(self::GET_ACCESS_TOKEN_URL, $queryarr);
+        $params = [];
+        parse_str($ret, $params);
+        return $params ? $params : [];
+    }
+
+    /**
+     * 获取open_id
+     * @param string $access_token
+     * @return string
+     */
+    private function getOpenId($access_token = '')
+    {
+        $response = Http::get(self::GET_OPENID_URL, ['access_token' => $access_token]);
+        if (strpos($response, "callback") !== false)
+        {
+            $lpos = strpos($response, "(");
+            $rpos = strrpos($response, ")");
+            $response = substr($response, $lpos + 1, $rpos - $lpos - 1);
+        }
+        $user = json_decode($response, TRUE);
+        return isset($user['openid']) ? $user['openid'] : '';
+    }
+
+}
diff --git a/extend/fast/third/Wechat.php b/extend/fast/third/Wechat.php
new file mode 100644
index 0000000..6dd7f29
--- /dev/null
+++ b/extend/fast/third/Wechat.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace fast\third;
+
+use fast\Http;
+use think\Config;
+use think\Session;
+
+/**
+ * 微博
+ */
+class Wechat
+{
+
+    const GET_AUTH_CODE_URL = "https://api.weixin.qq.com/sns/oauth2/authorize";
+    const GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";
+    const GET_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo";
+
+    /**
+     * 配置信息
+     * @var array
+     */
+    private $config = [];
+
+    public function __construct($options = [])
+    {
+        if ($config = Config::get('third.wechat'))
+        {
+            $this->config = array_merge($this->config, $config);
+        }
+        $this->config = array_merge($this->config, is_array($options) ? $options : []);
+    }
+
+    /**
+     * 登陆
+     */
+    public function login()
+    {
+        header("Location:" . $this->getAuthorizeUrl());
+    }
+
+    /**
+     * 获取authorize_url
+     */
+    public function getAuthorizeUrl()
+    {
+        $state = md5(uniqid(rand(), TRUE));
+        Session::set('state', $state);
+        $queryarr = array(
+            "app_id"        => $this->config['app_id'],
+            "redirect_uri"  => $this->config['callback'],
+            "response_type" => "code",
+            "scope"         => $this->config['scope'],
+            "state"         => $state,
+        );
+        request()->isMobile() && $queryarr['display'] = 'mobile';
+        $url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr) . '#wechat_redirect';
+        return $url;
+    }
+
+    /**
+     * 获取用户信息
+     * @param array $params
+     * @return array
+     */
+    public function getUserInfo($params = [])
+    {
+        $params = $params ? $params : $_GET;
+        if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code'])))
+        {
+            //获取access_token
+            $data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params;
+            $access_token = isset($data['access_token']) ? $data['access_token'] : '';
+            $refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : '';
+            $expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0;
+            if ($access_token)
+            {
+                $openid = isset($data['openid']) ? $data['openid'] : '';
+                //获取用户信息
+                $queryarr = [
+                    "access_token" => $access_token,
+                    "openid"       => $openid,
+                    "lang"         => 'zh_CN'
+                ];
+                $ret = Http::post(self::GET_USERINFO_URL, $queryarr);
+                $userinfo = json_decode($ret, TRUE);
+                if (!$userinfo || isset($userinfo['errcode']))
+                    return [];
+                $userinfo = $userinfo ? $userinfo : [];
+                $userinfo['avatar'] = isset($userinfo['headimgurl']) ? $userinfo['headimgurl'] : '';
+                $data = [
+                    'access_token'  => $access_token,
+                    'refresh_token' => $refresh_token,
+                    'expires_in'    => $expires_in,
+                    'openid'        => $openid,
+                    'userinfo'      => $userinfo
+                ];
+                return $data;
+            }
+        }
+        return [];
+    }
+
+    /**
+     * 获取access_token
+     * @param string code
+     * @return array
+     */
+    private function getAccessToken($code = '')
+    {
+        if (!$code)
+            return '';
+        $queryarr = array(
+            "appid"      => $this->config['app_id'],
+            "secret"     => $this->config['app_secret'],
+            "code"       => $code,
+            "grant_type" => "authorization_code",
+        );
+        $response = Http::post(self::GET_ACCESS_TOKEN_URL, $queryarr);
+        $ret = json_decode($response, TRUE);
+        return $ret ? $ret : [];
+    }
+
+}
diff --git a/extend/fast/third/Weibo.php b/extend/fast/third/Weibo.php
new file mode 100644
index 0000000..fb1dbab
--- /dev/null
+++ b/extend/fast/third/Weibo.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace fast\third;
+
+use fast\Http;
+use think\Config;
+use think\Session;
+
+/**
+ * 微博
+ */
+class Weibo
+{
+
+    const GET_AUTH_CODE_URL = "https://api.weibo.com/oauth2/authorize";
+    const GET_ACCESS_TOKEN_URL = "https://api.weibo.com/oauth2/access_token";
+    const GET_USERINFO_URL = "https://api.weibo.com/2/users/show.json";
+
+    /**
+     * 配置信息
+     * @var array
+     */
+    private $config = [];
+
+    public function __construct($options = [])
+    {
+        if ($config = Config::get('third.weibo'))
+        {
+            $this->config = array_merge($this->config, $config);
+        }
+        $this->config = array_merge($this->config, is_array($options) ? $options : []);
+    }
+
+    /**
+     * 登陆
+     */
+    public function login()
+    {
+        header("Location:" . $this->getAuthorizeUrl());
+    }
+
+    /**
+     * 获取authorize_url
+     */
+    public function getAuthorizeUrl()
+    {
+        $state = md5(uniqid(rand(), TRUE));
+        Session::set('state', $state);
+        $queryarr = array(
+            "response_type" => "code",
+            "client_id"     => $this->config['app_id'],
+            "redirect_uri"  => $this->config['callback'],
+            "state"         => $state,
+        );
+        request()->isMobile() && $queryarr['display'] = 'mobile';
+        $url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr);
+        return $url;
+    }
+
+    /**
+     * 获取用户信息
+     * @param array $params
+     * @return array
+     */
+    public function getUserInfo($params = [])
+    {
+        $params = $params ? $params : $_GET;
+        if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code'])))
+        {
+            //获取access_token
+            $data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params;
+            $access_token = isset($data['access_token']) ? $data['access_token'] : '';
+            $refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : '';
+            $expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0;
+            if ($access_token)
+            {
+                $uid = isset($data['uid']) ? $data['uid'] : '';
+                //获取用户信息
+                $queryarr = [
+                    "access_token" => $access_token,
+                    "uid"          => $uid,
+                ];
+                $ret = Http::get(self::GET_USERINFO_URL, $queryarr);
+                $userinfo = json_decode($ret, TRUE);
+                if (!$userinfo || isset($userinfo['error_code']))
+                    return [];
+                $userinfo = $userinfo ? $userinfo : [];
+                $userinfo['nickname'] = isset($userinfo['screen_name']) ? $userinfo['screen_name'] : '';
+                $userinfo['avatar'] = isset($userinfo['profile_image_url']) ? $userinfo['profile_image_url'] : '';
+                $data = [
+                    'access_token'  => $access_token,
+                    'refresh_token' => $refresh_token,
+                    'expires_in'    => $expires_in,
+                    'openid'        => $uid,
+                    'userinfo'      => $userinfo
+                ];
+                return $data;
+            }
+        }
+        return [];
+    }
+
+    /**
+     * 获取access_token
+     * @param string code
+     * @return array
+     */
+    private function getAccessToken($code = '')
+    {
+        if (!$code)
+            return '';
+        $queryarr = array(
+            "grant_type"    => "authorization_code",
+            "client_id"     => $this->config['app_id'],
+            "client_secret" => $this->config['app_secret'],
+            "redirect_uri"  => $this->config['callback'],
+            "code"          => $code,
+        );
+        $response = Http::post(self::GET_ACCESS_TOKEN_URL, $queryarr);
+        $ret = json_decode($response, TRUE);
+        return $ret ? $ret : [];
+    }
+
+}
diff --git a/extend/fast/ucenter/client/Client.php b/extend/fast/ucenter/client/Client.php
new file mode 100644
index 0000000..58ed17b
--- /dev/null
+++ b/extend/fast/ucenter/client/Client.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace fast\ucenter\client;
+
+use Exception;
+use think\Loader;
+
+class Client
+{
+
+    public function __construct()
+    {
+        $this->initConfig();
+        Loader::import('client', dirname(__FILE__) . "/uc_client/"); //加载uc客户端主脚本
+    }
+
+    //加载配置
+    public function initConfig()
+    {
+        if (!defined('UC_API'))
+        {
+            new Exception('未发现uc常量配置信息');
+        }
+    }
+
+    function __call($method, $params)
+    {
+        return call_user_func_array($method, $params);
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/client.php b/extend/fast/ucenter/client/uc_client/client.php
new file mode 100644
index 0000000..67f361f
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/client.php
@@ -0,0 +1,840 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: client.php 1179 2014-11-03 07:11:25Z hypowang $
+ */
+
+if (!defined('UC_API'))
+{
+    exit('Access denied');
+}
+
+error_reporting(0);
+
+define('IN_UC', TRUE);
+define('UC_CLIENT_VERSION', '1.6.0');
+define('UC_CLIENT_RELEASE', '20141101');
+define('UC_ROOT', substr(__FILE__, 0, -10));
+define('UC_DATADIR', UC_ROOT . './data/');
+define('UC_DATAURL', UC_API . '/data');
+define('UC_API_FUNC', UC_CONNECT == 'mysql' ? 'uc_api_mysql' : 'uc_api_post');
+$GLOBALS['uc_controls'] = array();
+
+function uc_addslashes($string, $force = 0, $strip = FALSE)
+{
+    !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
+    if (!MAGIC_QUOTES_GPC || $force)
+    {
+        if (is_array($string))
+        {
+            foreach ($string as $key => $val)
+            {
+                $string[$key] = uc_addslashes($val, $force, $strip);
+            }
+        }
+        else
+        {
+            $string = addslashes($strip ? stripslashes($string) : $string);
+        }
+    }
+    return $string;
+}
+
+if (!function_exists('daddslashes'))
+{
+
+    function daddslashes($string, $force = 0)
+    {
+        return uc_addslashes($string, $force);
+    }
+
+}
+
+if (!function_exists('dhtmlspecialchars'))
+{
+
+    function dhtmlspecialchars($string, $flags = null)
+    {
+        if (is_array($string))
+        {
+            foreach ($string as $key => $val)
+            {
+                $string[$key] = dhtmlspecialchars($val, $flags);
+            }
+        }
+        else
+        {
+            if ($flags === null)
+            {
+                $string = str_replace(array('&', '"', '<', '>'), array('&amp;', '&quot;', '&lt;', '&gt;'), $string);
+                if (strpos($string, '&amp;#') !== false)
+                {
+                    $string = preg_replace('/&amp;((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
+                }
+            }
+            else
+            {
+                if (PHP_VERSION < '5.4.0')
+                {
+                    $string = htmlspecialchars($string, $flags);
+                }
+                else
+                {
+                    if (strtolower(CHARSET) == 'utf-8')
+                    {
+                        $charset = 'UTF-8';
+                    }
+                    else
+                    {
+                        $charset = 'ISO-8859-1';
+                    }
+                    $string = htmlspecialchars($string, $flags, $charset);
+                }
+            }
+        }
+        return $string;
+    }
+
+}
+
+if (!function_exists('fsocketopen'))
+{
+
+    function fsocketopen($hostname, $port = 80, &$errno, &$errstr, $timeout = 15)
+    {
+        $fp = '';
+        if (function_exists('fsockopen'))
+        {
+            $fp = @fsockopen($hostname, $port, $errno, $errstr, $timeout);
+        }
+        elseif (function_exists('pfsockopen'))
+        {
+            $fp = @pfsockopen($hostname, $port, $errno, $errstr, $timeout);
+        }
+        elseif (function_exists('stream_socket_client'))
+        {
+            $fp = @stream_socket_client($hostname . ':' . $port, $errno, $errstr, $timeout);
+        }
+        return $fp;
+    }
+
+}
+
+function uc_stripslashes($string)
+{
+    !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
+    if (MAGIC_QUOTES_GPC)
+    {
+        return stripslashes($string);
+    }
+    else
+    {
+        return $string;
+    }
+}
+
+// 采用POST请求形式
+function uc_api_post($module, $action, $arg = array())
+{
+    $s = $sep = '';
+    foreach ($arg as $k => $v)
+    {
+        $k = urlencode($k);
+        if (is_array($v))
+        {
+            $s2 = $sep2 = '';
+            foreach ($v as $k2 => $v2)
+            {
+                $k2 = urlencode($k2);
+                $s2 .= "$sep2{$k}[$k2]=" . urlencode(uc_stripslashes($v2));
+                $sep2 = '&';
+            }
+            $s .= $sep . $s2;
+        }
+        else
+        {
+            $s .= "$sep$k=" . urlencode(uc_stripslashes($v));
+        }
+        $sep = '&';
+    }
+    $postdata = uc_api_requestdata($module, $action, $s);
+    return uc_fopen2(UC_API . '/index.php', 500000, $postdata, '', TRUE, UC_IP, 20);
+}
+
+function uc_api_requestdata($module, $action, $arg = '', $extra = '')
+{
+    $input = uc_api_input($arg);
+    $post = "m=$module&a=$action&inajax=2&release=" . UC_CLIENT_RELEASE . "&input=$input&appid=" . UC_APPID . $extra;
+    return $post;
+}
+
+function uc_api_url($module, $action, $arg = '', $extra = '')
+{
+    $url = UC_API . '/index.php?' . uc_api_requestdata($module, $action, $arg, $extra);
+    return $url;
+}
+
+function uc_api_input($data)
+{
+    $s = urlencode(uc_authcode($data . '&agent=' . md5($_SERVER['HTTP_USER_AGENT']) . "&time=" . time(), 'ENCODE', UC_KEY));
+    return $s;
+}
+
+// 直连数据库的形式
+function uc_api_mysql($model, $action, $args = array())
+{
+    global $uc_controls;
+    if (empty($uc_controls[$model]))
+    {
+        // 优先使用mysqli
+        if (function_exists("mysqli_connect"))
+        {
+            include_once UC_ROOT . './lib/dbi.class.php';
+        }
+        else
+        {
+            include_once UC_ROOT . './lib/db.class.php';
+        }
+        include_once UC_ROOT . './model/base.php';
+        include_once UC_ROOT . "./control/$model.php";
+        $classname = $model . 'control';
+        $uc_controls[$model] = new $classname();
+    }
+    if ($action{0} != '_')
+    {
+        $args = uc_addslashes($args, 1, TRUE);
+        $action = 'on' . $action;
+        $uc_controls[$model]->input = $args;
+        return $uc_controls[$model]->$action($args);
+    }
+    else
+    {
+        return '';
+    }
+}
+
+function uc_serialize($arr, $htmlon = 0)
+{
+    include_once UC_ROOT . './lib/xml.class.php';
+    return xml_serialize($arr, $htmlon);
+}
+
+function uc_unserialize($s)
+{
+    include_once UC_ROOT . './lib/xml.class.php';
+    return @xml_unserialize($s);
+}
+
+function uc_authcode($string, $operation = 'DECODE', $key = '', $expiry = 0)
+{
+
+    $ckey_length = 4;
+
+    $key = md5($key ? $key : UC_KEY);
+    $keya = md5(substr($key, 0, 16));
+    $keyb = md5(substr($key, 16, 16));
+    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
+
+    $cryptkey = $keya . md5($keya . $keyc);
+    $key_length = strlen($cryptkey);
+
+    $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
+    $string_length = strlen($string);
+
+    $result = '';
+    $box = range(0, 255);
+
+    $rndkey = array();
+    for ($i = 0; $i <= 255; $i++)
+    {
+        $rndkey[$i] = ord($cryptkey[$i % $key_length]);
+    }
+
+    for ($j = $i = 0; $i < 256; $i++)
+    {
+        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
+        $tmp = $box[$i];
+        $box[$i] = $box[$j];
+        $box[$j] = $tmp;
+    }
+
+    for ($a = $j = $i = 0; $i < $string_length; $i++)
+    {
+        $a = ($a + 1) % 256;
+        $j = ($j + $box[$a]) % 256;
+        $tmp = $box[$a];
+        $box[$a] = $box[$j];
+        $box[$j] = $tmp;
+        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
+    }
+
+    if ($operation == 'DECODE')
+    {
+        if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16))
+        {
+            return substr($result, 26);
+        }
+        else
+        {
+            return '';
+        }
+    }
+    else
+    {
+        return $keyc . str_replace('=', '', base64_encode($result));
+    }
+}
+
+function uc_fopen2($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE)
+{
+    $__times__ = isset($_GET['__times__']) ? intval($_GET['__times__']) + 1 : 1;
+    if ($__times__ > 2)
+    {
+        return '';
+    }
+    $url .= (strpos($url, '?') === FALSE ? '?' : '&') . "__times__=$__times__";
+    return uc_fopen($url, $limit, $post, $cookie, $bysocket, $ip, $timeout, $block);
+}
+
+function uc_fopen($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE)
+{
+    $return = '';
+    $matches = parse_url($url);
+    !isset($matches['scheme']) && $matches['scheme'] = '';
+    !isset($matches['host']) && $matches['host'] = '';
+    !isset($matches['path']) && $matches['path'] = '';
+    !isset($matches['query']) && $matches['query'] = '';
+    !isset($matches['port']) && $matches['port'] = '';
+    $scheme = $matches['scheme'];
+    $host = $matches['host'];
+    $path = $matches['path'] ? $matches['path'] . ($matches['query'] ? '?' . $matches['query'] : '') : '/';
+    $port = !empty($matches['port']) ? $matches['port'] : 80;
+    if ($post)
+    {
+        $out = "POST $path HTTP/1.0\r\n";
+        $header = "Accept: */*\r\n";
+        $header .= "Accept-Language: zh-cn\r\n";
+        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
+        $header .= "User-Agent: {$_SERVER['HTTP_USER_AGENT']}\r\n";
+        $header .= "Host: $host\r\n";
+        $header .= 'Content-Length: ' . strlen($post) . "\r\n";
+        $header .= "Connection: Close\r\n";
+        $header .= "Cache-Control: no-cache\r\n";
+        $header .= "Cookie: $cookie\r\n\r\n";
+        $out .= $header . $post;
+    }
+    else
+    {
+        $out = "GET $path HTTP/1.0\r\n";
+        $header = "Accept: */*\r\n";
+        $header .= "Accept-Language: zh-cn\r\n";
+        $header .= "User-Agent: {$_SERVER['HTTP_USER_AGENT']}\r\n";
+        $header .= "Host: $host\r\n";
+        $header .= "Connection: Close\r\n";
+        $header .= "Cookie: $cookie\r\n\r\n";
+        $out .= $header;
+    }
+    $fpflag = 0;
+    if (!$fp = @fsocketopen(($ip ? $ip : $host), $port, $errno, $errstr, $timeout))
+    {
+        $context = array(
+            'http' => array(
+                'method'  => $post ? 'POST' : 'GET',
+                'header'  => $header,
+                'content' => $post,
+                'timeout' => $timeout,
+            ),
+        );
+        $context = stream_context_create($context);
+        $fp = @fopen($scheme . '://' . ($ip ? $ip : $host) . ':' . $port . $path, 'b', false, $context);
+        $fpflag = 1;
+    }
+    if (!$fp)
+    {
+        return '';
+    }
+    else
+    {
+        stream_set_blocking($fp, $block);
+        stream_set_timeout($fp, $timeout);
+        @fwrite($fp, $out);
+        $status = stream_get_meta_data($fp);
+        if (!$status['timed_out'])
+        {
+            while (!feof($fp) && !$fpflag)
+            {
+                if (($header = @fgets($fp)) && ($header == "\r\n" || $header == "\n"))
+                {
+                    break;
+                }
+            }
+
+            $stop = false;
+            while (!feof($fp) && !$stop)
+            {
+                $data = fread($fp, ($limit == 0 || $limit > 8192 ? 8192 : $limit));
+                $return .= $data;
+                if ($limit)
+                {
+                    $limit -= strlen($data);
+                    $stop = $limit <= 0;
+                }
+            }
+        }
+        @fclose($fp);
+        return $return;
+    }
+}
+
+function uc_app_ls()
+{
+    $return = call_user_func(UC_API_FUNC, 'app', 'ls', array());
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_feed_add($icon, $uid, $username, $title_template = '', $title_data = '', $body_template = '', $body_data = '', $body_general = '', $target_ids = '', $images = array())
+{
+    return call_user_func(UC_API_FUNC, 'feed', 'add', array(
+        'icon'           => $icon,
+        'appid'          => UC_APPID,
+        'uid'            => $uid,
+        'username'       => $username,
+        'title_template' => $title_template,
+        'title_data'     => $title_data,
+        'body_template'  => $body_template,
+        'body_data'      => $body_data,
+        'body_general'   => $body_general,
+        'target_ids'     => $target_ids,
+        'image_1'        => $images[0]['url'],
+        'image_1_link'   => $images[0]['link'],
+        'image_2'        => $images[1]['url'],
+        'image_2_link'   => $images[1]['link'],
+        'image_3'        => $images[2]['url'],
+        'image_3_link'   => $images[2]['link'],
+        'image_4'        => $images[3]['url'],
+        'image_4_link'   => $images[3]['link']
+            )
+    );
+}
+
+function uc_feed_get($limit = 100, $delete = TRUE)
+{
+    $return = call_user_func(UC_API_FUNC, 'feed', 'get', array('limit' => $limit, 'delete' => $delete));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_friend_add($uid, $friendid, $comment = '')
+{
+    return call_user_func(UC_API_FUNC, 'friend', 'add', array('uid' => $uid, 'friendid' => $friendid, 'comment' => $comment));
+}
+
+function uc_friend_delete($uid, $friendids)
+{
+    return call_user_func(UC_API_FUNC, 'friend', 'delete', array('uid' => $uid, 'friendids' => $friendids));
+}
+
+function uc_friend_totalnum($uid, $direction = 0)
+{
+    return call_user_func(UC_API_FUNC, 'friend', 'totalnum', array('uid' => $uid, 'direction' => $direction));
+}
+
+function uc_friend_ls($uid, $page = 1, $pagesize = 10, $totalnum = 10, $direction = 0)
+{
+    $return = call_user_func(UC_API_FUNC, 'friend', 'ls', array('uid' => $uid, 'page' => $page, 'pagesize' => $pagesize, 'totalnum' => $totalnum, 'direction' => $direction));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_user_register($username, $password, $email = '', $mobile = '', $extend = [])
+{
+    return call_user_func(UC_API_FUNC, 'user', 'register', array('username' => $username, 'password' => $password, 'email' => $email, 'mobile' => $mobile, 'extend' => $extend));
+}
+
+function uc_user_login($username, $password, $isuid = 0, $extend = [])
+{
+    $isuid = intval($isuid);
+    $return = call_user_func(UC_API_FUNC, 'user', 'login', array('username' => $username, 'password' => $password, 'isuid' => $isuid, 'extend' => $extend));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+/**
+ * 同步注册
+ * @param int $uid
+ * @param string $password
+ * @return string
+ */
+function uc_user_synregister($uid, $password)
+{
+    $uid = intval($uid);
+    if (@include UC_ROOT . './data/cache/apps.php')
+    {
+        if (count($_CACHE['apps']) > 1)
+        {
+            $return = uc_api_post('user', 'synregister', array('uid' => $uid, 'password' => $password));
+        }
+        else
+        {
+            $return = '';
+        }
+    }
+    return $return;
+}
+
+/**
+ * 同步登录
+ * @param int $uid
+ * @return string
+ */
+function uc_user_synlogin($uid)
+{
+    $uid = intval($uid);
+    if (@include UC_ROOT . './data/cache/apps.php')
+    {
+        if (count($_CACHE['apps']) > 1)
+        {
+            $return = uc_api_post('user', 'synlogin', array('uid' => $uid));
+        }
+        else
+        {
+            $return = '';
+        }
+    }
+    return $return;
+}
+
+/**
+ * 同步退出
+ * @return string
+ */
+function uc_user_synlogout()
+{
+    if (@include UC_ROOT . './data/cache/apps.php')
+    {
+        if (count($_CACHE['apps']) > 1)
+        {
+            $return = uc_api_post('user', 'synlogout', array());
+        }
+        else
+        {
+            $return = '';
+        }
+    }
+    return $return;
+}
+
+/**
+ * 修改用户信息
+ * @param int $uid 会员ID
+ * @param string $username 用户名
+ * @param string $password 密码
+ * @param string $email 邮箱
+ * @param string $mobile 手机号
+ * @param array $extend 扩展信息
+ * @return type
+ */
+function uc_user_edit($uid, $username, $password, $email = '', $mobile = '', $extend = [])
+{
+    return call_user_func(UC_API_FUNC, 'user', 'edit', array('uid' => $uid, 'username' => $username, 'password' => $password, 'email' => $email, 'mobile' => $mobile, 'extend' => $extend));
+}
+
+function uc_user_delete($uid)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'delete', array('uid' => $uid));
+}
+
+function uc_user_deleteavatar($uid)
+{
+    uc_api_post('user', 'deleteavatar', array('uid' => $uid));
+}
+
+function uc_user_checkname($username)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'check_username', array('username' => $username));
+}
+
+function uc_user_checkemail($email)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'check_email', array('email' => $email));
+}
+
+function uc_user_checkmobile($mobile)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'check_mobile', array('mobile' => $mobile));
+}
+
+function uc_user_addprotected($username, $admin = '')
+{
+    return call_user_func(UC_API_FUNC, 'user', 'addprotected', array('username' => $username, 'admin' => $admin));
+}
+
+function uc_user_deleteprotected($username)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'deleteprotected', array('username' => $username));
+}
+
+function uc_user_getprotected()
+{
+    $return = call_user_func(UC_API_FUNC, 'user', 'getprotected', array('1' => 1));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_get_user($username, $isuid = 0)
+{
+    $return = call_user_func(UC_API_FUNC, 'user', 'get_user', array('username' => $username, 'isuid' => $isuid));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_user_merge($oldusername, $newusername, $uid, $password, $email)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'merge', array('oldusername' => $oldusername, 'newusername' => $newusername, 'uid' => $uid, 'password' => $password, 'email' => $email));
+}
+
+function uc_user_merge_remove($username)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'merge_remove', array('username' => $username));
+}
+
+function uc_user_getcredit($appid, $uid, $credit)
+{
+    return uc_api_post('user', 'getcredit', array('appid' => $appid, 'uid' => $uid, 'credit' => $credit));
+}
+
+function uc_user_logincheck($username, $ip)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'logincheck', array('username' => $username, 'ip' => $ip));
+}
+
+function uc_pm_location($uid, $newpm = 0)
+{
+    $apiurl = uc_api_url('pm_client', 'ls', "uid=$uid", ($newpm ? '&folder=newbox' : ''));
+    @header("Expires: 0");
+    @header("Cache-Control: private, post-check=0, pre-check=0, max-age=0", FALSE);
+    @header("Pragma: no-cache");
+    @header("location: $apiurl");
+}
+
+function uc_pm_checknew($uid, $more = 0)
+{
+    $return = call_user_func(UC_API_FUNC, 'pm', 'check_newpm', array('uid' => $uid, 'more' => $more));
+    return (!$more || UC_CONNECT == 'mysql') ? $return : uc_unserialize($return);
+}
+
+function uc_pm_send($fromuid, $msgto, $subject, $message, $instantly = 1, $replypmid = 0, $isusername = 0, $type = 0)
+{
+    if ($instantly)
+    {
+        $replypmid = @is_numeric($replypmid) ? $replypmid : 0;
+        return call_user_func(UC_API_FUNC, 'pm', 'sendpm', array('fromuid' => $fromuid, 'msgto' => $msgto, 'subject' => $subject, 'message' => $message, 'replypmid' => $replypmid, 'isusername' => $isusername, 'type' => $type));
+    }
+    else
+    {
+        $fromuid = intval($fromuid);
+        $subject = rawurlencode($subject);
+        $msgto = rawurlencode($msgto);
+        $message = rawurlencode($message);
+        $replypmid = @is_numeric($replypmid) ? $replypmid : 0;
+        $replyadd = $replypmid ? "&pmid=$replypmid&do=reply" : '';
+        $apiurl = uc_api_url('pm_client', 'send', "uid=$fromuid", "&msgto=$msgto&subject=$subject&message=$message$replyadd");
+        @header("Expires: 0");
+        @header("Cache-Control: private, post-check=0, pre-check=0, max-age=0", FALSE);
+        @header("Pragma: no-cache");
+        @header("location: " . $apiurl);
+    }
+}
+
+function uc_pm_delete($uid, $folder, $pmids)
+{
+    return call_user_func(UC_API_FUNC, 'pm', 'delete', array('uid' => $uid, 'pmids' => $pmids));
+}
+
+function uc_pm_deleteuser($uid, $touids)
+{
+    return call_user_func(UC_API_FUNC, 'pm', 'deleteuser', array('uid' => $uid, 'touids' => $touids));
+}
+
+function uc_pm_deletechat($uid, $plids, $type = 0)
+{
+    return call_user_func(UC_API_FUNC, 'pm', 'deletechat', array('uid' => $uid, 'plids' => $plids, 'type' => $type));
+}
+
+function uc_pm_readstatus($uid, $uids, $plids = array(), $status = 0)
+{
+    return call_user_func(UC_API_FUNC, 'pm', 'readstatus', array('uid' => $uid, 'uids' => $uids, 'plids' => $plids, 'status' => $status));
+}
+
+function uc_pm_list($uid, $page = 1, $pagesize = 10, $folder = 'inbox', $filter = 'newpm', $msglen = 0)
+{
+    $uid = intval($uid);
+    $page = intval($page);
+    $pagesize = intval($pagesize);
+    $return = call_user_func(UC_API_FUNC, 'pm', 'ls', array('uid' => $uid, 'page' => $page, 'pagesize' => $pagesize, 'filter' => $filter, 'msglen' => $msglen));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_pm_ignore($uid)
+{
+    $uid = intval($uid);
+    return call_user_func(UC_API_FUNC, 'pm', 'ignore', array('uid' => $uid));
+}
+
+function uc_pm_view($uid, $pmid = 0, $touid = 0, $daterange = 1, $page = 0, $pagesize = 10, $type = 0, $isplid = 0)
+{
+    $uid = intval($uid);
+    $touid = intval($touid);
+    $page = intval($page);
+    $pagesize = intval($pagesize);
+    $pmid = @is_numeric($pmid) ? $pmid : 0;
+    $return = call_user_func(UC_API_FUNC, 'pm', 'view', array('uid' => $uid, 'pmid' => $pmid, 'touid' => $touid, 'daterange' => $daterange, 'page' => $page, 'pagesize' => $pagesize, 'type' => $type, 'isplid' => $isplid));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_pm_view_num($uid, $touid, $isplid)
+{
+    $uid = intval($uid);
+    $touid = intval($touid);
+    $isplid = intval($isplid);
+    return call_user_func(UC_API_FUNC, 'pm', 'viewnum', array('uid' => $uid, 'touid' => $touid, 'isplid' => $isplid));
+}
+
+function uc_pm_viewnode($uid, $type, $pmid)
+{
+    $uid = intval($uid);
+    $type = intval($type);
+    $pmid = @is_numeric($pmid) ? $pmid : 0;
+    $return = call_user_func(UC_API_FUNC, 'pm', 'viewnode', array('uid' => $uid, 'type' => $type, 'pmid' => $pmid));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_pm_chatpmmemberlist($uid, $plid = 0)
+{
+    $uid = intval($uid);
+    $plid = intval($plid);
+    $return = call_user_func(UC_API_FUNC, 'pm', 'chatpmmemberlist', array('uid' => $uid, 'plid' => $plid));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_pm_kickchatpm($plid, $uid, $touid)
+{
+    $uid = intval($uid);
+    $plid = intval($plid);
+    $touid = intval($touid);
+    return call_user_func(UC_API_FUNC, 'pm', 'kickchatpm', array('uid' => $uid, 'plid' => $plid, 'touid' => $touid));
+}
+
+function uc_pm_appendchatpm($plid, $uid, $touid)
+{
+    $uid = intval($uid);
+    $plid = intval($plid);
+    $touid = intval($touid);
+    return call_user_func(UC_API_FUNC, 'pm', 'appendchatpm', array('uid' => $uid, 'plid' => $plid, 'touid' => $touid));
+}
+
+function uc_pm_blackls_get($uid)
+{
+    $uid = intval($uid);
+    return call_user_func(UC_API_FUNC, 'pm', 'blackls_get', array('uid' => $uid));
+}
+
+function uc_pm_blackls_set($uid, $blackls)
+{
+    $uid = intval($uid);
+    return call_user_func(UC_API_FUNC, 'pm', 'blackls_set', array('uid' => $uid, 'blackls' => $blackls));
+}
+
+function uc_pm_blackls_add($uid, $username)
+{
+    $uid = intval($uid);
+    return call_user_func(UC_API_FUNC, 'pm', 'blackls_add', array('uid' => $uid, 'username' => $username));
+}
+
+function uc_pm_blackls_delete($uid, $username)
+{
+    $uid = intval($uid);
+    return call_user_func(UC_API_FUNC, 'pm', 'blackls_delete', array('uid' => $uid, 'username' => $username));
+}
+
+function uc_domain_ls()
+{
+    $return = call_user_func(UC_API_FUNC, 'domain', 'ls', array('1' => 1));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_credit_exchange_request($uid, $from, $to, $toappid, $amount)
+{
+    $uid = intval($uid);
+    $from = intval($from);
+    $toappid = intval($toappid);
+    $to = intval($to);
+    $amount = intval($amount);
+    return uc_api_post('credit', 'request', array('uid' => $uid, 'from' => $from, 'to' => $to, 'toappid' => $toappid, 'amount' => $amount));
+}
+
+function uc_tag_get($tagname, $nums = 0)
+{
+    $return = call_user_func(UC_API_FUNC, 'tag', 'gettag', array('tagname' => $tagname, 'nums' => $nums));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_avatar($uid, $type = 'virtual', $returnhtml = 1)
+{
+    $uid = intval($uid);
+    $uc_input = uc_api_input("uid=$uid");
+    $uc_avatarflash = UC_API . '/images/camera.swf?inajax=1&appid=' . UC_APPID . '&input=' . $uc_input . '&agent=' . md5($_SERVER['HTTP_USER_AGENT']) . '&ucapi=' . urlencode(str_replace('http://', '', UC_API)) . '&avatartype=' . $type . '&uploadSize=2048';
+    if ($returnhtml)
+    {
+        return '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="450" height="253" id="mycamera" align="middle">
+			<param name="allowScriptAccess" value="always" />
+			<param name="scale" value="exactfit" />
+			<param name="wmode" value="transparent" />
+			<param name="quality" value="high" />
+			<param name="bgcolor" value="#ffffff" />
+			<param name="movie" value="' . $uc_avatarflash . '" />
+			<param name="menu" value="false" />
+			<embed src="' . $uc_avatarflash . '" quality="high" bgcolor="#ffffff" width="450" height="253" name="mycamera" align="middle" allowScriptAccess="always" allowFullScreen="false" scale="exactfit"  wmode="transparent" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
+		</object>';
+    }
+    else
+    {
+        return array(
+            'width', '450',
+            'height', '253',
+            'scale', 'exactfit',
+            'src', $uc_avatarflash,
+            'id', 'mycamera',
+            'name', 'mycamera',
+            'quality', 'high',
+            'bgcolor', '#ffffff',
+            'menu', 'false',
+            'swLiveConnect', 'true',
+            'allowScriptAccess', 'always'
+        );
+    }
+}
+
+function uc_mail_queue($uids, $emails, $subject, $message, $frommail = '', $charset = 'gbk', $htmlon = FALSE, $level = 1)
+{
+    return call_user_func(UC_API_FUNC, 'mail', 'add', array('uids' => $uids, 'emails' => $emails, 'subject' => $subject, 'message' => $message, 'frommail' => $frommail, 'charset' => $charset, 'htmlon' => $htmlon, 'level' => $level));
+}
+
+function uc_check_avatar($uid, $size = 'middle', $type = 'virtual')
+{
+    $url = UC_API . "/avatar.php?uid=$uid&size=$size&type=$type&check_file_exists=1";
+    $res = uc_fopen2($url, 500000, '', '', TRUE, UC_IP, 20);
+    if ($res == 1)
+    {
+        return 1;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+function uc_check_version()
+{
+    $return = uc_api_post('version', 'check', array());
+    $data = uc_unserialize($return);
+    return is_array($data) ? $data : $return;
+}
diff --git a/extend/fast/ucenter/client/uc_client/control/app.php b/extend/fast/ucenter/client/uc_client/control/app.php
new file mode 100644
index 0000000..dc66f54
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/control/app.php
@@ -0,0 +1,63 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: app.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class appcontrol extends base
+{
+
+    function __construct()
+    {
+        $this->appcontrol();
+    }
+
+    function appcontrol()
+    {
+        parent::__construct();
+        $this->load('app');
+    }
+
+    function onls()
+    {
+        $this->init_input();
+        $applist = $_ENV['app']->get_apps('appid, type, name, url, tagtemplates, viewprourl, synlogin');
+        $applist2 = array();
+        foreach ($applist as $key => $app)
+        {
+            $app['tagtemplates'] = $this->unserialize($app['tagtemplates']);
+            $applist2[$app['appid']] = $app;
+        }
+        return $applist2;
+    }
+
+    function onadd()
+    {
+
+    }
+
+    function onucinfo()
+    {
+
+    }
+
+    function _random($length, $numeric = 0)
+    {
+
+    }
+
+    function _generate_key()
+    {
+
+    }
+
+    function _format_notedata($notedata)
+    {
+
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/control/cache.php b/extend/fast/ucenter/client/uc_client/control/cache.php
new file mode 100644
index 0000000..e8a621c
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/control/cache.php
@@ -0,0 +1,30 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: cache.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class cachecontrol extends base
+{
+
+    function __construct()
+    {
+        $this->cachecontrol();
+    }
+
+    function cachecontrol()
+    {
+        parent::__construct();
+    }
+
+    function onupdate($arr)
+    {
+        $this->load("cache");
+        $_ENV['cache']->updatedata();
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/control/client.php b/extend/fast/ucenter/client/uc_client/control/client.php
new file mode 100644
index 0000000..9536bca
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/control/client.php
@@ -0,0 +1,817 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: client.php 1179 2014-11-03 07:11:25Z hypowang $
+ */
+
+if (!defined('UC_API'))
+{
+    exit('Access denied');
+}
+
+error_reporting(0);
+
+define('IN_UC', TRUE);
+define('UC_CLIENT_VERSION', '1.6.0');
+define('UC_CLIENT_RELEASE', '20141101');
+define('UC_ROOT', substr(__FILE__, 0, -10));
+define('UC_DATADIR', UC_ROOT . './data/');
+define('UC_DATAURL', UC_API . '/data');
+define('UC_API_FUNC', UC_CONNECT == 'mysql' ? 'uc_api_mysql' : 'uc_api_post');
+$GLOBALS['uc_controls'] = array();
+
+function uc_addslashes($string, $force = 0, $strip = FALSE)
+{
+    !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
+    if (!MAGIC_QUOTES_GPC || $force)
+    {
+        if (is_array($string))
+        {
+            foreach ($string as $key => $val)
+            {
+                $string[$key] = uc_addslashes($val, $force, $strip);
+            }
+        }
+        else
+        {
+            $string = addslashes($strip ? stripslashes($string) : $string);
+        }
+    }
+    return $string;
+}
+
+if (!function_exists('daddslashes'))
+{
+
+    function daddslashes($string, $force = 0)
+    {
+        return uc_addslashes($string, $force);
+    }
+
+}
+
+if (!function_exists('dhtmlspecialchars'))
+{
+
+    function dhtmlspecialchars($string, $flags = null)
+    {
+        if (is_array($string))
+        {
+            foreach ($string as $key => $val)
+            {
+                $string[$key] = dhtmlspecialchars($val, $flags);
+            }
+        }
+        else
+        {
+            if ($flags === null)
+            {
+                $string = str_replace(array('&', '"', '<', '>'), array('&amp;', '&quot;', '&lt;', '&gt;'), $string);
+                if (strpos($string, '&amp;#') !== false)
+                {
+                    $string = preg_replace('/&amp;((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
+                }
+            }
+            else
+            {
+                if (PHP_VERSION < '5.4.0')
+                {
+                    $string = htmlspecialchars($string, $flags);
+                }
+                else
+                {
+                    if (strtolower(CHARSET) == 'utf-8')
+                    {
+                        $charset = 'UTF-8';
+                    }
+                    else
+                    {
+                        $charset = 'ISO-8859-1';
+                    }
+                    $string = htmlspecialchars($string, $flags, $charset);
+                }
+            }
+        }
+        return $string;
+    }
+
+}
+
+if (!function_exists('fsocketopen'))
+{
+
+    function fsocketopen($hostname, $port = 80, &$errno, &$errstr, $timeout = 15)
+    {
+        $fp = '';
+        if (function_exists('fsockopen'))
+        {
+            $fp = @fsockopen($hostname, $port, $errno, $errstr, $timeout);
+        }
+        elseif (function_exists('pfsockopen'))
+        {
+            $fp = @pfsockopen($hostname, $port, $errno, $errstr, $timeout);
+        }
+        elseif (function_exists('stream_socket_client'))
+        {
+            $fp = @stream_socket_client($hostname . ':' . $port, $errno, $errstr, $timeout);
+        }
+        return $fp;
+    }
+
+}
+
+function uc_stripslashes($string)
+{
+    !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
+    if (MAGIC_QUOTES_GPC)
+    {
+        return stripslashes($string);
+    }
+    else
+    {
+        return $string;
+    }
+}
+
+// 采用POST请求形式
+function uc_api_post($module, $action, $arg = array())
+{
+    $s = $sep = '';
+    foreach ($arg as $k => $v)
+    {
+        $k = urlencode($k);
+        if (is_array($v))
+        {
+            $s2 = $sep2 = '';
+            foreach ($v as $k2 => $v2)
+            {
+                $k2 = urlencode($k2);
+                $s2 .= "$sep2{$k}[$k2]=" . urlencode(uc_stripslashes($v2));
+                $sep2 = '&';
+            }
+            $s .= $sep . $s2;
+        }
+        else
+        {
+            $s .= "$sep$k=" . urlencode(uc_stripslashes($v));
+        }
+        $sep = '&';
+    }
+    $postdata = uc_api_requestdata($module, $action, $s);
+    return uc_fopen2(UC_API . '/index.php', 500000, $postdata, '', TRUE, UC_IP, 20);
+}
+
+function uc_api_requestdata($module, $action, $arg = '', $extra = '')
+{
+    $input = uc_api_input($arg);
+    $post = "m=$module&a=$action&inajax=2&release=" . UC_CLIENT_RELEASE . "&input=$input&appid=" . UC_APPID . $extra;
+    return $post;
+}
+
+function uc_api_url($module, $action, $arg = '', $extra = '')
+{
+    $url = UC_API . '/index.php?' . uc_api_requestdata($module, $action, $arg, $extra);
+    return $url;
+}
+
+function uc_api_input($data)
+{
+    $s = urlencode(uc_authcode($data . '&agent=' . md5($_SERVER['HTTP_USER_AGENT']) . "&time=" . time(), 'ENCODE', UC_KEY));
+    return $s;
+}
+
+// 直连数据库的形式
+function uc_api_mysql($model, $action, $args = array())
+{
+    global $uc_controls;
+    if (empty($uc_controls[$model]))
+    {
+        // 优先使用mysqli
+        if (function_exists("mysqli_connect"))
+        {
+            include_once UC_ROOT . './lib/dbi.class.php';
+        }
+        else
+        {
+            include_once UC_ROOT . './lib/db.class.php';
+        }
+        include_once UC_ROOT . './model/base.php';
+        include_once UC_ROOT . "./control/$model.php";
+        $classname = $model . 'control';
+        $uc_controls[$model] = new $classname();
+    }
+    if ($action{0} != '_')
+    {
+        $args = uc_addslashes($args, 1, TRUE);
+        $action = 'on' . $action;
+        $uc_controls[$model]->input = $args;
+        return $uc_controls[$model]->$action($args);
+    }
+    else
+    {
+        return '';
+    }
+}
+
+function uc_serialize($arr, $htmlon = 0)
+{
+    include_once UC_ROOT . './lib/xml.class.php';
+    return xml_serialize($arr, $htmlon);
+}
+
+function uc_unserialize($s)
+{
+    include_once UC_ROOT . './lib/xml.class.php';
+    return @xml_unserialize($s);
+}
+
+function uc_authcode($string, $operation = 'DECODE', $key = '', $expiry = 0)
+{
+
+    $ckey_length = 4;
+
+    $key = md5($key ? $key : UC_KEY);
+    $keya = md5(substr($key, 0, 16));
+    $keyb = md5(substr($key, 16, 16));
+    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
+
+    $cryptkey = $keya . md5($keya . $keyc);
+    $key_length = strlen($cryptkey);
+
+    $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
+    $string_length = strlen($string);
+
+    $result = '';
+    $box = range(0, 255);
+
+    $rndkey = array();
+    for ($i = 0; $i <= 255; $i++)
+    {
+        $rndkey[$i] = ord($cryptkey[$i % $key_length]);
+    }
+
+    for ($j = $i = 0; $i < 256; $i++)
+    {
+        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
+        $tmp = $box[$i];
+        $box[$i] = $box[$j];
+        $box[$j] = $tmp;
+    }
+
+    for ($a = $j = $i = 0; $i < $string_length; $i++)
+    {
+        $a = ($a + 1) % 256;
+        $j = ($j + $box[$a]) % 256;
+        $tmp = $box[$a];
+        $box[$a] = $box[$j];
+        $box[$j] = $tmp;
+        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
+    }
+
+    if ($operation == 'DECODE')
+    {
+        if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16))
+        {
+            return substr($result, 26);
+        }
+        else
+        {
+            return '';
+        }
+    }
+    else
+    {
+        return $keyc . str_replace('=', '', base64_encode($result));
+    }
+}
+
+function uc_fopen2($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE)
+{
+    $__times__ = isset($_GET['__times__']) ? intval($_GET['__times__']) + 1 : 1;
+    if ($__times__ > 2)
+    {
+        return '';
+    }
+    $url .= (strpos($url, '?') === FALSE ? '?' : '&') . "__times__=$__times__";
+    return uc_fopen($url, $limit, $post, $cookie, $bysocket, $ip, $timeout, $block);
+}
+
+function uc_fopen($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE)
+{
+    $return = '';
+    $matches = parse_url($url);
+    !isset($matches['scheme']) && $matches['scheme'] = '';
+    !isset($matches['host']) && $matches['host'] = '';
+    !isset($matches['path']) && $matches['path'] = '';
+    !isset($matches['query']) && $matches['query'] = '';
+    !isset($matches['port']) && $matches['port'] = '';
+    $scheme = $matches['scheme'];
+    $host = $matches['host'];
+    $path = $matches['path'] ? $matches['path'] . ($matches['query'] ? '?' . $matches['query'] : '') : '/';
+    $port = !empty($matches['port']) ? $matches['port'] : 80;
+    if ($post)
+    {
+        $out = "POST $path HTTP/1.0\r\n";
+        $header = "Accept: */*\r\n";
+        $header .= "Accept-Language: zh-cn\r\n";
+        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
+        $header .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";
+        $header .= "Host: $host\r\n";
+        $header .= 'Content-Length: ' . strlen($post) . "\r\n";
+        $header .= "Connection: Close\r\n";
+        $header .= "Cache-Control: no-cache\r\n";
+        $header .= "Cookie: $cookie\r\n\r\n";
+        $out .= $header . $post;
+    }
+    else
+    {
+        $out = "GET $path HTTP/1.0\r\n";
+        $header = "Accept: */*\r\n";
+        $header .= "Accept-Language: zh-cn\r\n";
+        $header .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";
+        $header .= "Host: $host\r\n";
+        $header .= "Connection: Close\r\n";
+        $header .= "Cookie: $cookie\r\n\r\n";
+        $out .= $header;
+    }
+
+    $fpflag = 0;
+    if (!$fp = @fsocketopen(($ip ? $ip : $host), $port, $errno, $errstr, $timeout))
+    {
+        $context = array(
+            'http' => array(
+                'method'  => $post ? 'POST' : 'GET',
+                'header'  => $header,
+                'content' => $post,
+                'timeout' => $timeout,
+            ),
+        );
+        $context = stream_context_create($context);
+        $fp = @fopen($scheme . '://' . ($ip ? $ip : $host) . ':' . $port . $path, 'b', false, $context);
+        $fpflag = 1;
+    }
+
+    if (!$fp)
+    {
+        return '';
+    }
+    else
+    {
+        stream_set_blocking($fp, $block);
+        stream_set_timeout($fp, $timeout);
+        @fwrite($fp, $out);
+        $status = stream_get_meta_data($fp);
+        if (!$status['timed_out'])
+        {
+            while (!feof($fp) && !$fpflag)
+            {
+                if (($header = @fgets($fp)) && ($header == "\r\n" || $header == "\n"))
+                {
+                    break;
+                }
+            }
+
+            $stop = false;
+            while (!feof($fp) && !$stop)
+            {
+                $data = fread($fp, ($limit == 0 || $limit > 8192 ? 8192 : $limit));
+                $return .= $data;
+                if ($limit)
+                {
+                    $limit -= strlen($data);
+                    $stop = $limit <= 0;
+                }
+            }
+        }
+        @fclose($fp);
+        return $return;
+    }
+}
+
+function uc_app_ls()
+{
+    $return = call_user_func(UC_API_FUNC, 'app', 'ls', array());
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_feed_add($icon, $uid, $username, $title_template = '', $title_data = '', $body_template = '', $body_data = '', $body_general = '', $target_ids = '', $images = array())
+{
+    return call_user_func(UC_API_FUNC, 'feed', 'add', array(
+        'icon'           => $icon,
+        'appid'          => UC_APPID,
+        'uid'            => $uid,
+        'username'       => $username,
+        'title_template' => $title_template,
+        'title_data'     => $title_data,
+        'body_template'  => $body_template,
+        'body_data'      => $body_data,
+        'body_general'   => $body_general,
+        'target_ids'     => $target_ids,
+        'image_1'        => $images[0]['url'],
+        'image_1_link'   => $images[0]['link'],
+        'image_2'        => $images[1]['url'],
+        'image_2_link'   => $images[1]['link'],
+        'image_3'        => $images[2]['url'],
+        'image_3_link'   => $images[2]['link'],
+        'image_4'        => $images[3]['url'],
+        'image_4_link'   => $images[3]['link']
+            )
+    );
+}
+
+function uc_feed_get($limit = 100, $delete = TRUE)
+{
+    $return = call_user_func(UC_API_FUNC, 'feed', 'get', array('limit' => $limit, 'delete' => $delete));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_friend_add($uid, $friendid, $comment = '')
+{
+    return call_user_func(UC_API_FUNC, 'friend', 'add', array('uid' => $uid, 'friendid' => $friendid, 'comment' => $comment));
+}
+
+function uc_friend_delete($uid, $friendids)
+{
+    return call_user_func(UC_API_FUNC, 'friend', 'delete', array('uid' => $uid, 'friendids' => $friendids));
+}
+
+function uc_friend_totalnum($uid, $direction = 0)
+{
+    return call_user_func(UC_API_FUNC, 'friend', 'totalnum', array('uid' => $uid, 'direction' => $direction));
+}
+
+function uc_friend_ls($uid, $page = 1, $pagesize = 10, $totalnum = 10, $direction = 0)
+{
+    $return = call_user_func(UC_API_FUNC, 'friend', 'ls', array('uid' => $uid, 'page' => $page, 'pagesize' => $pagesize, 'totalnum' => $totalnum, 'direction' => $direction));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_user_register($username, $password, $email = '', $mobile = '', $extend = [])
+{
+    return call_user_func(UC_API_FUNC, 'user', 'register', array('username' => $username, 'password' => $password, 'email' => $email, 'mobile' => $mobile, 'extend' => $extend));
+}
+
+function uc_user_login($username, $password, $isuid = 0, $extend = [])
+{
+    $isuid = intval($isuid);
+    $return = call_user_func(UC_API_FUNC, 'user', 'login', array('username' => $username, 'password' => $password, 'isuid' => $isuid, 'extend' => $extend));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_user_synregister($uid, $password)
+{
+    $uid = intval($uid);
+    if (@include UC_ROOT . './data/cache/apps.php')
+    {
+        if (count($_CACHE['apps']) > 1)
+        {
+            $return = uc_api_post('user', 'synregister', array('uid' => $uid, 'password' => $password));
+        }
+        else
+        {
+            $return = '';
+        }
+    }
+    return $return;
+}
+
+function uc_user_synlogin($uid)
+{
+    $uid = intval($uid);
+    if (@include UC_ROOT . './data/cache/apps.php')
+    {
+        if (count($_CACHE['apps']) > 1)
+        {
+            $return = uc_api_post('user', 'synlogin', array('uid' => $uid));
+        }
+        else
+        {
+            $return = '';
+        }
+    }
+    return $return;
+}
+
+function uc_user_synlogout()
+{
+    if (@include UC_ROOT . './data/cache/apps.php')
+    {
+        if (count($_CACHE['apps']) > 1)
+        {
+            $return = uc_api_post('user', 'synlogout', array());
+        }
+        else
+        {
+            $return = '';
+        }
+    }
+    return $return;
+}
+
+function uc_user_edit($uid, $username, $password, $email = '', $mobile = '', $extend = [])
+{
+    return call_user_func(UC_API_FUNC, 'user', 'edit', array('uid' => $uid, 'username' => $username, 'password' => $password, 'email' => $email, 'mobile' => $mobile, 'extend' => $extend));
+}
+
+function uc_user_delete($uid)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'delete', array('uid' => $uid));
+}
+
+function uc_user_deleteavatar($uid)
+{
+    uc_api_post('user', 'deleteavatar', array('uid' => $uid));
+}
+
+function uc_user_checkname($username)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'check_username', array('username' => $username));
+}
+
+function uc_user_checkemail($email)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'check_email', array('email' => $email));
+}
+
+function uc_user_checkmobile($mobile)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'check_mobile', array('mobile' => $mobile));
+}
+
+function uc_user_addprotected($username, $admin = '')
+{
+    return call_user_func(UC_API_FUNC, 'user', 'addprotected', array('username' => $username, 'admin' => $admin));
+}
+
+function uc_user_deleteprotected($username)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'deleteprotected', array('username' => $username));
+}
+
+function uc_user_getprotected()
+{
+    $return = call_user_func(UC_API_FUNC, 'user', 'getprotected', array('1' => 1));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_get_user($username, $isuid = 0)
+{
+    $return = call_user_func(UC_API_FUNC, 'user', 'get_user', array('username' => $username, 'isuid' => $isuid));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_user_merge($oldusername, $newusername, $uid, $password, $email)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'merge', array('oldusername' => $oldusername, 'newusername' => $newusername, 'uid' => $uid, 'password' => $password, 'email' => $email));
+}
+
+function uc_user_merge_remove($username)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'merge_remove', array('username' => $username));
+}
+
+function uc_user_getcredit($appid, $uid, $credit)
+{
+    return uc_api_post('user', 'getcredit', array('appid' => $appid, 'uid' => $uid, 'credit' => $credit));
+}
+
+function uc_user_logincheck($username, $ip)
+{
+    return call_user_func(UC_API_FUNC, 'user', 'logincheck', array('username' => $username, 'ip' => $ip));
+}
+
+function uc_pm_location($uid, $newpm = 0)
+{
+    $apiurl = uc_api_url('pm_client', 'ls', "uid=$uid", ($newpm ? '&folder=newbox' : ''));
+    @header("Expires: 0");
+    @header("Cache-Control: private, post-check=0, pre-check=0, max-age=0", FALSE);
+    @header("Pragma: no-cache");
+    @header("location: $apiurl");
+}
+
+function uc_pm_checknew($uid, $more = 0)
+{
+    $return = call_user_func(UC_API_FUNC, 'pm', 'check_newpm', array('uid' => $uid, 'more' => $more));
+    return (!$more || UC_CONNECT == 'mysql') ? $return : uc_unserialize($return);
+}
+
+function uc_pm_send($fromuid, $msgto, $subject, $message, $instantly = 1, $replypmid = 0, $isusername = 0, $type = 0)
+{
+    if ($instantly)
+    {
+        $replypmid = @is_numeric($replypmid) ? $replypmid : 0;
+        return call_user_func(UC_API_FUNC, 'pm', 'sendpm', array('fromuid' => $fromuid, 'msgto' => $msgto, 'subject' => $subject, 'message' => $message, 'replypmid' => $replypmid, 'isusername' => $isusername, 'type' => $type));
+    }
+    else
+    {
+        $fromuid = intval($fromuid);
+        $subject = rawurlencode($subject);
+        $msgto = rawurlencode($msgto);
+        $message = rawurlencode($message);
+        $replypmid = @is_numeric($replypmid) ? $replypmid : 0;
+        $replyadd = $replypmid ? "&pmid=$replypmid&do=reply" : '';
+        $apiurl = uc_api_url('pm_client', 'send', "uid=$fromuid", "&msgto=$msgto&subject=$subject&message=$message$replyadd");
+        @header("Expires: 0");
+        @header("Cache-Control: private, post-check=0, pre-check=0, max-age=0", FALSE);
+        @header("Pragma: no-cache");
+        @header("location: " . $apiurl);
+    }
+}
+
+function uc_pm_delete($uid, $folder, $pmids)
+{
+    return call_user_func(UC_API_FUNC, 'pm', 'delete', array('uid' => $uid, 'pmids' => $pmids));
+}
+
+function uc_pm_deleteuser($uid, $touids)
+{
+    return call_user_func(UC_API_FUNC, 'pm', 'deleteuser', array('uid' => $uid, 'touids' => $touids));
+}
+
+function uc_pm_deletechat($uid, $plids, $type = 0)
+{
+    return call_user_func(UC_API_FUNC, 'pm', 'deletechat', array('uid' => $uid, 'plids' => $plids, 'type' => $type));
+}
+
+function uc_pm_readstatus($uid, $uids, $plids = array(), $status = 0)
+{
+    return call_user_func(UC_API_FUNC, 'pm', 'readstatus', array('uid' => $uid, 'uids' => $uids, 'plids' => $plids, 'status' => $status));
+}
+
+function uc_pm_list($uid, $page = 1, $pagesize = 10, $folder = 'inbox', $filter = 'newpm', $msglen = 0)
+{
+    $uid = intval($uid);
+    $page = intval($page);
+    $pagesize = intval($pagesize);
+    $return = call_user_func(UC_API_FUNC, 'pm', 'ls', array('uid' => $uid, 'page' => $page, 'pagesize' => $pagesize, 'filter' => $filter, 'msglen' => $msglen));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_pm_ignore($uid)
+{
+    $uid = intval($uid);
+    return call_user_func(UC_API_FUNC, 'pm', 'ignore', array('uid' => $uid));
+}
+
+function uc_pm_view($uid, $pmid = 0, $touid = 0, $daterange = 1, $page = 0, $pagesize = 10, $type = 0, $isplid = 0)
+{
+    $uid = intval($uid);
+    $touid = intval($touid);
+    $page = intval($page);
+    $pagesize = intval($pagesize);
+    $pmid = @is_numeric($pmid) ? $pmid : 0;
+    $return = call_user_func(UC_API_FUNC, 'pm', 'view', array('uid' => $uid, 'pmid' => $pmid, 'touid' => $touid, 'daterange' => $daterange, 'page' => $page, 'pagesize' => $pagesize, 'type' => $type, 'isplid' => $isplid));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_pm_view_num($uid, $touid, $isplid)
+{
+    $uid = intval($uid);
+    $touid = intval($touid);
+    $isplid = intval($isplid);
+    return call_user_func(UC_API_FUNC, 'pm', 'viewnum', array('uid' => $uid, 'touid' => $touid, 'isplid' => $isplid));
+}
+
+function uc_pm_viewnode($uid, $type, $pmid)
+{
+    $uid = intval($uid);
+    $type = intval($type);
+    $pmid = @is_numeric($pmid) ? $pmid : 0;
+    $return = call_user_func(UC_API_FUNC, 'pm', 'viewnode', array('uid' => $uid, 'type' => $type, 'pmid' => $pmid));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_pm_chatpmmemberlist($uid, $plid = 0)
+{
+    $uid = intval($uid);
+    $plid = intval($plid);
+    $return = call_user_func(UC_API_FUNC, 'pm', 'chatpmmemberlist', array('uid' => $uid, 'plid' => $plid));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_pm_kickchatpm($plid, $uid, $touid)
+{
+    $uid = intval($uid);
+    $plid = intval($plid);
+    $touid = intval($touid);
+    return call_user_func(UC_API_FUNC, 'pm', 'kickchatpm', array('uid' => $uid, 'plid' => $plid, 'touid' => $touid));
+}
+
+function uc_pm_appendchatpm($plid, $uid, $touid)
+{
+    $uid = intval($uid);
+    $plid = intval($plid);
+    $touid = intval($touid);
+    return call_user_func(UC_API_FUNC, 'pm', 'appendchatpm', array('uid' => $uid, 'plid' => $plid, 'touid' => $touid));
+}
+
+function uc_pm_blackls_get($uid)
+{
+    $uid = intval($uid);
+    return call_user_func(UC_API_FUNC, 'pm', 'blackls_get', array('uid' => $uid));
+}
+
+function uc_pm_blackls_set($uid, $blackls)
+{
+    $uid = intval($uid);
+    return call_user_func(UC_API_FUNC, 'pm', 'blackls_set', array('uid' => $uid, 'blackls' => $blackls));
+}
+
+function uc_pm_blackls_add($uid, $username)
+{
+    $uid = intval($uid);
+    return call_user_func(UC_API_FUNC, 'pm', 'blackls_add', array('uid' => $uid, 'username' => $username));
+}
+
+function uc_pm_blackls_delete($uid, $username)
+{
+    $uid = intval($uid);
+    return call_user_func(UC_API_FUNC, 'pm', 'blackls_delete', array('uid' => $uid, 'username' => $username));
+}
+
+function uc_domain_ls()
+{
+    $return = call_user_func(UC_API_FUNC, 'domain', 'ls', array('1' => 1));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_credit_exchange_request($uid, $from, $to, $toappid, $amount)
+{
+    $uid = intval($uid);
+    $from = intval($from);
+    $toappid = intval($toappid);
+    $to = intval($to);
+    $amount = intval($amount);
+    return uc_api_post('credit', 'request', array('uid' => $uid, 'from' => $from, 'to' => $to, 'toappid' => $toappid, 'amount' => $amount));
+}
+
+function uc_tag_get($tagname, $nums = 0)
+{
+    $return = call_user_func(UC_API_FUNC, 'tag', 'gettag', array('tagname' => $tagname, 'nums' => $nums));
+    return UC_CONNECT == 'mysql' ? $return : uc_unserialize($return);
+}
+
+function uc_avatar($uid, $type = 'virtual', $returnhtml = 1)
+{
+    $uid = intval($uid);
+    $uc_input = uc_api_input("uid=$uid");
+    $uc_avatarflash = UC_API . '/images/camera.swf?inajax=1&appid=' . UC_APPID . '&input=' . $uc_input . '&agent=' . md5($_SERVER['HTTP_USER_AGENT']) . '&ucapi=' . urlencode(str_replace('http://', '', UC_API)) . '&avatartype=' . $type . '&uploadSize=2048';
+    if ($returnhtml)
+    {
+        return '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="450" height="253" id="mycamera" align="middle">
+			<param name="allowScriptAccess" value="always" />
+			<param name="scale" value="exactfit" />
+			<param name="wmode" value="transparent" />
+			<param name="quality" value="high" />
+			<param name="bgcolor" value="#ffffff" />
+			<param name="movie" value="' . $uc_avatarflash . '" />
+			<param name="menu" value="false" />
+			<embed src="' . $uc_avatarflash . '" quality="high" bgcolor="#ffffff" width="450" height="253" name="mycamera" align="middle" allowScriptAccess="always" allowFullScreen="false" scale="exactfit"  wmode="transparent" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
+		</object>';
+    }
+    else
+    {
+        return array(
+            'width', '450',
+            'height', '253',
+            'scale', 'exactfit',
+            'src', $uc_avatarflash,
+            'id', 'mycamera',
+            'name', 'mycamera',
+            'quality', 'high',
+            'bgcolor', '#ffffff',
+            'menu', 'false',
+            'swLiveConnect', 'true',
+            'allowScriptAccess', 'always'
+        );
+    }
+}
+
+function uc_mail_queue($uids, $emails, $subject, $message, $frommail = '', $charset = 'gbk', $htmlon = FALSE, $level = 1)
+{
+    return call_user_func(UC_API_FUNC, 'mail', 'add', array('uids' => $uids, 'emails' => $emails, 'subject' => $subject, 'message' => $message, 'frommail' => $frommail, 'charset' => $charset, 'htmlon' => $htmlon, 'level' => $level));
+}
+
+function uc_check_avatar($uid, $size = 'middle', $type = 'virtual')
+{
+    $url = UC_API . "/avatar.php?uid=$uid&size=$size&type=$type&check_file_exists=1";
+    $res = uc_fopen2($url, 500000, '', '', TRUE, UC_IP, 20);
+    if ($res == 1)
+    {
+        return 1;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+function uc_check_version()
+{
+    $return = uc_api_post('version', 'check', array());
+    $data = uc_unserialize($return);
+    return is_array($data) ? $data : $return;
+}
diff --git a/extend/fast/ucenter/client/uc_client/control/domain.php b/extend/fast/ucenter/client/uc_client/control/domain.php
new file mode 100644
index 0000000..a7c76a9
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/control/domain.php
@@ -0,0 +1,31 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: domain.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class domaincontrol extends base
+{
+
+    function __construct()
+    {
+        $this->domaincontrol();
+    }
+
+    function domaincontrol()
+    {
+        parent::__construct();
+        $this->init_input();
+        $this->load('domain');
+    }
+
+    function onls()
+    {
+        return $_ENV['domain']->get_list(1, 9999, 9999);
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/control/feed.php b/extend/fast/ucenter/client/uc_client/control/feed.php
new file mode 100644
index 0000000..86a58b3
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/control/feed.php
@@ -0,0 +1,114 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: feed.php 1139 2012-05-08 09:02:11Z liulanbo $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class feedcontrol extends base
+{
+
+    function __construct()
+    {
+        $this->feedcontrol();
+    }
+
+    function feedcontrol()
+    {
+        parent::__construct();
+        $this->init_input();
+    }
+
+    function onadd()
+    {
+        $this->load('misc');
+        $appid = intval($this->input('appid'));
+        $icon = $this->input('icon');
+        $uid = intval($this->input('uid'));
+        $username = $this->input('username');
+        $body_data = $_ENV['misc']->array2string($this->input('body_data'));
+        $title_data = $_ENV['misc']->array2string($this->input('title_data'));
+
+        $title_template = $this->_parsetemplate($this->input('title_template'));
+        $body_template = $this->_parsetemplate($this->input('body_template'));
+        $body_general = $this->input('body_general');
+        $target_ids = $this->input('target_ids');
+        $image_1 = $this->input('image_1');
+        $image_1_link = $this->input('image_1_link');
+        $image_2 = $this->input('image_2');
+        $image_2_link = $this->input('image_2_link');
+        $image_3 = $this->input('image_3');
+        $image_3_link = $this->input('image_3_link');
+        $image_4 = $this->input('image_4');
+        $image_4_link = $this->input('image_4_link');
+
+        $hash_template = md5($title_template . $body_template);
+        $hash_data = md5($title_template . $title_data . $body_template . $body_data);
+        $dateline = $this->time;
+        $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "feeds SET appid='$appid', icon='$icon', uid='$uid', username='$username',
+			title_template='$title_template', title_data='$title_data', body_template='$body_template', body_data='$body_data', body_general='$body_general',
+			image_1='$image_1', image_1_link='$image_1_link', image_2='$image_2', image_2_link='$image_2_link',
+			image_3='$image_3', image_3_link='$image_3_link', image_4='$image_4', image_4_link='$image_4_link',
+			hash_template='$hash_template', hash_data='$hash_data', target_ids='$target_ids', dateline='$dateline'");
+        return $this->db->insert_id();
+    }
+
+    function ondelete()
+    {
+        $start = $this->input('start');
+        $limit = $this->input('limit');
+        $end = $start + $limit;
+        $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "feeds WHERE feedid>'$start' AND feedid<'$end'");
+    }
+
+    function onget()
+    {
+        $this->load('misc');
+        $limit = intval($this->input('limit'));
+        $delete = $this->input('delete');
+        $feedlist = $this->db->fetch_all("SELECT * FROM " . UC_DBTABLEPRE . "feeds ORDER BY feedid DESC LIMIT $limit");
+        if ($feedlist)
+        {
+            $maxfeedid = $feedlist[0]['feedid'];
+            foreach ($feedlist as $key => $feed)
+            {
+                $feed['body_data'] = $_ENV['misc']->string2array($feed['body_data']);
+                $feed['title_data'] = $_ENV['misc']->string2array($feed['title_data']);
+                $feedlist[$key] = $feed;
+            }
+        }
+        if (!empty($feedlist))
+        {
+            if (!isset($delete) || $delete)
+            {
+                $this->_delete(0, $maxfeedid);
+            }
+        }
+        return $feedlist;
+    }
+
+    function _delete($start, $end)
+    {
+        $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "feeds WHERE feedid>='$start' AND feedid<='$end'");
+    }
+
+    function _parsetemplate($template)
+    {
+        $template = str_replace(array("\r", "\n"), '', $template);
+        $template = str_replace(array('<br>', '<br />', '<BR>', '<BR />'), "\n", $template);
+        $template = str_replace(array('<b>', '<B>'), '[B]', $template);
+        $template = str_replace(array('<i>', '<I>'), '[I]', $template);
+        $template = str_replace(array('<u>', '<U>'), '[U]', $template);
+        $template = str_replace(array('</b>', '</B>'), '[/B]', $template);
+        $template = str_replace(array('</i>', '</I>'), '[/I]', $template);
+        $template = str_replace(array('</u>', '</U>'), '[/U]', $template);
+        $template = dhtmlspecialchars($template);
+        $template = nl2br($template);
+        $template = str_replace(array('[B]', '[I]', '[U]', '[/B]', '[/I]', '[/U]'), array('<b>', '<i>', '<u>', '</b>', '</i>', '</u>'), $template);
+        return $template;
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/control/friend.php b/extend/fast/ucenter/client/uc_client/control/friend.php
new file mode 100644
index 0000000..01768b5
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/control/friend.php
@@ -0,0 +1,64 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: friend.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class friendcontrol extends base
+{
+
+    function __construct()
+    {
+        $this->friendcontrol();
+    }
+
+    function friendcontrol()
+    {
+        parent::__construct();
+        $this->init_input();
+        $this->load('friend');
+    }
+
+    function ondelete()
+    {
+        $uid = intval($this->input('uid'));
+        $friendids = $this->input('friendids');
+        $id = $_ENV['friend']->delete($uid, $friendids);
+        return $id;
+    }
+
+    function onadd()
+    {
+        $uid = intval($this->input('uid'));
+        $friendid = $this->input('friendid');
+        $comment = $this->input('comment');
+        $id = $_ENV['friend']->add($uid, $friendid, $comment);
+        return $id;
+    }
+
+    function ontotalnum()
+    {
+        $uid = intval($this->input('uid'));
+        $direction = intval($this->input('direction'));
+        $totalnum = $_ENV['friend']->get_totalnum_by_uid($uid, $direction);
+        return $totalnum;
+    }
+
+    function onls()
+    {
+        $uid = intval($this->input('uid'));
+        $page = intval($this->input('page'));
+        $pagesize = intval($this->input('pagesize'));
+        $totalnum = intval($this->input('totalnum'));
+        $direction = intval($this->input('direction'));
+        $pagesize = $pagesize ? $pagesize : UC_PPP;
+        $totalnum = $totalnum ? $totalnum : $_ENV['friend']->get_totalnum_by_uid($uid);
+        $data = $_ENV['friend']->get_list($uid, $page, $pagesize, $totalnum, $direction);
+        return $data;
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/control/index.htm b/extend/fast/ucenter/client/uc_client/control/index.htm
new file mode 100644
index 0000000..0519ecb
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/control/index.htm
@@ -0,0 +1 @@
+ 
\ No newline at end of file
diff --git a/extend/fast/ucenter/client/uc_client/control/mail.php b/extend/fast/ucenter/client/uc_client/control/mail.php
new file mode 100644
index 0000000..28bdedd
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/control/mail.php
@@ -0,0 +1,42 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: mail.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class mailcontrol extends base
+{
+
+    function __construct()
+    {
+        $this->mailcontrol();
+    }
+
+    function mailcontrol()
+    {
+        parent::__construct();
+        $this->init_input();
+    }
+
+    function onadd()
+    {
+        $this->load('mail');
+        $mail = array();
+        $mail['appid'] = UC_APPID;
+        $mail['uids'] = explode(',', $this->input('uids'));
+        $mail['emails'] = explode(',', $this->input('emails'));
+        $mail['subject'] = $this->input('subject');
+        $mail['message'] = $this->input('message');
+        $mail['charset'] = $this->input('charset');
+        $mail['htmlon'] = intval($this->input('htmlon'));
+        $mail['level'] = abs(intval($this->input('level')));
+        $mail['frommail'] = $this->input('frommail');
+        $mail['dateline'] = $this->time;
+        return $_ENV['mail']->add($mail);
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/control/pm.php b/extend/fast/ucenter/client/uc_client/control/pm.php
new file mode 100644
index 0000000..940d398
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/control/pm.php
@@ -0,0 +1,463 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: pm.php 1067 2011-03-08 10:06:51Z svn_project_zhangjie $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+define('PRIVATEPMTHREADLIMIT_ERROR', -1);
+define('PMFLOODCTRL_ERROR', -2);
+define('PMMSGTONOTFRIEND', -3);
+define('PMSENDREGDAYS', -4);
+define('CHATPMTHREADLIMIT_ERROR', -5);
+define('CHATPMMEMBERLIMIT_ERROR', -7);
+
+class pmcontrol extends base
+{
+
+    function __construct()
+    {
+        $this->pmcontrol();
+    }
+
+    function pmcontrol()
+    {
+        parent::__construct();
+        $this->load('user');
+        $this->load('pm');
+    }
+
+    function oncheck_newpm()
+    {
+        $this->init_input();
+        $uid = intval($this->input('uid'));
+        $more = intval($this->input('more'));
+        if (!$_ENV['pm']->isnewpm($uid) && !$more)
+        {
+            return 0;
+        }
+        $newprvpm = $_ENV['pm']->getpmnum($uid, 1, 1);
+        $newchatpm = $_ENV['pm']->getpmnum($uid, 2, 1);
+        $newpm = $newprvpm + $newchatpm;
+        if ($more == 0)
+        {
+            return $newpm;
+        }
+        elseif ($more == 1)
+        {
+            return array('newpm' => $newpm, 'newprivatepm' => $newprvpm);
+        }
+        elseif ($more == 2 || $more == 3)
+        {
+            if ($more == 2)
+            {
+                return array('newpm' => $newpm, 'newprivatepm' => $newprvpm, 'newchatpm' => $newchatpm);
+            }
+            else
+            {
+                $lastpm = $_ENV['pm']->lastpm($uid);
+                require_once UC_ROOT . 'lib/uccode.class.php';
+                $this->uccode = new uccode();
+                $lastpm['lastsummary'] = $this->uccode->complie($lastpm['lastsummary']);
+                return array('newpm' => $newpm, 'newprivatepm' => $newprvpm, 'newchatpm' => $newchatpm, 'lastdate' => $lastpm['lastdateline'], 'lastmsgfromid' => $lastpm['lastauthorid'], 'lastmsgfrom' => $lastpm['lastauthorusername'], 'lastmsg' => $lastpm['lastsummary']);
+            }
+        }
+        elseif ($more == 4)
+        {
+            return array('newpm' => $newpm, 'newprivatepm' => $newprvpm, 'newchatpm' => $newchatpm);
+        }
+        else
+        {
+            return 0;
+        }
+    }
+
+    function onsendpm()
+    {
+        $this->init_input();
+        $fromuid = $this->input('fromuid');
+        $msgto = $this->input('msgto');
+        $subject = $this->input('subject');
+        $message = $this->input('message');
+        $replypmid = $this->input('replypmid');
+        $isusername = $this->input('isusername');
+        $type = $this->input('type');
+
+        if (!$fromuid)
+        {
+            return 0;
+        }
+
+        $user = $_ENV['user']->get_user_by_uid($fromuid);
+        $user = daddslashes($user, 1);
+        if (!$user)
+        {
+            return 0;
+        }
+        $this->user['uid'] = $user['uid'];
+        $this->user['username'] = $user['username'];
+
+        if ($replypmid)
+        {
+            $isusername = 0;
+            $plid = $_ENV['pm']->getplidbypmid($replypmid);
+            $msgto = $_ENV['pm']->getuidbyplid($plid);
+            unset($msgto[$this->user['uid']]);
+        }
+        else
+        {
+            if (!empty($msgto))
+            {
+                $msgto = array_unique(explode(',', $msgto));
+            }
+        }
+
+        if ($isusername)
+        {
+            $msgto = $_ENV['user']->name2id($msgto);
+        }
+        $countmsgto = count($msgto);
+
+        if ($this->settings['pmsendregdays'])
+        {
+            if ($user['regdate'] > $this->time - $this->settings['pmsendregdays'] * 86400)
+            {
+                return PMSENDREGDAYS;
+            }
+        }
+        if ($this->settings['chatpmmemberlimit'])
+        {
+            if ($type == 1 && ($countmsgto > ($this->settings['chatpmmemberlimit'] - 1)))
+            {
+                return CHATPMMEMBERLIMIT_ERROR;
+            }
+        }
+        if ($this->settings['pmfloodctrl'])
+        {
+            if (!$_ENV['pm']->ispminterval($this->user['uid'], $this->settings['pmfloodctrl']))
+            {
+                return PMFLOODCTRL_ERROR;
+            }
+        }
+        if ($this->settings['privatepmthreadlimit'])
+        {
+            if (!$_ENV['pm']->isprivatepmthreadlimit($this->user['uid'], $this->settings['privatepmthreadlimit']))
+            {
+                return PRIVATEPMTHREADLIMIT_ERROR;
+            }
+        }
+        if ($this->settings['chatpmthreadlimit'])
+        {
+            if (!$_ENV['pm']->ischatpmthreadlimit($this->user['uid'], $this->settings['chatpmthreadlimit']))
+            {
+                return CHATPMTHREADLIMIT_ERROR;
+            }
+        }
+
+        $lastpmid = 0;
+        if ($replypmid)
+        {
+            $lastpmid = $_ENV['pm']->replypm($plid, $this->user['uid'], $this->user['username'], $message);
+        }
+        else
+        {
+            $lastpmid = $_ENV['pm']->sendpm($this->user['uid'], $this->user['username'], $msgto, $subject, $message, $type);
+        }
+        return $lastpmid;
+    }
+
+    function ondelete()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $pmids = $this->input('pmids');
+        if (empty($pmids))
+        {
+            return 0;
+        }
+        if (is_array($pmids))
+        {
+            $this->apps = $this->cache('apps');
+            if ($this->apps[$this->app['appid']]['type'] == 'UCHOME')
+            {
+                $id = $_ENV['pm']->deletepmbyplids($this->user['uid'], $this->input('pmids'));
+            }
+            else
+            {
+                $id = $_ENV['pm']->deletepmbypmids($this->user['uid'], $this->input('pmids'));
+            }
+        }
+        else
+        {
+            $id = $_ENV['pm']->deletepmbypmid($this->user['uid'], $this->input('pmids'));
+        }
+        return $id;
+    }
+
+    function ondeletechat()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $plids = $this->input('plids');
+        $type = intval($this->input('type'));
+        if ($type == 1)
+        {
+            return $_ENV['pm']->deletepmbyplids($this->user['uid'], $plids);
+        }
+        else
+        {
+            return $_ENV['pm']->quitchatpm($this->user['uid'], $plids);
+        }
+    }
+
+    function ondeleteuser()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $id = $_ENV['pm']->deletepmbyplids($this->user['uid'], $this->input('touids'), 1);
+        return $id;
+    }
+
+    function onreadstatus()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $_ENV['pm']->setpmstatus($this->user['uid'], $this->input('uids'), $this->input('plids'), $this->input('status'));
+    }
+
+    function onignore()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        return $_ENV['pm']->set_ignore($this->user['uid']);
+    }
+
+    function onls()
+    {
+        $this->init_input();
+        $pagesize = $this->input('pagesize');
+        $filter = $this->input('filter');
+        $page = $this->input('page');
+        $msglen = $this->input('msglen');
+        $this->user['uid'] = intval($this->input('uid'));
+
+        $filter = $filter ? (in_array($filter, array('newpm', 'privatepm')) ? $filter : '') : '';
+        if ($filter == 'newpm')
+        {
+            $type = 0;
+            $new = 1;
+        }
+        elseif ($filter == 'privatepm')
+        {
+            $type = 0;
+            $new = 0;
+        }
+        else
+        {
+            return array();
+        }
+        $pmnum = $_ENV['pm']->getpmnum($this->user['uid'], $type, $new);
+        $start = $this->page_get_start($page, $pagesize, $pmnum);
+
+        if ($pagesize > 0)
+        {
+            $pms = $_ENV['pm']->getpmlist($this->user['uid'], $filter, $start, $pagesize);
+            if (is_array($pms) && !empty($pms))
+            {
+                foreach ($pms as $key => $pm)
+                {
+                    if ($msglen)
+                    {
+                        $pms[$key]['lastsummary'] = $_ENV['pm']->removecode($pms[$key]['lastsummary'], $msglen);
+                    }
+                    else
+                    {
+                        unset($pms[$key]['lastsummary']);
+                    }
+                }
+            }
+            $result['data'] = $pms;
+        }
+        $result['count'] = $pmnum;
+        return $result;
+    }
+
+    function onview()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $pmid = $this->input('pmid');
+        $touid = $this->input('touid');
+        $daterange = $this->input('daterange');
+        $page = $this->input('page');
+        $pagesize = $this->input('pagesize');
+        $isplid = $this->input('isplid');
+        $type = $this->input('type');
+
+        $daterange = empty($daterange) ? 1 : $daterange;
+        $today = $this->time - ($this->time + $this->settings['timeoffset']) % 86400;
+        if ($daterange == 1)
+        {
+            $starttime = $today;
+        }
+        elseif ($daterange == 2)
+        {
+            $starttime = $today - 86400;
+        }
+        elseif ($daterange == 3)
+        {
+            $starttime = $today - 172800;
+        }
+        elseif ($daterange == 4)
+        {
+            $starttime = $today - 604800;
+        }
+        elseif ($daterange == 5)
+        {
+            $starttime = 0;
+        }
+        $endtime = $this->time;
+
+        if (!$isplid)
+        {
+            $plid = $_ENV['pm']->getplidbytouid($this->user['uid'], $touid);
+        }
+        else
+        {
+            $plid = $touid;
+        }
+        if ($page)
+        {
+            $pmnum = $_ENV['pm']->getpmnumbyplid($this->user['uid'], $plid);
+            $start = $this->page_get_start($page, $pagesize, $pmnum);
+            $ppp = $pagesize;
+        }
+        else
+        {
+            $pmnum = 0;
+            $start = 0;
+            $ppp = 0;
+        }
+
+        if ($pmid)
+        {
+            $pms = $_ENV['pm']->getpmbypmid($this->user['uid'], $pmid);
+        }
+        else
+        {
+            $pms = $_ENV['pm']->getpmbyplid($this->user['uid'], $plid, $starttime, $endtime, $start, $ppp, $type);
+        }
+
+        require_once UC_ROOT . 'lib/uccode.class.php';
+        $this->uccode = new uccode();
+        if ($pms)
+        {
+            foreach ($pms as $key => $pm)
+            {
+                $pms[$key]['message'] = $this->uccode->complie($pms[$key]['message']);
+            }
+        }
+        return $pms;
+    }
+
+    function onviewnum()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $touid = $this->input('touid');
+        $isplid = $this->input('isplid');
+        if (!$isplid)
+        {
+            $plid = $_ENV['pm']->getplidbytouid($this->user['uid'], $touid);
+        }
+        else
+        {
+            $plid = $touid;
+        }
+        $pmnum = $_ENV['pm']->getpmnumbyplid($this->user['uid'], $plid);
+        return $pmnum;
+    }
+
+    function onviewnode()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $type = $this->input('type');
+        $pmid = $this->input('pmid');
+        $type = 0;
+        $pms = $_ENV['pm']->getpmbypmid($this->user['uid'], $pmid);
+
+        require_once UC_ROOT . 'lib/uccode.class.php';
+        $this->uccode = new uccode();
+        if ($pms)
+        {
+            foreach ($pms as $key => $pm)
+            {
+                $pms[$key]['message'] = $this->uccode->complie($pms[$key]['message']);
+            }
+        }
+        $pms = $pms[0];
+        return $pms;
+    }
+
+    function onchatpmmemberlist()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $plid = intval($this->input('plid'));
+        return $_ENV['pm']->chatpmmemberlist($this->user['uid'], $plid);
+    }
+
+    function onkickchatpm()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $plid = intval($this->input('plid'));
+        $touid = intval($this->input('touid'));
+        return $_ENV['pm']->kickchatpm($plid, $this->user['uid'], $touid);
+    }
+
+    function onappendchatpm()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $plid = intval($this->input('plid'));
+        $touid = intval($this->input('touid'));
+        return $_ENV['pm']->appendchatpm($plid, $this->user['uid'], $touid);
+    }
+
+    function onblackls_get()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        return $_ENV['pm']->get_blackls($this->user['uid']);
+    }
+
+    function onblackls_set()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $blackls = $this->input('blackls');
+        return $_ENV['pm']->set_blackls($this->user['uid'], $blackls);
+    }
+
+    function onblackls_add()
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $username = $this->input('username');
+        return $_ENV['pm']->update_blackls($this->user['uid'], $username, 1);
+    }
+
+    function onblackls_delete($arr)
+    {
+        $this->init_input();
+        $this->user['uid'] = intval($this->input('uid'));
+        $username = $this->input('username');
+        return $_ENV['pm']->update_blackls($this->user['uid'], $username, 2);
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/control/tag.php b/extend/fast/ucenter/client/uc_client/control/tag.php
new file mode 100644
index 0000000..8fc80aa
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/control/tag.php
@@ -0,0 +1,102 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: tag.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class tagcontrol extends base
+{
+
+    function __construct()
+    {
+        $this->tagcontrol();
+    }
+
+    function tagcontrol()
+    {
+        parent::__construct();
+        $this->init_input();
+        $this->load('tag');
+        $this->load('misc');
+    }
+
+    function ongettag()
+    {
+        $appid = $this->input('appid');
+        $tagname = $this->input('tagname');
+        $nums = $this->input('nums');
+        if (empty($tagname))
+        {
+            return NULL;
+        }
+        $return = $apparray = $appadd = array();
+
+        if ($nums && is_array($nums))
+        {
+            foreach ($nums as $k => $num)
+            {
+                $apparray[$k] = $k;
+            }
+        }
+
+        $data = $_ENV['tag']->get_tag_by_name($tagname);
+        if ($data)
+        {
+            $apparraynew = array();
+            foreach ($data as $tagdata)
+            {
+                $row = $r = array();
+                $tmp = explode("\t", $tagdata['data']);
+                $type = $tmp[0];
+                array_shift($tmp);
+                foreach ($tmp as $tmp1)
+                {
+                    $tmp1 != '' && $r[] = $_ENV['misc']->string2array($tmp1);
+                }
+                if (in_array($tagdata['appid'], $apparray))
+                {
+                    if ($tagdata['expiration'] > 0 && $this->time - $tagdata['expiration'] > 3600)
+                    {
+                        $appadd[] = $tagdata['appid'];
+                        $_ENV['tag']->formatcache($tagdata['appid'], $tagname);
+                    }
+                    else
+                    {
+                        $apparraynew[] = $tagdata['appid'];
+                    }
+                    $datakey = array();
+                    $count = 0;
+                    foreach ($r as $data)
+                    {
+                        $return[$tagdata['appid']]['data'][] = $data;
+                        $return[$tagdata['appid']]['type'] = $type;
+                        $count++;
+                        if ($count >= $nums[$tagdata['appid']])
+                        {
+                            break;
+                        }
+                    }
+                }
+            }
+            $apparray = array_diff($apparray, $apparraynew);
+        }
+        else
+        {
+            foreach ($apparray as $appid)
+            {
+                $_ENV['tag']->formatcache($appid, $tagname);
+            }
+        }
+        if ($apparray)
+        {
+            $this->load('note');
+            $_ENV['note']->add('gettag', "id=$tagname", '', $appadd, -1);
+        }
+        return $return;
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/control/user.php b/extend/fast/ucenter/client/uc_client/control/user.php
new file mode 100644
index 0000000..42d0b4e
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/control/user.php
@@ -0,0 +1,443 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: user.php 1174 2014-11-03 04:38:12Z hypowang $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+define('UC_USER_CHECK_USERNAME_FAILED', -1);
+define('UC_USER_USERNAME_BADWORD', -2);
+define('UC_USER_USERNAME_EXISTS', -3);
+define('UC_USER_EMAIL_FORMAT_ILLEGAL', -4);
+define('UC_USER_EMAIL_ACCESS_ILLEGAL', -5);
+define('UC_USER_EMAIL_EXISTS', -6);
+define('UC_USER_MOBILE_FORMAT_ILLEGAL', -7);
+define('UC_USER_MOBILE_ACCESS_ILLEGAL', -8);
+define('UC_USER_MOBILE_EXISTS', -9);
+
+class usercontrol extends base
+{
+
+    function __construct()
+    {
+        $this->usercontrol();
+    }
+
+    function usercontrol()
+    {
+        parent::__construct();
+        $this->load('user');
+        $this->app = $this->cache['apps'][UC_APPID];
+    }
+
+    // 同步注册
+    function onsynregister()
+    {
+        $this->init_input();
+        $uid = $this->input('uid');
+        $password = $this->input('password');
+        if ($this->app['synlogin'])
+        {
+            if ($this->user = $_ENV['user']->get_user_by_uid($uid))
+            {
+                $params = 'action=synregister' .
+                        '&username=' . $this->user['username'] .
+                        '&uid=' . $this->user['uid'] .
+                        '&password=' . $password .
+                        '&email=' . $this->user['email'] .
+                        '&mobile=' . $this->user['mobile'] .
+                        '&time=' . $this->time;
+                $synstr = '';
+                foreach ($this->cache['apps'] as $appid => $app)
+                {
+                    if ($app['synlogin'] && $app['appid'] != $this->app['appid'])
+                    {
+                        $synstr .= '<script type="text/javascript" src="' . $app['url'] . '/api/uc.php?time=' . $this->time . '&code=' . urlencode($this->authcode($params, 'ENCODE', $app['authkey'])) . '"></script>';
+                    }
+                }
+                return $synstr;
+            }
+        }
+        return '';
+    }
+
+    // 同步登录
+    function onsynlogin()
+    {
+        $this->init_input();
+        $uid = $this->input('uid');
+        if ($this->app['synlogin'])
+        {
+            if ($this->user = $_ENV['user']->get_user_by_uid($uid))
+            {
+                $params = 'action=synlogin' .
+                        '&username=' . $this->user['username'] .
+                        '&uid=' . $this->user['uid'] .
+                        '&password=' . $this->user['password'] .
+                        '&time=' . $this->time;
+                $synstr = '';
+                foreach ($this->cache['apps'] as $appid => $app)
+                {
+                    if ($app['synlogin'] && $app['appid'] != $this->app['appid'])
+                    {
+                        $synstr .= '<script type="text/javascript" src="' . $app['url'] . '/api/uc.php?time=' . $this->time . '&code=' . urlencode($this->authcode($params, 'ENCODE', $app['authkey'])) . '"></script>';
+                    }
+                }
+                return $synstr;
+            }
+        }
+        return '';
+    }
+
+    // 同步退出
+    function onsynlogout()
+    {
+        $this->init_input();
+        if ($this->app['synlogin'])
+        {
+            $synstr = '';
+            foreach ($this->cache['apps'] as $appid => $app)
+            {
+                if ($app['synlogin'] && $app['appid'] != $this->app['appid'])
+                {
+                    $synstr .= '<script type="text/javascript" src="' . $app['url'] . '/api/uc.php?time=' . $this->time . '&code=' . urlencode($this->authcode('action=synlogout&time=' . $this->time, 'ENCODE', $app['authkey'])) . '"></script>';
+                }
+            }
+            return $synstr;
+        }
+        return '';
+    }
+
+    // 同步注册
+    function onregister()
+    {
+        $this->init_input();
+        $username = $this->input('username');
+        $password = $this->input('password');
+        $email = $this->input('email');
+        $mobile = $this->input('mobile');
+        $extend = $this->input('extend');
+        $extend = $extend ? $extend : [];
+        if (($status = $this->_check_username($username)) < 0)
+        {
+            return $status;
+        }
+        if ($email && ($status = $this->_check_email($email)) < 0)
+        {
+            return $status;
+        }
+        if ($mobile && ($status = $this->_check_mobile($mobile)) < 0)
+        {
+            return $status;
+        }
+        $uid = $_ENV['user']->add_user($username, $password, $email, $mobile, 0, $extend);
+        return $uid;
+    }
+
+    // 编辑ucenter中信息
+    function onedit()
+    {
+        $this->init_input();
+        $uid = intval($this->input('uid'));
+        $username = $this->input('username');
+        $password = $this->input('password');
+        $email = $this->input('email');
+        $mobile = $this->input('mobile');
+        $extend = $this->input('extend');
+        $extend = $extend ? $extend : [];
+
+        $status = $_ENV['user']->edit_user($username, $password, $email, $mobile, $uid, $extend);
+
+        if ($status > 0)
+        {
+            if (@include UC_ROOT . './data/cache/apps.php')
+            {
+                if (count($_CACHE['apps']) > 1)
+                {
+                    //手动调用一次同步到所有应用
+                    uc_api_post('user', 'edit', $this->input);
+                }
+            }
+            return $status;
+            $this->load('note');
+            $_ENV['note']->add('updateinfo', http_build_query($this->input));
+        }
+        return $status;
+    }
+
+    // 登录ucenter账号
+    function onlogin()
+    {
+        $this->init_input();
+        $username = $this->input('username');
+        $password = $this->input('password');
+        $isuid = $this->input('isuid'); //0用户名 1UID 2邮箱 3手机
+
+        if ($isuid == 1)
+        {
+            $user = $_ENV['user']->get_user_by_uid($username);
+        }
+        elseif ($isuid == 2)
+        {
+            $user = $_ENV['user']->get_user_by_email($username);
+        }
+        elseif ($isuid == 3)
+        {
+            $user = $_ENV['user']->get_user_by_mobile($username);
+        }
+        else
+        {
+            $user = $_ENV['user']->get_user_by_username($username);
+        }
+
+        $passwordmd5 = preg_match('/^\w{32}$/', $password) ? $password : md5($password);
+        if (empty($user))
+        {
+            $status = -1;
+        }
+        elseif ($user['password'] != md5($passwordmd5 . $user['salt']))
+        {
+            $status = -2;
+        }
+        else
+        {
+            $status = $user['uid'];
+        }
+        $merge = $status != -1 && !$isuid && $_ENV['user']->check_mergeuser($username) ? 1 : 0;
+        return [
+            'status'   => $status,
+            'uid'      => isset($user['uid']) ? $user['uid'] : 0,
+            'username' => isset($user['username']) ? $user['username'] : '',
+            'password' => $password,
+            'email'    => isset($user['email']) ? $user['email'] : '',
+            'mobile'   => isset($user['mobile']) ? $user['mobile'] : '',
+            'merge'    => $merge
+        ];
+    }
+
+    function onlogincheck()
+    {
+        $this->init_input();
+        $username = $this->input('username');
+        $ip = $this->input('ip');
+        return $_ENV['user']->can_do_login($username, $ip);
+    }
+
+    function oncheck_email()
+    {
+        $this->init_input();
+        $email = $this->input('email');
+        return $this->_check_email($email);
+    }
+
+    function oncheck_username()
+    {
+        $this->init_input();
+        $username = $this->input('username');
+        if (($status = $this->_check_username($username)) < 0)
+        {
+            return $status;
+        }
+        else
+        {
+            return 1;
+        }
+    }
+
+    function onget_user()
+    {
+        $this->init_input();
+        $username = $this->input('username');
+        $isuid = $this->input('isuid'); //0用户名 1UID 2邮箱 3手机
+        if ($isuid == 1)
+        {
+            $member = $_ENV['user']->get_user_by_uid($username);
+        }
+        else if ($isuid == 2)
+        {
+            $member = $_ENV['user']->get_user_by_email($username);
+        }
+        else if ($isuid == 3)
+        {
+            $member = $_ENV['user']->get_user_by_mobile($username);
+        }
+        else
+        {
+            $member = $_ENV['user']->get_user_by_username($username);
+        }
+        if ($member)
+        {
+            // 加载扩展表
+            $memberfields = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "memberfields WHERE uid='{$member['uid']}'");
+
+            $memberfields = $memberfields ? $memberfields : [];
+            $member = [
+                'uid'      => $member['uid'],
+                'username' => $member['username'],
+                'email'    => $member['email'],
+                'mobile'   => $member['mobile']
+            ];
+            return array_merge($member, $memberfields);
+        }
+        else
+        {
+            return 0;
+        }
+    }
+
+    function ongetprotected()
+    {
+        $this->init_input();
+        $protectedmembers = $this->db->fetch_all("SELECT uid,username FROM " . UC_DBTABLEPRE . "protectedmembers GROUP BY username");
+        return $protectedmembers;
+    }
+
+    function ondelete()
+    {
+        $this->init_input();
+        $uid = $this->input('uid');
+        return $_ENV['user']->delete_user($uid);
+    }
+
+    function onaddprotected()
+    {
+        $this->init_input();
+        $username = $this->input('username');
+        $admin = $this->input('admin');
+        $appid = $this->app['appid'];
+        $usernames = (array) $username;
+        foreach ($usernames as $username)
+        {
+            $user = $_ENV['user']->get_user_by_username($username);
+            $uid = $user['uid'];
+            $this->db->query("REPLACE INTO " . UC_DBTABLEPRE . "protectedmembers SET uid='$uid', username='$username', appid='$appid', dateline='{$this->time}', admin='$admin'", 'SILENT');
+        }
+        return $this->db->errno() ? -1 : 1;
+    }
+
+    function ondeleteprotected()
+    {
+        $this->init_input();
+        $username = $this->input('username');
+        $appid = $this->app['appid'];
+        $usernames = (array) $username;
+        foreach ($usernames as $username)
+        {
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "protectedmembers WHERE username='$username' AND appid='$appid'");
+        }
+        return $this->db->errno() ? -1 : 1;
+    }
+
+    function onmerge()
+    {
+        $this->init_input();
+        $uid = $this->input('uid');
+        $oldusername = $this->input('oldusername');
+        $newusername = $this->input('newusername');
+        $password = $this->input('password');
+        $email = $this->input('email');
+        $mobile = $this->input('mobile');
+        $extend = $this->input('extend');
+        $extend = $extend ? $extend : [];
+        if (($status = $this->_check_username($newusername)) < 0)
+        {
+            return $status;
+        }
+        $uid = $_ENV['user']->add_user($newusername, $password, $email, $mobile, $uid, $extend);
+        $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "mergemembers WHERE appid='" . $this->app['appid'] . "' AND username='$oldusername'");
+        return $uid;
+    }
+
+    function onmerge_remove()
+    {
+        $this->init_input();
+        $username = $this->input('username');
+        $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "mergemembers WHERE appid='" . $this->app['appid'] . "' AND username='$username'");
+        return NULL;
+    }
+
+    function _check_username($username)
+    {
+        $username = addslashes(trim(stripslashes($username)));
+        if (!$_ENV['user']->check_username($username))
+        {
+            return UC_USER_CHECK_USERNAME_FAILED;
+        }
+        elseif (!$_ENV['user']->check_usernamecensor($username))
+        {
+            return UC_USER_USERNAME_BADWORD;
+        }
+        elseif ($_ENV['user']->check_usernameexists($username))
+        {
+            return UC_USER_USERNAME_EXISTS;
+        }
+        return 1;
+    }
+
+    function _check_email($email, $username = '')
+    {
+        if (empty($this->settings))
+        {
+            $this->settings = $this->cache('settings');
+        }
+        if (!$_ENV['user']->check_emailformat($email))
+        {
+            return UC_USER_EMAIL_FORMAT_ILLEGAL;
+        }
+        elseif (!$_ENV['user']->check_emailaccess($email))
+        {
+            return UC_USER_EMAIL_ACCESS_ILLEGAL;
+        }
+        elseif (!$this->settings['doublee'] && $_ENV['user']->check_emailexists($email, $username))
+        {
+            return UC_USER_EMAIL_EXISTS;
+        }
+        else
+        {
+            return 1;
+        }
+    }
+
+    function _check_mobile($mobile, $username = '')
+    {
+        if (empty($this->settings))
+        {
+            $this->settings = $this->cache('settings');
+        }
+        if (!$_ENV['user']->check_mobileformat($mobile))
+        {
+            return UC_USER_EMAIL_FORMAT_ILLEGAL;
+        }
+        elseif (!$_ENV['user']->check_mobileaccess($mobile))
+        {
+            return UC_USER_EMAIL_ACCESS_ILLEGAL;
+        }
+        elseif (!$this->settings['doublee'] && $_ENV['user']->check_mobileexists($mobile, $username))
+        {
+            return UC_USER_EMAIL_EXISTS;
+        }
+        else
+        {
+            return 1;
+        }
+    }
+
+    function onuploadavatar()
+    {
+
+    }
+
+    function onrectavatar()
+    {
+
+    }
+
+    function flashdata_decode($s)
+    {
+
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/data/cache/apps.php b/extend/fast/ucenter/client/uc_client/data/cache/apps.php
new file mode 100644
index 0000000..eddad2f
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/data/cache/apps.php
@@ -0,0 +1,40 @@
+<?php
+$_CACHE['apps'] = array (
+  1 => 
+  array (
+    'appid' => '1',
+    'type' => 'OTHER',
+    'name' => 'FastAdmin',
+    'url' => 'http://www.tp.com/index/ucenter',
+    'ip' => '',
+    'viewprourl' => '',
+    'apifilename' => 'index',
+    'charset' => '',
+    'synlogin' => '1',
+    'extra' => 
+    array (
+      'apppath' => '',
+      'extraurl' => '			',
+    ),
+    'recvnote' => '1',
+  ),
+  2 => 
+  array (
+    'appid' => '2',
+    'type' => 'DISCUZX',
+    'name' => 'Discuz!',
+    'url' => 'http://www.discuz.com',
+    'ip' => '',
+    'viewprourl' => '',
+    'apifilename' => 'uc.php',
+    'charset' => 'utf-8',
+    'synlogin' => '1',
+    'extra' => 
+    array (
+      'apppath' => '',
+      'extraurl' => '			',
+    ),
+    'recvnote' => '1',
+  ),
+  'UC_API' => 'http://www.ucenter.com',
+);
diff --git a/extend/fast/ucenter/client/uc_client/data/cache/badwords.php b/extend/fast/ucenter/client/uc_client/data/cache/badwords.php
new file mode 100644
index 0000000..c3ca52b
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/data/cache/badwords.php
@@ -0,0 +1,5 @@
+<?php
+$_CACHE['badwords'] = array (
+);
+
+?>
\ No newline at end of file
diff --git a/extend/fast/ucenter/client/uc_client/data/cache/index.htm b/extend/fast/ucenter/client/uc_client/data/cache/index.htm
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/data/cache/index.htm
diff --git a/extend/fast/ucenter/client/uc_client/data/cache/settings.php b/extend/fast/ucenter/client/uc_client/data/cache/settings.php
new file mode 100644
index 0000000..069021e
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/data/cache/settings.php
@@ -0,0 +1,32 @@
+<?php
+$_CACHE['settings'] = array (
+  'accessemail' => '',
+  'censoremail' => '',
+  'censorusername' => '',
+  'dateformat' => 'y-n-j',
+  'doublee' => '0',
+  'nextnotetime' => '0',
+  'timeoffset' => '28800',
+  'privatepmthreadlimit' => '25',
+  'chatpmthreadlimit' => '30',
+  'chatpmmemberlimit' => '35',
+  'pmfloodctrl' => '15',
+  'pmcenter' => '1',
+  'sendpmseccode' => '1',
+  'pmsendregdays' => '0',
+  'maildefault' => 'username@21cn.com',
+  'mailsend' => '1',
+  'mailserver' => 'smtp.21cn.com',
+  'mailport' => '25',
+  'mailauth' => '1',
+  'mailfrom' => 'UCenter <username@21cn.com>',
+  'mailauth_username' => 'username@21cn.com',
+  'mailauth_password' => 'password',
+  'maildelimiter' => '0',
+  'mailusername' => '1',
+  'mailsilent' => '1',
+  'version' => '1.6.0',
+  'credits' => 'N;',
+);
+
+?>
\ No newline at end of file
diff --git a/extend/fast/ucenter/client/uc_client/data/index.htm b/extend/fast/ucenter/client/uc_client/data/index.htm
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/data/index.htm
diff --git a/extend/fast/ucenter/client/uc_client/index.htm b/extend/fast/ucenter/client/uc_client/index.htm
new file mode 100644
index 0000000..0519ecb
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/index.htm
@@ -0,0 +1 @@
+ 
\ No newline at end of file
diff --git a/extend/fast/ucenter/client/uc_client/lib/db.class.php b/extend/fast/ucenter/client/uc_client/lib/db.class.php
new file mode 100644
index 0000000..0c1f20b
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/lib/db.class.php
@@ -0,0 +1,210 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: db.class.php 1171 2014-11-03 03:33:47Z hypowang $
+ */
+
+class ucclient_db
+{
+
+    var $querynum = 0;
+    var $link;
+    var $histories;
+    var $dbhost;
+    var $dbuser;
+    var $dbpw;
+    var $dbcharset;
+    var $pconnect;
+    var $tablepre;
+    var $time;
+    var $goneaway = 5;
+
+    function connect($dbhost, $dbuser, $dbpw, $dbname = '', $dbcharset = '', $pconnect = 0, $tablepre = '', $time = 0)
+    {
+        $this->dbhost = $dbhost;
+        $this->dbuser = $dbuser;
+        $this->dbpw = $dbpw;
+        $this->dbname = $dbname;
+        $this->dbcharset = $dbcharset;
+        $this->pconnect = $pconnect;
+        $this->tablepre = $tablepre;
+        $this->time = $time;
+
+        if ($pconnect)
+        {
+            if (!$this->link = mysql_pconnect($dbhost, $dbuser, $dbpw))
+            {
+                $this->halt('Can not connect to MySQL server');
+            }
+        }
+        else
+        {
+            if (!$this->link = mysql_connect($dbhost, $dbuser, $dbpw))
+            {
+                $this->halt('Can not connect to MySQL server');
+            }
+        }
+
+        if ($this->version() > '4.1')
+        {
+            if ($dbcharset)
+            {
+                mysql_query("SET character_set_connection=" . $dbcharset . ", character_set_results=" . $dbcharset . ", character_set_client=binary", $this->link);
+            }
+
+            if ($this->version() > '5.0.1')
+            {
+                mysql_query("SET sql_mode=''", $this->link);
+            }
+        }
+
+        if ($dbname)
+        {
+            mysql_select_db($dbname, $this->link);
+        }
+    }
+
+    function fetch_array($query, $result_type = MYSQL_ASSOC)
+    {
+        return mysql_fetch_array($query, $result_type);
+    }
+
+    function result_first($sql)
+    {
+        $query = $this->query($sql);
+        return $this->result($query, 0);
+    }
+
+    function fetch_first($sql)
+    {
+        $query = $this->query($sql);
+        return $this->fetch_array($query);
+    }
+
+    function fetch_all($sql, $id = '')
+    {
+        $arr = array();
+        $query = $this->query($sql);
+        while ($data = $this->fetch_array($query))
+        {
+            $id ? $arr[$data[$id]] = $data : $arr[] = $data;
+        }
+        return $arr;
+    }
+
+    function cache_gc()
+    {
+        $this->query("DELETE FROM {$this->tablepre}sqlcaches WHERE expiry<$this->time");
+    }
+
+    function query($sql, $type = '', $cachetime = FALSE)
+    {
+        $func = $type == 'UNBUFFERED' && @function_exists('mysql_unbuffered_query') ? 'mysql_unbuffered_query' : 'mysql_query';
+        if (!($query = $func($sql, $this->link)) && $type != 'SILENT')
+        {
+            $this->halt('MySQL Query Error', $sql);
+        }
+        $this->querynum++;
+        $this->histories[] = $sql;
+        return $query;
+    }
+
+    function affected_rows()
+    {
+        return mysql_affected_rows($this->link);
+    }
+
+    function error()
+    {
+        return (($this->link) ? mysql_error($this->link) : mysql_error());
+    }
+
+    function errno()
+    {
+        return intval(($this->link) ? mysql_errno($this->link) : mysql_errno());
+    }
+
+    function result($query, $row)
+    {
+        $query = @mysql_result($query, $row);
+        return $query;
+    }
+
+    function num_rows($query)
+    {
+        $query = mysql_num_rows($query);
+        return $query;
+    }
+
+    function num_fields($query)
+    {
+        return mysql_num_fields($query);
+    }
+
+    function free_result($query)
+    {
+        return mysql_free_result($query);
+    }
+
+    function insert_id()
+    {
+        return ($id = mysql_insert_id($this->link)) >= 0 ? $id : $this->result($this->query("SELECT last_insert_id()"), 0);
+    }
+
+    function fetch_row($query)
+    {
+        $query = mysql_fetch_row($query);
+        return $query;
+    }
+
+    function fetch_fields($query)
+    {
+        return mysql_fetch_field($query);
+    }
+
+    function version()
+    {
+        return mysql_get_server_info($this->link);
+    }
+
+    function escape_string($str)
+    {
+        return mysql_escape_string($str);
+    }
+
+    function close()
+    {
+        return mysql_close($this->link);
+    }
+
+    function halt($message = '', $sql = '')
+    {
+        $error = mysql_error();
+        $errorno = mysql_errno();
+        if ($errorno == 2006 && $this->goneaway-- > 0)
+        {
+            $this->connect($this->dbhost, $this->dbuser, $this->dbpw, $this->dbname, $this->dbcharset, $this->pconnect, $this->tablepre, $this->time);
+            $this->query($sql);
+        }
+        else
+        {
+            $s = '';
+            if ($message)
+            {
+                $s = "<b>UCenter info:</b> $message<br />";
+            }
+            if ($sql)
+            {
+                $s .= '<b>SQL:</b>' . htmlspecialchars($sql) . '<br />';
+            }
+            $s .= '<b>Error:</b>' . $error . '<br />';
+            $s .= '<b>Errno:</b>' . $errorno . '<br />';
+            $s = str_replace(UC_DBTABLEPRE, '[Table]', $s);
+            exit($s);
+        }
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/lib/dbi.class.php b/extend/fast/ucenter/client/uc_client/lib/dbi.class.php
new file mode 100644
index 0000000..2972e92
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/lib/dbi.class.php
@@ -0,0 +1,200 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2009 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: db.class.php 922 2009-02-19 01:30:22Z zhaoxiongfei $
+ */
+
+class ucclient_db
+{
+
+    var $querynum = 0;
+    var $link;
+    var $histories;
+    var $dbhost;
+    var $dbuser;
+    var $dbpw;
+    var $dbcharset;
+    var $pconnect;
+    var $tablepre;
+    var $time;
+    var $goneaway = 5;
+
+    function connect($dbhost, $dbuser, $dbpw, $dbname = '', $dbcharset = '', $pconnect = 0, $tablepre = '', $time = 0)
+    {
+        $this->dbhost = $dbhost;
+        $this->dbuser = $dbuser;
+        $this->dbpw = $dbpw;
+        $this->dbname = $dbname;
+        $this->dbcharset = $dbcharset;
+        $this->pconnect = $pconnect;
+        $this->tablepre = $tablepre;
+        $this->time = $time;
+
+        if (!$this->link = new mysqli($dbhost, $dbuser, $dbpw, $dbname))
+        {
+            $this->halt('Can not connect to MySQL server');
+        }
+
+        if ($this->version() > '4.1')
+        {
+            if ($dbcharset)
+            {
+                $this->link->set_charset($dbcharset);
+            }
+
+            if ($this->version() > '5.0.1')
+            {
+                $this->link->query("SET sql_mode=''");
+            }
+        }
+    }
+
+    function fetch_array($query, $result_type = MYSQLI_ASSOC)
+    {
+        return $query ? $query->fetch_array($result_type) : null;
+    }
+
+    function result_first($sql)
+    {
+        $query = $this->query($sql);
+        return $this->result($query, 0);
+    }
+
+    function fetch_first($sql)
+    {
+        $query = $this->query($sql);
+        return $this->fetch_array($query);
+    }
+
+    function fetch_all($sql, $id = '')
+    {
+        $arr = array();
+        $query = $this->query($sql);
+        while ($data = $this->fetch_array($query))
+        {
+            $id ? $arr[$data[$id]] = $data : $arr[] = $data;
+        }
+        return $arr;
+    }
+
+    function cache_gc()
+    {
+        $this->query("DELETE FROM {$this->tablepre}sqlcaches WHERE expiry<$this->time");
+    }
+
+    function query($sql, $type = '', $cachetime = FALSE)
+    {
+        $resultmode = $type == 'UNBUFFERED' ? MYSQLI_USE_RESULT : MYSQLI_STORE_RESULT;
+        if (!($query = $this->link->query($sql, $resultmode)) && $type != 'SILENT')
+        {
+            $this->halt('MySQL Query Error', $sql);
+        }
+        $this->querynum++;
+        $this->histories[] = $sql;
+        return $query;
+    }
+
+    function affected_rows()
+    {
+        return $this->link->affected_rows;
+    }
+
+    function error()
+    {
+        return (($this->link) ? $this->link->error : mysqli_error());
+    }
+
+    function errno()
+    {
+        return intval(($this->link) ? $this->link->errno : mysqli_errno());
+    }
+
+    function result($query, $row)
+    {
+        if (!$query || $query->num_rows == 0)
+        {
+            return null;
+        }
+        $query->data_seek($row);
+        $assocs = $query->fetch_row();
+        return $assocs[0];
+    }
+
+    function num_rows($query)
+    {
+        $query = $query ? $query->num_rows : 0;
+        return $query;
+    }
+
+    function num_fields($query)
+    {
+        return $query ? $query->field_count : 0;
+    }
+
+    function free_result($query)
+    {
+        return $query ? $query->free() : false;
+    }
+
+    function insert_id()
+    {
+        return ($id = $this->link->insert_id) >= 0 ? $id : $this->result($this->query("SELECT last_insert_id()"), 0);
+    }
+
+    function fetch_row($query)
+    {
+        $query = $query ? $query->fetch_row() : null;
+        return $query;
+    }
+
+    function fetch_fields($query)
+    {
+        return $query ? $query->fetch_field() : null;
+    }
+
+    function version()
+    {
+        return $this->link->server_info;
+    }
+
+    function escape_string($str)
+    {
+        return $this->link->escape_string($str);
+    }
+
+    function close()
+    {
+        return $this->link->close();
+    }
+
+    function halt($message = '', $sql = '')
+    {
+        $error = $this->error();
+        $errorno = $this->errno();
+        if ($errorno == 2006 && $this->goneaway-- > 0)
+        {
+            $this->connect($this->dbhost, $this->dbuser, $this->dbpw, $this->dbname, $this->dbcharset, $this->pconnect, $this->tablepre, $this->time);
+            $this->query($sql);
+        }
+        else
+        {
+            $s = '';
+            if ($message)
+            {
+                $s = "<b>UCenter info:</b> $message<br />";
+            }
+            if ($sql)
+            {
+                $s .= '<b>SQL:</b>' . htmlspecialchars($sql) . '<br />';
+            }
+            $s .= '<b>Error:</b>' . $error . '<br />';
+            $s .= '<b>Errno:</b>' . $errorno . '<br />';
+            $s = str_replace(UC_DBTABLEPRE, '[Table]', $s);
+            exit($s);
+        }
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/lib/index.htm b/extend/fast/ucenter/client/uc_client/lib/index.htm
new file mode 100644
index 0000000..0519ecb
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/lib/index.htm
@@ -0,0 +1 @@
+ 
\ No newline at end of file
diff --git a/extend/fast/ucenter/client/uc_client/lib/sendmail.inc.php b/extend/fast/ucenter/client/uc_client/lib/sendmail.inc.php
new file mode 100644
index 0000000..dcc165d
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/lib/sendmail.inc.php
@@ -0,0 +1,159 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: sendmail.inc.php 1124 2011-12-06 11:03:23Z svn_project_zhangjie $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+if ($mail_setting['mailsilent'])
+{
+    error_reporting(0);
+}
+
+$maildelimiter = $mail_setting['maildelimiter'] == 1 ? "\r\n" : ($mail_setting['maildelimiter'] == 2 ? "\r" : "\n");
+$mailusername = isset($mail_setting['mailusername']) ? $mail_setting['mailusername'] : 1;
+$appname = $this->base->cache['apps'][$mail['appid']]['name'];
+$mail['subject'] = '=?' . $mail['charset'] . '?B?' . base64_encode(str_replace("\r", '', str_replace("\n", '', '[' . $appname . '] ' . $mail['subject']))) . '?=';
+$mail['message'] = chunk_split(base64_encode(str_replace("\r\n.", " \r\n..", str_replace("\n", "\r\n", str_replace("\r", "\n", str_replace("\r\n", "\n", str_replace("\n\r", "\r", $mail['message'])))))));
+
+$email_from = $mail['frommail'] == '' ? '=?' . $mail['charset'] . '?B?' . base64_encode($appname) . "?= <$mail_setting[maildefault]>" : (preg_match('/^(.+?) \<(.+?)\>$/', $email_from, $from) ? '=?' . $mail['charset'] . '?B?' . base64_encode($from[1]) . "?= <$from[2]>" : $mail['frommail']);
+
+foreach (explode(',', $mail['email_to']) as $touser)
+{
+    $tousers[] = preg_match('/^(.+?) \<(.+?)\>$/', $touser, $to) ? ($mailusername ? '=?' . $mail['charset'] . '?B?' . base64_encode($to[1]) . "?= <$to[2]>" : $to[2]) : $touser;
+}
+
+$mail['email_to'] = implode(',', $tousers);
+
+$headers = "From: $email_from{$maildelimiter}X-Priority: 3{$maildelimiter}X-Mailer: Discuz! $version{$maildelimiter}MIME-Version: 1.0{$maildelimiter}Content-type: text/" . ($mail['htmlon'] ? 'html' : 'plain') . "; charset=$mail[charset]{$maildelimiter}Content-Transfer-Encoding: base64{$maildelimiter}";
+
+$mail_setting['mailport'] = $mail_setting['mailport'] ? $mail_setting['mailport'] : 25;
+
+if ($mail_setting['mailsend'] == 1 && function_exists('mail'))
+{
+
+    return @mail($mail['email_to'], $mail['subject'], $mail['message'], $headers);
+}
+elseif ($mail_setting['mailsend'] == 2)
+{
+
+    if (!$fp = fsocketopen($mail_setting['mailserver'], $mail_setting['mailport'], $errno, $errstr, 30))
+    {
+        return false;
+    }
+
+    stream_set_blocking($fp, true);
+
+    $lastmessage = fgets($fp, 512);
+    if (substr($lastmessage, 0, 3) != '220')
+    {
+        return false;
+    }
+
+    fputs($fp, ($mail_setting['mailauth'] ? 'EHLO' : 'HELO') . " discuz\r\n");
+    $lastmessage = fgets($fp, 512);
+    if (substr($lastmessage, 0, 3) != 220 && substr($lastmessage, 0, 3) != 250)
+    {
+        return false;
+    }
+
+    while (1)
+    {
+        if (substr($lastmessage, 3, 1) != '-' || empty($lastmessage))
+        {
+            break;
+        }
+        $lastmessage = fgets($fp, 512);
+    }
+
+    if ($mail_setting['mailauth'])
+    {
+        fputs($fp, "AUTH LOGIN\r\n");
+        $lastmessage = fgets($fp, 512);
+        if (substr($lastmessage, 0, 3) != 334)
+        {
+            return false;
+        }
+
+        fputs($fp, base64_encode($mail_setting['mailauth_username']) . "\r\n");
+        $lastmessage = fgets($fp, 512);
+        if (substr($lastmessage, 0, 3) != 334)
+        {
+            return false;
+        }
+
+        fputs($fp, base64_encode($mail_setting['mailauth_password']) . "\r\n");
+        $lastmessage = fgets($fp, 512);
+        if (substr($lastmessage, 0, 3) != 235)
+        {
+            return false;
+        }
+
+        $email_from = $mail_setting['mailfrom'];
+    }
+
+    fputs($fp, "MAIL FROM: <" . preg_replace("/.*\<(.+?)\>.*/", "\\1", $email_from) . ">\r\n");
+    $lastmessage = fgets($fp, 512);
+    if (substr($lastmessage, 0, 3) != 250)
+    {
+        fputs($fp, "MAIL FROM: <" . preg_replace("/.*\<(.+?)\>.*/", "\\1", $email_from) . ">\r\n");
+        $lastmessage = fgets($fp, 512);
+        if (substr($lastmessage, 0, 3) != 250)
+        {
+            return false;
+        }
+    }
+
+    $email_tos = array();
+    foreach (explode(',', $mail['email_to']) as $touser)
+    {
+        $touser = trim($touser);
+        if ($touser)
+        {
+            fputs($fp, "RCPT TO: <" . preg_replace("/.*\<(.+?)\>.*/", "\\1", $touser) . ">\r\n");
+            $lastmessage = fgets($fp, 512);
+            if (substr($lastmessage, 0, 3) != 250)
+            {
+                fputs($fp, "RCPT TO: <" . preg_replace("/.*\<(.+?)\>.*/", "\\1", $touser) . ">\r\n");
+                $lastmessage = fgets($fp, 512);
+                return false;
+            }
+        }
+    }
+
+    fputs($fp, "DATA\r\n");
+    $lastmessage = fgets($fp, 512);
+    if (substr($lastmessage, 0, 3) != 354)
+    {
+        return false;
+    }
+
+    $headers .= 'Message-ID: <' . gmdate('YmdHs') . '.' . substr(md5($mail['message'] . microtime()), 0, 6) . rand(100000, 999999) . '@' . $_SERVER['HTTP_HOST'] . ">{$maildelimiter}";
+
+    fputs($fp, "Date: " . gmdate('r') . "\r\n");
+    fputs($fp, "To: " . $mail['email_to'] . "\r\n");
+    fputs($fp, "Subject: " . $mail['subject'] . "\r\n");
+    fputs($fp, $headers . "\r\n");
+    fputs($fp, "\r\n\r\n");
+    fputs($fp, "$mail[message]\r\n.\r\n");
+    $lastmessage = fgets($fp, 512);
+    if (substr($lastmessage, 0, 3) != 250)
+    {
+        return false;
+    }
+
+    fputs($fp, "QUIT\r\n");
+    return true;
+}
+elseif ($mail_setting['mailsend'] == 3)
+{
+
+    ini_set('SMTP', $mail_setting['mailserver']);
+    ini_set('smtp_port', $mail_setting['mailport']);
+    ini_set('sendmail_from', $email_from);
+
+    return @mail($mail['email_to'], $mail['subject'], $mail['message'], $headers);
+}
\ No newline at end of file
diff --git a/extend/fast/ucenter/client/uc_client/lib/uccode.class.php b/extend/fast/ucenter/client/uc_client/lib/uccode.class.php
new file mode 100644
index 0000000..47150b5
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/lib/uccode.class.php
@@ -0,0 +1,174 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: uccode.class.php 1166 2014-11-03 01:49:32Z hypowang $
+ */
+
+class uccode
+{
+
+    var $uccodes;
+
+    function __construct()
+    {
+        $this->uccode();
+    }
+
+    function uccode()
+    {
+        $this->uccode = array(
+            'pcodecount' => -1,
+            'codecount'  => 0,
+            'codehtml'   => ''
+        );
+    }
+
+    function codedisp($code)
+    {
+        $this->uccode['pcodecount'] ++;
+        $code = str_replace('\\"', '"', preg_replace("/^[\n\r]*(.+?)[\n\r]*$/is", "\\1", $code));
+        $this->uccode['codehtml'][$this->uccode['pcodecount']] = $this->tpl_codedisp($code);
+        $this->uccode['codecount'] ++;
+        return "[\tUCENTER_CODE_" . $this->uccode[pcodecount] . "\t]";
+    }
+
+    function complie($message)
+    {
+        $message = dhtmlspecialchars($message);
+        if (strpos($message, '[/code]') !== FALSE)
+        {
+            $message = preg_replace("/\s*\[code\](.+?)\[\/code\]\s*/ies", "\$this->codedisp('\\1')", $message);
+        }
+        if (strpos($message, '[/url]') !== FALSE)
+        {
+            $message = preg_replace("/\[url(=((https?|ftp|gopher|news|telnet|rtsp|mms|callto|bctp|ed2k|thunder|synacast){1}:\/\/|www\.)([^\[\"']+?))?\](.+?)\[\/url\]/ies", "\$this->parseurl('\\1', '\\5')", $message);
+        }
+        if (strpos($message, '[/email]') !== FALSE)
+        {
+            $message = preg_replace("/\[email(=([a-z0-9\-_.+]+)@([a-z0-9\-_]+[.][a-z0-9\-_.]+))?\](.+?)\[\/email\]/ies", "\$this->parseemail('\\1', '\\4')", $message);
+        }
+        $message = str_replace(array(
+            '[/color]', '[/size]', '[/font]', '[/align]', '[b]', '[/b]',
+            '[i]', '[/i]', '[u]', '[/u]', '[list]', '[list=1]', '[list=a]',
+            '[list=A]', '[*]', '[/list]', '[indent]', '[/indent]', '[/float]'
+                ), array(
+            '</font>', '</font>', '</font>', '</p>', '<strong>', '</strong>', '<i>',
+            '</i>', '<u>', '</u>', '<ul>', '<ul type="1">', '<ul type="a">',
+            '<ul type="A">', '<li>', '</ul>', '<blockquote>', '</blockquote>', '</span>'
+                ), preg_replace(array(
+            "/\[color=([#\w]+?)\]/i",
+            "/\[size=(\d+?)\]/i",
+            "/\[size=(\d+(\.\d+)?(px|pt|in|cm|mm|pc|em|ex|%)+?)\]/i",
+            "/\[font=([^\[\<]+?)\]/i",
+            "/\[align=(left|center|right)\]/i",
+            "/\[float=(left|right)\]/i"
+                        ), array(
+            "<font color=\"\\1\">",
+            "<font size=\"\\1\">",
+            "<font style=\"font-size: \\1\">",
+            "<font face=\"\\1 \">",
+            "<p align=\"\\1\">",
+            "<span style=\"float: \\1;\">"
+                        ), $message));
+        if (strpos($message, '[/quote]') !== FALSE)
+        {
+            $message = preg_replace("/\s*\[quote\][\n\r]*(.+?)[\n\r]*\[\/quote\]\s*/is", $this->tpl_quote(), $message);
+        }
+        if (strpos($message, '[/img]') !== FALSE)
+        {
+            $message = preg_replace(array(
+                "/\[img\]\s*([^\[\<\r\n]+?)\s*\[\/img\]/ies",
+                "/\[img=(\d{1,4})[x|\,](\d{1,4})\]\s*([^\[\<\r\n]+?)\s*\[\/img\]/ies"
+                    ), array(
+                "\$this->bbcodeurl('\\1', '<img src=\"%s\" border=\"0\" alt=\"\" />')",
+                "\$this->bbcodeurl('\\3', '<img width=\"\\1\" height=\"\\2\" src=\"%s\" border=\"0\" alt=\"\" />')"
+                    ), $message);
+        }
+        for ($i = 0; $i <= $this->uccode['pcodecount']; $i++)
+        {
+            $message = str_replace("[\tUCENTER_CODE_$i\t]", $this->uccode['codehtml'][$i], $message);
+        }
+        return nl2br(str_replace(array("\t", '   ', '  '), array('&nbsp; &nbsp; &nbsp; &nbsp; ', '&nbsp; &nbsp;', '&nbsp;&nbsp;'), $message));
+    }
+
+    function parseurl($url, $text)
+    {
+        if (!$url && preg_match("/((https?|ftp|gopher|news|telnet|rtsp|mms|callto|bctp|ed2k|thunder|synacast){1}:\/\/|www\.)[^\[\"']+/i", trim($text), $matches))
+        {
+            $url = $matches[0];
+            $length = 65;
+            if (strlen($url) > $length)
+            {
+                $text = substr($url, 0, intval($length * 0.5)) . ' ... ' . substr($url, - intval($length * 0.3));
+            }
+            return '<a href="' . (substr(strtolower($url), 0, 4) == 'www.' ? 'http://' . $url : $url) . '" target="_blank">' . $text . '</a>';
+        }
+        else
+        {
+            $url = substr($url, 1);
+            if (substr(strtolower($url), 0, 4) == 'www.')
+            {
+                $url = 'http://' . $url;
+            }
+            return '<a href="' . $url . '" target="_blank">' . $text . '</a>';
+        }
+    }
+
+    function parseemail($email, $text)
+    {
+        $text = str_replace('\"', '"', $text);
+        if (!$email && preg_match("/\s*([a-z0-9\-_.+]+)@([a-z0-9\-_]+[.][a-z0-9\-_.]+)\s*/i", $text, $matches))
+        {
+            $email = trim($matches[0]);
+            return '<a href="mailto:' . $email . '">' . $email . '</a>';
+        }
+        else
+        {
+            return '<a href="mailto:' . substr($email, 1) . '">' . $text . '</a>';
+        }
+    }
+
+    function bbcodeurl($url, $tags)
+    {
+        if (!preg_match("/<.+?>/s", $url))
+        {
+            if (!in_array(strtolower(substr($url, 0, 6)), array('http:/', 'https:', 'ftp://', 'rtsp:/', 'mms://')))
+            {
+                $url = 'http://' . $url;
+            }
+            return str_replace(array('submit', 'logging.php'), array('', ''), sprintf($tags, $url, addslashes($url)));
+        }
+        else
+        {
+            return '&nbsp;' . $url;
+        }
+    }
+
+    function tpl_codedisp($code)
+    {
+        return '<div class="blockcode"><code id="code' . $this->uccodes['codecount'] . '">' . $code . '</code></div>';
+    }
+
+    function tpl_quote()
+    {
+        return '<div class="quote"><blockquote>\\1</blockquote></div>';
+    }
+
+}
+
+/*
+
+Usage:
+$str = <<<EOF
+1
+2
+3
+EOF;
+require_once 'lib/uccode.class.php';
+$this->uccode = new uccode();
+echo $this->uccode->complie($str);
+
+*/
\ No newline at end of file
diff --git a/extend/fast/ucenter/client/uc_client/lib/xml.class.php b/extend/fast/ucenter/client/uc_client/lib/xml.class.php
new file mode 100644
index 0000000..73c528b
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/lib/xml.class.php
@@ -0,0 +1,129 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: xml.class.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+
+function xml_unserialize(&$xml, $isnormal = FALSE)
+{
+    $xml_parser = new XML($isnormal);
+    $data = $xml_parser->parse($xml);
+    $xml_parser->destruct();
+    return $data;
+}
+
+function xml_serialize($arr, $htmlon = FALSE, $isnormal = FALSE, $level = 1)
+{
+    $s = $level == 1 ? "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n<root>\r\n" : '';
+    $space = str_repeat("\t", $level);
+    foreach ($arr as $k => $v)
+    {
+        if (!is_array($v))
+        {
+            $s .= $space . "<item id=\"$k\">" . ($htmlon ? '<![CDATA[' : '') . $v . ($htmlon ? ']]>' : '') . "</item>\r\n";
+        }
+        else
+        {
+            $s .= $space . "<item id=\"$k\">\r\n" . xml_serialize($v, $htmlon, $isnormal, $level + 1) . $space . "</item>\r\n";
+        }
+    }
+    $s = preg_replace("/([\x01-\x08\x0b-\x0c\x0e-\x1f])+/", ' ', $s);
+    return $level == 1 ? $s . "</root>" : $s;
+}
+
+class XML
+{
+
+    var $parser;
+    var $document;
+    var $stack;
+    var $data;
+    var $last_opened_tag;
+    var $isnormal;
+    var $attrs = array();
+    var $failed = FALSE;
+
+    function __construct($isnormal)
+    {
+        $this->XML($isnormal);
+    }
+
+    function XML($isnormal)
+    {
+        $this->isnormal = $isnormal;
+        $this->parser = xml_parser_create('ISO-8859-1');
+        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
+        xml_set_object($this->parser, $this);
+        xml_set_element_handler($this->parser, 'open', 'close');
+        xml_set_character_data_handler($this->parser, 'data');
+    }
+
+    function destruct()
+    {
+        xml_parser_free($this->parser);
+    }
+
+    function parse(&$data)
+    {
+        $this->document = array();
+        $this->stack = array();
+        return xml_parse($this->parser, $data, true) && !$this->failed ? $this->document : '';
+    }
+
+    function open(&$parser, $tag, $attributes)
+    {
+        $this->data = '';
+        $this->failed = FALSE;
+        if (!$this->isnormal)
+        {
+            if (isset($attributes['id']) && !is_string($this->document[$attributes['id']]))
+            {
+                $this->document = &$this->document[$attributes['id']];
+            }
+            else
+            {
+                $this->failed = TRUE;
+            }
+        }
+        else
+        {
+            if (!isset($this->document[$tag]) || !is_string($this->document[$tag]))
+            {
+                $this->document = &$this->document[$tag];
+            }
+            else
+            {
+                $this->failed = TRUE;
+            }
+        }
+        $this->stack[] = &$this->document;
+        $this->last_opened_tag = $tag;
+        $this->attrs = $attributes;
+    }
+
+    function data(&$parser, $data)
+    {
+        if ($this->last_opened_tag != NULL)
+        {
+            $this->data .= $data;
+        }
+    }
+
+    function close(&$parser, $tag)
+    {
+        if ($this->last_opened_tag == $tag)
+        {
+            $this->document = $this->data;
+            $this->last_opened_tag = NULL;
+        }
+        array_pop($this->stack);
+        if ($this->stack)
+        {
+            $this->document = &$this->stack[count($this->stack) - 1];
+        }
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/model/app.php b/extend/fast/ucenter/client/uc_client/model/app.php
new file mode 100644
index 0000000..a3fdac5
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/app.php
@@ -0,0 +1,40 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: app.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class appmodel
+{
+
+    var $db;
+    var $base;
+
+    function __construct(&$base)
+    {
+        $this->appmodel($base);
+    }
+
+    function appmodel(&$base)
+    {
+        $this->base = $base;
+        $this->db = $base->db;
+    }
+
+    function get_apps($col = '*', $where = '')
+    {
+        $arr = $this->db->fetch_all("SELECT $col FROM " . UC_DBTABLEPRE . "applications" . ($where ? ' WHERE ' . $where : ''), 'appid');
+        foreach ($arr as $k => $v)
+        {
+            isset($v['extra']) && !empty($v['extra']) && $v['extra'] = unserialize($v['extra']);
+            unset($v['authkey']);
+            $arr[$k] = $v;
+        }
+        return $arr;
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/model/base.php b/extend/fast/ucenter/client/uc_client/model/base.php
new file mode 100644
index 0000000..32dcf16
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/base.php
@@ -0,0 +1,347 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: base.php 1167 2014-11-03 03:06:21Z hypowang $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+if (!function_exists('getgpc'))
+{
+
+    function getgpc($k, $var = 'G')
+    {
+        switch ($var)
+        {
+            case 'G': $var = &$_GET;
+                break;
+            case 'P': $var = &$_POST;
+                break;
+            case 'C': $var = &$_COOKIE;
+                break;
+            case 'R': $var = &$_REQUEST;
+                break;
+        }
+        return isset($var[$k]) ? $var[$k] : NULL;
+    }
+
+}
+
+class base
+{
+
+    var $sid;
+    var $time;
+    var $onlineip;
+    var $db;
+    var $key;
+    var $settings = array();
+    var $cache = array();
+    var $app = array();
+    var $user = array();
+    var $input = array();
+
+    function __construct()
+    {
+        $this->base();
+    }
+
+    function base()
+    {
+        $this->init_var();
+        $this->init_db();
+        $this->init_cache();
+        $this->init_note();
+        $this->init_mail();
+    }
+
+    function init_var()
+    {
+        $this->time = time();
+        $cip = getenv('HTTP_CLIENT_IP');
+        $xip = getenv('HTTP_X_FORWARDED_FOR');
+        $rip = getenv('REMOTE_ADDR');
+        $srip = $_SERVER['REMOTE_ADDR'];
+        if ($cip && strcasecmp($cip, 'unknown'))
+        {
+            $this->onlineip = $cip;
+        }
+        elseif ($xip && strcasecmp($xip, 'unknown'))
+        {
+            $this->onlineip = $xip;
+        }
+        elseif ($rip && strcasecmp($rip, 'unknown'))
+        {
+            $this->onlineip = $rip;
+        }
+        elseif ($srip && strcasecmp($srip, 'unknown'))
+        {
+            $this->onlineip = $srip;
+        }
+        preg_match("/[\d\.]{7,15}/", $this->onlineip, $match);
+        $this->onlineip = $match[0] ? $match[0] : 'unknown';
+        $this->app['appid'] = UC_APPID;
+    }
+
+    function init_input()
+    {
+
+    }
+
+    function init_db()
+    {
+        if (function_exists("mysqli_connect"))
+        {
+            require_once UC_ROOT . 'lib/dbi.class.php';
+        }
+        else
+        {
+            require_once UC_ROOT . 'lib/db.class.php';
+        }
+        $this->db = new ucclient_db();
+        $this->db->connect(UC_DBHOST, UC_DBUSER, UC_DBPW, '', UC_DBCHARSET, UC_DBCONNECT, UC_DBTABLEPRE);
+    }
+
+    function load($model, $base = NULL)
+    {
+        $base = $base ? $base : $this;
+        if (empty($_ENV[$model]))
+        {
+            require_once UC_ROOT . "./model/{$model}.php";
+            $classname = $model . 'model';
+            $_ENV[$model] = new $classname($base);
+        }
+        return $_ENV[$model];
+    }
+
+    function date($time, $type = 3)
+    {
+        if (!$this->settings)
+        {
+            $this->settings = $this->cache('settings');
+        }
+        $format[] = $type & 2 ? (!empty($this->settings['dateformat']) ? $this->settings['dateformat'] : 'Y-n-j') : '';
+        $format[] = $type & 1 ? (!empty($this->settings['timeformat']) ? $this->settings['timeformat'] : 'H:i') : '';
+        return gmdate(implode(' ', $format), $time + $this->settings['timeoffset']);
+    }
+
+    function page_get_start($page, $ppp, $totalnum)
+    {
+        $totalpage = ceil($totalnum / $ppp);
+        $page = max(1, min($totalpage, intval($page)));
+        return ($page - 1) * $ppp;
+    }
+
+    function implode($arr)
+    {
+        return "'" . implode("','", (array) $arr) . "'";
+    }
+
+    function &cache($cachefile)
+    {
+        static $_CACHE = array();
+        if (!isset($_CACHE[$cachefile]))
+        {
+            $cachepath = UC_DATADIR . './cache/' . $cachefile . '.php';
+            if (!file_exists($cachepath))
+            {
+                $this->load('cache');
+                $_ENV['cache']->updatedata($cachefile);
+            }
+            else
+            {
+                include_once $cachepath;
+            }
+        }
+        return $_CACHE[$cachefile];
+    }
+
+    function get_setting($k = array(), $decode = FALSE)
+    {
+        $return = array();
+        $sqladd = $k ? "WHERE k IN (" . $this->implode($k) . ")" : '';
+        $settings = $this->db->fetch_all("SELECT * FROM " . UC_DBTABLEPRE . "settings $sqladd");
+        if (is_array($settings))
+        {
+            foreach ($settings as $arr)
+            {
+                $return[$arr['k']] = $decode ? unserialize($arr['v']) : $arr['v'];
+            }
+        }
+        return $return;
+    }
+
+    function init_cache()
+    {
+        $this->settings = $this->cache('settings');
+        $this->cache['apps'] = $this->cache('apps');
+
+        if (PHP_VERSION > '5.1')
+        {
+            $timeoffset = intval($this->settings['timeoffset'] / 3600);
+            @date_default_timezone_set('Etc/GMT' . ($timeoffset > 0 ? '-' : '+') . (abs($timeoffset)));
+        }
+    }
+
+    function cutstr($string, $length, $dot = ' ...')
+    {
+        if (strlen($string) <= $length)
+        {
+            return $string;
+        }
+
+        $string = str_replace(array('&amp;', '&quot;', '&lt;', '&gt;'), array('&', '"', '<', '>'), $string);
+
+        $strcut = '';
+        if (strtolower(UC_CHARSET) == 'utf-8')
+        {
+
+            $n = $tn = $noc = 0;
+            while ($n < strlen($string))
+            {
+
+                $t = ord($string[$n]);
+                if ($t == 9 || $t == 10 || (32 <= $t && $t <= 126))
+                {
+                    $tn = 1;
+                    $n++;
+                    $noc++;
+                }
+                elseif (194 <= $t && $t <= 223)
+                {
+                    $tn = 2;
+                    $n += 2;
+                    $noc += 2;
+                }
+                elseif (224 <= $t && $t < 239)
+                {
+                    $tn = 3;
+                    $n += 3;
+                    $noc += 2;
+                }
+                elseif (240 <= $t && $t <= 247)
+                {
+                    $tn = 4;
+                    $n += 4;
+                    $noc += 2;
+                }
+                elseif (248 <= $t && $t <= 251)
+                {
+                    $tn = 5;
+                    $n += 5;
+                    $noc += 2;
+                }
+                elseif ($t == 252 || $t == 253)
+                {
+                    $tn = 6;
+                    $n += 6;
+                    $noc += 2;
+                }
+                else
+                {
+                    $n++;
+                }
+
+                if ($noc >= $length)
+                {
+                    break;
+                }
+            }
+            if ($noc > $length)
+            {
+                $n -= $tn;
+            }
+
+            $strcut = substr($string, 0, $n);
+        }
+        else
+        {
+            for ($i = 0; $i < $length; $i++)
+            {
+                $strcut .= ord($string[$i]) > 127 ? $string[$i] . $string[++$i] : $string[$i];
+            }
+        }
+
+        $strcut = str_replace(array('&', '"', '<', '>'), array('&amp;', '&quot;', '&lt;', '&gt;'), $strcut);
+
+        return $strcut . $dot;
+    }
+
+    function init_note()
+    {
+        if ($this->note_exists())
+        {
+            $this->load('note');
+            $_ENV['note']->send();
+        }
+    }
+
+    function note_exists()
+    {
+        $noteexists = $this->db->result_first("SELECT value FROM " . UC_DBTABLEPRE . "vars WHERE name='noteexists" . UC_APPID . "'");
+        if (empty($noteexists))
+        {
+            return FALSE;
+        }
+        else
+        {
+            return TRUE;
+        }
+    }
+
+    function init_mail()
+    {
+        if ($this->mail_exists() && !getgpc('inajax'))
+        {
+            $this->load('mail');
+            $_ENV['mail']->send();
+        }
+    }
+
+    function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0)
+    {
+        return uc_authcode($string, $operation, $key, $expiry);
+    }
+
+    function unserialize($s)
+    {
+        return uc_unserialize($s);
+    }
+
+    function input($k)
+    {
+        return isset($this->input[$k]) ? (is_array($this->input[$k]) ? $this->input[$k] : trim($this->input[$k])) : NULL;
+    }
+
+    function mail_exists()
+    {
+        $mailexists = $this->db->result_first("SELECT value FROM " . UC_DBTABLEPRE . "vars WHERE name='mailexists'");
+        if (empty($mailexists))
+        {
+            return FALSE;
+        }
+        else
+        {
+            return TRUE;
+        }
+    }
+
+    function dstripslashes($string)
+    {
+        if (is_array($string))
+        {
+            foreach ($string as $key => $val)
+            {
+                $string[$key] = $this->dstripslashes($val);
+            }
+        }
+        else
+        {
+            $string = stripslashes($string);
+        }
+        return $string;
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/model/cache.php b/extend/fast/ucenter/client/uc_client/model/cache.php
new file mode 100644
index 0000000..d0851ed
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/cache.php
@@ -0,0 +1,119 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: cache.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+if (!function_exists('file_put_contents'))
+{
+
+    function file_put_contents($filename, $s)
+    {
+        $fp = @fopen($filename, 'w');
+        @fwrite($fp, $s);
+        @fclose($fp);
+    }
+
+}
+
+class cachemodel
+{
+
+    var $db;
+    var $base;
+    var $map;
+
+    function __construct(&$base)
+    {
+        $this->cachemodel($base);
+    }
+
+    function cachemodel(&$base)
+    {
+        $this->base = $base;
+        $this->db = $base->db;
+        $this->map = array(
+            'settings' => array('settings'),
+            'badwords' => array('badwords'),
+            'apps'     => array('apps')
+        );
+    }
+
+    function updatedata($cachefile = '')
+    {
+        if ($cachefile)
+        {
+            foreach ((array) $this->map[$cachefile] as $modules)
+            {
+                $s = "<?php\r\n";
+                foreach ((array) $modules as $m)
+                {
+                    $method = "_get_$m";
+                    $s .= '$_CACHE[\'' . $m . '\'] = ' . var_export($this->$method(), TRUE) . ";\r\n";
+                }
+                $s .= "\r\n?>";
+                @file_put_contents(UC_DATADIR . "./cache/$cachefile.php", $s);
+            }
+        }
+        else
+        {
+            foreach ((array) $this->map as $file => $modules)
+            {
+                $s = "<?php\r\n";
+                foreach ($modules as $m)
+                {
+                    $method = "_get_$m";
+                    $s .= '$_CACHE[\'' . $m . '\'] = ' . var_export($this->$method(), TRUE) . ";\r\n";
+                }
+                $s .= "\r\n?>";
+                @file_put_contents(UC_DATADIR . "./cache/$file.php", $s);
+            }
+        }
+    }
+
+    function updatetpl()
+    {
+
+    }
+
+    function _get_badwords()
+    {
+        $data = $this->db->fetch_all("SELECT * FROM " . UC_DBTABLEPRE . "badwords");
+        $return = array();
+        if (is_array($data))
+        {
+            foreach ($data as $k => $v)
+            {
+                $return['findpattern'][$k] = $v['findpattern'];
+                $return['replace'][$k] = $v['replacement'];
+            }
+        }
+        return $return;
+    }
+
+    function _get_apps()
+    {
+        $this->base->load('app');
+        $apps = $_ENV['app']->get_apps();
+        $apps2 = array();
+        if (is_array($apps))
+        {
+            foreach ($apps as $v)
+            {
+                $v['extra'] = unserialize($v['extra']);
+                $apps2[$v['appid']] = $v;
+            }
+        }
+        return $apps2;
+    }
+
+    function _get_settings()
+    {
+        return $this->base->get_setting();
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/model/domain.php b/extend/fast/ucenter/client/uc_client/model/domain.php
new file mode 100644
index 0000000..6fc966f
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/domain.php
@@ -0,0 +1,63 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: domain.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class domainmodel
+{
+
+    var $db;
+    var $base;
+
+    function __construct(&$base)
+    {
+        $this->domainmodel($base);
+    }
+
+    function domainmodel(&$base)
+    {
+        $this->base = $base;
+        $this->db = $base->db;
+    }
+
+    function add_domain($domain, $ip)
+    {
+        if ($domain)
+        {
+            $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "domains SET domain='$domain', ip='$ip'");
+        }
+        return $this->db->insert_id();
+    }
+
+    function get_total_num()
+    {
+        $data = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "domains");
+        return $data;
+    }
+
+    function get_list($page, $ppp, $totalnum)
+    {
+        $start = $this->base->page_get_start($page, $ppp, $totalnum);
+        $data = $this->db->fetch_all("SELECT * FROM " . UC_DBTABLEPRE . "domains LIMIT $start, $ppp");
+        return $data;
+    }
+
+    function delete_domain($arr)
+    {
+        $domainids = $this->base->implode($arr);
+        $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "domains WHERE id IN ($domainids)");
+        return $this->db->affected_rows();
+    }
+
+    function update_domain($domain, $ip, $id)
+    {
+        $this->db->query("UPDATE " . UC_DBTABLEPRE . "domains SET domain='$domain', ip='$ip' WHERE id='$id'");
+        return $this->db->affected_rows();
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/model/friend.php b/extend/fast/ucenter/client/uc_client/model/friend.php
new file mode 100644
index 0000000..58c641a
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/friend.php
@@ -0,0 +1,148 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: friend.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class friendmodel
+{
+
+    var $db;
+    var $base;
+
+    function __construct(&$base)
+    {
+        $this->friendmodel($base);
+    }
+
+    function friendmodel(&$base)
+    {
+        $this->base = $base;
+        $this->db = $base->db;
+    }
+
+    function add($uid, $friendid, $comment = '')
+    {
+        $direction = $this->db->result_first("SELECT direction FROM " . UC_DBTABLEPRE . "friends WHERE uid='$friendid' AND friendid='$uid' LIMIT 1");
+        if ($direction == 1)
+        {
+            $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "friends SET uid='$uid', friendid='$friendid', comment='$comment', direction='3'", 'SILENT');
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . "friends SET direction='3' WHERE uid='$friendid' AND friendid='$uid'");
+            return 1;
+        }
+        elseif ($direction == 2)
+        {
+            return 1;
+        }
+        elseif ($direction == 3)
+        {
+            return -1;
+        }
+        else
+        {
+            $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "friends SET uid='$uid', friendid='$friendid', comment='$comment', direction='1'", 'SILENT');
+            return $this->db->insert_id();
+        }
+    }
+
+    function delete($uid, $friendids)
+    {
+        $friendids = $this->base->implode($friendids);
+        $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "friends WHERE uid='$uid' AND friendid IN ($friendids)");
+        $affectedrows = $this->db->affected_rows();
+        if ($affectedrows > 0)
+        {
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . "friends SET direction=1 WHERE uid IN ($friendids) AND friendid='$uid' AND direction='3'");
+        }
+        return $affectedrows;
+    }
+
+    function get_totalnum_by_uid($uid, $direction = 0)
+    {
+        $sqladd = '';
+        if ($direction == 0)
+        {
+            $sqladd = "uid='$uid'";
+        }
+        elseif ($direction == 1)
+        {
+            $sqladd = "uid='$uid' AND direction='1'";
+        }
+        elseif ($direction == 2)
+        {
+            $sqladd = "friendid='$uid' AND direction='1'";
+        }
+        elseif ($direction == 3)
+        {
+            $sqladd = "uid='$uid' AND direction='3'";
+        }
+        $totalnum = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "friends WHERE $sqladd");
+        return $totalnum;
+    }
+
+    function get_list($uid, $page, $pagesize, $totalnum, $direction = 0)
+    {
+        $start = $this->base->page_get_start($page, $pagesize, $totalnum);
+        $sqladd = '';
+        if ($direction == 0)
+        {
+            $sqladd = "f.uid='$uid'";
+        }
+        elseif ($direction == 1)
+        {
+            $sqladd = "f.uid='$uid' AND f.direction='1'";
+        }
+        elseif ($direction == 2)
+        {
+            $sqladd = "f.friendid='$uid' AND f.direction='1'";
+        }
+        elseif ($direction == 3)
+        {
+            $sqladd = "f.uid='$uid' AND f.direction='3'";
+        }
+        if ($sqladd)
+        {
+            $data = $this->db->fetch_all("SELECT f.*, m.username FROM " . UC_DBTABLEPRE . "friends f LEFT JOIN " . UC_DBTABLEPRE . "members m ON f.friendid=m.uid WHERE $sqladd LIMIT $start, $pagesize");
+            return $data;
+        }
+        else
+        {
+            return array();
+        }
+    }
+
+    function is_friend($uid, $friendids, $direction = 0)
+    {
+        $friendid_str = implode("', '", $friendids);
+        $sqladd = '';
+        if ($direction == 0)
+        {
+            $sqladd = "uid='$uid'";
+        }
+        elseif ($direction == 1)
+        {
+            $sqladd = "uid='$uid' AND friendid IN ('$friendid_str') AND direction='1'";
+        }
+        elseif ($direction == 2)
+        {
+            $sqladd = "friendid='$uid' AND uid IN ('$friendid_str') AND direction='1'";
+        }
+        elseif ($direction == 3)
+        {
+            $sqladd = "uid='$uid' AND friendid IN ('$friendid_str') AND direction='3'";
+        }
+        if ($this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "friends WHERE $sqladd") == count($friendids))
+        {
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/model/index.htm b/extend/fast/ucenter/client/uc_client/model/index.htm
new file mode 100644
index 0000000..0519ecb
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/index.htm
@@ -0,0 +1 @@
+ 
\ No newline at end of file
diff --git a/extend/fast/ucenter/client/uc_client/model/mail.php b/extend/fast/ucenter/client/uc_client/model/mail.php
new file mode 100644
index 0000000..46e9983
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/mail.php
@@ -0,0 +1,181 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: mail.php 1139 2012-05-08 09:02:11Z liulanbo $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+define('UC_MAIL_REPEAT', 5);
+
+class mailmodel
+{
+
+    var $db;
+    var $base;
+    var $apps;
+
+    function __construct(&$base)
+    {
+        $this->mailmodel($base);
+    }
+
+    function mailmodel(&$base)
+    {
+        $this->base = $base;
+        $this->db = $base->db;
+        $this->apps = &$this->base->cache['apps'];
+    }
+
+    function get_total_num()
+    {
+        $data = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "mailqueue");
+        return $data;
+    }
+
+    function get_list($page, $ppp, $totalnum)
+    {
+        $start = $this->base->page_get_start($page, $ppp, $totalnum);
+        $data = $this->db->fetch_all("SELECT m.*, u.username, u.email FROM " . UC_DBTABLEPRE . "mailqueue m LEFT JOIN " . UC_DBTABLEPRE . "members u ON m.touid=u.uid ORDER BY dateline DESC LIMIT $start, $ppp");
+        foreach ((array) $data as $k => $v)
+        {
+            $data[$k]['subject'] = dhtmlspecialchars($v['subject']);
+            $data[$k]['tomail'] = empty($v['tomail']) ? $v['email'] : $v['tomail'];
+            $data[$k]['dateline'] = $v['dateline'] ? $this->base->date($data[$k]['dateline']) : '';
+            $data[$k]['appname'] = $this->base->cache['apps'][$v['appid']]['name'];
+        }
+        return $data;
+    }
+
+    function delete_mail($ids)
+    {
+        $ids = $this->base->implode($ids);
+        $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "mailqueue WHERE mailid IN ($ids)");
+        return $this->db->affected_rows();
+    }
+
+    function add($mail)
+    {
+        if ($mail['level'])
+        {
+            $sql = "INSERT INTO " . UC_DBTABLEPRE . "mailqueue (touid, tomail, subject, message, frommail, charset, htmlon, level, dateline, failures, appid) VALUES ";
+            $values_arr = array();
+            foreach ($mail['uids'] as $uid)
+            {
+                if (empty($uid))
+                    continue;
+                $values_arr[] = "('$uid', '', '$mail[subject]', '$mail[message]', '$mail[frommail]', '$mail[charset]', '$mail[htmlon]', '$mail[level]', '$mail[dateline]', '0', '$mail[appid]')";
+            }
+            foreach ($mail['emails'] as $email)
+            {
+                if (empty($email))
+                    continue;
+                $values_arr[] = "('', '$email', '$mail[subject]', '$mail[message]', '$mail[frommail]', '$mail[charset]', '$mail[htmlon]', '$mail[level]', '$mail[dateline]', '0', '$mail[appid]')";
+            }
+            $sql .= implode(',', $values_arr);
+            $this->db->query($sql);
+            $insert_id = $this->db->insert_id();
+            $insert_id && $this->db->query("REPLACE INTO " . UC_DBTABLEPRE . "vars SET name='mailexists', value='1'");
+            return $insert_id;
+        } else
+        {
+            $mail['email_to'] = array();
+            $uids = 0;
+            foreach ($mail['uids'] as $uid)
+            {
+                if (empty($uid))
+                    continue;
+                $uids .= ',' . $uid;
+            }
+            $users = $this->db->fetch_all("SELECT uid, username, email FROM " . UC_DBTABLEPRE . "members WHERE uid IN ($uids)");
+            foreach ($users as $v)
+            {
+                $mail['email_to'][] = $v['username'] . '<' . $v['email'] . '>';
+            }
+            foreach ($mail['emails'] as $email)
+            {
+                if (empty($email))
+                    continue;
+                $mail['email_to'][] = $email;
+            }
+            $mail['message'] = str_replace('\"', '"', $mail['message']);
+            $mail['email_to'] = implode(',', $mail['email_to']);
+            return $this->send_one_mail($mail);
+        }
+    }
+
+    function send()
+    {
+        register_shutdown_function(array($this, '_send'));
+    }
+
+    function _send()
+    {
+
+        $mail = $this->_get_mail();
+        if (empty($mail))
+        {
+            $this->db->query("REPLACE INTO " . UC_DBTABLEPRE . "vars SET name='mailexists', value='0'");
+            return NULL;
+        }
+        else
+        {
+            $mail['email_to'] = $mail['tomail'] ? $mail['tomail'] : $mail['username'] . '<' . $mail['email'] . '>';
+            if ($this->send_one_mail($mail))
+            {
+                $this->_delete_one_mail($mail['mailid']);
+                return true;
+            }
+            else
+            {
+                $this->_update_failures($mail['mailid']);
+                return false;
+            }
+        }
+    }
+
+    function send_by_id($mailid)
+    {
+        if ($this->send_one_mail($this->_get_mail_by_id($mailid)))
+        {
+            $this->_delete_one_mail($mailid);
+            return true;
+        }
+    }
+
+    function send_one_mail($mail)
+    {
+        if (empty($mail))
+            return;
+        $mail['email_to'] = $mail['email_to'] ? $mail['email_to'] : $mail['username'] . '<' . $mail['email'] . '>';
+        $mail_setting = $this->base->settings;
+        return include UC_ROOT . 'lib/sendmail.inc.php';
+    }
+
+    function _get_mail()
+    {
+        $data = $this->db->fetch_first("SELECT m.*, u.username, u.email FROM " . UC_DBTABLEPRE . "mailqueue m LEFT JOIN " . UC_DBTABLEPRE . "members u ON m.touid=u.uid WHERE failures<'" . UC_MAIL_REPEAT . "' ORDER BY level DESC, mailid ASC LIMIT 1");
+        return $data;
+    }
+
+    function _get_mail_by_id($mailid)
+    {
+        $data = $this->db->fetch_first("SELECT m.*, u.username, u.email FROM " . UC_DBTABLEPRE . "mailqueue m LEFT JOIN " . UC_DBTABLEPRE . "members u ON m.touid=u.uid WHERE mailid='$mailid'");
+        return $data;
+    }
+
+    function _delete_one_mail($mailid)
+    {
+        $mailid = intval($mailid);
+        return $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "mailqueue WHERE mailid='$mailid'");
+    }
+
+    function _update_failures($mailid)
+    {
+        $mailid = intval($mailid);
+        return $this->db->query("UPDATE " . UC_DBTABLEPRE . "mailqueue SET failures=failures+1 WHERE mailid='$mailid'");
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/model/misc.php b/extend/fast/ucenter/client/uc_client/model/misc.php
new file mode 100644
index 0000000..bb83ab5
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/misc.php
@@ -0,0 +1,198 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: misc.php 1182 2014-11-17 08:57:52Z andyzheng $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+define('UC_ARRAY_SEP_1', 'UC_ARRAY_SEP_1');
+define('UC_ARRAY_SEP_2', 'UC_ARRAY_SEP_2');
+
+class miscmodel
+{
+
+    var $db;
+    var $base;
+
+    function __construct(&$base)
+    {
+        $this->miscmodel($base);
+    }
+
+    function miscmodel(&$base)
+    {
+        $this->base = $base;
+        $this->db = $base->db;
+    }
+
+    function get_apps($col = '*', $where = '')
+    {
+        $arr = $this->db->fetch_all("SELECT $col FROM " . UC_DBTABLEPRE . "applications" . ($where ? ' WHERE ' . $where : ''));
+        return $arr;
+    }
+
+    function delete_apps($appids)
+    {
+
+    }
+
+    function update_app($appid, $name, $url, $authkey, $charset, $dbcharset)
+    {
+
+    }
+
+    function alter_app_table($appid, $operation = 'ADD')
+    {
+
+    }
+
+    function get_host_by_url($url)
+    {
+
+    }
+
+    function check_url($url)
+    {
+
+    }
+
+    function check_ip($url)
+    {
+
+    }
+
+    function test_api($url, $ip = '')
+    {
+
+    }
+
+    function dfopen2($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE, $encodetype = 'URLENCODE')
+    {
+        $__times__ = isset($_GET['__times__']) ? intval($_GET['__times__']) + 1 : 1;
+        if ($__times__ > 2)
+        {
+            return '';
+        }
+        $url .= (strpos($url, '?') === FALSE ? '?' : '&') . "__times__=$__times__";
+        return $this->dfopen($url, $limit, $post, $cookie, $bysocket, $ip, $timeout, $block, $encodetype);
+    }
+
+    function dfopen($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE, $encodetype = 'URLENCODE')
+    {
+        $return = '';
+        $matches = parse_url($url);
+        $scheme = $matches['scheme'];
+        $host = $matches['host'];
+        $path = $matches['path'] ? $matches['path'] . ($matches['query'] ? '?' . $matches['query'] : '') : '/';
+        $port = !empty($matches['port']) ? $matches['port'] : 80;
+
+        if ($post)
+        {
+            $out = "POST $path HTTP/1.0\r\n";
+            $header = "Accept: */*\r\n";
+            $header .= "Accept-Language: zh-cn\r\n";
+            $boundary = $encodetype == 'URLENCODE' ? '' : ';' . substr($post, 0, trim(strpos($post, "\n")));
+            $header .= $encodetype == 'URLENCODE' ? "Content-Type: application/x-www-form-urlencoded\r\n" : "Content-Type: multipart/form-data$boundary\r\n";
+            $header .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";
+            $header .= "Host: $host:$port\r\n";
+            $header .= 'Content-Length: ' . strlen($post) . "\r\n";
+            $header .= "Connection: Close\r\n";
+            $header .= "Cache-Control: no-cache\r\n";
+            $header .= "Cookie: $cookie\r\n\r\n";
+            $out .= $header . $post;
+        }
+        else
+        {
+            $out = "GET $path HTTP/1.0\r\n";
+            $header = "Accept: */*\r\n";
+            $header .= "Accept-Language: zh-cn\r\n";
+            $header .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";
+            $header .= "Host: $host:$port\r\n";
+            $header .= "Connection: Close\r\n";
+            $header .= "Cookie: $cookie\r\n\r\n";
+            $out .= $header;
+        }
+
+        $fpflag = 0;
+        if (!$fp = @fsocketopen(($ip ? $ip : $host), $port, $errno, $errstr, $timeout))
+        {
+            $context = array(
+                'http' => array(
+                    'method'  => $post ? 'POST' : 'GET',
+                    'header'  => $header,
+                    'content' => $post,
+                    'timeout' => $timeout,
+                ),
+            );
+            $context = stream_context_create($context);
+            $fp = @fopen($scheme . '://' . ($ip ? $ip : $host) . ':' . $port . $path, 'b', false, $context);
+            $fpflag = 1;
+        }
+
+        if (!$fp)
+        {
+            return '';
+        }
+        else
+        {
+            stream_set_blocking($fp, $block);
+            stream_set_timeout($fp, $timeout);
+            @fwrite($fp, $out);
+            $status = stream_get_meta_data($fp);
+            if (!$status['timed_out'])
+            {
+                while (!feof($fp) && !$fpflag)
+                {
+                    if (($header = @fgets($fp)) && ($header == "\r\n" || $header == "\n"))
+                    {
+                        break;
+                    }
+                }
+
+                $stop = false;
+                while (!feof($fp) && !$stop)
+                {
+                    $data = fread($fp, ($limit == 0 || $limit > 8192 ? 8192 : $limit));
+                    $return .= $data;
+                    if ($limit)
+                    {
+                        $limit -= strlen($data);
+                        $stop = $limit <= 0;
+                    }
+                }
+            }
+            @fclose($fp);
+            return $return;
+        }
+    }
+
+    function array2string($arr)
+    {
+        $s = $sep = '';
+        if ($arr && is_array($arr))
+        {
+            foreach ($arr as $k => $v)
+            {
+                $s .= $sep . addslashes($k) . UC_ARRAY_SEP_1 . $v;
+                $sep = UC_ARRAY_SEP_2;
+            }
+        }
+        return $s;
+    }
+
+    function string2array($s)
+    {
+        $arr = explode(UC_ARRAY_SEP_2, $s);
+        $arr2 = array();
+        foreach ($arr as $k => $v)
+        {
+            list($key, $val) = explode(UC_ARRAY_SEP_1, $v);
+            $arr2[$key] = $val;
+        }
+        return $arr2;
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/model/note.php b/extend/fast/ucenter/client/uc_client/model/note.php
new file mode 100644
index 0000000..0ca5141
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/note.php
@@ -0,0 +1,230 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: note.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+define('UC_NOTE_REPEAT', 5);
+define('UC_NOTE_TIMEOUT', 15);
+define('UC_NOTE_GC', 10000);
+
+define('API_RETURN_FAILED', '-1');
+
+class notemodel
+{
+
+    var $db;
+    var $base;
+    var $apps;
+    var $noteid;
+    var $operations = array();
+    var $notetype = 'HTTP';
+
+    function __construct(&$base)
+    {
+        $this->notemodel($base);
+    }
+
+    function notemodel(&$base)
+    {
+        $this->base = $base;
+        $this->db = $base->db;
+        $this->apps = $this->base->cache('apps');
+        $this->operations = array(
+            'test'                 => array('', 'action=test'),
+            'deleteuser'           => array('', 'action=deleteuser'),
+            'deletefriend'         => array('', 'action=deletefriend'),
+            'gettag'               => array('', 'action=gettag', 'tag', 'updatedata'),
+            'getcreditsettings'    => array('', 'action=getcreditsettings'),
+            'getcredit'            => array('', 'action=getcredit'),
+            'updatecreditsettings' => array('', 'action=updatecreditsettings'),
+            'updateclient'         => array('', 'action=updateclient'),
+            'updatepw'             => array('', 'action=updatepw'),
+            'updateinfo'           => array('', 'action=updateinfo'),
+            'updatebadwords'       => array('', 'action=updatebadwords'),
+            'updatehosts'          => array('', 'action=updatehosts'),
+            'updateapps'           => array('', 'action=updateapps'),
+            'updatecredit'         => array('', 'action=updatecredit'),
+        );
+    }
+
+    function get_total_num($all = TRUE)
+    {
+
+    }
+
+    function get_list($page, $ppp, $totalnum, $all = TRUE)
+    {
+
+    }
+
+    function delete_note($ids)
+    {
+
+    }
+
+    function add($operation, $getdata = '', $postdata = '', $appids = array(), $pri = 0)
+    {
+        $extra = $varextra = '';
+        $appadd = $varadd = array();
+        foreach ((array) $this->apps as $appid => $app)
+        {
+            $appid = $app['appid'];
+            if ($appid == intval($appid))
+            {
+                if ($appids && !in_array($appid, $appids))
+                {
+                    $appadd[] = 'app' . $appid . "='1'";
+                }
+                else
+                {
+                    $varadd[] = "('noteexists{$appid}', '1')";
+                }
+            }
+        }
+        if ($appadd)
+        {
+            $extra = implode(',', $appadd);
+            $extra = $extra ? ', ' . $extra : '';
+        }
+        if ($varadd)
+        {
+            $varextra = implode(', ', $varadd);
+            $varextra = $varextra ? ', ' . $varextra : '';
+        }
+
+        $getdata = addslashes($getdata);
+        $postdata = addslashes($postdata);
+        $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "notelist SET getdata='$getdata', operation='$operation', pri='$pri', postdata='$postdata'$extra");
+        $insert_id = $this->db->insert_id();
+        $insert_id && $this->db->query("REPLACE INTO " . UC_DBTABLEPRE . "vars (name, value) VALUES ('noteexists', '1')$varextra");
+        $this->noteid = $insert_id;
+        return $insert_id;
+    }
+
+    function send()
+    {
+        register_shutdown_function(array($this, '_send'));
+    }
+
+    function _send()
+    {
+        $note = $this->_get_note();
+        if (empty($note))
+        {
+            $this->db->query("REPLACE INTO " . UC_DBTABLEPRE . "vars SET name='noteexists" . UC_APPID . "', value='0'");
+            return NULL;
+        }
+        $this->sendone(UC_APPID, 0, $note);
+        $this->_gc();
+    }
+
+    function sendone($appid, $noteid = 0, $note = '')
+    {
+        require_once UC_ROOT . './lib/xml.class.php';
+        $return = FALSE;
+        $app = $this->apps[$appid];
+        if ($noteid)
+        {
+            $note = $this->_get_note_by_id($noteid);
+        }
+        $this->base->load('misc');
+        $apifilename = isset($app['apifilename']) && $app['apifilename'] ? $app['apifilename'] : 'uc.php';
+        if ($app['extra']['apppath'] && @include $app['extra']['apppath'] . './api/' . $apifilename)
+        {
+            $uc_note = new uc_note();
+            $method = $note['operation'];
+            if (is_string($method) && !empty($method))
+            {
+                parse_str($note['getdata'], $note['getdata']);
+                if (get_magic_quotes_gpc())
+                {
+                    $note['getdata'] = $this->base->dstripslashes($note['getdata']);
+                }
+                $note['postdata'] = @xml_unserialize($note['postdata']);
+                $response = $uc_note->$method($note['getdata'], $note['postdata']);
+            }
+            unset($uc_note);
+        }
+        else
+        {
+            $url = $this->get_url_code($note['operation'], $note['getdata'], $appid);
+            $note['postdata'] = str_replace(array("\n", "\r"), '', $note['postdata']);
+            $response = trim($_ENV['misc']->dfopen2($url, 0, $note['postdata'], '', 1, $app['ip'], UC_NOTE_TIMEOUT, TRUE));
+        }
+
+        $returnsucceed = $response != '' && ($response == 1 || is_array(@xml_unserialize($response)));
+
+        $closedsqladd = $this->_close_note($note, $this->apps, $returnsucceed, $appid) ? ",closed='1'" : '';
+
+        if ($returnsucceed)
+        {
+            if ($this->operations[$note['operation']][2])
+            {
+                $this->base->load($this->operations[$note['operation']][2]);
+                $func = $this->operations[$note['operation']][3];
+                $_ENV[$this->operations[$note['operation']][2]]->$func($appid, $response);
+            }
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . "notelist SET app$appid='1', totalnum=totalnum+1, succeednum=succeednum+1, dateline='{$this->base->time}' $closedsqladd WHERE noteid='$note[noteid]'", 'SILENT');
+            $return = TRUE;
+        }
+        else
+        {
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . "notelist SET app$appid = app$appid-'1', totalnum=totalnum+1, dateline='{$this->base->time}' $closedsqladd WHERE noteid='$note[noteid]'", 'SILENT');
+            $return = FALSE;
+        }
+        return $return;
+    }
+
+    function _get_note()
+    {
+        $app_field = 'app' . UC_APPID;
+        $data = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "notelist WHERE closed='0' AND $app_field<'1' AND $app_field>'-" . UC_NOTE_REPEAT . "' LIMIT 1");
+        return $data;
+    }
+
+    function _gc()
+    {
+        rand(0, UC_NOTE_GC) == 0 && $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "notelist WHERE closed='1'");
+    }
+
+    function _close_note($note, $apps, $returnsucceed, $appid)
+    {
+        $note['app' . $appid] = $returnsucceed ? 1 : $note['app' . $appid] - 1;
+        $appcount = count($apps);
+        foreach ($apps as $key => $app)
+        {
+            $appstatus = $note['app' . $app['appid']];
+            if (!$app['recvnote'] || $appstatus == 1 || $appstatus <= -UC_NOTE_REPEAT)
+            {
+                $appcount--;
+            }
+        }
+        if ($appcount < 1)
+        {
+            return TRUE;
+        }
+    }
+
+    function _get_note_by_id($noteid)
+    {
+        $data = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "notelist WHERE noteid='$noteid'");
+        return $data;
+    }
+
+    function get_url_code($operation, $getdata, $appid)
+    {
+        $app = $this->apps[$appid];
+        $authkey = UC_KEY;
+        $url = $app['url'];
+        $apifilename = isset($app['apifilename']) && $app['apifilename'] ? $app['apifilename'] : 'uc.php';
+        $action = $this->operations[$operation][1];
+        $code = urlencode($this->base->authcode("$action&" . ($getdata ? "$getdata&" : '') . "time=" . $this->base->time, 'ENCODE', $authkey));
+        return $url . "/api/$apifilename?code=$code";
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/model/pm.php b/extend/fast/ucenter/client/uc_client/model/pm.php
new file mode 100644
index 0000000..672ec0f
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/pm.php
@@ -0,0 +1,1261 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: pm.php 1160 2013-10-24 08:04:45Z jeffjzhang $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+define('PMINBALCKLIST_ERROR', -6);
+define('PMSENDSELF_ERROR', -8);
+define('PMSENDNONE_ERROR', -9);
+define('PMSENDCHATNUM_ERROR', -10);
+define('PMTHREADNONE_ERROR', -11);
+define('PMPRIVILEGENONE_ERROR', -12);
+define('PMCHATTYPE_ERROR', -13);
+define('PMUIDTYPE_ERROR', -14);
+define('PMDATA_ERROR', -15);
+
+class pmmodel
+{
+
+    var $db;
+    var $base;
+
+    function __construct(&$base)
+    {
+        $this->pmmodel($base);
+    }
+
+    function pmmodel(&$base)
+    {
+        $this->base = $base;
+        $this->db = $base->db;
+    }
+
+    function pmintval($pmid)
+    {
+        return @is_numeric($pmid) ? $pmid : 0;
+    }
+
+    function getpmbypmid($uid, $pmid)
+    {
+        if (!$pmid)
+        {
+            return array();
+        }
+        $arr = array();
+        $pm = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "pm_indexes i LEFT JOIN " . UC_DBTABLEPRE . "pm_lists t ON t.plid=i.plid WHERE i.pmid='$pmid'");
+        if ($this->isprivilege($pm['plid'], $uid))
+        {
+            $pms = $this->db->fetch_all("SELECT t.*, p.*, t.authorid as founderuid, t.dateline as founddateline FROM " . UC_DBTABLEPRE . $this->getposttablename($pm['plid']) . " p LEFT JOIN " . UC_DBTABLEPRE . "pm_lists t ON t.plid=p.plid WHERE p.pmid='$pm[pmid]'");
+            $arr = $this->getpostlist($pms);
+        }
+        return $arr;
+    }
+
+    function isprivilege($plid, $uid)
+    {
+        if (!$plid || !$uid)
+        {
+            return true;
+        }
+        $query = $this->db->query("SELECT * FROM " . UC_DBTABLEPRE . "pm_members WHERE plid='$plid' AND uid='$uid'");
+        if ($this->db->fetch_array($query))
+        {
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    function getpmbyplid($uid, $plid, $starttime, $endtime, $start, $ppp, $type = 0)
+    {
+        if (!$type)
+        {
+            $pm = $this->getprivatepmbyplid($uid, $plid, $starttime, $endtime, $start, $ppp);
+        }
+        else
+        {
+            $pm = $this->getchatpmbyplid($uid, $plid, $starttime, $endtime, $start, $ppp);
+        }
+        return $this->getpostlist($pm);
+    }
+
+    function getpostlist($list)
+    {
+        if (empty($list))
+        {
+            return array();
+        }
+        $authoridarr = $authorarr = array();
+        foreach ($list as $key => $value)
+        {
+            $authoridarr[$value['authorid']] = $value['authorid'];
+        }
+        if ($authoridarr)
+        {
+            $this->base->load('user');
+            $authorarr = $_ENV['user']->id2name($authoridarr);
+        }
+        foreach ($list as $key => $value)
+        {
+            if ($value['pmtype'] == 1)
+            {
+                $users = explode('_', $value['min_max']);
+                if ($value['authorid'] == $users[0])
+                {
+                    $value['touid'] = $users[1];
+                }
+                else
+                {
+                    $value['touid'] = $users[0];
+                }
+            }
+            else
+            {
+                $value['touid'] = 0;
+            }
+            $value['author'] = $authorarr[$value['authorid']];
+
+            $value['msgfromid'] = $value['authorid'];
+            $value['msgfrom'] = $value['author'];
+            $value['msgtoid'] = $value['touid'];
+
+            unset($value['min_max']);
+            unset($value['delstatus']);
+            unset($value['lastmessage']);
+            $list[$key] = $value;
+        }
+        return $list;
+    }
+
+    function setpmstatus($uid, $touids, $plids, $status = 0)
+    {
+        if (!$uid)
+        {
+            return false;
+        }
+        if (!$status)
+        {
+            $oldstatus = 1;
+            $newstatus = 0;
+        }
+        else
+        {
+            $oldstatus = 0;
+            $newstatus = 1;
+        }
+        if ($touids)
+        {
+            foreach ($touids as $key => $value)
+            {
+                if ($uid == $value || !$value)
+                {
+                    return false;
+                }
+                $relastionship[] = $this->relationship($uid, $value);
+            }
+            $plid = $plidpostarr = array();
+            $query = $this->db->query("SELECT plid FROM " . UC_DBTABLEPRE . "pm_lists WHERE min_max IN (" . $this->base->implode($relationship) . ")");
+            while ($thread = $this->db->fetch_array($query))
+            {
+                $plidarr[] = $thread['plid'];
+            }
+            if ($plidarr)
+            {
+                $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_members SET isnew='$newstatus' WHERE plid IN (" . $this->base->implode($plidarr) . ") AND uid='$uid' AND isnew='$oldstatus'");
+            }
+        }
+        if ($plids)
+        {
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_members SET isnew='$newstatus' WHERE plid IN (" . $this->base->implode($plids) . ") AND uid='$uid' AND isnew='$oldstatus'");
+        }
+        return true;
+    }
+
+    function set_ignore($uid)
+    {
+        return $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "newpm WHERE uid='$uid'");
+    }
+
+    function isnewpm($uid)
+    {
+        return $this->db->result_first("SELECT uid FROM " . UC_DBTABLEPRE . "newpm WHERE uid='$uid'");
+    }
+
+    function lastpm($uid)
+    {
+        $lastpm = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "pm_members m LEFT JOIN " . UC_DBTABLEPRE . "pm_lists t ON m.plid=t.plid WHERE m.uid='$uid' ORDER BY m.lastdateline DESC LIMIT 1");
+        $lastmessage = unserialize($lastpm['lastmessage']);
+        if ($lastmessage['lastauthorid'])
+        {
+            $lastpm['lastauthorid'] = $lastmessage['lastauthorid'];
+            $lastpm['lastauthor'] = $lastmessage['lastauthor'];
+            $lastpm['lastsummary'] = $lastmessage['lastsummary'];
+        }
+        else
+        {
+            $lastpm['lastauthorid'] = $lastmessage['firstauthorid'];
+            $lastpm['lastauthor'] = $lastmessage['firstauthor'];
+            $lastpm['lastsummary'] = $lastmessage['firstsummary'];
+        }
+        return $lastpm;
+    }
+
+    function getpmnum($uid, $type = 0, $isnew = 0)
+    {
+        $newsql = '';
+        $newnum = 0;
+
+        if ($isnew)
+        {
+            $newsql = 'AND m.isnew=1';
+        }
+        if (!$type)
+        {
+            $newnum = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "pm_members m WHERE m.uid='$uid' $newsql");
+        }
+        else
+        {
+            $newnum = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "pm_members m LEFT JOIN " . UC_DBTABLEPRE . "pm_lists t ON t.plid=m.plid WHERE m.uid='$uid' $newsql AND t.pmtype='$type'");
+        }
+        return $newnum;
+    }
+
+    function getpmnumbyplid($uid, $plid)
+    {
+        return $this->db->result_first("SELECT pmnum FROM " . UC_DBTABLEPRE . "pm_members WHERE plid='$plid' AND uid='$uid'");
+    }
+
+    function sendpm($fromuid, $fromusername, $touids, $subject, $message, $type = 0)
+    {
+        if (!$fromuid || !$fromusername || !$touids || !$message)
+        {
+            return 0;
+        }
+        $touids = array_unique($touids);
+        $relationship = $existplid = $pm_member_insertsql = array();
+        $this->base->load('user');
+        $tmptouidarr = $touids;
+        $blackls = $this->get_blackls($fromuid, $touids);
+
+        foreach ($tmptouidarr as $key => $value)
+        {
+            if ($fromuid == $value || !$value)
+            {
+                return PMSENDSELF_ERROR;
+            }
+
+            if (in_array('{ALL}', $blackls[$value]))
+            {
+                unset($touids[$key]);
+                continue;
+            }
+            $blackls[$value] = $_ENV['user']->name2id($blackls[$value]);
+            if (!(isset($blackls[$value]) && !in_array($fromuid, $blackls[$value])))
+            {
+                unset($touids[$key]);
+            }
+            else
+            {
+                $relationship[$value] = $this->relationship($fromuid, $value);
+            }
+        }
+        if (empty($touids))
+        {
+            return PMSENDNONE_ERROR;
+        }
+        if ($type == 1 && count($touids) < 2)
+        {
+            return PMSENDCHATNUM_ERROR;
+        }
+
+        $_CACHE['badwords'] = $this->base->cache('badwords');
+        if ($_CACHE['badwords']['findpattern'])
+        {
+            $subject = @preg_replace($_CACHE['badwords']['findpattern'], $_CACHE['badwords']['replace'], $subject);
+            $message = @preg_replace($_CACHE['badwords']['findpattern'], $_CACHE['badwords']['replace'], $message);
+        }
+        if (!$subject)
+        {
+            $subject = $this->removecode(trim($message), 80);
+        }
+        else
+        {
+            $subject = dhtmlspecialchars($subject);
+        }
+        $lastsummary = $this->removecode(trim(stripslashes($message)), 150);
+
+        if (!$type)
+        {
+            $query = $this->db->query("SELECT plid, min_max FROM " . UC_DBTABLEPRE . "pm_lists WHERE min_max IN (" . $this->base->implode($relationship) . ")");
+            while ($thread = $this->db->fetch_array($query))
+            {
+                $existplid[$thread['min_max']] = $thread['plid'];
+            }
+            $lastmessage = array('lastauthorid' => $fromuid, 'lastauthor' => $fromusername, 'lastsummary' => $lastsummary);
+            $lastmessage = addslashes(serialize($lastmessage));
+            foreach ($relationship as $key => $value)
+            {
+                if (!isset($existplid[$value]))
+                {
+                    $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_lists(authorid, pmtype, subject, members, min_max, dateline, lastmessage) VALUES('$fromuid', '1', '$subject', 2, '$value', '" . $this->base->time . "', '$lastmessage')");
+                    $plid = $this->db->insert_id();
+                    $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_indexes(plid) VALUES('$plid')");
+                    $pmid = $this->db->insert_id();
+                    $this->db->query("INSERT INTO " . UC_DBTABLEPRE . $this->getposttablename($plid) . "(pmid, plid, authorid, message, dateline, delstatus) VALUES('$pmid', '$plid', '$fromuid', '$message', '" . $this->base->time . "', 0)");
+                    $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_members(plid, uid, isnew, pmnum, lastupdate, lastdateline) VALUES('$plid', '$key', '1', '1', '0', '" . $this->base->time . "')");
+                    $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_members(plid, uid, isnew, pmnum, lastupdate, lastdateline) VALUES('$plid', '$fromuid', '0', '1', '" . $this->base->time . "', '" . $this->base->time . "')");
+                }
+                else
+                {
+                    $plid = $existplid[$value];
+                    $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_indexes(plid) VALUES('$plid')");
+                    $pmid = $this->db->insert_id();
+                    $this->db->query("INSERT INTO " . UC_DBTABLEPRE . $this->getposttablename($plid) . "(pmid, plid, authorid, message, dateline, delstatus) VALUES('$pmid', '$plid', '$fromuid', '$message', '" . $this->base->time . "', 0)");
+                    $result = $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_members(plid, uid, isnew, pmnum, lastupdate, lastdateline) VALUES('$plid', '$key', '1', '1', '0', '" . $this->base->time . "')", 'SILENT');
+                    if (!$result)
+                    {
+                        $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_members SET isnew=1, pmnum=pmnum+1, lastdateline='" . $this->base->time . "' WHERE plid='$plid' AND uid='$key'");
+                    }
+                    $result = $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_members(plid, uid, isnew, pmnum, lastupdate, lastdateline) VALUES('$plid', '$fromuid', '0', '1', '" . $this->base->time . "', '" . $this->base->time . "')", 'SILENT');
+                    if (!$result)
+                    {
+                        $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_members SET isnew=0, pmnum=pmnum+1, lastupdate='" . $this->base->time . "', lastdateline='" . $this->base->time . "' WHERE plid='$plid' AND uid='$fromuid'");
+                    }
+                    $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_lists SET lastmessage='$lastmessage' WHERE plid='$plid'");
+                }
+            }
+        }
+        else
+        {
+            $lastmessage = array('firstauthorid' => $fromuid, 'firstauthor' => $fromusername, 'firstsummary' => $lastsummary);
+            $lastmessage = addslashes(serialize($lastmessage));
+            $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_lists(authorid, pmtype, subject, members, min_max, dateline, lastmessage) VALUES('$fromuid', '2', '$subject', '" . (count($touids) + 1) . "', '', '" . $this->base->time . "', '$lastmessage')");
+            $plid = $this->db->insert_id();
+            $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_indexes(plid) VALUES('$plid')");
+            $pmid = $this->db->insert_id();
+            $this->db->query("INSERT INTO " . UC_DBTABLEPRE . $this->getposttablename($plid) . "(pmid, plid, authorid, message, dateline, delstatus) VALUES('$pmid', '$plid', '$fromuid', '$message', '" . $this->base->time . "', 0)");
+            $pm_member_insertsql[] = "('$plid', '$fromuid', '0', '1', '" . $this->base->time . "', '" . $this->base->time . "')";
+            foreach ($touids as $key => $value)
+            {
+                $pm_member_insertsql[] = "('$plid', '$value', '1', '1', '0', '" . $this->base->time . "')";
+            }
+            $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_members(plid, uid, isnew, pmnum, lastupdate, lastdateline) VALUES " . implode(',', $pm_member_insertsql));
+        }
+
+        $newpm = array();
+        foreach ($touids as $key => $value)
+        {
+            $newpm[] = "('$value')";
+        }
+        $this->db->query("REPLACE INTO " . UC_DBTABLEPRE . "newpm(uid) VALUES " . implode(',', $newpm));
+        return $pmid;
+    }
+
+    function replypm($plid, $fromuid, $fromusername, $message)
+    {
+        if (!$plid || !$fromuid || !$fromusername || !$message)
+        {
+            return 0;
+        }
+
+        $threadpm = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "pm_lists WHERE plid='$plid'");
+        if (empty($threadpm))
+        {
+            return PMTHREADNONE_ERROR;
+        }
+
+        if ($threadpm['pmtype'] == 1)
+        {
+            $users = explode('_', $threadpm['min_max']);
+            if ($users[0] == $fromuid)
+            {
+                $touid = $users[1];
+            }
+            elseif ($users[1] == $fromuid)
+            {
+                $touid = $users[0];
+            }
+            else
+            {
+                return PMPRIVILEGENONE_ERROR;
+            }
+
+            $blackls = $this->get_blackls($fromuid, $touid);
+            if (in_array('{ALL}', $blackls[$touid]))
+            {
+                return PMINBALCKLIST_ERROR;
+            }
+            $this->base->load('user');
+            $blackls[$touid] = $_ENV['user']->name2id($blackls[$touid]);
+            if (!(isset($blackls[$touid]) && !in_array($fromuid, $blackls[$touid])))
+            {
+                return PMINBALCKLIST_ERROR;
+            }
+        }
+
+        $memberuid = array();
+        $query = $this->db->query("SELECT * FROM " . UC_DBTABLEPRE . "pm_members WHERE plid='$plid'");
+        while ($member = $this->db->fetch_array($query))
+        {
+            $memberuid[$member['uid']] = "('$member[uid]')";
+        }
+        if (!isset($memberuid[$fromuid]))
+        {
+            return PMPRIVILEGENONE_ERROR;
+        }
+
+        $_CACHE['badwords'] = $this->base->cache('badwords');
+        if ($_CACHE['badwords']['findpattern'])
+        {
+            $message = @preg_replace($_CACHE['badwords']['findpattern'], $_CACHE['badwords']['replace'], $message);
+        }
+        $lastsummary = $this->removecode(trim(stripslashes($message)), 150);
+
+        $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_indexes(plid) VALUES('$plid')");
+        $pmid = $this->db->insert_id();
+        $this->db->query("INSERT INTO " . UC_DBTABLEPRE . $this->getposttablename($plid) . "(pmid, plid, authorid, message, dateline, delstatus) VALUES('$pmid', '$plid', '$fromuid', '$message', '" . $this->base->time . "', 0)");
+        if ($threadpm['pmtype'] == 1)
+        {
+            $lastmessage = array('lastauthorid' => $fromuid, 'lastauthor' => $fromusername, 'lastsummary' => $lastsummary);
+            $lastmessage = addslashes(serialize($lastmessage));
+            $result = $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_members(plid, uid, isnew, pmnum, lastupdate, lastdateline) VALUES('$plid', '$touid', '1', '1', '0', '" . $this->base->time . "')", 'SILENT');
+            if (!$result)
+            {
+                $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_members SET isnew=1, pmnum=pmnum+1, lastdateline='" . $this->base->time . "' WHERE plid='$plid' AND uid='$touid'");
+            }
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_members SET isnew=0, pmnum=pmnum+1, lastupdate='" . $this->base->time . "', lastdateline='" . $this->base->time . "' WHERE plid='$plid' AND uid='$fromuid'");
+        }
+        else
+        {
+            $lastmessage = unserialize($threadpm['lastmessage']);
+            $lastmessage = array('firstauthorid' => $lastmessage['firstauthorid'], 'firstauthor' => $lastmessage['firstauthor'], 'firstsummary' => $lastmessage['firstsummary'], 'lastauthorid' => $fromuid, 'lastauthor' => $fromusername, 'lastsummary' => $lastsummary);
+            $lastmessage = addslashes(serialize($lastmessage));
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_members SET isnew=1, pmnum=pmnum+1, lastdateline='" . $this->base->time . "' WHERE plid='$plid'");
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_members SET isnew=0, lastupdate='" . $this->base->time . "' WHERE plid='$plid' AND uid='$fromuid'");
+        }
+        $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_lists SET lastmessage='$lastmessage' WHERE plid='$plid'");
+
+        $this->db->query("REPLACE INTO " . UC_DBTABLEPRE . "newpm(uid) VALUES " . implode(',', $memberuid) . "");
+
+        return $pmid;
+    }
+
+    function appendchatpm($plid, $uid, $touid)
+    {
+        if (!$plid || !$uid || !$touid)
+        {
+            return 0;
+        }
+        $threadpm = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "pm_lists WHERE plid='$plid'");
+        if (empty($threadpm))
+        {
+            return PMTHREADNONE_ERROR;
+        }
+        if ($threadpm['pmtype'] != 2)
+        {
+            return PMCHATTYPE_ERROR;
+        }
+        if ($threadpm['authorid'] != $uid)
+        {
+            return PMPRIVILEGENONE_ERROR;
+        }
+
+        $blackls = $this->get_blackls($uid, $touid);
+        if (in_array('{ALL}', $blackls[$touid]))
+        {
+            return PMINBALCKLIST_ERROR;
+        }
+        $this->base->load('user');
+        $blackls[$touid] = $_ENV['user']->name2id($blackls[$touid]);
+        if (!(isset($blackls[$touid]) && !in_array($uid, $blackls[$touid])))
+        {
+            return PMINBALCKLIST_ERROR;
+        }
+
+        $pmnum = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . $this->getposttablename($plid) . " WHERE plid='$plid'");
+        $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "pm_members(plid, uid, isnew, pmnum, lastupdate, lastdateline) VALUES('$plid', '$touid', '1', '$pmnum', '0', '0')", 'SILENT');
+        $num = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "pm_members WHERE plid='$plid'");
+        $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_lists SET members='$num' WHERE plid='$plid'");
+
+        return 1;
+    }
+
+    function kickchatpm($plid, $uid, $touid)
+    {
+        if (!$uid || !$touid || !$plid || $uid == $touid)
+        {
+            return 0;
+        }
+        $threadpm = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "pm_lists WHERE plid='$plid'");
+        if ($threadpm['pmtype'] != 2)
+        {
+            return PMCHATTYPE_ERROR;
+        }
+        if ($threadpm['authorid'] != $uid)
+        {
+            return PMPRIVILEGENONE_ERROR;
+        }
+        $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_members WHERE plid='$plid' AND uid='$touid'");
+        $num = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "pm_members WHERE plid='$plid'");
+        $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_lists SET members='$num' WHERE plid='$plid'");
+        return 1;
+    }
+
+    function quitchatpm($uid, $plids)
+    {
+        if (!$uid || !$plids)
+        {
+            return 0;
+        }
+        $list = array();
+        $query = $this->db->query("SELECT * FROM " . UC_DBTABLEPRE . "pm_members m LEFT JOIN " . UC_DBTABLEPRE . "pm_lists t ON m.plid=t.plid WHERE m.plid IN (" . $this->base->implode($plids) . ") AND m.uid='$uid'");
+        while ($threadpm = $this->db->fetch_array($query))
+        {
+            if ($threadpm['pmtype'] != 2)
+            {
+                return PMCHATTYPE_ERROR;
+            }
+            if ($threadpm['authorid'] == $uid)
+            {
+                return PMPRIVILEGENONE_ERROR;
+            }
+            $list[] = $threadpm['plid'];
+        }
+
+        if ($list)
+        {
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_members WHERE plid IN (" . $this->base->implode($list) . ") AND uid='$uid'");
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_lists SET members=members-1 WHERE plid IN (" . $this->base->implode($list) . ")");
+        }
+
+        return 1;
+    }
+
+    function deletepmbypmid($uid, $pmid)
+    {
+        if (!$uid || !$pmid)
+        {
+            return 0;
+        }
+        $index = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "pm_indexes i LEFT JOIN " . UC_DBTABLEPRE . "pm_lists t ON i.plid=t.plid WHERE i.pmid='$pmid'");
+        if ($index['pmtype'] != 1)
+        {
+            return PMUIDTYPE_ERROR;
+        }
+        $users = explode('_', $index['min_max']);
+        if (!in_array($uid, $users))
+        {
+            return PMPRIVILEGENONE_ERROR;
+        }
+        if ($index['authorid'] != $uid)
+        {
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . $this->getposttablename($index['plid']) . " SET delstatus=2 WHERE pmid='$pmid' AND delstatus=0");
+            $updatenum = $this->db->affected_rows();
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . $this->getposttablename($index['plid']) . " WHERE pmid='$pmid' AND delstatus=1");
+            $deletenum = $this->db->affected_rows();
+        }
+        else
+        {
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . $this->getposttablename($index['plid']) . " SET delstatus=1 WHERE pmid='$pmid' AND delstatus=0");
+            $updatenum = $this->db->affected_rows();
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . $this->getposttablename($index['plid']) . " WHERE pmid='$pmid' AND delstatus=2");
+            $deletenum = $this->db->affected_rows();
+        }
+
+        if (!$this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . $this->getposttablename($index['plid']) . " WHERE plid='$index[plid]'"))
+        {
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_lists WHERE plid='$index[plid]'");
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_members WHERE plid='$index[plid]'");
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_indexes WHERE plid='$index[plid]'");
+        }
+        else
+        {
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_members SET pmnum=pmnum-" . ($updatenum + $deletenum) . " WHERE plid='" . $index['plid'] . "' AND uid='$uid'");
+        }
+        return 1;
+    }
+
+    function deletepmbypmids($uid, $pmids)
+    {
+        if ($pmids)
+        {
+            foreach ($pmids as $key => $pmid)
+            {
+                $this->deletepmbypmid($uid, $pmid);
+            }
+        }
+        return 1;
+    }
+
+    /*
+      function deletepmbypmids($uid, $pmids) {
+      if(!$uid || !$pmids) {
+      return 0;
+      }
+      $pmplid = $delstatus1pm = $delstatus2pm = $pmplidpost = array();
+      $existplid = array();
+      $query = $this->db->query("SELECT * FROM ".UC_DBTABLEPRE."pm_indexes i LEFT JOIN ".UC_DBTABLEPRE."pm_lists t ON i.plid=t.plid WHERE i.pmid IN (".$this->base->implode($pmids).") AND t.pmtype=1");
+      while($index = $this->db->fetch_array($query)) {
+      $users = explode('_', $index['min_max']);
+      if(!in_array($uid, $users)) {
+      return PMPRIVILEGENONE_ERROR;
+      }
+      if($index['authorid'] == $uid) {
+      $delstatus1pm[$this->getposttablename($index['plid'])][] = $index['pmid'];
+      } else {
+      $delstatus2pm[$this->getposttablename($index['plid'])][] = $index['pmid'];
+      }
+      $pmplidpost[$this->getposttablename($index['plid'])][] = $index['plid'];
+      $pmplid[$index['plid']] = $index['plid'];
+      }
+      if(empty($pmplidpost)) {
+      return 0;
+      }
+
+      if($delstatus1pm) {
+      foreach($delstatus1pm as $key => $value) {
+      $this->db->query("UPDATE ".UC_DBTABLEPRE."$key SET delstatus=1 WHERE pmid IN (".$this->base->implode($value).") AND delstatus=0");
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."$key WHERE pmid IN (".$this->base->implode($value).") AND delstatus=2");
+      }
+      }
+
+      if($delstatus2pm) {
+      foreach($delstatus2pm as $key => $value) {
+      $this->db->query("UPDATE ".UC_DBTABLEPRE."$key SET delstatus=2 WHERE pmid IN (".$this->base->implode($value).") AND delstatus=0");
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."$key WHERE pmid IN (".$this->base->implode($value).") AND delstatus=1");
+      }
+      }
+
+      foreach($pmplidpost as $key => $value) {
+      $query = $this->db->query("SELECT DISTINCT plid FROM ".UC_DBTABLEPRE."$key WHERE plid IN (".$this->base->implode($value).")");
+      while($pmpostarr = $this->db->fetch_array($query)) {
+      $existplid[] = $pmpostarr['plid'];
+      }
+      }
+      $pmplid = array_diff($pmplid, $existplid);
+      if($pmplid) {
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."pm_lists WHERE plid IN (".$this->base->implode($pmplid).")");
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."pm_members WHERE plid IN (".$this->base->implode($pmplid).")");
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."pm_indexes WHERE plid IN (".$this->base->implode($pmplid).")");
+      }
+      return 1;
+      }
+     */
+
+    function deletepmbyplid($uid, $plid, $isuser = 0)
+    {
+        if (!$uid || !$plid)
+        {
+            return 0;
+        }
+
+        if ($isuser)
+        {
+            $relationship = $this->relationship($uid, $plid);
+            $sql = "SELECT * FROM " . UC_DBTABLEPRE . "pm_lists WHERE min_max='$relationship'";
+        }
+        else
+        {
+            $sql = "SELECT * FROM " . UC_DBTABLEPRE . "pm_lists WHERE plid='$plid'";
+        }
+
+        $query = $this->db->query($sql);
+        if ($list = $this->db->fetch_array($query))
+        {
+            if ($list['pmtype'] == 1)
+            {
+                $user = explode('_', $list['min_max']);
+                if (!in_array($uid, $user))
+                {
+                    return PMPRIVILEGENONE_ERROR;
+                }
+            }
+            else
+            {
+                if ($uid != $list['authorid'])
+                {
+                    return PMPRIVILEGENONE_ERROR;
+                }
+            }
+        }
+        else
+        {
+            return PMTHREADNONE_ERROR;
+        }
+
+        if ($list['pmtype'] == 1)
+        {
+            if ($uid == $list['authorid'])
+            {
+                $this->db->query("DELETE FROM " . UC_DBTABLEPRE . $this->getposttablename($list['plid']) . " WHERE plid='$list[plid]' AND delstatus=2");
+                $this->db->query("UPDATE " . UC_DBTABLEPRE . $this->getposttablename($list['plid']) . " SET delstatus=1 WHERE plid='$list[plid]' AND delstatus=0");
+            }
+            else
+            {
+                $this->db->query("DELETE FROM " . UC_DBTABLEPRE . $this->getposttablename($list['plid']) . " WHERE plid='$list[plid]' AND delstatus=1");
+                $this->db->query("UPDATE " . UC_DBTABLEPRE . $this->getposttablename($list['plid']) . " SET delstatus=2 WHERE plid='$list[plid]' AND delstatus=0");
+            }
+            $count = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . $this->getposttablename($list['plid']) . " WHERE plid='$list[plid]'");
+            if (!$count)
+            {
+                $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_lists WHERE plid='$list[plid]'");
+                $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_members WHERE plid='$list[plid]'");
+                $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_indexes WHERE plid='$list[plid]'");
+            }
+            else
+            {
+                $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_members WHERE plid='$list[plid]' AND uid='$uid'");
+            }
+        }
+        else
+        {
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . $this->getposttablename($list['plid']) . " WHERE plid='$list[plid]'");
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_lists WHERE plid='$list[plid]'");
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_members WHERE plid='$list[plid]'");
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "pm_indexes WHERE plid='$list[plid]'");
+        }
+        return 1;
+    }
+
+    function deletepmbyplids($uid, $plids, $isuser = 0)
+    {
+        if ($plids)
+        {
+            foreach ($plids as $key => $plid)
+            {
+                $this->deletepmbyplid($uid, $plid, $isuser);
+            }
+        }
+        return 1;
+    }
+
+    /*
+      function deletepmbyplid($uid, $plids, $isuser = 0) {
+      if(!$uid || !$plids) {
+      return 0;
+      }
+
+      $privatepm = $chatpm = array();
+      $privatepmauthorpost = $privatepmpost = $privatepmpostall = array();
+      $existplid = array();
+      if($isuser) {
+      $relationship = array();
+      foreach($plids as $key => $value) {
+      if($uid == $value || !$value) {
+      return PMDATA_ERROR;
+      }
+      $relationship[] = $this->relationship($uid, $value);
+      }
+      $sql = "SELECT * FROM ".UC_DBTABLEPRE."pm_lists WHERE min_max IN (".$this->base->implode($relationship).")";
+      } else {
+      $sql = "SELECT * FROM ".UC_DBTABLEPRE."pm_lists WHERE plid IN (".$this->base->implode($plids).")";
+      }
+      $query = $this->db->query($sql);
+      while($threadpm = $this->db->fetch_array($query)) {
+      if($threadpm['pmtype'] == 1) {
+      $users = explode('_', $threadpm['min_max']);
+      if($users[0] == $uid) {
+      $touid = $users[1];
+      } elseif($users[1] == $uid) {
+      $touid = $users[0];
+      } else {
+      continue;
+      }
+
+      if($threadpm['authorid'] == $uid) {
+      $privatepmauthorpost[$this->getposttablename($threadpm['plid'])][] = $threadpm['plid'];
+      } else {
+      $privatepmpost[$this->getposttablename($threadpm['plid'])][] = $threadpm['plid'];
+      }
+      $privatepmpostall[$this->getposttablename($threadpm['plid'])][] = $threadpm['plid'];
+      $privatepm[] = $threadpm['plid'];
+      } else {
+      if($uid != $threadpm['authorid']) {
+      continue;
+      }
+      $chatpm[] = $threadpm['plid'];
+      $chatpmpost[$this->getposttablename($threadpm['plid'])][] = $threadpm['plid'];
+      }
+      }
+
+      if($privatepmauthorpost) {
+      foreach($privatepmauthorpost as $key => $value) {
+      $this->db->query("UPDATE ".UC_DBTABLEPRE."$key SET delstatus=1 WHERE plid IN (".$this->base->implode($value).") AND delstatus=0");
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."$key WHERE plid IN (".$this->base->implode($value).") AND delstatus=2");
+      }
+      }
+
+      if($privatepmpost) {
+      foreach($privatepmpost as $key => $value) {
+      $this->db->query("UPDATE ".UC_DBTABLEPRE."$key SET delstatus=2 WHERE plid IN (".$this->base->implode($value).") AND delstatus=0");
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."$key WHERE plid IN (".$this->base->implode($value).") AND delstatus=1");
+      }
+      }
+      if($privatepmpostall) {
+      foreach($privatepmpostall as $key => $value) {
+      $query = $this->db->query("SELECT DISTINCT plid FROM ".UC_DBTABLEPRE."$key WHERE plid IN (".$this->base->implode($value).")");
+      while($postpm = $this->db->fetch_array($query)) {
+      $existplid[] = $postpm['plid'];
+      }
+      }
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."pm_members WHERE plid IN (".$this->base->implode($privatepm).") AND uid='$uid'");
+      $privatepm = array_diff($privatepm, $existplid);
+      if(!empty($privatepm)) {
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."pm_lists WHERE plid IN (".$this->base->implode($privatepm).")");
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."pm_members WHERE plid IN (".$this->base->implode($privatepm).")");
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."pm_indexes WHERE plid IN (".$this->base->implode($privatepm).")");
+      }
+      }
+      if($chatpmpost) {
+      foreach($chatpmpost as $key => $value) {
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."$key WHERE plid IN (".$this->base->implode($value).")");
+      }
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."pm_lists WHERE plid IN (".$this->base->implode($chatpm).")");
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."pm_members WHERE plid IN (".$this->base->implode($chatpm).")");
+      $this->db->query("DELETE FROM ".UC_DBTABLEPRE."pm_indexes WHERE plid IN (".$this->base->implode($chatpm).")");
+      }
+      return 1;
+      }
+     */
+
+    function getprivatepmbyplid($uid, $plid, $starttime = 0, $endtime = 0, $start = 0, $ppp = 0)
+    {
+        if (!$uid || !$plid)
+        {
+            return 0;
+        }
+        if (!$this->isprivilege($plid, $uid))
+        {
+            return 0;
+        }
+        $thread = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "pm_lists WHERE plid='$plid'");
+        if ($thread['pmtype'] != 1)
+        {
+            return 0;
+        }
+        $pms = $addsql = array();
+        $addsql[] = "p.plid='$plid'";
+        if ($thread['authorid'] == $uid)
+        {
+            $addsql[] = 'p.delstatus IN (0,2)';
+        }
+        else
+        {
+            $addsql[] = 'p.delstatus IN (0,1)';
+        }
+        if ($starttime)
+        {
+            $addsql[] = "p.dateline>'$starttime'";
+        }
+        if ($endtime)
+        {
+            $addsql[] = "p.dateline<'$endtime'";
+        }
+        if ($addsql)
+        {
+            $addsql = implode(' AND ', $addsql);
+        }
+        else
+        {
+            $addsql = '';
+        }
+        if ($ppp)
+        {
+            $limitsql = 'LIMIT ' . intval($start) . ', ' . intval($ppp);
+        }
+        else
+        {
+            $limitsql = '';
+        }
+        $pms = $this->db->fetch_all("SELECT t.*, p.*, t.authorid as founderuid, t.dateline as founddateline FROM " . UC_DBTABLEPRE . $this->getposttablename($plid) . " p LEFT JOIN " . UC_DBTABLEPRE . "pm_lists t ON p.plid=t.plid WHERE $addsql ORDER BY p.dateline DESC $limitsql");
+        $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_members SET isnew=0 WHERE plid='$plid' AND uid='$uid' AND isnew=1");
+        return array_reverse($pms);
+    }
+
+    function getchatpmbyplid($uid, $plid, $starttime = 0, $endtime = 0, $start = 0, $ppp = 0)
+    {
+        if (!$uid || !$plid)
+        {
+            return 0;
+        }
+        if (!$this->isprivilege($plid, $uid))
+        {
+            return 0;
+        }
+        $pms = $addsql = array();
+        $addsql[] = "p.plid='$plid'";
+        if ($starttime)
+        {
+            $addsql[] = "p.dateline>'$starttime'";
+        }
+        if ($endtime)
+        {
+            $addsql[] = "p.dateline<'$endtime'";
+        }
+        if ($addsql)
+        {
+            $addsql = implode(' AND ', $addsql);
+        }
+        else
+        {
+            $addsql = '';
+        }
+        if ($ppp)
+        {
+            $limitsql = 'LIMIT ' . intval($start) . ', ' . intval($ppp);
+        }
+        else
+        {
+            $limitsql = '';
+        }
+        $query = $this->db->query("SELECT t.*, p.*, t.authorid as founderuid, t.dateline as founddateline FROM " . UC_DBTABLEPRE . $this->getposttablename($plid) . " p LEFT JOIN " . UC_DBTABLEPRE . "pm_lists t ON p.plid=t.plid WHERE $addsql ORDER BY p.dateline DESC $limitsql");
+        while ($pm = $this->db->fetch_array($query))
+        {
+            if ($pm['pmtype'] != 2)
+            {
+                return 0;
+            }
+            $pms[] = $pm;
+        }
+        $this->db->query("UPDATE " . UC_DBTABLEPRE . "pm_members SET isnew=0 WHERE plid='$plid' AND uid='$uid' AND isnew=1");
+        return array_reverse($pms);
+    }
+
+    function getpmlist($uid, $filter, $start, $ppp = 10)
+    {
+        if (!$uid)
+        {
+            return 0;
+        }
+        $members = $touidarr = $tousernamearr = array();
+
+        if ($filter == 'newpm')
+        {
+            $addsql = 'm.isnew=1 AND ';
+        }
+        else
+        {
+            $addsql = '';
+        }
+        $query = $this->db->query("SELECT * FROM " . UC_DBTABLEPRE . "pm_members m LEFT JOIN " . UC_DBTABLEPRE . "pm_lists t ON t.plid=m.plid WHERE $addsql m.uid='$uid' ORDER BY m.lastdateline DESC LIMIT $start, $ppp");
+        while ($member = $this->db->fetch_array($query))
+        {
+            if ($member['pmtype'] == 1)
+            {
+                $users = explode('_', $member['min_max']);
+                $member['touid'] = $users[0] == $uid ? $users[1] : $users[0];
+            }
+            else
+            {
+                $member['touid'] = 0;
+            }
+            $touidarr[$member['touid']] = $member['touid'];
+            $members[] = $member;
+        }
+
+        $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "newpm WHERE uid='$uid'");
+
+        $array = array();
+        if ($members)
+        {
+            $today = $this->base->time - $this->base->time % 86400;
+            $this->base->load('user');
+            $tousernamearr = $_ENV['user']->id2name($touidarr);
+            foreach ($members as $key => $data)
+            {
+
+                $daterange = 5;
+                $data['founddateline'] = $data['dateline'];
+                $data['dateline'] = $data['lastdateline'];
+                $data['pmid'] = $data['plid'];
+                $lastmessage = unserialize($data['lastmessage']);
+                if ($lastmessage['firstauthorid'])
+                {
+                    $data['firstauthorid'] = $lastmessage['firstauthorid'];
+                    $data['firstauthor'] = $lastmessage['firstauthor'];
+                    $data['firstsummary'] = $lastmessage['firstsummary'];
+                }
+                if ($lastmessage['lastauthorid'])
+                {
+                    $data['lastauthorid'] = $lastmessage['lastauthorid'];
+                    $data['lastauthor'] = $lastmessage['lastauthor'];
+                    $data['lastsummary'] = $lastmessage['lastsummary'];
+                }
+                $data['msgfromid'] = $lastmessage['lastauthorid'];
+                $data['msgfrom'] = $lastmessage['lastauthor'];
+                $data['message'] = $lastmessage['lastsummary'];
+
+                $data['new'] = $data['isnew'];
+
+                $data['msgtoid'] = $data['touid'];
+                if ($data['lastdateline'] >= $today)
+                {
+                    $daterange = 1;
+                }
+                elseif ($data['lastdateline'] >= $today - 86400)
+                {
+                    $daterange = 2;
+                }
+                elseif ($data['lastdateline'] >= $today - 172800)
+                {
+                    $daterange = 3;
+                }
+                elseif ($data['lastdateline'] >= $today - 604800)
+                {
+                    $daterange = 4;
+                }
+                $data['daterange'] = $daterange;
+
+                $data['tousername'] = $tousernamearr[$data['touid']];
+                unset($data['min_max']);
+                $array[] = $data;
+            }
+        }
+        return $array;
+    }
+
+    function getplidbypmid($pmid)
+    {
+        if (!$pmid)
+        {
+            return false;
+        }
+        return $this->db->result_first("SELECT plid FROM " . UC_DBTABLEPRE . "pm_indexes WHERE pmid='$pmid'");
+    }
+
+    function getplidbytouid($uid, $touid)
+    {
+        if (!$uid || !$touid)
+        {
+            return 0;
+        }
+        return $this->db->result_first("SELECT plid FROM " . UC_DBTABLEPRE . "pm_lists WHERE min_max='" . $this->relationship($uid, $touid) . "'");
+    }
+
+    function getuidbyplid($plid)
+    {
+        if (!$plid)
+        {
+            return array();
+        }
+        $uidarr = array();
+        $query = $this->db->query("SELECT uid FROM " . UC_DBTABLEPRE . "pm_members WHERE plid='$plid'");
+        while ($uid = $this->db->fetch_array($query))
+        {
+            $uidarr[$uid['uid']] = $uid['uid'];
+        }
+        return $uidarr;
+    }
+
+    function chatpmmemberlist($uid, $plid)
+    {
+        if (!$uid || !$plid)
+        {
+            return 0;
+        }
+        $uidarr = $this->getuidbyplid($plid);
+        if (empty($uidarr))
+        {
+            return 0;
+        }
+        if (!isset($uidarr[$uid]))
+        {
+            return 0;
+        }
+        $authorid = $this->db->result_first("SELECT authorid FROM " . UC_DBTABLEPRE . "pm_lists WHERE plid='$plid'");
+        return array('author' => $authorid, 'member' => $uidarr);
+    }
+
+    function relationship($fromuid, $touid)
+    {
+        if ($fromuid < $touid)
+        {
+            return $fromuid . '_' . $touid;
+        }
+        elseif ($fromuid > $touid)
+        {
+            return $touid . '_' . $fromuid;
+        }
+        else
+        {
+            return '';
+        }
+    }
+
+    function getposttablename($plid)
+    {
+        $id = substr((string) $plid, -1, 1);
+        return 'pm_messages_' . intval($id);
+    }
+
+    function get_blackls($uid, $uids = array())
+    {
+        if (!$uids)
+        {
+            $blackls = $this->db->result_first("SELECT blacklist FROM " . UC_DBTABLEPRE . "memberfields WHERE uid='$uid'");
+        }
+        else
+        {
+            $uids = $this->base->implode($uids);
+            $blackls = array();
+            $query = $this->db->query("SELECT uid, blacklist FROM " . UC_DBTABLEPRE . "memberfields WHERE uid IN ($uids)");
+            while ($data = $this->db->fetch_array($query))
+            {
+                $blackls[$data['uid']] = explode(',', $data['blacklist']);
+            }
+        }
+        return $blackls;
+    }
+
+    function set_blackls($uid, $blackls)
+    {
+        $this->db->query("UPDATE " . UC_DBTABLEPRE . "memberfields SET blacklist='$blackls' WHERE uid='$uid'");
+        return $this->db->affected_rows();
+    }
+
+    function update_blackls($uid, $username, $action = 1)
+    {
+        $username = !is_array($username) ? array($username) : $username;
+        if ($action == 1)
+        {
+            if (!in_array('{ALL}', $username))
+            {
+                $usernames = $this->base->implode($username);
+                $query = $this->db->query("SELECT username FROM " . UC_DBTABLEPRE . "members WHERE username IN ($usernames)");
+                $usernames = array();
+                while ($data = $this->db->fetch_array($query))
+                {
+                    $usernames[addslashes($data['username'])] = addslashes($data['username']);
+                }
+                if (!$usernames)
+                {
+                    return 0;
+                }
+                $blackls = addslashes($this->db->result_first("SELECT blacklist FROM " . UC_DBTABLEPRE . "memberfields WHERE uid='$uid'"));
+                if ($blackls)
+                {
+                    $list = explode(',', $blackls);
+                    foreach ($list as $k => $v)
+                    {
+                        if (in_array($v, $usernames))
+                        {
+                            unset($usernames[$v]);
+                        }
+                    }
+                }
+                if (!$usernames)
+                {
+                    return 1;
+                }
+                $listnew = implode(',', $usernames);
+                $blackls .= $blackls !== '' ? ',' . $listnew : $listnew;
+            }
+            else
+            {
+                $blackls = addslashes($this->db->result_first("SELECT blacklist FROM " . UC_DBTABLEPRE . "memberfields WHERE uid='$uid'"));
+                $blackls .= ',{ALL}';
+            }
+        }
+        else
+        {
+            $blackls = addslashes($this->db->result_first("SELECT blacklist FROM " . UC_DBTABLEPRE . "memberfields WHERE uid='$uid'"));
+            $list = $blackls = explode(',', $blackls);
+            foreach ($list as $k => $v)
+            {
+                if (in_array($v, $username))
+                {
+                    unset($blackls[$k]);
+                }
+            }
+            $blackls = implode(',', $blackls);
+        }
+        $this->db->query("UPDATE " . UC_DBTABLEPRE . "memberfields SET blacklist='$blackls' WHERE uid='$uid'");
+        return 1;
+    }
+
+    function removecode($str, $length)
+    {
+        static $uccode = null;
+        if ($uccode === null)
+        {
+            require_once UC_ROOT . 'lib/uccode.class.php';
+            $uccode = new uccode();
+        }
+        $str = $uccode->complie($str);
+        return trim($this->base->cutstr(strip_tags($str), $length));
+    }
+
+    function ispminterval($uid, $interval = 0)
+    {
+        if (!$uid)
+        {
+            return 0;
+        }
+        $interval = intval($interval);
+        if (!$interval)
+        {
+            return 1;
+        }
+        $lastupdate = $this->db->result_first("SELECT lastupdate FROM " . UC_DBTABLEPRE . "pm_members WHERE uid='$uid' ORDER BY lastupdate DESC LIMIT 1");
+        if (($this->base->time - $lastupdate) > $interval)
+        {
+            return 1;
+        }
+        else
+        {
+            return 0;
+        }
+    }
+
+    function isprivatepmthreadlimit($uid, $maxnum = 0)
+    {
+        if (!$uid)
+        {
+            return 0;
+        }
+        $maxnum = intval($maxnum);
+        if (!$maxnum)
+        {
+            return 1;
+        }
+        $num = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "pm_members m LEFT JOIN " . UC_DBTABLEPRE . "pm_lists t ON m.plid=t.plid WHERE uid='$uid' AND lastupdate>'" . ($this->base->time - 86400) . "' AND t.pmtype=1");
+        if ($maxnum - $num < 0)
+        {
+            return 0;
+        }
+        else
+        {
+            return 1;
+        }
+    }
+
+    function ischatpmthreadlimit($uid, $maxnum = 0)
+    {
+        if (!$uid)
+        {
+            return 0;
+        }
+        $maxnum = intval($maxnum);
+        if (!$maxnum)
+        {
+            return 1;
+        }
+        $num = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "pm_lists WHERE authorid='$uid' AND dateline>'" . ($this->base->time - 86400) . "'");
+        if ($maxnum - $num < 0)
+        {
+            return 0;
+        }
+        else
+        {
+            return 1;
+        }
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/model/tag.php b/extend/fast/ucenter/client/uc_client/model/tag.php
new file mode 100644
index 0000000..4a8261f
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/tag.php
@@ -0,0 +1,84 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: tag.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class tagmodel
+{
+
+    var $db;
+    var $base;
+
+    function __construct(&$base)
+    {
+        $this->tagmodel($base);
+    }
+
+    function tagmodel(&$base)
+    {
+        $this->base = $base;
+        $this->db = $base->db;
+    }
+
+    function get_tag_by_name($tagname)
+    {
+        $arr = $this->db->fetch_all("SELECT * FROM " . UC_DBTABLEPRE . "tags WHERE tagname='$tagname'");
+        return $arr;
+    }
+
+    function get_template($appid)
+    {
+        $result = $this->db->result_first("SELECT tagtemplates FROM " . UC_DBTABLEPRE . "applications WHERE appid='$appid'");
+        return $result;
+    }
+
+    function updatedata($appid, $data)
+    {
+        $appid = intval($appid);
+        include_once UC_ROOT . 'lib/xml.class.php';
+        $data = @xml_unserialize($data);
+        $this->base->load('app');
+        $data[0] = addslashes($data[0]);
+        $datanew = array();
+        if (is_array($data[1]))
+        {
+            foreach ($data[1] as $r)
+            {
+                $datanew[] = $_ENV['misc']->array2string($r);
+            }
+        }
+        $tmp = $_ENV['app']->get_apps('type', "appid='$appid'");
+        $datanew = addslashes($tmp[0]['type'] . "\t" . implode("\t", $datanew));
+        if (!empty($data[0]))
+        {
+            $return = $this->db->result_first("SELECT count(*) FROM " . UC_DBTABLEPRE . "tags WHERE tagname='$data[0]' AND appid='$appid'");
+            if ($return)
+            {
+                $this->db->query("UPDATE " . UC_DBTABLEPRE . "tags SET data='$datanew', expiration='" . $this->base->time . "' WHERE tagname='$data[0]' AND appid='$appid'");
+            }
+            else
+            {
+                $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "tags (tagname, appid, data, expiration) VALUES ('$data[0]', '$appid', '$datanew', '" . $this->base->time . "')");
+            }
+        }
+    }
+
+    function formatcache($appid, $tagname)
+    {
+        $return = $this->db->result_first("SELECT count(*) FROM " . UC_DBTABLEPRE . "tags WHERE tagname='$tagname' AND appid='$appid'");
+        if ($return)
+        {
+            $this->db->query("UPDATE " . UC_DBTABLEPRE . "tags SET expiration='0' WHERE tagname='$tagname' AND appid='$appid'");
+        }
+        else
+        {
+            $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "tags (tagname, appid, expiration) VALUES ('$tagname', '$appid', '0')");
+        }
+    }
+
+}
diff --git a/extend/fast/ucenter/client/uc_client/model/user.php b/extend/fast/ucenter/client/uc_client/model/user.php
new file mode 100644
index 0000000..0766227
--- /dev/null
+++ b/extend/fast/ucenter/client/uc_client/model/user.php
@@ -0,0 +1,439 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: user.php 1179 2014-11-03 07:11:25Z hypowang $
+ */
+!defined('IN_UC') && exit('Access Denied');
+
+class usermodel
+{
+
+    var $db;
+    var $base;
+
+    function __construct(&$base)
+    {
+        $this->usermodel($base);
+    }
+
+    function usermodel(&$base)
+    {
+        $this->base = $base;
+        $this->db = $base->db;
+    }
+
+    function get_user_by_uid($uid)
+    {
+        $arr = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "members WHERE uid='$uid'");
+        return $arr;
+    }
+
+    function get_user_by_username($username)
+    {
+        $arr = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "members WHERE username='$username'");
+        return $arr;
+    }
+
+    function get_user_by_email($email)
+    {
+        $arr = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "members WHERE email='$email'");
+        return $arr;
+    }
+
+    function get_user_by_mobile($mobile)
+    {
+        $arr = $this->db->fetch_first("SELECT * FROM " . UC_DBTABLEPRE . "members WHERE mobile='$mobile'");
+        return $arr;
+    }
+
+    function check_username($username)
+    {
+        $guestexp = '\xA1\xA1|\xAC\xA3|^Guest|^\xD3\xCE\xBF\xCD|\xB9\x43\xAB\xC8';
+        $len = $this->dstrlen($username);
+        if ($len > 15 || $len < 3 || preg_match("/\s+|^c:\\con\\con|[%,\*\"\s\<\>\&]|$guestexp/is", $username))
+        {
+            return FALSE;
+        }
+        else
+        {
+            return TRUE;
+        }
+    }
+
+    function dstrlen($str)
+    {
+        if (strtolower(UC_CHARSET) != 'utf-8')
+        {
+            return strlen($str);
+        }
+        $count = 0;
+        for ($i = 0; $i < strlen($str); $i++)
+        {
+            $value = ord($str[$i]);
+            if ($value > 127)
+            {
+                $count++;
+                if ($value >= 192 && $value <= 223)
+                    $i++;
+                elseif ($value >= 224 && $value <= 239)
+                    $i = $i + 2;
+                elseif ($value >= 240 && $value <= 247)
+                    $i = $i + 3;
+            }
+            $count++;
+        }
+        return $count;
+    }
+
+    function check_mergeuser($username)
+    {
+        $data = $this->db->result_first("SELECT count(*) FROM " . UC_DBTABLEPRE . "mergemembers WHERE appid='" . $this->base->app['appid'] . "' AND username='$username'");
+        return $data;
+    }
+
+    function check_usernamecensor($username)
+    {
+        $_CACHE['badwords'] = $this->base->cache('badwords');
+        $censorusername = $this->base->get_setting('censorusername');
+        $censorusername = $censorusername['censorusername'];
+        $censorexp = '/^(' . str_replace(array('\\*', "\r\n", ' '), array('.*', '|', ''), preg_quote(($censorusername = trim($censorusername)), '/')) . ')$/i';
+        $usernamereplaced = isset($_CACHE['badwords']['findpattern']) && !empty($_CACHE['badwords']['findpattern']) ? @preg_replace($_CACHE['badwords']['findpattern'], $_CACHE['badwords']['replace'], $username) : $username;
+        return !(($usernamereplaced != $username) || ($censorusername && preg_match($censorexp, $username)));
+    }
+
+    function check_usernameexists($username)
+    {
+        $data = $this->db->result_first("SELECT username FROM " . UC_DBTABLEPRE . "members WHERE username='$username'");
+        return $data;
+    }
+
+    function check_emailformat($email)
+    {
+        return strlen($email) > 6 && strlen($email) <= 32 && preg_match("/^([a-z0-9\-_.+]+)@([a-z0-9\-]+[.][a-z0-9\-.]+)$/", $email);
+    }
+
+    function check_emailaccess($email)
+    {
+        $setting = $this->base->get_setting(array('accessemail', 'censoremail'));
+        $accessemail = $setting['accessemail'];
+        $censoremail = $setting['censoremail'];
+        $accessexp = '/(' . str_replace("\r\n", '|', preg_quote(trim($accessemail), '/')) . ')$/i';
+        $censorexp = '/(' . str_replace("\r\n", '|', preg_quote(trim($censoremail), '/')) . ')$/i';
+        if ($accessemail || $censoremail)
+        {
+            return !(($accessemail && !preg_match($accessexp, $email)) || ($censoremail && preg_match($censorexp, $email)));
+        }
+        else
+        {
+            return TRUE;
+        }
+    }
+
+    function check_emailexists($email, $username = '')
+    {
+        $sqladd = $username !== '' ? "AND username<>'$username'" : '';
+        $email = $this->db->result_first("SELECT email FROM  " . UC_DBTABLEPRE . "members WHERE email='$email' $sqladd");
+        return $email;
+    }
+
+    function check_mobileformat($mobile)
+    {
+        return strlen($mobile) >= 11 && preg_match("/^1[34578]\d{9}$/", $mobile);
+    }
+
+    function check_mobileaccess($mobile)
+    {
+        $setting = $this->base->get_setting(array('accessmobile', 'censormobile'));
+        $accessmobile = $setting['accessmobile'];
+        $censormobile = $setting['censormobile'];
+        $accessexp = '/(' . str_replace("\r\n", '|', preg_quote(trim($accessmobile), '/')) . ')$/i';
+        $censorexp = '/(' . str_replace("\r\n", '|', preg_quote(trim($censormobile), '/')) . ')$/i';
+        if ($accessmobile || $censormobile)
+        {
+            return !(($accessmobile && !preg_match($accessexp, $mobile)) || ($censormobile && preg_match($censorexp, $mobile)));
+        }
+        else
+        {
+            return TRUE;
+        }
+    }
+
+    function check_mobileexists($mobile, $username = '')
+    {
+        $sqladd = $username !== '' ? "AND username<>'$username'" : '';
+        $mobile = $this->db->result_first("SELECT mobile FROM  " . UC_DBTABLEPRE . "members WHERE mobile='$mobile' $sqladd");
+        return $mobile;
+    }
+
+    /**
+     * 检测是否登录
+     * @param string $username
+     * @param string $password
+     * @param array $user
+     * @return int
+     */
+    function check_login($username, $password, &$user)
+    {
+        $user = $this->get_user_by_username($username);
+        if (empty($user['username']))
+        {
+            return -1;
+        }
+        elseif ($user['password'] != md5(md5($password) . $user['salt']))
+        {
+            return -2;
+        }
+        return $user['uid'];
+    }
+
+    /**
+     * 在Ucenter中添加一个账号
+     * @param string $username
+     * @param string $password
+     * @param string $email
+     * @param string $mobile
+     * @param int $uid
+     * @param array $extend
+     * @return int
+     */
+    function add_user($username, $password, $email = '', $mobile = '', $uid = 0, $extend = [])
+    {
+        $regip = $this->base->onlineip;
+        $salt = substr(uniqid(rand()), -6);
+        $password = md5(md5($password) . $salt);
+
+        $values = $uid ? "uid='" . intval($uid) . "'," : '';
+        $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "members SET $values username='$username', password='$password', email='$email', regip='$regip', regdate='" . $this->base->time . "', salt='$salt'");
+        $uid = $this->db->insert_id();
+        $extendfields = [];
+        foreach ($extend as $k => $v)
+        {
+            $extendfields[] = "{$k} = '{$v}'";
+        }
+        $extendfields = $extendfields ? ',' . implode(',', $extendfields) : '';
+        $this->db->query("INSERT INTO " . UC_DBTABLEPRE . "memberfields SET uid='$uid' {$extendfields}");
+        return $uid;
+    }
+
+    /**
+     * 编辑Ucenter中的账号
+     * @param string $username
+     * @param string $password
+     * @param string $email
+     * @param string $mobile
+     * @return int
+     */
+    function edit_user($username, $password = '', $email = '', $mobile = '', $uid = 0, $extend = [])
+    {
+        $uid = intval($uid);
+        $data = $this->db->fetch_first("SELECT username, uid, password, salt FROM " . UC_DBTABLEPRE . "members WHERE uid='$uid'");
+        if (!$data)
+        {
+            return -7;
+        }
+        $isprotected = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "protectedmembers WHERE uid = '{$data['uid']}'");
+        if ($isprotected)
+        {
+            return -8;
+        }
+        $fields = [];
+        if ($username)
+            $fields[] = "username = '{$username}'";
+        if ($email)
+            $fields[] = "email = '{$email}'";
+        if ($mobile)
+            $fields[] = "mobile = '{$mobile}'";
+        if ($password)
+            $fields[] = "password = '" . md5(md5($password) . $data['salt']) . "'";
+        $values = implode(',', $fields);
+        if ($values || $extend)
+        {
+            if ($values)
+            {
+                $this->db->query("UPDATE " . UC_DBTABLEPRE . "members SET $values WHERE uid='{$data['uid']}'");
+            }
+            $extendfields = [];
+            foreach ($extend as $k => $v)
+            {
+                $extendfields[] = "{$k} = '{$v}'";
+            }
+            $extendfields = $extendfields ? implode(',', $extendfields) : '';
+            if ($extendfields)
+            {
+                $this->db->query("UPDATE " . UC_DBTABLEPRE . "memberfields SET {$extendfields} WHERE uid='{$data['uid']}'");
+            }
+            return 1;
+        }
+        else
+        {
+            return -7;
+        }
+    }
+
+    /**
+     * 删除一个账号
+     * @param array $uidsarr
+     * @return int
+     */
+    function delete_user($uidsarr)
+    {
+        $uidsarr = (array) $uidsarr;
+        if (!$uidsarr)
+        {
+            return 0;
+        }
+        $uids = $this->base->implode($uidsarr);
+        $arr = $this->db->fetch_all("SELECT uid FROM " . UC_DBTABLEPRE . "protectedmembers WHERE uid IN ($uids)");
+        $puids = array();
+        foreach ((array) $arr as $member)
+        {
+            $puids[] = $member['uid'];
+        }
+        $uids = $this->base->implode(array_diff($uidsarr, $puids));
+        if ($uids)
+        {
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "members WHERE uid IN($uids)");
+            $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "memberfields WHERE uid IN($uids)");
+            uc_user_deleteavatar($uidsarr);
+            $this->base->load('note');
+            $_ENV['note']->add('deleteuser', "ids=$uids");
+            return $this->db->affected_rows();
+        }
+        else
+        {
+            return 0;
+        }
+    }
+
+    function delete_useravatar($uidsarr)
+    {
+        $uidsarr = (array) $uidsarr;
+        foreach ((array) $uidsarr as $uid)
+        {
+            file_exists($avatar_file = UC_DATADIR . './avatar/' . $this->base->get_avatar($uid, 'big', 'real')) && unlink($avatar_file);
+            file_exists($avatar_file = UC_DATADIR . './avatar/' . $this->base->get_avatar($uid, 'middle', 'real')) && unlink($avatar_file);
+            file_exists($avatar_file = UC_DATADIR . './avatar/' . $this->base->get_avatar($uid, 'small', 'real')) && unlink($avatar_file);
+            file_exists($avatar_file = UC_DATADIR . './avatar/' . $this->base->get_avatar($uid, 'big')) && unlink($avatar_file);
+            file_exists($avatar_file = UC_DATADIR . './avatar/' . $this->base->get_avatar($uid, 'middle')) && unlink($avatar_file);
+            file_exists($avatar_file = UC_DATADIR . './avatar/' . $this->base->get_avatar($uid, 'small')) && unlink($avatar_file);
+        }
+    }
+
+    /**
+     * 获取账号总数
+     * @param string $sqladd
+     * @return int
+     */
+    function get_total_num($sqladd = '')
+    {
+        $data = $this->db->result_first("SELECT COUNT(*) FROM " . UC_DBTABLEPRE . "members $sqladd");
+        return $data;
+    }
+
+    /**
+     * 获取列表
+     * @param int $page
+     * @param int $ppp
+     * @param int $totalnum
+     * @param string $sqladd
+     * @return array
+     */
+    function get_list($page, $ppp, $totalnum, $sqladd)
+    {
+        $start = $this->base->page_get_start($page, $ppp, $totalnum);
+        $data = $this->db->fetch_all("SELECT * FROM " . UC_DBTABLEPRE . "members $sqladd LIMIT $start, $ppp");
+        return $data;
+    }
+
+    function name2id($usernamesarr)
+    {
+        $usernamesarr = uc_addslashes($usernamesarr, 1, TRUE);
+        $usernames = $this->base->implode($usernamesarr);
+        $query = $this->db->query("SELECT uid FROM " . UC_DBTABLEPRE . "members WHERE username IN($usernames)");
+        $arr = array();
+        while ($user = $this->db->fetch_array($query))
+        {
+            $arr[] = $user['uid'];
+        }
+        return $arr;
+    }
+
+    function id2name($uidarr)
+    {
+        $arr = array();
+        $query = $this->db->query("SELECT uid, username FROM " . UC_DBTABLEPRE . "members WHERE uid IN (" . $this->base->implode($uidarr) . ")");
+        while ($user = $this->db->fetch_array($query))
+        {
+            $arr[$user['uid']] = $user['username'];
+        }
+        return $arr;
+    }
+
+    function quescrypt($questionid, $answer)
+    {
+        return $questionid > 0 && $answer != '' ? substr(md5($answer . md5($questionid)), 16, 8) : '';
+    }
+
+    function can_do_login($username, $ip = '')
+    {
+
+        $check_times = $this->base->settings['login_failedtime'] < 1 ? 5 : $this->base->settings['login_failedtime'];
+
+        $username = substr(md5($username), 8, 15);
+        $expire = 15 * 60;
+        if (!$ip)
+        {
+            $ip = $this->base->onlineip;
+        }
+
+        $ip_check = $user_check = array();
+        $query = $this->db->query("SELECT * FROM " . UC_DBTABLEPRE . "failedlogins WHERE ip='" . $ip . "' OR ip='$username'");
+        while ($row = $this->db->fetch_array($query))
+        {
+            if ($row['ip'] === $username)
+            {
+                $user_check = $row;
+            }
+            elseif ($row['ip'] === $ip)
+            {
+                $ip_check = $row;
+            }
+        }
+
+        if (empty($ip_check) || ($this->base->time - $ip_check['lastupdate'] > $expire))
+        {
+            $ip_check = array();
+            $this->db->query("REPLACE INTO " . UC_DBTABLEPRE . "failedlogins (ip, count, lastupdate) VALUES ('{$ip}', '0', '{$this->base->time}')");
+        }
+
+        if (empty($user_check) || ($this->base->time - $user_check['lastupdate'] > $expire))
+        {
+            $user_check = array();
+            $this->db->query("REPLACE INTO " . UC_DBTABLEPRE . "failedlogins (ip, count, lastupdate) VALUES ('{$username}', '0', '{$this->base->time}')");
+        }
+
+        if ($ip_check || $user_check)
+        {
+            $time_left = min(($check_times - $ip_check['count']), ($check_times - $user_check['count']));
+            return $time_left;
+        }
+
+        $this->db->query("DELETE FROM " . UC_DBTABLEPRE . "failedlogins WHERE lastupdate<" . ($this->base->time - ($expire + 1)), 'UNBUFFERED');
+
+        return $check_times;
+    }
+
+    function loginfailed($username, $ip = '')
+    {
+        $username = substr(md5($username), 8, 15);
+        if (!$ip)
+        {
+            $ip = $this->base->onlineip;
+        }
+        $this->db->query("UPDATE " . UC_DBTABLEPRE . "failedlogins SET count=count+1, lastupdate='" . $this->base->time . "' WHERE ip='" . $ip . "' OR ip='$username'");
+    }
+
+}
diff --git a/extend/fast/ucenter/common/Functions.php b/extend/fast/ucenter/common/Functions.php
new file mode 100644
index 0000000..9f9df9d
--- /dev/null
+++ b/extend/fast/ucenter/common/Functions.php
@@ -0,0 +1,98 @@
+<?php
+
+function _uc_authcode($string, $operation = 'DECODE', $key = '', $expiry = 0)
+{
+    $ckey_length = 4;
+
+    $key = md5($key ? $key : UC_KEY);
+    $keya = md5(substr($key, 0, 16));
+    $keyb = md5(substr($key, 16, 16));
+    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
+
+    $cryptkey = $keya . md5($keya . $keyc);
+    $key_length = strlen($cryptkey);
+
+    $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
+    $string_length = strlen($string);
+
+    $result = '';
+    $box = range(0, 255);
+
+    $rndkey = array();
+    for ($i = 0; $i <= 255; $i++)
+    {
+        $rndkey[$i] = ord($cryptkey[$i % $key_length]);
+    }
+
+    for ($j = $i = 0; $i < 256; $i++)
+    {
+        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
+        $tmp = $box[$i];
+        $box[$i] = $box[$j];
+        $box[$j] = $tmp;
+    }
+
+    for ($a = $j = $i = 0; $i < $string_length; $i++)
+    {
+        $a = ($a + 1) % 256;
+        $j = ($j + $box[$a]) % 256;
+        $tmp = $box[$a];
+        $box[$a] = $box[$j];
+        $box[$j] = $tmp;
+        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
+    }
+
+    if ($operation == 'DECODE')
+    {
+        if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16))
+        {
+            return substr($result, 26);
+        }
+        else
+        {
+            return '';
+        }
+    }
+    else
+    {
+        return $keyc . str_replace('=', '', base64_encode($result));
+    }
+}
+
+function _uc_stripslashes($string)
+{
+    if (is_array($string))
+    {
+        foreach ($string as $key => $val)
+        {
+            $string[$key] = _stripslashes($val);
+        }
+    }
+    else
+    {
+        $string = stripslashes($string);
+    }
+    return $string;
+}
+
+/**
+ * 字符串命名风格转换
+ * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
+ * @param string $name 字符串
+ * @param integer $type 转换类型
+ * @return string
+ */
+function parse_name($name, $type = 0)
+{
+    if ($type)
+    {
+        return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function($match)
+                {
+                    return strtoupper($match[1]);
+                }, $name));
+    }
+    else
+    {
+        return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
+    }
+}
diff --git a/extend/fast/ucenter/common/XML.php b/extend/fast/ucenter/common/XML.php
new file mode 100644
index 0000000..938fbd5
--- /dev/null
+++ b/extend/fast/ucenter/common/XML.php
@@ -0,0 +1,129 @@
+<?php
+
+/*
+  [UCenter] (C)2001-2099 Comsenz Inc.
+  This is NOT a freeware, use is subject to license terms
+
+  $Id: xml.class.php 1059 2011-03-01 07:25:09Z monkey $
+ */
+
+function xml_unserialize(&$xml, $isnormal = FALSE)
+{
+    $xml_parser = new XML($isnormal);
+    $data = $xml_parser->parse($xml);
+    $xml_parser->destruct();
+    return $data;
+}
+
+function xml_serialize($arr, $htmlon = FALSE, $isnormal = FALSE, $level = 1)
+{
+    $s = $level == 1 ? "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n<root>\r\n" : '';
+    $space = str_repeat("\t", $level);
+    foreach ($arr as $k => $v)
+    {
+        if (!is_array($v))
+        {
+            $s .= $space . "<item id=\"$k\">" . ($htmlon ? '<![CDATA[' : '') . $v . ($htmlon ? ']]>' : '') . "</item>\r\n";
+        }
+        else
+        {
+            $s .= $space . "<item id=\"$k\">\r\n" . xml_serialize($v, $htmlon, $isnormal, $level + 1) . $space . "</item>\r\n";
+        }
+    }
+    $s = preg_replace("/([\x01-\x08\x0b-\x0c\x0e-\x1f])+/", ' ', $s);
+    return $level == 1 ? $s . "</root>" : $s;
+}
+
+class XML
+{
+
+    var $parser;
+    var $document;
+    var $stack;
+    var $data;
+    var $last_opened_tag;
+    var $isnormal;
+    var $attrs = array();
+    var $failed = FALSE;
+
+    function __construct($isnormal)
+    {
+        $this->XML($isnormal);
+    }
+
+    function XML($isnormal)
+    {
+        $this->isnormal = $isnormal;
+        $this->parser = xml_parser_create('ISO-8859-1');
+        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
+        xml_set_object($this->parser, $this);
+        xml_set_element_handler($this->parser, 'open', 'close');
+        xml_set_character_data_handler($this->parser, 'data');
+    }
+
+    function destruct()
+    {
+        xml_parser_free($this->parser);
+    }
+
+    function parse(&$data)
+    {
+        $this->document = array();
+        $this->stack = array();
+        return xml_parse($this->parser, $data, true) && !$this->failed ? $this->document : '';
+    }
+
+    function open(&$parser, $tag, $attributes)
+    {
+        $this->data = '';
+        $this->failed = FALSE;
+        if (!$this->isnormal)
+        {
+            if (isset($attributes['id']) && !is_string($this->document[$attributes['id']]))
+            {
+                $this->document = &$this->document[$attributes['id']];
+            }
+            else
+            {
+                $this->failed = TRUE;
+            }
+        }
+        else
+        {
+            if (!isset($this->document[$tag]) || !is_string($this->document[$tag]))
+            {
+                $this->document = &$this->document[$tag];
+            }
+            else
+            {
+                $this->failed = TRUE;
+            }
+        }
+        $this->stack[] = &$this->document;
+        $this->last_opened_tag = $tag;
+        $this->attrs = $attributes;
+    }
+
+    function data(&$parser, $data)
+    {
+        if ($this->last_opened_tag != NULL)
+        {
+            $this->data .= $data;
+        }
+    }
+
+    function close(&$parser, $tag)
+    {
+        if ($this->last_opened_tag == $tag)
+        {
+            $this->document = $this->data;
+            $this->last_opened_tag = NULL;
+        }
+        array_pop($this->stack);
+        if ($this->stack)
+        {
+            $this->document = &$this->stack[count($this->stack) - 1];
+        }
+    }
+
+}