diff --git a/README.md b/README.md index e9c41e9..c936d93 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ -TwoNav 是一款开源免费的书签(导航)管理程序,使用PHP + SQLite 3开发,界面简洁,安装简单,使用方便。TwoNav可帮助你你将浏览器书签集中式管理,解决跨设备、跨平台、跨浏览器之间同步和访问困难问题,做到一处部署,随处访问。 +TwoNav 是一款开源免费的书签(导航)管理程序,界面简洁,安装简单,使用方便。TwoNav可帮助你将浏览器书签集中式管理,解决跨设备、跨平台、跨浏览器之间同步和访问困难问题,做到一处部署,随处访问。 - **演示站**: [http://two.lm21.top](http://two.lm21.top) - **仅供体验,定期清理数据** 账号密码`admin` -### 内测版转正 -* 删除安装目录下`system`文件夹后在解压覆盖 ### 相关文档 * [安装教程](https://gitee.com/tznb/TwoNav/wikis/pages?sort_id=7968668&doc_id=3767990) | [使用说明](https://gitee.com/tznb/TwoNav/wikis) | [下载TwoNav](https://gitee.com/tznb/TwoNav/releases) @@ -18,7 +16,12 @@ TwoNav 是一款开源免费的书签(导航)管理程序,使用PHP + SQLi - QQ: 271152681 - QQ群: 695720839 +### 运行环境 +* PHP: 7.3 - 8.2 +* 数据库: SQLite3 或 MySQL > 5.6.0 + ### 功能特色 +* 支持 * 支持后台管理 * 支持私有链接 * 支持加密链接 @@ -34,4 +37,4 @@ TwoNav 是一款开源免费的书签(导航)管理程序,使用PHP + SQLi * 支持Chromium内核的[浏览器扩展]  - \ No newline at end of file + diff --git a/system/api.php b/system/api.php index 2bb4cfd..d9e62b6 100644 --- a/system/api.php +++ b/system/api.php @@ -159,8 +159,14 @@ function write_category(){ msg(-1,'加密组不存在'); } + //长度检测 + if(strlen(htmlspecialchars($_POST['name'],ENT_QUOTES)) > 128 ){ + msg(-1,'分类名称长度超限'); + } + if(strlen(htmlspecialchars($_POST['description'],ENT_QUOTES)) > 128 ){ + msg(-1,'分类描述长度超限'); + } //取最大CID - //$cid = intval(max_db('user_categorys','cid',['uid'=>UID])) +1; $cid = get_maxid('category_id'); //插入数据库 insert_db('user_categorys',[ @@ -186,17 +192,14 @@ function write_category(){ if($_POST['cid'] == $_POST['fid']){ msg(-1,'父分类不能是自己'); } - //查CID是否存在 if(!get_db('user_categorys','cid',['uid'=>UID ,"cid" => intval($_POST['cid'])])){ msg(-1,'分类不存在'); } - //分类名查重(排除自身) if(get_db('user_categorys','cid',['uid'=>UID,'cid[!]'=>intval($_POST['cid']),"name" => $_POST['name']])){ msg(-1,'分类名称已存在'); } - //父分类不能是二级分类 if(intval($_POST['fid']) !=0 && get_db('user_categorys','fid',['uid'=>UID ,"cid" => intval($_POST['fid']) ]) !=0 ){ msg(-1,'父分类不能是二级分类'); @@ -205,16 +208,22 @@ function write_category(){ if( $_POST['fid']!=0 && count_db('user_categorys',['uid'=>UID,'fid'=>$_POST['cid']])>0){ msg(-1,'该分类下已存在子分类!'); } - //查父分类是否存在 if( $_POST['fid'] !=0 && !get_db('user_categorys','cid',['uid'=>UID ,"cid" => intval($_POST['fid'])])){ msg(-1,'父分类不存在'); } - //加密组pid是否存在 if(intval($_POST['pwd_id']) !=0 && empty(get_db('user_pwd_group','pid',['uid'=>UID ,"pid" => intval($_POST['pwd_id'])]))){ msg(-1,'加密组不存在'); } + //长度检测 + if(strlen(htmlspecialchars($_POST['name'],ENT_QUOTES)) > 128 ){ + msg(-1,'分类名称长度超限'); + } + if(strlen(htmlspecialchars($_POST['description'],ENT_QUOTES)) > 128 ){ + msg(-1,'分类描述长度超限'); + } + //更新数据 $data = [ 'fid'=>$_POST['fid'], @@ -363,22 +372,16 @@ function write_link(){ $description = empty($_POST['description']) ? '' : $_POST['description']; $property = empty($_POST['property']) ? 0 : 1; //检测链接是否合法 - check_link($fid,$title,$url); + check_link($fid,$title,$url,$_POST['url_standby']); //检查链接是否已存在 if(get_db('user_links','lid',['uid'=>UID ,"url" => $url])){ msg(-1,'链接已存在!'); } - //备用链接检测 - if(!empty($_POST['url_standby'])){ - foreach ($_POST['url_standby'] as $key => $url_standby){ - //尝试匹配Markdown语法的URL,如果没有则认为直接输入 - if(preg_match('/\[(.*?)\]\((.*?)\)/', $url_standby, $match)){ - check_link($fid,$title,$match[2]); - }else{ - check_link($fid,$title,$url_standby); - } - } + //描述长度检测 + if(strlen($description) > 128 || strlen(htmlspecialchars($description,ENT_QUOTES)) > 128 ){ + msg(-1,'描述长度超限'); } + //取最大链接ID $lid = get_maxid('link_id'); //图标处理 @@ -526,22 +529,15 @@ function write_link(){ $description = empty($_POST['description']) ? '' : $_POST['description']; $property = empty($_POST['property']) ? 0 : 1; //检测链接是否合法 - check_link($fid,$title,$url); - //检查链接是否已存在 - if(get_db('user_links','lid',['uid'=>UID ,'lid[!]'=>$lid, "url" => $url])){msg(-1,'链接已存在!');} - //检查链接ID是否存在 - if(!get_db('user_links','lid',['uid'=>UID ,'lid'=>$lid])){msg(-1,'链接ID不存在!');} - //备用链接检测 - if(!empty($_POST['url_standby'])){ - foreach ($_POST['url_standby'] as $key => $url_standby){ - //尝试匹配Markdown语法的URL,如果没有则认为直接输入 - if(preg_match('/\[(.*?)\]\((.*?)\)/', $url_standby, $match)){ - check_link($fid,$title,$match[2]); - }else{ - check_link($fid,$title,$url_standby); - } - } + check_link($fid,$title,$url,$_POST['url_standby']); + //描述长度检测 + if(strlen($description) > 128 || strlen(htmlspecialchars($description,ENT_QUOTES)) > 128 ){ + msg(-1,'描述长度超限'); } + //检查链接是否已存在 + if(has_db('user_links',['uid'=>UID ,'lid[!]'=>$lid, "url" => $url])){msg(-1,'链接已存在!');} + //检查链接ID是否存在 + if(!has_db('user_links',['uid'=>UID ,'lid'=>$lid])){msg(-1,'链接ID不存在!');} $data = [ 'fid' => $fid, @@ -948,6 +944,7 @@ function write_site_setting(){ 'keywords'=>['empty'=>true], 'description'=>['empty'=>true], 'link_model'=>['v'=>['direct','Privacy','Privacy_js','Privacy_meta','301','302','Transition'],'msg'=>'链接模式参数错误'], + 'main_link_priority'=>['int'=>true,'min'=>0,'max'=>1,'msg'=>'主链优先参数错误'], 'link_icon'=>['int'=>true,'min'=>0,'max'=>10,'msg'=>'链接图标参数错误'], 'site_icon'=>['empty'=>true], 'top_link'=>['int'=>true,'min'=>0,'max'=>20,'msg'=>'热门链接参数错误'], @@ -1454,8 +1451,8 @@ function read_data(){ $php_version = floatval(PHP_VERSION); $log .= "PHP版本:{$php_version}\n"; $log .= "Web版本:{$_SERVER['SERVER_SOFTWARE']}\n"; - if( ( $php_version < 7.3 ) || ( $php_version > 8.1 ) ) { - $log .= "PHP版本:不满足要求,需要7.3 <= PHP <= 8.1 )\n"; + if( ( $php_version < 7.3 ) || ( $php_version > 8.2 ) ) { + $log .= "PHP版本:不满足要求,支持范围7.3 - 8.2 )\n"; } //获取加载的模块 $ext = get_loaded_extensions(); diff --git a/system/click.php b/system/click.php index 59e8ab8..03e43b9 100644 --- a/system/click.php +++ b/system/click.php @@ -131,15 +131,26 @@ if($global_config['link_extend'] == 1 && check_purview('link_extend',1) && in_ar //如果存在备用链接,则强制载入过渡页 if(!empty($link['url_standby'])) { $link['url_standby'] = unserialize($link['url_standby']); - require $transit_path; - exit; + //主链优先模式 + if($site['main_link_priority'] == 1){ + $code = get_http_code($link['url'],3); + if(in_array(intval($code),[200,301,302]) ){ + $site['link_model'] = $site['link_model'] == 'direct' ? '302' : $site['link_model']; + }else{ + require $transit_path; + exit; + } + }else{ + require $transit_path; + exit; + } } -if ($site['link_model'] == '302'){ //302重定向 +if ($site['link_model'] == '302'){ //302重定向(临时) header("HTTP/1.1 302 Moved Permanently"); header("Location: ".$link['url']); exit; -}elseif($site['link_model'] == '301'){ //301重定向 +}elseif($site['link_model'] == '301'){ //301重定向(永久) header("HTTP/1.1 301 Moved Permanently"); header("Location: ".$link['url']); exit; diff --git a/system/install.php b/system/install.php index 341b66e..617c67b 100644 --- a/system/install.php +++ b/system/install.php @@ -32,7 +32,7 @@ function check_env() { $php_version = floatval(PHP_VERSION); //获取PHP版本 if( ( $php_version < 7.3 ) || ( $php_version > 8.2 ) ) { - exit("当前PHP版本{$php_version}不满足要求,需要7.3 <= PHP <= 8.2"); + exit("当前PHP版本{$php_version}不满足要求,支持范围7.3 - 8.2"); } //检查是否支持pdo_sqlite diff --git a/system/public.php b/system/public.php index 68f3070..de24e63 100644 --- a/system/public.php +++ b/system/public.php @@ -233,18 +233,42 @@ function echo_pwds(){ } } //检查链接 -function check_link($fid,$title,$url,$url_standby=''){ - global $db; +function check_link($fid,$title,$url,$url_standby_s=''){ $pattern = "/^(http:\/\/|https:\/\/|ftp:\/\/|ftps:\/\/|sftp:\/\/|magnet:?|ed2k:\/\/|thunder:\/\/|tcp:\/\/|udp:\/\/|rtsp:\/\/).+/"; + if (empty($fid)) msg(-1,'分类id(fid)不能为空'); + if (empty($title)) msg(-1,'名称不能为空'); + if (strlen($title) > 64 ) msg(-1,'名称长度超限'); + if (strlen(htmlspecialchars($title,ENT_QUOTES)) > 128 ) msg(-1,'名称长度超限-2'); + if (!has_db('user_categorys',['uid'=>UID ,"cid" => $fid])) msg(-1,'分类不存在'); + //主链接检测 + if (empty($url)) msg(-1,'URL不能为空'); + if (!preg_match($pattern,$url)) msg(-1,'URL无效'); + if (strlen($url) > 1024 ) msg(-1,'URL长度超限'); + if (check_xss($url)) msg(-1,'URL存在非法字符'); + + //备用链接检测 + if(!empty($url_standby_s)){ + foreach ($url_standby_s as $key => $url_standby){ + //尝试匹配Markdown语法的URL,如果没有则认为直接输入 + if(preg_match('/\[(.*?)\]\((.*?)\)/', $url_standby, $match)){ + if (empty($match[1])) msg(-1,'备用链接名称不能为空,若不需要名称请直接输入URL'); + if (strlen($match[1]) > 64 ) msg(-1,'备用链接名称长度超限'); + if (strlen(htmlspecialchars($match[1],ENT_QUOTES)) > 128 ) msg(-1,'备用链接名称长度超限-2'); + $url = $match[2]; + }else{ + $url = $url_standby; + } + + if(!preg_match($pattern,$url)){ + msg(-1,'备选URL无效'); + }elseif(strlen($url) > 1024){ + msg(-1,'备选URL长度超限'); + }elseif(check_xss($url)){ + msg(-1,'备用URL存在非法字符'); + } + } + } - if(empty($fid)) {msg(-1007,'分类id(fid)不能为空!');} - if(!get_db('user_categorys','cid',['uid'=>UID ,"cid" => $fid])){msg(-1,'分类不存在');} - if (empty($title)){msg(-1008,'标题不能为空!');} - if (empty($url)){msg(-1009,'URL不能为空!');} - if (check_xss($url)){msg(-1010,'URL存在非法字符!');} - if (check_xss($url_standby)){msg(-1010,'备用URL存在非法字符!');} - if (!preg_match($pattern,$url)){msg(-1010,'URL无效!');} - if ( ( !empty($url_standby) ) && ( !preg_match($pattern,$url_standby) ) ) {msg(-1010,'备选URL无效!');} return true; } //获取版本号 @@ -473,13 +497,13 @@ function Get_IP() { } //获取URL状态码 -function get_http_code($url) { +function get_http_code($url,$TIMEOUT = 10) { $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_HEADER, 1); curl_setopt($curl, CURLOPT_NOBODY, true); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($curl, CURLOPT_TIMEOUT, 10); + curl_setopt($curl, CURLOPT_TIMEOUT, $TIMEOUT); curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'); $data = curl_exec($curl); $return = curl_getinfo($curl, CURLINFO_HTTP_CODE); diff --git a/system/templates.php b/system/templates.php index daf0b2e..7c7a8e6 100644 --- a/system/templates.php +++ b/system/templates.php @@ -51,7 +51,7 @@ if(empty($c) || in_array($c,['index','click'])){ }elseif ($icon ==1){ return('./favicon/index2.php?url='.$link['real_url']); }elseif($icon ==2){ - return('//favicon.rss.ink/v1/'.base64($link['real_url'])); + return('//favicon.png.pub/v1/'.base64($link['real_url'])); }elseif($icon ==4){ return('//api.15777.cn/get.php?url='.$link['real_url']); }elseif($icon ==5){ diff --git a/system/version.txt b/system/version.txt index 62f2079..0cb23ac 100644 --- a/system/version.txt +++ b/system/version.txt @@ -1 +1 @@ -v2.0.17-20230428 \ No newline at end of file +v2.0.18-20230510 \ No newline at end of file diff --git a/templates/admin/js/link_list.js b/templates/admin/js/link_list.js index 9c285d3..792a63b 100644 --- a/templates/admin/js/link_list.js +++ b/templates/admin/js/link_list.js @@ -19,27 +19,12 @@ layui.use(['form','table','dropdown','miniTab'], function () { layer.alert("获取分类数据失败,请刷新重试",{icon:5,title:'错误',anim: 2,closeBtn: 0,btn: ['刷新页面']},function () {location.reload();}); return; }else{ - $("#fid").empty(); - $("#fid").append(''); - $("#fid").append(''); - layui.form.render("select"); categorys = data.data;//赋值分类数据 renderTable2();//开始渲染表格 } }); } - var cols=[[ //表头 {type:'checkbox'} //开启复选框 ,{field: 'lid', title: 'ID', width:80, sort: true,hide:false} diff --git a/templates/admin/page/SiteSetting.php b/templates/admin/page/SiteSetting.php index b346c28..dc8148c 100644 --- a/templates/admin/page/SiteSetting.php +++ b/templates/admin/page/SiteSetting.php @@ -52,13 +52,24 @@