PHPIndex

This page lists files in the current directory. You can view content, get download/execute commands for Wget, Curl, or PowerShell, or filter the list using wildcards (e.g., `*.sh`).

i18n
php
static
app.php
wget 'https://sme10.lists2.roe3.org/kodbox/plugins/webdav/app.php'
View Content
<?php

/**
 * webdav服务端;
 * 独立模块,不需要登录,权限内部自行处理;
 */
class webdavPlugin extends PluginBase{
	protected $dav;
	function __construct(){
		parent::__construct();
	}
	public function regist(){
		$this->hookRegist(array(
			'user.commonJs.insert'  		=> 'webdavPlugin.echoJs',
			'globalRequest'					=> 'webdavPlugin.route',
			'admin.storage.add.before'		=> 'webdavPlugin.storeSaveBefore',
			'admin.storage.edit.before'		=> 'webdavPlugin.storeSaveBefore',
		));
	}
	public function echoJs(){
		$config = $this->getConfig();
		$allow  = $this->isOpen() && $this->authCheck();
		
		// windows PC客户端自动挂载webdav;关闭后不自动挂载;
		$systemAutoMount = $config['systemAutoMount'] === '0' ? false:true;
		if(!$allow){$systemAutoMount = false;}
		$assign = array(
			"{{isAllow}}" 	 => intval($allow),
			'{{systemAutoMount}}'=> intval($systemAutoMount),
			"{{pathAllow}}"	 => $config['pathAllow'],
			"{{webdavName}}" => $this->webdavName(),
		);
		$this->echoFile('static/main.js',$assign);
	}
	private function webdavName(){
		$config = $this->getConfig();
		return $config['webdavName'] ? $config['webdavName']:'kodbox';
	}
	
	//存储新增/编辑前,数据处理
	public function storeSaveBefore(){
		if(strtolower($this->in['driver']) != 'webdav') return;
		$data = Input::getArray(array(
			"id"		=> array("default"=>null),
			"driver" 	=> array("check"=>"require"),
			"config" 	=> array("check"=>"require"),
		));		
		$config = json_decode($data['config'], true);
		$configBefore = Model('Storage')->getConfig($data['id']); // 未修改密码情况处理;
		if($config['password'] == str_repeat('*',strlen($configBefore['password']))){
			$config['password'] = $configBefore['password'];
		}
		
		$dav  = new WebdavClient($config);
		$data = $dav->check();
		if(!$data['status']){
			$message = _get($data,'data.message');
			$message = $message ? $message.'!':'';
			show_json($message.$data['header'][0].'<br/>连接失败,请检查连接URL,或用户名密码是否正确.',false);
		}
	}
	
	public function route(){
		include_once($this->pluginPath.'php/webdavClient.class.php');
		include_once($this->pluginPath.'php/pathDriverWebdav.class.php');
		include_once($this->pluginPath.'php/pathDriverNFS.class.php');
		include_once($this->pluginPath.'php/pathDriverSamba.class.php');

		if(strtolower(MOD.'.'.ST) == 'plugin.index') exit;
		if(strtolower(ACTION) == 'plugin.webdav.check'){return;}
		$this->_checkConfig();
		if(MOD === 'dav'){
			$uriDav = '/index.php/dav/';
			$this->run($uriDav);exit;
		}
		
		if(strtolower(MOD.'.'.ST) != 'plugin.webdav') return;
		$action = ACT;//dav/download;
		if( method_exists($this,$action) ){
			$this->$action();exit;
		}
		$uriDav = '/index.php/plugin/webdav/'.$this->webdavName().'/';// 适配window多一层;
		$this->run($uriDav);exit;
	}
	public function run($uriDav){
		if(!$this->isOpen()) return show_json("not open webdav",false);
		require($this->pluginPath.'php/webdavServer.class.php');
		require($this->pluginPath.'php/webdavServerKod.class.php');
		register_shutdown_function(array(&$this, 'endLog'));
		
		define('KOD_FROM_WEBDAV',1);
		$this->allowCROS();
		$this->dav = new webdavServerKod($uriDav);
		$this->debug();
		$this->dav->run();
	}
	
	// 允许跨域,兼容以浏览器为客户端的情况;
	private function allowCROS(){
		$allowMethods = 'GET, POST, OPTIONS, DELETE, HEAD, MOVE, COPY, PUT, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK';
		$allerHeaders = 'ETag, Content-Type, Content-Length, Accept-Encoding, X-Requested-with, Origin, Authorization';
		header('Access-Control-Allow-Origin: *');    				// 允许的域名来源;
		header('Access-Control-Allow-Methods: '.$allowMethods); 	// 允许请求的类型
		header('Access-Control-Allow-Headers: '.$allerHeaders);		// 允许请求时带入的header
		header('Access-Control-Allow-Credentials: true'); 			// 设置是否允许发送 cookie; js需设置:xhr.withCredentials = true;
		header('Access-Control-Max-Age: 3600');
	}
	
	public function download(){
		IO::fileOut($this->pluginPath.'static/webdav.cmd',true);
	}
	public function _checkConfig(){
		$nowSize=_get($_SERVER,'_afileSize','');$enSize=_get($_SERVER,'_afileSizeIn','');
		if(function_exists('_kodDe') && (!$nowSize || !$enSize || $nowSize != $enSize)){exit;}
	}
	public function check(){
		echo htmlentities($_SERVER['HTTP_AUTHORIZATION']);
	}
	public function checkSupport(){
		CacheLock::unlockRuntime();
		$url = APP_HOST.'index.php/plugin/webdav/check';
		$auth   = "Basic ".base64_encode('usr:pass');
		$header = array("Authorization: ".$auth);
		$res 	= @url_request($url,"GET",false,$header,false,false,3);
		if($res && substr($res['data'],0,11) == 'API call to') return true; //请求自己失败;
		if($res && $res['data'] == $auth) return true;
		
		@$this->setConfig(array('isOpen'=>'0'));
		return false;
	}

	public function onSetConfig($config){
		if($config['isOpen'] != '1') return;
		$this->onGetConfig($config);
	}
	public function onGetConfig($config){
		if(!is_array($config) || $config['isOpen'] != '1'){return;}
		$this->autoApplyApache();
		if($this->checkSupport()) return;
		show_tips(LNG('webdav.tips.configErr'),false);exit;
	}
	
	// apache 丢失Authorization情况自动加入配置;
	private function autoApplyApache(){
		$file = BASIC_PATH . '.htaccess';
		$isApache = strtolower($_SERVER['SERVER_SOFTWARE']) == 'apache';
		if(!$isApache || file_exists($file)) return;
		$arr = array(
			'RewriteEngine On',
			'RewriteCond %{HTTP:Authorization} ^(.*)',
			'RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]',
		);
		file_put_contents($file,implode("\n",$arr));
	}

	private function isOpen(){
		$option = $this->getConfig();
		return $option['isOpen'] == '1';
	}
	private function debug(){
		// $this->log('start;'.$this->dav->pathGet().';'.$this->dav->path);
		// 兼容处理chrome插件访问webdav;
		// PROPFIND;GET;MOVE;COPY,HEAD,PUT
		if( $_SERVER['REQUEST_METHOD'] == 'GET' && 
			strstr($_SERVER['HTTP_USER_AGENT'],'Chrome') &&
			isset($_COOKIE['kodUserID']) ){
			$_SERVER['REQUEST_METHOD'] = 'PROPFIND';
		}
	}
	
	public function endLog(){
		$logInfo = 'dav-error';
		if($this->dav){
			$logInfo = $this->dav->pathGet().';'.$this->dav->path;
		}
		// $logInfo .= get_caller_msg();
		$this->log('end;['.http_response_code().'];'.$logInfo);
	}
	
	private function serverInfo($pick = ''){
		$ignore = 'USER,HOME,PATH_TRANSLATED,ORIG_SCRIPT_FILENAME,HTTP_CONNECTION,HTTP_ACCEPT,HTTP_HOST,SERVER_NAME,SERVER_PORT,SERVER_ADDR,REMOTE_PORT,REMOTE_ADDR,SERVER_SOFTWARE,GATEWAY_INTERFACE,REQUEST_SCHEME,SERVER_PROTOCOL,DOCUMENT_ROOT,DOCUMENT_URI,REQUEST_URI,SCRIPT_NAME,CONTENT_LENGTH,CONTENT_TYPE,REQUEST_METHOD,QUERY_STRING,PATH_INFO,SCRIPT_FILENAME,FCGI_ROLE,PHP_SELF,REQUEST_TIME_FLOAT,REQUEST_TIME,REDIRECT_STATUS,HTTP_ACCEPT_ENCODING,HTTP_CACHE_CONTROL,HTTP_UPGRADE_INSECURE_REQUESTS,HTTP_CONTENT_LENGTH,HTTP_CONTENT_TYPE,HTTP_REFERER';
		$ignore .= ',HTTP_COOKIE,HTTP_ACCEPT_LANGUAGE,HTTP_USER_AGENT';
		$ignore .= ',HTTP_AUTHORIZATION,PHP_AUTH_USER,PHP_AUTH_PW';
		$ignore = explode(',',$ignore);
		$pick   = $pick ? explode(',',$pick) : array();
		
		$result = array();
		foreach($GLOBALS['__SERVER'] as $key => $val){
			if($pick){
				if(in_array($key,$pick)){$result[$key] = $val;}
			}else{
				if(!in_array($key,$ignore)){$result[$key] = $val;}
			}
		}
		return $result ? json_encode($result):'';
	}
	
	public function log($data){
		static $logIndex = 0;
		$config = $this->getConfig();
		if(empty($config['echoLog'])) return;
		if(is_array($data)){$data = json_encode_force($data);}
		if($_SERVER['REQUEST_METHOD'] == 'PROPFIND' ) return;
		
		$prefix = "     [S-$logIndex] ";
		if(!$logIndex){
			$prefix = "[SERVER-$logIndex] ";$logIndex++;
			$data   = $_SERVER['REQUEST_METHOD'].':'.$_SERVER['REQUEST_URI'].";".$this->serverInfo('').$data;
		}
		write_log($prefix.$data,'webdav');
		//write_log($GLOBALS['__SERVER'],'webdav');
	}
	public function clientLog($data){
		static $logIndex = 0;
		$config = $this->getConfig();
		if(empty($config['echoLog'])) return;
		if(is_array($data)){$data = json_encode_force($data);}

		$prefix = "     [C-$logIndex] ";
		if(!$logIndex){$prefix = "[CLIENT-$logIndex] ";$logIndex++;}
		write_log($prefix.$data,'webdav');
	}
}
package.json
wget 'https://sme10.lists2.roe3.org/kodbox/plugins/webdav/package.json'
View Content
{
	"id":"webdav",
	"name":"{{LNG['webdav.meta.name']}}",
	"title":"{{LNG['webdav.meta.title']}}",
	"version":"1.75",
	"source":{
		"className":" font-icon ri-hard-drive-fill-2 bg-yellow-6",
		"icon":""
	},
	"category":"file",
	"description":"{{LNG['webdav.meta.desc']}}",
	"keywords":"",
	"auther":{
		"copyright":"kodcloud.",
		"homePage":"http://www.kodcloud.com",
	},
	"configItem":{
		"formStyle":{
			"tabs":{
				"{{LNG['webdav.config.tab1']}}":"pluginAuthOpen,webdavName,isOpen,pluginAuth,pathAllow,sep001,echoLog,systemAutoMount,detail,detailConnect",
				"{{LNG['webdav.config.tab2']}}":"mountWebdav,mountDetail,kodboxUpload,kodboxDownload",
			}
		},
		"pluginAuthOpen":{
			"type":"switch",
			"value":1,
            "className":"hidden"
		},
		"webdavName":{
			"type":"input",
			"value":"kodbox",
            "className":"hidden"
		},
		"isOpen":{
			"type":"switch",
			"value":0,
			"display":"{{LNG['webdav.config.isOpen']}}",
			"desc":"{{LNG['webdav.config.isOpenDesc']}}",
			"switchItem":{"1":"pluginAuth,detailAddress,pathAllow,detailConnect,systemAutoMount,echoLog"}
		},
		"pluginAuth":{
			"type":"userSelect",
			"value":{"all":1},
			"display":"{{LNG['admin.plugin.auth']}}",
			"desc":"{{LNG['admin.plugin.authDesc']}}",
			"require":1
		},
		"pathAllow":{
			"type":"segment",
			"value":"all","className":"style-simple",
			"display":"{{LNG['webdav.config.pathAllow']}}",
			"info":{
				"all":"<i class='ri-node-tree font-icon'></i>{{LNG['webdav.config.pathAllowAll']}}",
				"self":"<i class='ri-account-circle-fill font-icon'></i>{{LNG['webdav.config.pathAllowSelf']}}"
			},
			"desc":"{{LNG['webdav.config.pathAllowDesc']}}",
		},
		"systemAutoMount":{
			"type":"switch",
			"value":"1",
			"display":"{{LNG['webdav.config.systemAutoMount']}}",
			"desc":"{{LNG['webdav.config.systemAutoMountDesc']}}"
		},
		"sep001":"<hr/>",
		"echoLog":{
			"type":"switch",
			"value":"0",
			"display":"{{LNG['webdav.config.logTitle']}}",
			"desc":"{{LNG['webdav.config.logDesc']}}:./data/temp/log/webdav/"
		},
		"detail":{
			"display":"",
			"value":
			"<div class='info-alert info-alert-blue p-10 align-left can-select can-right-menu mb-10'>
			<div class='mb-15'><b>{{LNG['common.tips']}}:</b></div>
			<li>{{LNG['webdav.meta.desc']}}</li><hr/>
			<li>{{LNG['webdav.tips.https']}}</li>
			<li>{{LNG['webdav.tips.upload']}}</li>
			<li>{{LNG['webdav.tips.auth']}}</li>
			</div>			
			<p class='info-alert info-alert-green p-10 align-left can-select can-right-menu'>
			{{LNG['webdav.tips.use']}}<br/>{{LNG['webdav.tips.use3thAccount']}}
			</p>"
		},
		"detailConnect":{
			"display":"",
			"value":"<div class='kui-btn kui-btn-blue goto-connect-webdav' 
					 link-href='#setting/user/webdav'>{{LNG['webdav.help.connect']}}</div></p>"
		},
		
		
		"mountWebdav":{
			"type":"switch",
			"value":1,
			"display":"{{LNG['webdav.config.mountWebdav']}}",
			"desc":"{{LNG['webdav.config.mountWebdavDesc']}}",
			"switchItem":{"1":"mountDetail,kodboxUpload,kodboxDownload"}
		},
		"mountDetail":{
			"display":"",
			"value":"<div class='info-alert info-alert-blue p-10 align-left can-select can-right-menu'>
			<li>{{LNG['webdav.config.mountDetail1']}}</li>
			<li>{{LNG['webdav.config.mountDetail2']}}</li>
			<li>{{LNG['webdav.config.mountDetail3']}}</li>
			</div>"
		}		
	}
}
readme.md
wget 'https://sme10.lists2.roe3.org/kodbox/plugins/webdav/readme.md'
View Content
##### 1.71 更新内容
- alist挂载阿里云盘, 作为本地存储再挂在到kod时,阿里云盘上的内容文件打开异常情况兼容
  原因: 301跳转后,获取阿里云文件,阿里云盘服务器做了referer校验处理(为空才能访问)


##### 1.49 更新内容
- 挂载存储检测优化; 挂载存储返回数据兼容包含url域名的情况;
- 挂载IIS搭建的服务器: 挂载识别兼容处理;xml解析兼容问题处理;文件夹重命名报错问题兼容;

##### 1.47 更新内容
- httpOPTIONS 不校验用户名(兼容部分客户端)
- httpHEAD    获取文件基本信息, 如果文件存在则header返回最后修改时间;(兼容部分客户端业务)
- 允许跨域,兼容以浏览器为客户端的情况;优化兼容super-productivity等程序
##### 1.44 更新内容
- 存储支持作为默认存储;
- webdav地址默认显示个人空间地址(避免粘贴根目录无法读写情况);
- 兼容优化: 兼容支持Noteshelf;
- 收藏夹中回收站内容支持删除,复制,剪切操作;

##### 1.40 更新内容
- 存储为对象存储等情况, 文件下载兼容goodsync (header跳转下载支持不足)

##### 1.39 更新内容
- 支持挂载webdav存储;
	- 文件操作支持: 属性;列表;移动;复制;删除;上传;下载.
	- 挂载处理: 后台挂载及管理, 有效性检测.
	- 挂载整合到io: 属性,列表;新建文件夹,重命名,移动,复制,编辑保存,删除; 跨存储间复制移动,进度处理.
	- 删除时回收站处理: 删除到回收站,/.recycle不存在则直接调用删除.
- 针对kod服务器增强
	- 属性增强: 包含文件夹数,文件数;文件夹大小;读写权限继承;扩展属性等.
	- 列表: 文件/文件夹图标处理; 文件缩略图.
	- 安全及性能优化: 登录优化处理(内部交互保持cookie); 前端上传下载使用临时限时授权链接访问.
	- 跨存储间相互拷贝剪切: i本地存储/对象存储等io路径相互复制剪切; 进度处理(单个文件,多个文件)
	- webdav上传下载优化
		- 后端上传: copy文件; 大文件分片上传支持; 秒传支持;
		- 前端下载: 直传处理;图片缩略图处理(不走当前服务器流量,性能提升,减少后端传输多次网络传输)
		- 前端上传: 秒传支持,分片上传支持; (支持前端上传目标为对象存等跨io自适应前端上传支持) 


##### 1.36 更新内容
- 兼容支持goodSync: 支持直接绑定同步,及挂载到本地网络驱动同步; 双向同步兼容支持
- 兼容支持FreeFileSync: 支持直接绑定同步,及挂载到本地网络驱动同步; 双向同步兼容支持
- 其他支持: 支持joplin笔记同步; zetero同步支持; floccus书签同步兼容支持(207=>200).

##### 1.33 更新内容
- 支持中文用户名登录(PC客户端自动登录挂载; 手动挂载用户名=$$+urlEncode(用户名))
##### 1.30 更新内容
- 新建文件夹: 当前目录下已存在同名文件夹,并且在回收站中时处理;
- 上传或新建文件: 当前目录下已存在同名文件,并且在回收站中时处理;
##### 1.23 更新内容 (2021.4.1)
- office编辑保存逻辑处理兼容(保留历史版本,)
	- 上传~tmp1601041332501525796.TMP //锁定,上传,解锁;
	- 移动 test.docx => test~388C66.tmp 				// 改造,识别到之后不进行移动重命名;
	- 移动 ~tmp1601041332501525796.TMP => test.docx; 	// 改造;目标文件已存在则更新文件;删除原文件;
	- 删除 test~388C66.tmp  
- 无保存权限处理;

##### 1.22 更新内容 (2021.3.26)
- 粘贴文件为0字节问题兼容;
- 登陆日志记录; 
- 复制移动兼容处理; 支持移动到收藏的各类IO路径