From b60deb4226d27a06599ddb10a7a11476ab1d8bdf Mon Sep 17 00:00:00 2001 From: tianya Date: Wed, 28 Oct 2020 11:00:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=97=85=E6=AF=92=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=EF=BC=8C=E6=96=87=E4=BB=B6=E6=A0=A1=E9=AA=8C=E5=90=88?= =?UTF-8?q?=E5=B9=B6=E5=88=B0=E7=97=85=E6=AF=92=E6=89=AB=E6=8F=8F=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dede/inc/inc_action_info.php | 6 - src/dede/inc/inc_menu.php | 1 - src/dede/inc/inc_menu_map.php | 1 - src/dede/mytag_tag_guide_ok.php | 5 +- src/dede/plus_edit.php | 10 +- src/dede/plus_main.php | 2 +- src/dede/recycling.php | 2 +- src/dede/search_keywords_main.php | 2 +- src/dede/shops_delivery.php | 16 +- src/dede/shops_operations.php | 12 +- src/dede/shops_operations_cart.php | 8 +- src/dede/shops_operations_userinfo.php | 6 +- src/dede/sys_safetest.php | 181 ++++---- src/dede/sys_verifies.php | 470 -------------------- src/dede/templets/sys_safetest.htm | 7 +- src/dede/templets/sys_safetest_viewdiff.htm | 95 ++++ src/dede/templets/sys_verifies.htm | 78 ---- src/dede/templets/sys_verifies_getfiles.htm | 56 --- src/dede/templets/sys_verifies_manage.htm | 74 --- src/dede/templets/sys_verifies_verify.htm | 73 --- src/include/dedehttpdown.class.php | 18 + src/static/css/diffview.css | 83 ++++ src/static/js/difflib.js | 411 +++++++++++++++++ src/static/js/diffview.js | 194 ++++++++ 24 files changed, 927 insertions(+), 884 deletions(-) delete mode 100755 src/dede/sys_verifies.php create mode 100644 src/dede/templets/sys_safetest_viewdiff.htm delete mode 100755 src/dede/templets/sys_verifies.htm delete mode 100755 src/dede/templets/sys_verifies_getfiles.htm delete mode 100755 src/dede/templets/sys_verifies_manage.htm delete mode 100755 src/dede/templets/sys_verifies_verify.htm create mode 100644 src/static/css/diffview.css create mode 100644 src/static/js/difflib.js create mode 100644 src/static/js/diffview.js diff --git a/src/dede/inc/inc_action_info.php b/src/dede/inc/inc_action_info.php index 1a68f1d5..1b1d38e2 100755 --- a/src/dede/inc/inc_action_info.php +++ b/src/dede/inc/inc_action_info.php @@ -446,12 +446,6 @@ $actionSearch[10] = array( 'purview' =>'sys_data', 'linkurl' =>'sys_sql_query.php' ), - 13 => array( - 'title' =>'文件校验[S]', - 'description' =>'文件校验将检查本站文件是否和dedecms原始文件是否完全一致', - 'purview' =>'sys_verifies', - 'linkurl' =>'sys_verifies.php' - ), 14 => array( 'title' =>'病毒扫描[S]', 'description' =>'以DedeCMS开发模式为标准对现有的文件进行扫描并进行判断', diff --git a/src/dede/inc/inc_menu.php b/src/dede/inc/inc_menu.php index b24e3ecb..cec1585c 100755 --- a/src/dede/inc/inc_menu.php +++ b/src/dede/inc/inc_menu.php @@ -72,7 +72,6 @@ $adminMenu2 = " - diff --git a/src/dede/inc/inc_menu_map.php b/src/dede/inc/inc_menu_map.php index 94a075e5..1955bb57 100755 --- a/src/dede/inc/inc_menu_map.php +++ b/src/dede/inc/inc_menu_map.php @@ -123,7 +123,6 @@ $menusMain = " - diff --git a/src/dede/mytag_tag_guide_ok.php b/src/dede/mytag_tag_guide_ok.php index 4b94cb3f..9a577b91 100755 --- a/src/dede/mytag_tag_guide_ok.php +++ b/src/dede/mytag_tag_guide_ok.php @@ -45,13 +45,12 @@ if($dopost=='savetag') { $fulltag = addslashes($fulltag); $tagname = "auto"; - $inQuery = " - INSERT INTO #@__mytag(typeid,tagname,timeset,starttime,endtime,normbody,expbody) + $inQuery = "INSERT INTO `#@__mytag`(typeid,tagname,timeset,starttime,endtime,normbody,expbody) VALUES('0','$tagname','0','0','0','$fulltag',''); "; $dsql->ExecuteNoneQuery($inQuery); $id = $dsql->GetLastID(); - $dsql->ExecuteNoneQuery("UPDATE #@__mytag SET tagname='{$tagname}_{$id}' WHERE aid='$id'"); + $dsql->ExecuteNoneQuery("UPDATE `#@__mytag` SET tagname='{$tagname}_{$id}' WHERE aid='$id'"); $fulltag = "{dede:mytag name='{$tagname}_{$id}' ismake='yes'/}"; } include DedeInclude('templets/mytag_tag_guide_ok.htm'); \ No newline at end of file diff --git a/src/dede/plus_edit.php b/src/dede/plus_edit.php index f769ce9c..f4a3b644 100755 --- a/src/dede/plus_edit.php +++ b/src/dede/plus_edit.php @@ -13,13 +13,13 @@ CheckPurview('sys_plus'); $aid = preg_replace("#[^0-9]#", "", $aid); if($dopost=="show") { - $dsql->ExecuteNoneQuery("UPDATE #@__plus SET isshow=1 WHERE aid='$aid';"); + $dsql->ExecuteNoneQuery("UPDATE `#@__plus` SET isshow=1 WHERE aid='$aid';"); ShowMsg("成功启用一个插件,请刷新导航菜单!","plus_main.php"); exit(); } else if($dopost=="hide") { - $dsql->ExecuteNoneQuery("UPDATE #@__plus SET isshow=0 WHERE aid='$aid';"); + $dsql->ExecuteNoneQuery("UPDATE `#@__plus` SET isshow=0 WHERE aid='$aid';"); ShowMsg("成功禁用一个插件,请刷新导航菜单!","plus_main.php"); exit(); } @@ -44,17 +44,17 @@ else if($dopost=="delete") } else if($job=="yes") //操作 { - $dsql->ExecuteNoneQuery("DELETE FROM #@__plus WHERE aid='$aid';"); + $dsql->ExecuteNoneQuery("DELETE FROM `#@__plus` WHERE aid='$aid';"); ShowMsg("成功删除一个插件,请刷新导航菜单!","plus_main.php"); exit(); } } else if($dopost=="saveedit") //保存更改 { - $inquery = "UPDATE #@__plus SET plusname='$plusname',menustring='$menustring',filelist='$filelist' WHERE aid='$aid';"; + $inquery = "UPDATE `#@__plus` SET plusname='$plusname',menustring='$menustring',filelist='$filelist' WHERE aid='$aid';"; $dsql->ExecuteNoneQuery($inquery); ShowMsg("成功更改插件的配置!","plus_main.php"); exit(); } -$row = $dsql->GetOne("SELECT * FROM #@__plus WHERE aid='$aid'"); +$row = $dsql->GetOne("SELECT * FROM `#@__plus` WHERE aid='$aid'"); include DedeInclude('templets/plus_edit.htm'); \ No newline at end of file diff --git a/src/dede/plus_main.php b/src/dede/plus_main.php index aa75c1a8..f3389e99 100755 --- a/src/dede/plus_main.php +++ b/src/dede/plus_main.php @@ -13,7 +13,7 @@ CheckPurview('sys_plus'); require_once(DEDEINC."/datalistcp.class.php"); setcookie("ENV_GOBACK_URL",$dedeNowurl,time()+3600,"/"); -$sql = "SELECT aid,plusname,writer,isshow FROM #@__plus ORDER BY aid ASC"; +$sql = "SELECT aid,plusname,writer,isshow FROM `#@__plus` ORDER BY aid ASC"; $dlist = new DataListCP(); $dlist->SetTemplet(DEDEADMIN."/templets/plus_main.htm"); $dlist->SetSource($sql); diff --git a/src/dede/recycling.php b/src/dede/recycling.php index e4b02535..afa485b9 100755 --- a/src/dede/recycling.php +++ b/src/dede/recycling.php @@ -22,7 +22,7 @@ if($cid!=0) $whereSql = " AND arc.typeid IN (".GetSonIds($cid).")"; } $query = "SELECT arc.*,tp.typename FROM `#@__archives` AS arc -LEFT JOIN #@__arctype AS tp ON arc.typeid = tp.id +LEFT JOIN `#@__arctype` AS tp ON arc.typeid = tp.id WHERE arc.arcrank = '-2' $whereSql order by arc.id desc"; $dlist = new DataListCP(); $dlist->SetTemplet(DEDEADMIN."/templets/recycling.htm"); diff --git a/src/dede/search_keywords_main.php b/src/dede/search_keywords_main.php index 5861bc35..0c73ee7d 100755 --- a/src/dede/search_keywords_main.php +++ b/src/dede/search_keywords_main.php @@ -84,7 +84,7 @@ function GetKeywordList($dsql,$pageno,$pagesize,$orderby='aid') echo $printhead; if($orderby=='result') $orderby = $orderby." ASC"; else $orderby = $orderby." DESC"; - $dsql->SetQuery("SELECT * FROM #@__search_keywords ORDER BY $orderby LIMIT $start,$pagesize "); + $dsql->SetQuery("SELECT * FROM `#@__search_keywords` ORDER BY $orderby LIMIT $start,$pagesize "); $dsql->Execute(); while($row = $dsql->GetArray()) { diff --git a/src/dede/shops_delivery.php b/src/dede/shops_delivery.php index eedddb3a..516d530f 100755 --- a/src/dede/shops_delivery.php +++ b/src/dede/shops_delivery.php @@ -26,7 +26,7 @@ if($do=='add') $price = '0.00'; } $des = cn_substrR($des,255); - $InQuery = "INSERT INTO #@__shops_delivery(`dname`,`price`,`des`) VALUES ('$dname','$price','$des');"; + $InQuery = "INSERT INTO `#@__shops_delivery`(`dname`,`price`,`des`) VALUES ('$dname','$price','$des');"; $result = $dsql->ExecuteNoneQuery($InQuery); if($result) { @@ -40,7 +40,7 @@ if($do=='add') } else if ($do == 'del') { $id = intval($id); - $dsql->ExecuteNoneQuery("DELETE FROM #@__shops_delivery WHERE pid='$id'"); + $dsql->ExecuteNoneQuery("DELETE FROM `#@__shops_delivery` WHERE pid='$id'"); ShowMsg("已删除当前配送方式!","shops_delivery.php"); exit(); } else if ($do == 'edit') @@ -48,7 +48,7 @@ if($do=='add') foreach($pid as $id) { $id = intval($id); - $row = $dsql->GetOne("SELECT pid,dname,price,des FROM #@__shops_delivery WHERE pid='$id' LIMIT 0,1"); + $row = $dsql->GetOne("SELECT pid,dname,price,des FROM `#@__shops_delivery` WHERE pid='$id' LIMIT 0,1"); if(!is_array($row)) { continue; @@ -73,22 +73,22 @@ if($do=='add') { $des = cn_substrR($des,255); } - $dsql->ExecuteNoneQuery("UPDATE #@__shops_delivery SET dname='$dname',price='$price',des='$des' WHERE pid='$id'"); + $dsql->ExecuteNoneQuery("UPDATE `#@__shops_delivery` SET dname='$dname',price='$price',des='$des' WHERE pid='$id'"); } ShowMsg("成功修改配送方式!","shops_delivery.php"); exit(); } $deliveryarr = array(); -$dsql->SetQuery("SELECT pid,dname,price,des FROM #@__shops_delivery ORDER BY orders ASC"); +$dsql->SetQuery("SELECT pid,dname,price,des FROM `#@__shops_delivery` ORDER BY orders ASC"); $dsql->Execute(); while($row = $dsql->GetArray()) { $deliveryarr[] = $row; } $dlist = new DataListCP(); -$dlist->pageSize = 25; //设定每页显示记录数(默认25条) +$dlist->pageSize = 25; //设定每页显示记录数(默认25条) //这两句的顺序不能更换 $dlist->SetTemplate(DEDEADMIN."/templets/shops_delivery.htm"); //载入模板 -$dlist->SetSource("SELECT `pid`,`dname`,`price`,`des` FROM #@__shops_delivery ORDER BY `orders` ASC"); //设定查询SQL -$dlist->Display(); //显示 \ No newline at end of file +$dlist->SetSource("SELECT `pid`,`dname`,`price`,`des` FROM `#@__shops_delivery` ORDER BY `orders` ASC"); // 设定查询SQL +$dlist->Display(); //显示 \ No newline at end of file diff --git a/src/dede/shops_operations.php b/src/dede/shops_operations.php index 625176b8..d72d432c 100755 --- a/src/dede/shops_operations.php +++ b/src/dede/shops_operations.php @@ -24,7 +24,7 @@ if(isset($dopost)) if($wh=='') $wh = " WHERE oid='$n' "; else $wh .= " OR oid='$n' "; } - $sql="UPDATE #@__shops_orders SET `state`='1' $wh "; + $sql="UPDATE `#@__shops_orders` SET `state`='1' $wh "; $dsql->ExecuteNoneQuery($sql); } else if ($dopost == 'push') @@ -36,7 +36,7 @@ if(isset($dopost)) if($wh=='') $wh = " WHERE oid='$n' "; else $wh .= " OR oid='$n' "; } - $sql="UPDATE #@__shops_orders SET `state`='2' $wh "; + $sql="UPDATE `#@__shops_orders` SET `state`='2' $wh "; $dsql->ExecuteNoneQuery($sql); } else if ($dopost == 'ok') @@ -48,7 +48,7 @@ if(isset($dopost)) if($wh=='') $wh = " WHERE oid='$n' "; else $wh .= " OR oid='$n' "; } - $sql="UPDATE #@__shops_orders SET `state`='4' $wh "; + $sql="UPDATE `#@__shops_orders` SET `state`='4' $wh "; $dsql->ExecuteNoneQuery($sql); } else if ($dopost == 'delete') @@ -87,7 +87,7 @@ if(isset($sta)) { $addsql = "WHERE s.`state`='$sta'"; } -$sql = "SELECT s.`oid`,s.`cartcount`,s.`price`,s.`state`,s.`stime`,s.priceCount,s.dprice,s.paytype,u.`consignee`,u.`tel`,s.`userid` FROM #@__shops_orders AS s LEFT JOIN #@__shops_userinfo AS u ON s.oid=u.oid $addsql ORDER BY `stime` DESC"; +$sql = "SELECT s.`oid`,s.`cartcount`,s.`price`,s.`state`,s.`stime`,s.priceCount,s.dprice,s.paytype,u.`consignee`,u.`tel`,s.`userid` FROM `#@__shops_orders` AS s LEFT JOIN `#@__shops_userinfo` AS u ON s.oid=u.oid $addsql ORDER BY `stime` DESC"; $dlist = new DataListCP(); $dlist->SetParameter("oid",$oid); @@ -127,7 +127,7 @@ function GetsType($pid) { global $dsql; $pid = intval($pid); - $row = $dsql->GetOne("SELECT name FROM #@__payment WHERE id='$pid'"); + $row = $dsql->GetOne("SELECT name FROM `#@__payment` WHERE id='$pid'"); if(is_array($row)) { return $row['name']; @@ -142,7 +142,7 @@ function GetMemberID($mid) { global $dsql; if($mid==0) return '0'; - $row = $dsql->GetOne("SELECT userid FROM #@__member WHERE mid='$mid' "); + $row = $dsql->GetOne("SELECT userid FROM `#@__member` WHERE mid='$mid' "); if(is_array($row)) { return "".$row['userid'].""; diff --git a/src/dede/shops_operations_cart.php b/src/dede/shops_operations_cart.php index fa8641ee..4c57bc41 100755 --- a/src/dede/shops_operations_cart.php +++ b/src/dede/shops_operations_cart.php @@ -16,8 +16,8 @@ if(!isset($oid)) exit("无效操作!"); $oid = preg_replace("#[^-0-9A-Z]#", "", $oid); if(empty($oid)) exit("无效订单号!"); -$row = $dsql->GetOne("SELECT * FROM #@__shops_userinfo WHERE oid='$oid'"); -$sql="SELECT o.*,p.title,p.price as uprice,d.dname FROM #@__shops_orders as o left join #@__shops_products as p on o.oid=p.oid left join #@__shops_delivery as d on d.pid=o.pid WHERE o.oid='$oid'"; +$row = $dsql->GetOne("SELECT * FROM `#@__shops_userinfo` WHERE oid='$oid'"); +$sql="SELECT o.*,p.title,p.price as uprice,d.dname FROM `#@__shops_orders` as o left join `#@__shops_products` as p on o.oid=p.oid left join `#@__shops_delivery` as d on d.pid=o.pid WHERE o.oid='$oid'"; $dlist = new DataListCP(); $dlist->pageSize = 20; @@ -30,8 +30,8 @@ $dlist->Close(); function GetSta($sta,$oid) { global $dsql; - $row = $dsql->GetOne("SELECT paytype FROM #@__shops_orders WHERE oid='$oid'"); - $payname = $dsql->GetOne("SELECT name,fee FROM #@__payment WHERE id='{$row['paytype']}'"); + $row = $dsql->GetOne("SELECT paytype FROM `#@__shops_orders` WHERE oid='$oid'"); + $payname = $dsql->GetOne("SELECT name,fee FROM `#@__payment` WHERE id='{$row['paytype']}'"); if($sta==0) { return $payname['name']." 手续费:".$payname['fee']."元"; diff --git a/src/dede/shops_operations_userinfo.php b/src/dede/shops_operations_userinfo.php index e8878b0e..ccdca4e9 100755 --- a/src/dede/shops_operations_userinfo.php +++ b/src/dede/shops_operations_userinfo.php @@ -14,16 +14,16 @@ if(!isset($oid)) exit("无效操作!"); $oid = preg_replace("#[^-0-9A-Z]#", "", $oid); if(empty($oid)) exit("无效订单号!"); -$rows = $dsql->GetOne("SELECT * FROM #@__shops_userinfo WHERE oid='$oid' LIMIT 0,1"); +$rows = $dsql->GetOne("SELECT * FROM `#@__shops_userinfo` WHERE oid='$oid' LIMIT 0,1"); if(!is_array($rows)){ $dsql->Close(); exit("该订单下没相关用户信息!"); } -$row = $dsql->GetOne("SELECT pid,dprice FROM #@__shops_orders WHERE oid='$oid'"); +$row = $dsql->GetOne("SELECT pid,dprice FROM `#@__shops_orders` WHERE oid='$oid'"); if(is_array($row)) { - $rs = $dsql->GetOne("SELECT dname FROM #@__shops_delivery WHERE pid='$row[pid]'"); + $rs = $dsql->GetOne("SELECT dname FROM `#@__shops_delivery` WHERE pid='$row[pid]'"); $rows['dname'] = $rs['dname']; $rows['dprice'] = $row['dprice']; } diff --git a/src/dede/sys_safetest.php b/src/dede/sys_safetest.php index 292798eb..b5e08700 100755 --- a/src/dede/sys_safetest.php +++ b/src/dede/sys_safetest.php @@ -1,4 +1,5 @@ OpenUrl($fileHashURL); +$filelist = $del->GetJSON(); +$offFiles = array(); +foreach ($filelist as $key => $ff) { + $offFiles[$ff->filename] = $ff->hash; +} + +$alter = ""; + +if (count($offFiles) == 0) { + $alter = << + 无法同官方网站文件服务器通信,校验时候无法保证本地文件是否同官方服务器文件是否一致。 + +EOT;; +} function TestOneFile($f) { - global $message, $info; + global $message, $info,$offFiles; $str = ''; //排除safefile和data/tplcache目录 - if(NotCheckFile($f) || preg_match("#data/tplcache|.svn#", $f)) return -1; - + if (preg_match("#data/tplcache|.svn|data/cache#", $f)) return -1; + $fp = fopen($f, 'r'); - while(!feof($fp)) { $str .= fgets($fp,1024); } + while (!feof($fp)) { + $str .= fgets($fp, 1024); + } fclose($fp); - if(preg_match("#(".$info.")[ \r\n\t]{0,}([\[\(])#i", $str)) - { - $trfile = preg_replace("#^".DEDEROOT."#", '', $f); - $message .= "
+ + if (preg_match("#(" . $info . ")[ \r\n\t]{0,}([\[\(])#i", $str)) { + $trfile = preg_replace("#^" . DEDEROOT . "#", '', $f ); + $oldTrfile = $trfile; + $trfile = substr(str_replace("/","\\",$trfile) ,1); + + $localFilehash = md5_file($f); + $remoteFilehash = isset($offFiles[$trfile])? $offFiles[$trfile] : ''; + if ($localFilehash === $remoteFilehash) { + return 0; + } + + $message .= "
可疑文件:{$trfile}
-
\r\n"; + 更改记录 + 删除 + 查看源码 +

\r\n"; return 1; } return 0; } -function NotCheckFile($f) -{ - global $safefiles, $safefile; - if($safefile != '') - { - foreach($safefiles as $v) - { - //if(empty($v)) continue; - if( preg_match("#".$v."#i", $f) ) return TRUE; - } - } - return false; -} - function TestSafe($tdir) { global $filetype; $dh = dir($tdir); - while($fname=$dh->read()) - { - $fnamef = $tdir.'/'.$fname; - if(@is_dir($fnamef) && $fname != '.' && $fname != '..') - { + while ($fname = $dh->read()) { + $fnamef = $tdir . '/' . $fname; + if (@is_dir($fnamef) && $fname != '.' && $fname != '..') { TestSafe($fnamef); } - if(preg_match("#\.(" . $filetype . ")#i", $fnamef)) - { + if (preg_match("#\.(" . $filetype . ")#i", $fnamef)) { TestOneFile($fnamef); } } } //检测 -if($action=='test') -{ - $message = ''; - AjaxHead(); - TestSafe(DEDEROOT); - if($message=='') $message = "没发现可疑文件!"; - echo $message; - exit(); +if ($action == 'test') { + $message = ''; + + AjaxHead(); + TestSafe(DEDEROOT); + if ($message == '') $message = "没发现可疑文件!"; + echo $message; + exit(); } +else if($action =='viewdiff'){ + $filename = isset($filename)? $filename : ""; + if (empty($filename)) { + ShowMsg("没有选择对应的文件", "-1"); + exit; + } + $baseFile = "https://cdn.dedebiz.com/release/{$cfg_version_detail}$filename"; + $del = new DedeHttpDown(); + $del->OpenUrl($baseFile); + $base = $del->GetHTML(); + + $file = "$cfg_basedir/$filename"; + $new = ""; + if(is_file($file)) + { + $fp = fopen($file,"r"); + $new = fread($fp,filesize($file)); + fclose($fp); + } + + include(dirname(__FILE__) . '/templets/sys_safetest_viewdiff.htm'); + + exit(); +} //清空模板缓存 -else if($action=='clear') -{ +else if ($action == 'clear') { global $cfg_tplcache_dir; $message = ''; - $d = DEDEROOT.$cfg_tplcache_dir; + $d = DEDEROOT . $cfg_tplcache_dir; AjaxHead(); sleep(1); - if(preg_match("#data\/#", $cfg_tplcache_dir) && file_exists($d) && is_dir($d)) - { + if (preg_match("#data\/#", $cfg_tplcache_dir) && file_exists($d) && is_dir($d)) { $dh = dir($d); - while($filename = $dh->read()) - { - if($filename=='.'||$filename=='..'||$filename=='index.html') continue; - @unlink($d.'/'.$filename); + while ($filename = $dh->read()) { + if ($filename == '.' || $filename == '..' || $filename == 'index.html') continue; + @unlink($d . '/' . $filename); } } $message = "成功清空模板缓存!"; @@ -139,4 +140,4 @@ else if($action=='clear') exit(); } -include(dirname(__FILE__).'/templets/sys_safetest.htm'); \ No newline at end of file +include(dirname(__FILE__) . '/templets/sys_safetest.htm'); diff --git a/src/dede/sys_verifies.php b/src/dede/sys_verifies.php deleted file mode 100755 index ea2be648..00000000 --- a/src/dede/sys_verifies.php +++ /dev/null @@ -1,470 +0,0 @@ -SetQuery("SELECT * FROM `#@__verifies` "); - $dsql->Execute(); - $filelist = array(); - while($row = $dsql->GetArray()) - { - $turefile = str_replace('../dede', '.', $row['filename']); - //跳过不存在的文件 - if(!file_exists($turefile)) { - continue; - } - if( filesize($turefile)==0 ) { - continue; - } - $ct = file_get_contents($turefile); - $ct = preg_replace("/\/\*\*[\r\n]{1,}(.*)[\r\n]{1,} \*\//sU", '', $ct); - $cthash = md5($ct); - if($cthash != $row['cthash']) - { - $row['localhash'] = $cthash; - $row['mtime'] = MyDate('Y-m-d H:i:s', filemtime($turefile)); - $row['turefile'] = $turefile; - $filelist[] = $row; - } - } - if(!isset($filelist[0])) - { - ShowMsg("所有文件都通过效验证,核心文件没有被改动过!","sys_verifies.php"); - } - else - { - include(DEDEADMIN.'/templets/sys_verifies_verify.htm'); - } - exit(); -} -/*-------------------- -查看单个本地文件 -function _view() { } -----------------------*/ -else if ($action == 'view') -{ - require_once(DEDEINC."/oxwindow.class.php"); - - $filetxt = ''; - if( !preg_match("#data(.*)common.inc.php#i", $filename) ) - { - $fp = fopen($filename, 'r'); - $filetxt = fread($fp, filesize($filename)); - fclose($fp); - } - - $filetxt = str_replace('textarea', '!textarea', $filetxt); - - $wintitle = "文件效验::查看文件"; - $wecome_info = "文件效验::查看文件"; - $win = new OxWindow(); - $win->Init(); - $win->AddTitle("以下为文件 $filename 的内容,请检查是否可疑:"); - $winform = $win->GetWindow("hand",""); - $win->Display(); - exit(); -} -/*----------------- -管理指纹码 -function _manage() { } --------------------*/ -else if ($action == 'manage') -{ - $dsql->SetQuery("SELECT * FROM `#@__verifies` "); - $dsql->Execute(); - $filelist = array(); - while($row = $dsql->GetArray()) - { - $filelist[] = $row; - } - include(DEDEADMIN.'/templets/sys_verifies_manage.htm'); - exit(); -} -/*----------------------- -下载文件 -function _getfiles() -------------------------*/ -else if ($action == 'getfiles') -{ - if(!isset($refiles)) - { - ShowMsg("你没进行任何操作!","sys_verifies.php"); - exit(); - } - $cacheFiles = DEDEDATA.'/modifytmp.inc'; - $fp = fopen($cacheFiles, 'w'); - fwrite($fp, '<'.'?php'."\r\n"); - fwrite($fp, '$tmpdir = "'.$tmpdir.'";'."\r\n"); - $dirs = array(); - $i = -1; - $adminDir = preg_replace("#(.*)[\/\\\\]#", "", dirname(__FILE__)); - foreach($refiles as $filename) - { - $filename = substr($filename,3,strlen($filename)-3); - if(preg_match("#^dede/#i", $filename)) - { - $curdir = GetDirName( preg_replace("#^dede/#i", $adminDir.'/', $filename) ); - } else { - $curdir = GetDirName($filename); - } - if( !isset($dirs[$curdir]) ) - { - $dirs[$curdir] = TestIsFileDir($curdir); - } - $i++; - fwrite($fp, '$files['.$i.'] = "'.$filename.'";'."\r\n"); - } - fwrite($fp, '$fileConut = '.$i.';'."\r\n"); - fwrite($fp, '?'.'>'); - fclose($fp); - - $dirinfos = ''; - if($i > -1) - { - $dirinfos = ''; - $dirinfos .= "本次升级需要在下面文件夹写入更新文件,请注意文件夹是否有写入权限:
\r\n"; - foreach($dirs as $curdir) - { - $dirinfos .= $curdir['name']." 状态:".($curdir['writeable'] ? "[√正常]" : "[×不可写]")."
\r\n"; - } - $dirinfos .= "\r\n"; - } - - $doneStr = "\r\n"; - - include(DEDEADMIN.'/templets/sys_verifies_getfiles.htm'); - - exit(); -} -/*----------------------- -下载文件 -function _down() -------------------------*/ -else if($action=='down') -{ - $cacheFiles = DEDEDATA.'/modifytmp.inc'; - require_once($cacheFiles); - - if($fileConut==-1 || $curfile > $fileConut) - { - ShowMsg("已下载所有文件
[直接替换文件]   [我自己手动替换文件]","javascript:;"); - exit(); - } - - //检查临时文件保存目录是否可用 - MkTmpDir($tmpdir, $files[$curfile]); - - $downfile = UPDATEHOST.$cfg_soft_lang.'/source/'.$files[$curfile]; - - $dhd = new DedeHttpDown(); - $dhd->OpenUrl($downfile); - $dhd->SaveToBin(DEDEDATA.'/'.$tmpdir.'/'.$files[$curfile]); - $dhd->Close(); - - ShowMsg("成功下载文件:{$files[$curfile]}; 继续下载下一个文件。","sys_verifies.php?action=down&curfile=".($curfile+1)); - exit(); -} -/*----------------------- -修改效验方式 -function _modify() -------------------------*/ -else if($action=='modify') -{ - if(!isset($modifys)) - { - ShowMsg("没选定要修改的文件!","-1"); - exit(); - } - else - { - foreach($modifys as $fname) - { - if($method=='local') - { - $tureFilename = str_replace('../dede','./',$fname); - if(file_exists($tureFilename)) - { - $fp = fopen($tureFilename,'r'); - $ct = fread($fp,filesize($tureFilename)); - fclose($fp); - $cthash = md5($ct); - $dsql->ExecuteNoneQuery("UPDATE `#@__verifies` SET `method`='local',cthash='$cthash' WHERE filename='$fname' "); - } - } - else - { - $dsql->ExecuteNoneQuery("UPDATE `#@__verifies` SET `method`='offical' WHERE filename='$fname' "); - } - } - } - if($method=='local') - { - ShowMsg("成功修改指定文件的验证方式!","sys_verifies.php?action=manage"); - } - else - { - ShowMsg("成功修改指定文件的验证方式!
由于你修改了文件为远程验证方式,因此需进行更新操作
[更新]   [返回]","javascript:;"); - } - exit(); -} -/*----------------------- -还原文件 -function _applyRecover() -------------------------*/ -else if ($action == 'apply') -{ - $cacheFiles = DEDEDATA.'/modifytmp.inc'; - require_once($cacheFiles); - $sDir = DEDEDATA."/$tmpdir"; - $tDir = DEDEROOT; - - $badcp = 0; - $adminDir = preg_replace("#(.*)[\/\\\\]#", "", dirname(__FILE__)); - - if(isset($files) && is_array($files)) - { - foreach($files as $f) - { - if(preg_match("#^dede#", $f)) $tf = preg_replace("#^dede#", $adminDir, $f); - else $tf = $f; - - if(file_exists($sDir.'/'.$f)) - { - //还原文件前先进行文件效验 - $ct = file_get_contents($sDir.'/'.$f); - $ct = preg_replace("/\/\*\*[\r\n]{1,}(.*)[\r\n]{1,} \*\//sU", '', $ct); - $newhash = md5($ct); - $row = $dsql->GetOne("SELECT * FROM `#@__verifies` WHERE filename='../{$f}' "); - if(is_array($row) && $row['cthash'] != $newhash) - { - $badcp++; - } else { - $rs = @copy($sDir.'/'.$f, $tDir.'/'.$tf); - if($rs) unlink($sDir.'/'.$f); - else $badcp++; - } - } - } - } - - $badmsg = '!'; - if($badcp > 0) - { - $badmsg = ",其 {$badcp} 个文件效验码不正确或复制失败,
请从临时目录[../data/{$tmpdir}]中取出这几个文件手动还原。"; - } - - ShowMsg("成功完成还原指定文件{$badmsg}", "javascript:;"); - exit(); -} -/*--------------- -在线更新指纹码 -function _update() ------------------*/ -else if($action == 'update') -{ - $rmFile = UPDATEHOST.$cfg_soft_lang.'/verifys.txt'; - $dhd = new DedeHttpDown(); - $dhd->OpenUrl($rmFile); - $ct = $dhd->GetHtml(); - $dhd->Close(); - $cts = split("[\r\n]{1,}",$ct); - foreach($cts as $ct) - { - $ct = trim($ct); - if(empty($ct)) continue; - list($nameid, $cthash, $fname) = explode("\t", $ct); - $row = $dsql->GetOne("SELECT * FROM `#@__verifies` WHERE nameid='$nameid' "); - if(!is_array($row) || ($row['method']=='official' && $row['cthash']!=$cthash )) - { - $dsql->ExecuteNoneQuery("REPLACE INTO `#@__verifies`(nameid,cthash,method,filename) VALUES ('$nameid','$cthash','official','$fname'); "); - } - } - $fp = fopen($verifiesLockFile,'w'); - fwrite($fp, MyDate('Ymd',time())); - fclose($fp); - ShowMsg("完成效验码更新,是否马上进行效验操作?
[开始效验]   [管理]   [返回]","javascript:;"); - exit(); -} -/*----------------- -生成指纹码 -function _make() { } --------------------*/ -else if ($action == 'make') -{ - $fp = fopen(DEDEROOT.'/../verifys.txt','w'); - foreach (preg_ls ('../', TRUE, "/.*\.(php|htm|html|js)$/i", 'CVS,data,html,uploads,templets,special') as $onefile) - { - $nameid = md5($onefile); - $ctbody = file_get_contents(DEDEADMIN.'/'.$onefile); - $ctbody = preg_replace("/\/\*\*[\r\n]{1,}(.*)[\r\n]{1,} \*\//sU", '', $ctbody); - $cthash = md5($ctbody); - fwrite($fp,"{$nameid}\t{$cthash}\t{$onefile}\r\n"); - } - fclose($fp); - ShowMsg("操作成功!","sys_verifies.php"); - exit(); -} - -//获取所有文件列表 -function preg_ls($path=".", $rec=FALSE, $pat="/.*/", $ignoredir='') -{ - while (substr ($path,-1,1) =="/") - { - $path=substr ($path,0,-1); - } - if (!is_dir ($path) ) - { - $path=dirname ($path); - } - if ($rec!==TRUE) - { - $rec=FALSE; - } - $d=dir ($path); - $ret=Array (); - while (FALSE!== ($e=$d->read () ) ) - { - if ( ($e==".") || ($e=="..") ) - { - continue; - } - if ($rec && is_dir ($path."/".$e) && ($ignoredir == '' || strpos($ignoredir,$e ) === FALSE)) - { - $ret = array_merge ($ret, preg_ls($path."/".$e, $rec, $pat, $ignoredir)); - continue; - } - if (!preg_match ($pat, $e) ) - { - continue; - } - $ret[] = $path."/".$e; - } - return (empty ($ret) && preg_match ($pat,basename($path))) ? Array ($path."/") : $ret; -} - -function TestWriteAble($d) -{ - $tfile = '_dedet.txt'; - $fp = @fopen($d.$tfile, 'w'); - if(!$fp) - { - return FALSE; - } - else { - fclose($fp); - $rs = @unlink($d.'/'.$tfile); - return TRUE; - } -} - -function GetDirName($filename) -{ - $dirname = '../'.preg_replace("#[\\\\\/]{1,}#", '/', $filename); - $dirname = preg_replace("#([^\/]*)$#", '', $dirname); - return $dirname; -} - -function TestIsFileDir($dirname) -{ - $dirs = array('name'=>'','isdir'=>FALSE,'writeable'=>FALSE); - $dirs['name'] = $dirname; - if(is_dir($dirname)) - { - $dirs['isdir'] = TRUE; - $dirs['writeable'] = TestWriteAble($dirname); - } - return $dirs; -} - -function MkTmpDir($tmpdir,$filename) -{ - $basedir = DEDEDATA.'/'.$tmpdir; - $dirname = trim(preg_replace("#[\\\\\/]{1,}#", '/', $filename)); - $dirname = preg_replace("#([^\/]*)$#", "", $dirname); - if(!is_dir($basedir)) - { - mkdir($basedir, 0777); - } - if($dirname=='') - { - return TRUE; - } - $dirs = explode('/', $dirname); - $curdir = $basedir; - foreach($dirs as $d) - { - $d = trim($d); - if(empty($d)) continue; - $curdir = $curdir.'/'.$d; - if(!is_dir($curdir)) - { - mkdir($curdir,0777) or die($curdir); - } - } - return TRUE; -} \ No newline at end of file diff --git a/src/dede/templets/sys_safetest.htm b/src/dede/templets/sys_safetest.htm index 8b7e0750..a712da38 100755 --- a/src/dede/templets/sys_safetest.htm +++ b/src/dede/templets/sys_safetest.htm @@ -5,8 +5,8 @@ 木马自检程序 - - + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/src/dede/templets/sys_verifies.htm b/src/dede/templets/sys_verifies.htm deleted file mode 100755 index c3697319..00000000 --- a/src/dede/templets/sys_verifies.htm +++ /dev/null @@ -1,78 +0,0 @@ - - - - -文件校验 - - - - - - - - - - - - - -
- - - - - -
文件校验操作: -
-
- - - - - - - - -
  - 您的系统最后升级时间:;指纹码最后同步时间: -
- 第一次效验文件时或您的系统已经升级过,请先点击“在线获取最新指纹码” -
- (文件校验将检查本站文件是否和DedeCMS原始文件是否完全一致,如果要替换差异文件,必须按提示设置被更改文件的目录有可写入权限) -
  -
 效对操作:
-
- 在线获取最新指纹码==> -   - 效对本地文件==> -   - 下载差异文件==> -   - 替换差异文件==> -
- -
 管理指纹码:(对于您自己修改过的文件,您可以在列表中屏蔽,下次忽略这些文件的效验)
- - -
-
-
-
-
- - \ No newline at end of file diff --git a/src/dede/templets/sys_verifies_getfiles.htm b/src/dede/templets/sys_verifies_getfiles.htm deleted file mode 100755 index 2a4d81a4..00000000 --- a/src/dede/templets/sys_verifies_getfiles.htm +++ /dev/null @@ -1,56 +0,0 @@ - - - - -文件效验程序--下载更新文件 - - - - - - - - - - - - - - - - - -
-
- 文件校验操作 >> 下载差异文件 -
- -
- 下载的文件临时存放在文件夹(../data/)内,如果某些基础类有重要的改动导致更新中途中错,您可以从这文件夹提取文件手工更新。 -
- - - - - -
进行状态: - - [增大] [缩小] -
-
-
- -
-
- - diff --git a/src/dede/templets/sys_verifies_manage.htm b/src/dede/templets/sys_verifies_manage.htm deleted file mode 100755 index c1b07bb2..00000000 --- a/src/dede/templets/sys_verifies_manage.htm +++ /dev/null @@ -1,74 +0,0 @@ - - - - -文件校验 - - - - - - - - - - -
- - - - - -
- 文件校验 >> 文件效验管理 - -
-
- - - - - - - - - - - - $file) - { - ?> - - - - - - - - - - - - - - -
 说明:这里是系统文件列表,如果本地对某些文件做过修改,但又不希望同官方服务器上面的镜像版本(发布版本)对应文件进行同步,可将文件校验方式修改为“本地(local)”,以确保不会重新下载文件修复。
修改? 文件名(用 Ctrl+F 搜索) 文件HASH 校验方式
- - - - - - - -
- - -
- -
-
- - \ No newline at end of file diff --git a/src/dede/templets/sys_verifies_verify.htm b/src/dede/templets/sys_verifies_verify.htm deleted file mode 100755 index 9c5809a9..00000000 --- a/src/dede/templets/sys_verifies_verify.htm +++ /dev/null @@ -1,73 +0,0 @@ - - - - -文件校验 - - - - - - - - - - -
- - - - - -
- 文件效验 - >> - 校验结果: 共有 个文件不同 - -
-
- - - - - - - - - - $file) - { - ?> - - - - - - - - - - - -
恢复? 文件名(Ctrl+F查找) 校验方式 最后修改时间 指纹码/点击可浏览文件内容
- - - - - - - - - - - -
- -
- -
- - \ No newline at end of file diff --git a/src/include/dedehttpdown.class.php b/src/include/dedehttpdown.class.php index 9edc6124..06c1b50f 100755 --- a/src/include/dedehttpdown.class.php +++ b/src/include/dedehttpdown.class.php @@ -283,6 +283,24 @@ class DedeHttpDown return $this->m_html; } + function GetJSON() + { + if ($this->m_html != '') { + return json_decode($this->m_html); + } + if (!$this->IsText()) { + return ''; + } + if (!$this->m_fp || @feof($this->m_fp)) { + return ''; + } + while (!feof($this->m_fp)) { + $this->m_html .= fgets($this->m_fp, 256); + } + @fclose($this->m_fp); + return json_decode($this->m_html); + } + /** * 开始HTTP会话 * diff --git a/src/static/css/diffview.css b/src/static/css/diffview.css new file mode 100644 index 00000000..811a593b --- /dev/null +++ b/src/static/css/diffview.css @@ -0,0 +1,83 @@ +/* +This is part of jsdifflib v1.0. + +Copyright 2007 - 2011 Chas Emerick . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY Chas Emerick ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Chas Emerick OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Chas Emerick. +*/ +table.diff { + border-collapse:collapse; + border:1px solid darkgray; + white-space:pre-wrap +} +table.diff tbody { + font-family:Courier, monospace +} +table.diff tbody th { + font-family:verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif; + background:#EED; + font-size:11px; + font-weight:normal; + border:1px solid #BBC; + color:#886; + padding:.3em .5em .1em 2em; + text-align:right; + vertical-align:top +} +table.diff thead { + border-bottom:1px solid #BBC; + background:#EFEFEF; + font-family:Verdana +} +table.diff thead th.texttitle { + text-align:left +} +table.diff tbody td { + padding:0px .4em; + padding-top:.4em; + vertical-align:top; +} +table.diff .empty { + background-color:#DDD; +} +table.diff .replace { + background-color:#FD8 +} +table.diff .delete { + background-color:#E99; +} +table.diff .skip { + background-color:#EFEFEF; + border:1px solid #AAA; + border-right:1px solid #BBC; +} +table.diff .insert { + background-color:#9E9 +} +table.diff th.author { + text-align:right; + border-top:1px solid #BBC; + background:#EFEFEF +} \ No newline at end of file diff --git a/src/static/js/difflib.js b/src/static/js/difflib.js new file mode 100644 index 00000000..5b8d769b --- /dev/null +++ b/src/static/js/difflib.js @@ -0,0 +1,411 @@ +/*** +This is part of jsdifflib v1.0. + +Copyright (c) 2007, Snowtide Informatics Systems, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the Snowtide Informatics Systems nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +***/ +/* Author: Chas Emerick */ +__whitespace = {" ":true, "\t":true, "\n":true, "\f":true, "\r":true}; + +difflib = { + defaultJunkFunction: function (c) { + return __whitespace.hasOwnProperty(c); + }, + + stripLinebreaks: function (str) { return str.replace(/^[\n\r]*|[\n\r]*$/g, ""); }, + + stringAsLines: function (str) { + var lfpos = str.indexOf("\n"); + var crpos = str.indexOf("\r"); + var linebreak = ((lfpos > -1 && crpos > -1) || crpos < 0) ? "\n" : "\r"; + + var lines = str.split(linebreak); + for (var i = 0; i < lines.length; i++) { + lines[i] = difflib.stripLinebreaks(lines[i]); + } + + return lines; + }, + + // iteration-based reduce implementation + __reduce: function (func, list, initial) { + if (initial != null) { + var value = initial; + var idx = 0; + } else if (list) { + var value = list[0]; + var idx = 1; + } else { + return null; + } + + for (; idx < list.length; idx++) { + value = func(value, list[idx]); + } + + return value; + }, + + // comparison function for sorting lists of numeric tuples + __ntuplecomp: function (a, b) { + var mlen = Math.max(a.length, b.length); + for (var i = 0; i < mlen; i++) { + if (a[i] < b[i]) return -1; + if (a[i] > b[i]) return 1; + } + + return a.length == b.length ? 0 : (a.length < b.length ? -1 : 1); + }, + + __calculate_ratio: function (matches, length) { + return length ? 2.0 * matches / length : 1.0; + }, + + // returns a function that returns true if a key passed to the returned function + // is in the dict (js object) provided to this function; replaces being able to + // carry around dict.has_key in python... + __isindict: function (dict) { + return function (key) { return dict.hasOwnProperty(key); }; + }, + + // replacement for python's dict.get function -- need easy default values + __dictget: function (dict, key, defaultValue) { + return dict.hasOwnProperty(key) ? dict[key] : defaultValue; + }, + + SequenceMatcher: function (a, b, isjunk) { + this.set_seqs = function (a, b) { + this.set_seq1(a); + this.set_seq2(b); + } + + this.set_seq1 = function (a) { + if (a == this.a) return; + this.a = a; + this.matching_blocks = this.opcodes = null; + } + + this.set_seq2 = function (b) { + if (b == this.b) return; + this.b = b; + this.matching_blocks = this.opcodes = this.fullbcount = null; + this.__chain_b(); + } + + this.__chain_b = function () { + var b = this.b; + var n = b.length; + var b2j = this.b2j = {}; + var populardict = {}; + for (var i = 0; i < b.length; i++) { + var elt = b[i]; + if (b2j.hasOwnProperty(elt)) { + var indices = b2j[elt]; + if (n >= 200 && indices.length * 100 > n) { + populardict[elt] = 1; + delete b2j[elt]; + } else { + indices.push(i); + } + } else { + b2j[elt] = [i]; + } + } + + for (var elt in populardict) { + if (populardict.hasOwnProperty(elt)) { + delete b2j[elt]; + } + } + + var isjunk = this.isjunk; + var junkdict = {}; + if (isjunk) { + for (var elt in populardict) { + if (populardict.hasOwnProperty(elt) && isjunk(elt)) { + junkdict[elt] = 1; + delete populardict[elt]; + } + } + for (var elt in b2j) { + if (b2j.hasOwnProperty(elt) && isjunk(elt)) { + junkdict[elt] = 1; + delete b2j[elt]; + } + } + } + + this.isbjunk = difflib.__isindict(junkdict); + this.isbpopular = difflib.__isindict(populardict); + } + + this.find_longest_match = function (alo, ahi, blo, bhi) { + var a = this.a; + var b = this.b; + var b2j = this.b2j; + var isbjunk = this.isbjunk; + var besti = alo; + var bestj = blo; + var bestsize = 0; + var j = null; + + var j2len = {}; + var nothing = []; + for (var i = alo; i < ahi; i++) { + var newj2len = {}; + var jdict = difflib.__dictget(b2j, a[i], nothing); + for (var jkey in jdict) { + if (jdict.hasOwnProperty(jkey)) { + j = jdict[jkey]; + if (j < blo) continue; + if (j >= bhi) break; + newj2len[j] = k = difflib.__dictget(j2len, j - 1, 0) + 1; + if (k > bestsize) { + besti = i - k + 1; + bestj = j - k + 1; + bestsize = k; + } + } + } + j2len = newj2len; + } + + while (besti > alo && bestj > blo && !isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) { + besti--; + bestj--; + bestsize++; + } + + while (besti + bestsize < ahi && bestj + bestsize < bhi && + !isbjunk(b[bestj + bestsize]) && + a[besti + bestsize] == b[bestj + bestsize]) { + bestsize++; + } + + while (besti > alo && bestj > blo && isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) { + besti--; + bestj--; + bestsize++; + } + + while (besti + bestsize < ahi && bestj + bestsize < bhi && isbjunk(b[bestj + bestsize]) && + a[besti + bestsize] == b[bestj + bestsize]) { + bestsize++; + } + + return [besti, bestj, bestsize]; + } + + this.get_matching_blocks = function () { + if (this.matching_blocks != null) return this.matching_blocks; + var la = this.a.length; + var lb = this.b.length; + + var queue = [[0, la, 0, lb]]; + var matching_blocks = []; + var alo, ahi, blo, bhi, qi, i, j, k, x; + while (queue.length) { + qi = queue.pop(); + alo = qi[0]; + ahi = qi[1]; + blo = qi[2]; + bhi = qi[3]; + x = this.find_longest_match(alo, ahi, blo, bhi); + i = x[0]; + j = x[1]; + k = x[2]; + + if (k) { + matching_blocks.push(x); + if (alo < i && blo < j) + queue.push([alo, i, blo, j]); + if (i+k < ahi && j+k < bhi) + queue.push([i + k, ahi, j + k, bhi]); + } + } + + matching_blocks.sort(difflib.__ntuplecomp); + + var i1 = j1 = k1 = block = 0; + var non_adjacent = []; + for (var idx in matching_blocks) { + if (matching_blocks.hasOwnProperty(idx)) { + block = matching_blocks[idx]; + i2 = block[0]; + j2 = block[1]; + k2 = block[2]; + if (i1 + k1 == i2 && j1 + k1 == j2) { + k1 += k2; + } else { + if (k1) non_adjacent.push([i1, j1, k1]); + i1 = i2; + j1 = j2; + k1 = k2; + } + } + } + + if (k1) non_adjacent.push([i1, j1, k1]); + + non_adjacent.push([la, lb, 0]); + this.matching_blocks = non_adjacent; + return this.matching_blocks; + } + + this.get_opcodes = function () { + if (this.opcodes != null) return this.opcodes; + var i = 0; + var j = 0; + var answer = []; + this.opcodes = answer; + var block, ai, bj, size, tag; + var blocks = this.get_matching_blocks(); + for (var idx in blocks) { + if (blocks.hasOwnProperty(idx)) { + block = blocks[idx]; + ai = block[0]; + bj = block[1]; + size = block[2]; + tag = ''; + if (i < ai && j < bj) { + tag = 'replace'; + } else if (i < ai) { + tag = 'delete'; + } else if (j < bj) { + tag = 'insert'; + } + if (tag) answer.push([tag, i, ai, j, bj]); + i = ai + size; + j = bj + size; + + if (size) answer.push(['equal', ai, i, bj, j]); + } + } + + return answer; + } + + // this is a generator function in the python lib, which of course is not supported in javascript + // the reimplementation builds up the grouped opcodes into a list in their entirety and returns that. + this.get_grouped_opcodes = function (n) { + if (!n) n = 3; + var codes = this.get_opcodes(); + if (!codes) codes = [["equal", 0, 1, 0, 1]]; + var code, tag, i1, i2, j1, j2; + if (codes[0][0] == 'equal') { + code = codes[0]; + tag = code[0]; + i1 = code[1]; + i2 = code[2]; + j1 = code[3]; + j2 = code[4]; + codes[0] = [tag, Math.max(i1, i2 - n), i2, Math.max(j1, j2 - n), j2]; + } + if (codes[codes.length - 1][0] == 'equal') { + code = codes[codes.length - 1]; + tag = code[0]; + i1 = code[1]; + i2 = code[2]; + j1 = code[3]; + j2 = code[4]; + codes[codes.length - 1] = [tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)]; + } + + var nn = n + n; + var group = []; + var groups = []; + for (var idx in codes) { + if (codes.hasOwnProperty(idx)) { + code = codes[idx]; + tag = code[0]; + i1 = code[1]; + i2 = code[2]; + j1 = code[3]; + j2 = code[4]; + if (tag == 'equal' && i2 - i1 > nn) { + group.push([tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)]); + groups.push(group); + group = []; + i1 = Math.max(i1, i2-n); + j1 = Math.max(j1, j2-n); + } + + group.push([tag, i1, i2, j1, j2]); + } + } + + if (group && !(group.length == 1 && group[0][0] == 'equal')) groups.push(group) + + return groups; + } + + this.ratio = function () { + matches = difflib.__reduce( + function (sum, triple) { return sum + triple[triple.length - 1]; }, + this.get_matching_blocks(), 0); + return difflib.__calculate_ratio(matches, this.a.length + this.b.length); + } + + this.quick_ratio = function () { + var fullbcount, elt; + if (this.fullbcount == null) { + this.fullbcount = fullbcount = {}; + for (var i = 0; i < this.b.length; i++) { + elt = this.b[i]; + fullbcount[elt] = difflib.__dictget(fullbcount, elt, 0) + 1; + } + } + fullbcount = this.fullbcount; + + var avail = {}; + var availhas = difflib.__isindict(avail); + var matches = numb = 0; + for (var i = 0; i < this.a.length; i++) { + elt = this.a[i]; + if (availhas(elt)) { + numb = avail[elt]; + } else { + numb = difflib.__dictget(fullbcount, elt, 0); + } + avail[elt] = numb - 1; + if (numb > 0) matches++; + } + + return difflib.__calculate_ratio(matches, this.a.length + this.b.length); + } + + this.real_quick_ratio = function () { + var la = this.a.length; + var lb = this.b.length; + return _calculate_ratio(Math.min(la, lb), la + lb); + } + + this.isjunk = isjunk ? isjunk : difflib.defaultJunkFunction; + this.a = this.b = null; + this.set_seqs(a, b); + } +}; + diff --git a/src/static/js/diffview.js b/src/static/js/diffview.js new file mode 100644 index 00000000..b88bac9d --- /dev/null +++ b/src/static/js/diffview.js @@ -0,0 +1,194 @@ +/* +This is part of jsdifflib v1.0. + +Copyright 2007 - 2011 Chas Emerick . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY Chas Emerick ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Chas Emerick OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Chas Emerick. +*/ +diffview = { + /** + * Builds and returns a visual diff view. The single parameter, `params', should contain + * the following values: + * + * - baseTextLines: the array of strings that was used as the base text input to SequenceMatcher + * - newTextLines: the array of strings that was used as the new text input to SequenceMatcher + * - opcodes: the array of arrays returned by SequenceMatcher.get_opcodes() + * - baseTextName: the title to be displayed above the base text listing in the diff view; defaults + * to "Base Text" + * - newTextName: the title to be displayed above the new text listing in the diff view; defaults + * to "New Text" + * - contextSize: the number of lines of context to show around differences; by default, all lines + * are shown + * - viewType: if 0, a side-by-side diff view is generated (default); if 1, an inline diff view is + * generated + */ + buildView: function (params) { + var baseTextLines = params.baseTextLines; + var newTextLines = params.newTextLines; + var opcodes = params.opcodes; + var baseTextName = params.baseTextName ? params.baseTextName : "Base Text"; + var newTextName = params.newTextName ? params.newTextName : "New Text"; + var contextSize = params.contextSize; + var inline = (params.viewType == 0 || params.viewType == 1) ? params.viewType : 0; + + if (baseTextLines == null) + throw "Cannot build diff view; baseTextLines is not defined."; + if (newTextLines == null) + throw "Cannot build diff view; newTextLines is not defined."; + if (!opcodes) + throw "Canno build diff view; opcodes is not defined."; + + function celt (name, clazz) { + var e = document.createElement(name); + e.className = clazz; + return e; + } + + function telt (name, text) { + var e = document.createElement(name); + e.appendChild(document.createTextNode(text)); + return e; + } + + function ctelt (name, clazz, text) { + var e = document.createElement(name); + e.className = clazz; + e.appendChild(document.createTextNode(text)); + return e; + } + + var tdata = document.createElement("thead"); + var node = document.createElement("tr"); + tdata.appendChild(node); + if (inline) { + node.appendChild(document.createElement("th")); + node.appendChild(document.createElement("th")); + node.appendChild(ctelt("th", "texttitle", baseTextName + " vs. " + newTextName)); + } else { + node.appendChild(document.createElement("th")); + node.appendChild(ctelt("th", "texttitle", baseTextName)); + node.appendChild(document.createElement("th")); + node.appendChild(ctelt("th", "texttitle", newTextName)); + } + tdata = [tdata]; + + var rows = []; + var node2; + + /** + * Adds two cells to the given row; if the given row corresponds to a real + * line number (based on the line index tidx and the endpoint of the + * range in question tend), then the cells will contain the line number + * and the line of text from textLines at position tidx (with the class of + * the second cell set to the name of the change represented), and tidx + 1 will + * be returned. Otherwise, tidx is returned, and two empty cells are added + * to the given row. + */ + function addCells (row, tidx, tend, textLines, change) { + if (tidx < tend) { + row.appendChild(telt("th", (tidx + 1).toString())); + row.appendChild(ctelt("td", change, textLines[tidx].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0"))); + return tidx + 1; + } else { + row.appendChild(document.createElement("th")); + row.appendChild(celt("td", "empty")); + return tidx; + } + } + + function addCellsInline (row, tidx, tidx2, textLines, change) { + row.appendChild(telt("th", tidx == null ? "" : (tidx + 1).toString())); + row.appendChild(telt("th", tidx2 == null ? "" : (tidx2 + 1).toString())); + row.appendChild(ctelt("td", change, textLines[tidx != null ? tidx : tidx2].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0"))); + } + + for (var idx = 0; idx < opcodes.length; idx++) { + code = opcodes[idx]; + change = code[0]; + var b = code[1]; + var be = code[2]; + var n = code[3]; + var ne = code[4]; + var rowcnt = Math.max(be - b, ne - n); + var toprows = []; + var botrows = []; + for (var i = 0; i < rowcnt; i++) { + // jump ahead if we've alredy provided leading context or if this is the first range + if (contextSize && opcodes.length > 1 && ((idx > 0 && i == contextSize) || (idx == 0 && i == 0)) && change=="equal") { + var jump = rowcnt - ((idx == 0 ? 1 : 2) * contextSize); + if (jump > 1) { + toprows.push(node = document.createElement("tr")); + + b += jump; + n += jump; + i += jump - 1; + node.appendChild(telt("th", "...")); + if (!inline) node.appendChild(ctelt("td", "skip", "")); + node.appendChild(telt("th", "...")); + node.appendChild(ctelt("td", "skip", "")); + + // skip last lines if they're all equal + if (idx + 1 == opcodes.length) { + break; + } else { + continue; + } + } + } + + toprows.push(node = document.createElement("tr")); + if (inline) { + if (change == "insert") { + addCellsInline(node, null, n++, newTextLines, change); + } else if (change == "replace") { + botrows.push(node2 = document.createElement("tr")); + if (b < be) addCellsInline(node, b++, null, baseTextLines, "delete"); + if (n < ne) addCellsInline(node2, null, n++, newTextLines, "insert"); + } else if (change == "delete") { + addCellsInline(node, b++, null, baseTextLines, change); + } else { + // equal + addCellsInline(node, b++, n++, baseTextLines, change); + } + } else { + b = addCells(node, b, be, baseTextLines, change); + n = addCells(node, n, ne, newTextLines, change); + } + } + + for (var i = 0; i < toprows.length; i++) rows.push(toprows[i]); + for (var i = 0; i < botrows.length; i++) rows.push(botrows[i]); + } + + + tdata.push(node = document.createElement("tbody")); + for (var idx in rows) rows.hasOwnProperty(idx) && node.appendChild(rows[idx]); + + node = celt("table", "diff" + (inline ? " inlinediff" : "")); + for (var idx in tdata) tdata.hasOwnProperty(idx) && node.appendChild(tdata[idx]); + return node; + } +}; +