Files
plugins/UploadPlugin/Action.php
chorblack e75f275ef4
Some checks failed
定时更新GitHub源插件 / 自动更新GitHub插件 (push) Has been cancelled
Initial commit
2026-03-07 11:19:25 +08:00

351 lines
13 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* @author DEFE
* @link http://defe.me
*/
class UploadPlugin_Action extends Typecho_Widget implements Widget_Interface_Do
{
private $_file;
/**
* 删除插件
* @param mixed $plugName 待删除的插件名称
* @access public
* @return void
*/
public function del($plugName)
{
$this->widget('Widget_User')->pass('administrator');
if($plugName){
$dir='.'.__TYPECHO_PLUGIN_DIR__;
if (is_writable($dir)){
chdir($dir);
if(is_dir($plugName)){
if(false==$this->delTree($plugName)){
$this->widget('Widget_Notice')->set(_t('插件:'.$plugName.'删除失败'), NULL, 'error');
}else{
$this->widget('Widget_Notice')->set(_t('成功删除插件:'.$plugName), NULL, 'success');
}
}else{
$file=$plugName.'.php';
if(@unlink($file)){
$this->widget('Widget_Notice')->set(_t('成功删除插件:'.$plugName), NULL, 'success');
}else{
$this->widget('Widget_Notice')->set(_t('插件:'.$plugName.'删除失败'), NULL, 'error');
}
}
}else{
$this->widget('Widget_Notice')->set(_t('插件主目录没有写权限:'.$plugName.'删除失败'), NULL, 'error');
}
}else{
$this->widget('Widget_Notice')->set(_t('插件不存在!'), NULL, 'notice');
}
}
/**
* 删除模板
* @param mixed $plugName 待删除的插件名称
* @access public
* @return void
*/
public function delTheme($themeName)
{
$this->widget('Widget_User')->pass('administrator');
$dir='.'.__TYPECHO_THEME_DIR__;
if (is_writable($dir)){
chdir($dir);
if(is_dir($themeName)){
if(false==$this->delTree($themeName)){
$this->widget('Widget_Notice')->set(_t('模板:'.$themeName.'删除失败'), NULL, 'error');
}else{
$this->widget('Widget_Notice')->set(_t('成功删除模板:'.$themeName), NULL, 'success');
}
}else{
$this->widget('Widget_Notice')->set(_t('没发现'.$themeName.'模板!'), NULL, 'notice');
}
}else{
$this->widget('Widget_Notice')->set(_t('themes主目录没有写权限'.$themeName.'删除失败'), NULL, 'error');
}
}
/**
* 删除目录
* @param mixed $dir 待删除的插件目录
* @access public
* @return boolean
*/
private function delTree($dir){
chdir($dir);
$dh=opendir('.');
while(false !== ($et=readdir($dh))){
if(is_file($et)){
if(!@unlink($et)){
return false;
break;
}
}else{
if(is_dir($et) && $et!='.' && $et!='..'){
$this->delTree('./'.$et);
}
}
}
closedir($dh);
chdir('..');
if(@rmdir($dir)){
return true;
}else{
return false;
}
}
/**
* 上传处理功能
*/
public function upload($filead){
if (!empty($_FILES['file']['name']) || $filead) {
if (!$filead and !isset($_FILES['file']) || !is_uploaded_file($_FILES['file']['tmp_name']) || $_FILES['file']['error'] != 0) {
echo '<script>alert("上传失败!")</script>';
exit(0);
}elseif ($filead) {
$this->_file = basename($filead);
$part = pathinfo($this->_file);
if($part['extension'] !== "zip"){
$this->_file = "tmp".Typecho_Common::randString(6).".zip";
}
$ff = @file_get_contents($filead);
if($ff){
file_put_contents($this->_file, $ff);
$up = False;
if(!$this->isZip($this->_file)) $this->showMsg('取得的文件不是zip类型' , FALSE, TRUE);
}else{
$this->showMsg('获取远程文件失败。' , FALSE, TRUE);
}
} else {
$this->_file = $_FILES['file']['tmp_name'];
$up = True;
}
$path = '';
$upType = '插件';
if (class_exists('ZipArchive')) {
$zip = new ZipArchive;
}else{
$this->showMsg("服务器不支持 php ZipArchive 类", $up);
}
if ($zip->open($this->_file) === TRUE) {
//单文件插件
if($zip->numFiles === 1){
$contents = $zip->getFromIndex(0);
if($this->isPlugin($contents)) $path= '.' . __TYPECHO_PLUGIN_DIR__.'/' ;
}else{
//多文件插件搜索Plugin.php文件路径获取插件信息
$index=$zip->locateName('Plugin.php', ZIPARCHIVE::FL_NOCASE|ZIPARCHIVE::FL_NODIR);
if($index || 0===$index){
$dirs = count(explode('/', $zip->getNameIndex($index)));
if($dirs>2){
$this->showMsg('压缩路径太深,无法正常安装', $up, TRUE) ;
}
$contents = $zip->getFromIndex($index);
$name = $this->isPlugin($contents);
if($name){
if(2==$dirs){
$path='.' . __TYPECHO_PLUGIN_DIR__.'/';
}else{
$path='.' . __TYPECHO_PLUGIN_DIR__ .'/'. $name.'/';
}
}
}else{
//如果不是插件,则按模板搜索判断
$index=$zip->locateName('index.php', ZIPARCHIVE::FL_NOCASE|ZIPARCHIVE::FL_NODIR);
if($index || 0===$index){
$upType = '外观';
$dirs = count(explode('/', $zip->getNameIndex($index)));
if($dirs>2){
$this->showMsg('压缩路径太深,无法正常安装', $up, TRUE) ;
}
$contents = $zip->getFromIndex($index);
if($this->isTheme($contents)){
if(2==$dirs){
$path='.' . __TYPECHO_THEME_DIR__.'/';
}else{
$name = basename($_FILES['file']['name'],'.zip');
$path='.' . __TYPECHO_THEME_DIR__ .'/'. $name.'/';
}
}
}
}
}
if($path!==''){
if($zip->extractTo($path)){
$zip->close();
$this->showMsg("安装成功,请到 控制台-->" . $upType . " 中激活使用。", $up, TRUE);
}else{
$this->showMsg("解压失败,请确认" . $upType . "目录是否有写权限。", $up) ;
}
} else {
$this->showMsg('上传的文件不是Typecho插件和模板', $up);
}
} else {
$this->showMsg('无法打开压缩包,请检查压缩包是否损坏。', $up);
}
@unlink($this->_file);
}else{
$this->widget('Widget_Archive@404', 'type=404')->render();
exit;
}
}
public function showMsg($str, $up = True, $exit=FALSE){
if($up && !$exit){
//echo("<script>parent.fileUploadError('" . $this->request->_id . "','" . $str . "');</script>");
$this->widget('Widget_Notice')->set(_t($str), NULL, 'error');
}
if($up && $exit){
@unlink($this->_file);
//exit("<script>parent.fileUploadComplete('" . $this->request->_id . "','typecho','" . $str . "');</script>");
$this->widget('Widget_Notice')->set(_t($str), NULL, 'success');
}
if(!$up && $exit){
@unlink($this->_file);
exit($str);
}
if(!$up && !$exit) echo($str);
}
/**
* 判断上传文件是否为插件
* @param type $contents
* @return boolean
*/
public function isPlugin($contents){
$info = $this->parseInfo($contents);
if( $info['description']!=="" && $info['name']!=="" && !empty($info['version']) && !empty($info['author'])){
return $info['name'] ;
}else{
return FALSE;
}
}
/**
* 判断上传文件是否为模板
* @param type $contents
* @return boolean
*/
public function isTheme($contents){
$info = $this->parseInfo($contents);
if($info['title']!=="" && !empty($info['version']) && !empty($info['author'])){
return TRUE;
}else{
return FALSE;
}
}
/**
*
* @param type $file
* @return boolean
*/
public function isZip($file){
$file = @fopen($file,"rb");
$bin = fread($file, 15);
fclose($file);
$blen=strlen(pack("H*","504B0304")); //得到文件头标记字节数
$tbin=substr($bin,0,intval($blen)); ///需要比较文件头长度
$upack = unpack("H*",$tbin);
if(strtolower("504B0304")==strtolower(array_shift($upack)))
{
return TRUE;
} else {
return FALSE;
}
}
//解析Plugin.php文件
public function parseInfo($pluginFile)
{
$tokens = token_get_all($pluginFile);
$isDoc = false;
$isClass = false;
/** 初始信息 */
$info = array(
'name' => '',
'description' => '',
'title' => '',
'author' => '',
'homepage' => '',
'version' => '',
'dependence' => '',
);
$map = array(
'package' => 'title',
'author' => 'author',
'link' => 'homepage',
'dependence'=> 'dependence',
'version' => 'version'
);
foreach ($tokens as $token) {
/** 获取doc comment */
if (!$isDoc && is_array($token) && T_DOC_COMMENT == $token[0]) {
/** 分行读取 */
$described = false;
$lines = preg_split("(\r|\n)", $token[1]);
foreach ($lines as $line) {
$line = trim($line);
if (!empty($line) && '*' == $line[0]) {
$line = trim(substr($line, 1));
if (!$described && !empty($line) && '@' == $line[0]) {
$described = true;
}
if (!$described && !empty($line)) {
$info['description'] .= $line . "\n";
} else if ($described && !empty($line) && '@' == $line[0]) {
$info['description'] = trim($info['description']);
$line = trim(substr($line, 1));
$args = explode(' ', $line);
$key = array_shift($args);
if (isset($map[$key])) {
$info[$map[$key]] = trim(implode(' ', $args));
}
}
}
}
$isDoc = true;
}
if (!$isClass && is_array($token) && T_CLASS == $token[0]) {
$isClass = true;
}
if ($isClass && is_array($token) && T_STRING == $token[0]){
$name=explode('_',$token[1]);
$info['name']=$name[0];
break;
}
}
return $info;
}
public function action()
{
$this->widget('Widget_User')->pass('administrator');
$this->on($this->request->is('del'))->del($this->request->del);
$this->on($this->request->is('delTheme'))->delTheme($this->request->delTheme);
$this->on($this->request->is('upload'))->upload($this->request->upload);
$this->response->goBack();
}
}
?>