开源首发
This commit is contained in:
parent
7dc6657cbb
commit
075b3f12c7
27
.gitignore
vendored
27
.gitignore
vendored
@ -1,23 +1,4 @@
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
**/target/**
|
||||
.vscode/**
|
||||
**/dependency-reduced-pom.xml
|
||||
.idea/**
|
||||
|
20
README.md
20
README.md
@ -81,7 +81,12 @@
|
||||
|
||||
|
||||
## 安装和使用
|
||||
> 【推荐】万能自动部署一键安装
|
||||
|
||||
源码部署[戳我](other/README.md)【功能少,不推荐】
|
||||
|
||||
官方部署参见下面【功能全,推荐】
|
||||
|
||||
> 万能自动部署一键安装
|
||||
|
||||
`if [ -f /usr/bin/curl ];then curl -sSO https://support.tenfell.cn/install.sh;else wget -O install.sh https://support.tenfell.cn/install.sh;fi;bash install.sh`
|
||||
|
||||
@ -102,22 +107,12 @@ Docker版部署教程<a href="https://tfyun.gitee.io/common/jump.html?url=https%
|
||||
已支持挂载的部分网盘:
|
||||
- [√] 阿里云盘
|
||||
- [√] 天翼云盘
|
||||
- [√] 百度云盘
|
||||
- [√] 夸克网盘
|
||||
- [√] 一刻相册
|
||||
- [√] 115网盘
|
||||
- [√] OneDrive
|
||||
- [√] 123云盘
|
||||
- [√] WebDav
|
||||
- [√] 可道云
|
||||
- [√] 本地磁盘
|
||||
- [x] 迅雷网盘
|
||||
- [x] 移动云盘
|
||||
- [x] 曲奇网盘
|
||||
|
||||
## 部分功能
|
||||
下面是一些常用功能 `~o~)/`
|
||||
- [√] 第三方登录
|
||||
- [√] 离线下载
|
||||
- [√] 网页上传/下载(删除、mkdir、重命名、移动/复制/剪切/粘贴)
|
||||
- [√] 全盘断点续传(意外断网,刷新,重新在同一个目录选同一个文件能接着传)
|
||||
- [√] 跨盘秒传(任意盘之间复制粘贴会检测是否支持,不支持采用先下载再上传)
|
||||
@ -128,7 +123,6 @@ Docker版部署教程<a href="https://tfyun.gitee.io/common/jump.html?url=https%
|
||||
- [√] 独立的应用商店(可下载应用插件、后期可注册开发者发布自己制作的插件)
|
||||
- [√] 文件永久链接复制和直接文件下载
|
||||
- [√] 全盘文件分享(可控制是否允许下载)
|
||||
- [√] 用户权限管控
|
||||
> 更多功能就不意义介绍了,请到官网体验
|
||||
|
||||
## 问答
|
||||
|
153
README_EN.md
153
README_EN.md
@ -1,153 +0,0 @@
|
||||
# 仿Windows 11 网页版 私有云
|
||||
# 腾飞Webos
|
||||
|
||||
> 腾飞WebOS是一个支持多种存储。
|
||||
> 云端存储&协同办公新体验 如Windows11体验的私有云盘/企业网盘。
|
||||
> 🎉🎉全平台兼容性:Win、Linux、Mac、Docker (Apache、Nginx、IIS)。从百元级别的智能路由器和盒子产品
|
||||
> 到NAS网络存储设备,到服务器面板,服务器,硬件集成,ERP集成,私有云、公有云、SaaS服务搭建等各种场景都可自如适用
|
||||
支持跨平台浏览器的无缝访问。仅需一个浏览器即在web端完成文档的上传下载、管理查看,不需要安装任何类似ftp客户端的软件。
|
||||
> 只需要一个浏览器,三端都能访问 ✨PC✨H5✨ipad🎊🎈`o(^o^)o`
|
||||
|
||||
|
||||
- <a href="https://tfyun.gitee.io/common/jump.html?url=https%3A%2F%2Fos.tenfell.cn" target="_blank">腾飞Webos</a>
|
||||
- [前言](#前言)
|
||||
- [效果展示](#效果展示)
|
||||
- [在线体验](#在线体验)
|
||||
- [安装和使用](#安装和使用)
|
||||
- [网盘挂载](#网盘挂载)
|
||||
- [部分功能](#部分功能)
|
||||
- [问答](#问答)
|
||||
|
||||
## 前言
|
||||
腾飞Webos是免费的,个人随意部署,应用商店提供大量插件,且全部免费, <a href="https://tfyun.gitee.io/common/jump.html?url=https%3A%2F%2Fbbs.tenfell.cn%2Fupdate" target="_blank">更新日志</a> ←。
|
||||
|
||||
参考API<a href="https://tfyun.gitee.io/common/jump.html?url=https%3A%2F%2Fthoughts.teambition.com%2Fshare%2F64531c70be7a0f004263891d" target="_blank">插件开发</a>
|
||||
|
||||
仿win11的操作习惯,占用内存小,各种高级的功能(支持开发者自行开发插件)
|
||||
> 安卓、IOS,桌面版火速开发中....... `>v-)o`
|
||||
|
||||
|
||||
## 效果展示
|
||||
> 每次更新都会对效果作出调整,达到满意为止(你点一下这里查看<a href="https://tfyun.gitee.io/common/jump.html?url=https%3A%2F%2Ftfyun.gitee.io%2Findex.html%3FtoLoginNo%3D10001%26toLoginUser%3Dtest%26toLoginPassword%3D123456" target="_blank">官方演示</a> `-_-)o` )
|
||||
|
||||
[]()
|
||||
*登录*
|
||||
|
||||
[]()
|
||||
*桌面*
|
||||
|
||||
[]()
|
||||
*开始*
|
||||
|
||||
[]()
|
||||
*设置*
|
||||
|
||||
[]()
|
||||
*小工具*
|
||||
|
||||
[]()
|
||||
*文件管理器*
|
||||
|
||||
[]()
|
||||
*上传*
|
||||
|
||||
[]()
|
||||
*新建*
|
||||
|
||||
[]()
|
||||
*共享*
|
||||
|
||||
[]()
|
||||
*文档编辑*
|
||||
|
||||
[]()
|
||||
*图片编辑*
|
||||
|
||||
[]()
|
||||
*在线音视频*
|
||||
|
||||
[]()
|
||||
*休闲娱乐*
|
||||
|
||||
[]()
|
||||
*应用商店*
|
||||
|
||||
[]()
|
||||
*网盘模式*
|
||||
## 在线体验
|
||||
|
||||
<a href="https://tfyun.gitee.io/common/jump.html?url=https%3A%2F%2Ftfyun.gitee.io%2Findex.html%3FtoLoginNo%3D10001%26toLoginUser%3Dtest%26toLoginPassword%3D123456" target="_blank">在线预览</a>
|
||||
|
||||
|
||||
|
||||
## 安装和使用
|
||||
> 【推荐】万能自动部署一键安装
|
||||
|
||||
`if [ -f /usr/bin/curl ];then curl -sSO https://support.tenfell.cn/install.sh;else wget -O install.sh https://support.tenfell.cn/install.sh;fi;bash install.sh`
|
||||
|
||||
win版安装教程<a href="https://tfyun.gitee.io/common/jump.html?url=https%3A%2F%2Fos.tenfell.cn%2Fdoc%2F%23%2Fdoc%2Fwin" target="_blank">点击查看</a>
|
||||
|
||||
linux版部署教程<a href="https://tfyun.gitee.io/common/jump.html?url=https%3A%2F%2Fos.tenfell.cn%2Fdoc%2F%23%2Fdoc%2Flinux" target="_blank">点击查看</a>
|
||||
|
||||
MAC版部署教程<a href="https://tfyun.gitee.io/common/jump.html?url=https%3A%2F%2Fos.tenfell.cn%2Fdoc%2F%23%2Fdoc%2Fmac" target="_blank">点击查看</a>
|
||||
|
||||
宝塔版部署教程<a href="https://tfyun.gitee.io/common/jump.html?url=https%3A%2F%2Fos.tenfell.cn%2Fdoc%2F%23%2Fdoc%2Fbt" target="_blank">点击查看</a>
|
||||
|
||||
Docker版部署教程<a href="https://tfyun.gitee.io/common/jump.html?url=https%3A%2F%2Fos.tenfell.cn%2Fdoc%2F%23%2Fdoc%2Fdocker" target="_blank">点击查看</a>
|
||||
|
||||
群辉版部署教程<a href="https://tfyun.gitee.io/common/jump.html?url=https%3A%2F%2Fos.tenfell.cn%2Fdoc%2F%23%2Fdoc%2Fqunhui" target="_blank">点击查看</a>
|
||||
|
||||
|
||||
## 网盘挂载
|
||||
已支持挂载的部分网盘:
|
||||
- [√] 阿里云盘
|
||||
- [√] 天翼云盘
|
||||
- [√] 百度云盘
|
||||
- [√] 夸克网盘
|
||||
- [√] 一刻相册
|
||||
- [√] 115网盘
|
||||
- [√] OneDrive
|
||||
- [√] 123云盘
|
||||
- [√] WebDav
|
||||
- [√] 本地磁盘
|
||||
- [x] 迅雷网盘
|
||||
- [x] 移动云盘
|
||||
- [x] 曲奇网盘
|
||||
|
||||
## 部分功能
|
||||
下面是一些常用功能 `~o~)/`
|
||||
- [√] 第三方登录
|
||||
- [√] 离线下载
|
||||
- [√] 网页上传/下载(删除、mkdir、重命名、移动/复制/剪切/粘贴)
|
||||
- [√] 全盘断点续传(意外断网,刷新,重新在同一个目录选同一个文件能接着传)
|
||||
- [√] 跨盘秒传(任意盘之间复制粘贴会检测是否支持,不支持采用先下载再上传)
|
||||
- [√] 文件编辑预览(文本,代码,文档,图片等在线编辑和预览,后面会支持更多)
|
||||
- [√] 协同办公(采用金山文档绑定文件支持协同办公)
|
||||
- [√] 直连上传(全盘支持从浏览器直接上传到服务器,不经过中转)
|
||||
- [√] 直连下载(全盘支持从服务器直接下载文件到本地,不经过中转)
|
||||
- [√] 独立的应用商店(可下载应用插件、后期可注册开发者发布自己制作的插件)
|
||||
- [√] 文件永久链接复制和直接文件下载
|
||||
- [√] 全盘文件分享(可控制是否允许下载)
|
||||
- [√] 用户权限管控
|
||||
> 更多功能就不意义介绍了,请到官网体验
|
||||
|
||||
## 问答
|
||||
AD:腾飞webos是干什么的\
|
||||
AN:腾飞ebos是一款免费的个人私有云
|
||||
|
||||
AD:支持什么环境安装部署\
|
||||
AN:基本支持全平台部署`-_-)o`
|
||||
|
||||
AD:我能用webos做什么\
|
||||
AN:在任何场景下,只需要一个浏览器就能在线办公
|
||||
|
||||
AD:腾飞webos后期会收费吗\
|
||||
AN:腾飞webos对个人用户是免费的,即使是中小企业,
|
||||
我们提供的方案也够您使用,不会强制转换收费模式。
|
||||
|
||||
P:应用商店插件以后会不会收费\
|
||||
AN:官方提供的插件永不收费,第三方作者自行开发的
|
||||
插件,我们不保证存在赞助或关注要求。
|
||||
|
||||
|
||||
最近准备重绘图标,大家觉得这个logo怎么样`>_-)o`
|
123
api/pom.xml
Normal file
123
api/pom.xml
Normal file
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>cn.tenfell</groupId>
|
||||
<artifactId>webos</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!--ssh插件-->
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jsch</artifactId>
|
||||
<version>0.1.54</version>
|
||||
</dependency>
|
||||
<!--solon容器-->
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon.boot.smarthttp</artifactId>
|
||||
<version>1.11.3</version>
|
||||
</dependency>
|
||||
<!--solon静态文件插件-->
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon.web.staticfiles</artifactId>
|
||||
<version>1.11.3</version>
|
||||
</dependency>
|
||||
<!--连接池-->
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>3.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.3</version>
|
||||
</dependency>
|
||||
<!--mysql-->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.49</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!--redis-->
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>3.7.0</version>
|
||||
</dependency>
|
||||
<!--sqlite-->
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.28.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.24</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>cn.tenfell.webos.WebOsApp</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>mysql:mysql-connector-java</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>org.xerial:sqlite-jdbc</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>org.noear:*</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>com.jcraft:jsch</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
26
api/src/main/java/cn/tenfell/webos/WebOsApp.java
Normal file
26
api/src/main/java/cn/tenfell/webos/WebOsApp.java
Normal file
@ -0,0 +1,26 @@
|
||||
package cn.tenfell.webos;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.http.HttpGlobalConfig;
|
||||
import cn.hutool.log.dialect.console.ConsoleLog;
|
||||
import cn.hutool.log.level.Level;
|
||||
import cn.tenfell.webos.common.util.ProjectUtil;
|
||||
|
||||
public class WebOsApp {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
ConsoleLog.setLevel(Level.INFO);
|
||||
SecureUtil.disableBouncyCastle();
|
||||
// 设置HttpUtil超时时间为1小时
|
||||
HttpGlobalConfig.setTimeout(60 * 60 * 1000);
|
||||
// 设置系统时区固定为东八区,真实显示时间以语言切换为主
|
||||
final TimeZone timeZone = TimeZone.getTimeZone("GMT+8");
|
||||
TimeZone.setDefault(timeZone);
|
||||
ProjectUtil.init();
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.authRestart(e);
|
||||
}
|
||||
}
|
||||
}
|
16
api/src/main/java/cn/tenfell/webos/common/annt/Action.java
Normal file
16
api/src/main/java/cn/tenfell/webos/common/annt/Action.java
Normal file
@ -0,0 +1,16 @@
|
||||
package cn.tenfell.webos.common.annt;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 行为注解
|
||||
* 和@Bean配合实现url路由
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Action {
|
||||
String val();
|
||||
|
||||
int type() default 0;
|
||||
}
|
13
api/src/main/java/cn/tenfell/webos/common/annt/Auth.java
Normal file
13
api/src/main/java/cn/tenfell/webos/common/annt/Auth.java
Normal file
@ -0,0 +1,13 @@
|
||||
package cn.tenfell.webos.common.annt;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 权限标识注解
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Auth {
|
||||
String[] val();
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cn.tenfell.webos.common.annt;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 实例注解
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface BeanAction {
|
||||
String val();
|
||||
}
|
13
api/src/main/java/cn/tenfell/webos/common/annt/Login.java
Normal file
13
api/src/main/java/cn/tenfell/webos/common/annt/Login.java
Normal file
@ -0,0 +1,13 @@
|
||||
package cn.tenfell.webos.common.annt;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 登录控制注解
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Login {
|
||||
boolean val() default true;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package cn.tenfell.webos.common.annt;
|
||||
|
||||
import cn.hutool.db.transaction.TransactionLevel;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Transactional {
|
||||
TransactionLevel level() default TransactionLevel.READ_COMMITTED;
|
||||
}
|
162
api/src/main/java/cn/tenfell/webos/common/bean/CommonBean.java
Normal file
162
api/src/main/java/cn/tenfell/webos/common/bean/CommonBean.java
Normal file
@ -0,0 +1,162 @@
|
||||
package cn.tenfell.webos.common.bean;
|
||||
|
||||
import cn.hutool.db.Entity;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public class CommonBean {
|
||||
@Data
|
||||
public static class PageRes<T> implements Serializable {
|
||||
private List<T> data;
|
||||
private long count;
|
||||
private long pages;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class CopyMoveInfo implements Serializable {
|
||||
private PathInfo info;
|
||||
private String cipherPath;
|
||||
|
||||
public static CopyMoveInfo init(PathInfo info, String cipherPath) {
|
||||
CopyMoveInfo cmInfo = new CopyMoveInfo();
|
||||
cmInfo.setInfo(info);
|
||||
cmInfo.setCipherPath(cipherPath);
|
||||
return cmInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Data
|
||||
public static class PathInfo implements Serializable {
|
||||
private String name;//例如 图片.jpg
|
||||
private String path;//picture_id //第三方云盘的id,本地盘的名称
|
||||
private String createdAt;
|
||||
private String updatedAt;
|
||||
private long size;
|
||||
private int type;//1文件 2目录
|
||||
private String thumbnail;//缩略图
|
||||
private String ext;//拓展名
|
||||
private String hash;//sha1数据
|
||||
private String md5;//md5数据
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Page<T> implements Serializable {
|
||||
//分页类型0不分页,1分页
|
||||
private int type;//必填
|
||||
private List<T> list;//必填
|
||||
private String next;//下一页参数
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class AccessToken implements Serializable {
|
||||
private String webosToken;
|
||||
private Long expireTime;
|
||||
private String refreshToken;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class SoftStore {
|
||||
private String id;
|
||||
private String firstCat;
|
||||
private String secondCat;
|
||||
private String imgPath;
|
||||
private String name;
|
||||
private String code;
|
||||
private String descr;
|
||||
private String screenShots;
|
||||
private Integer score1;
|
||||
private Integer score2;
|
||||
private Integer score3;
|
||||
private Integer score4;
|
||||
private Integer score5;
|
||||
private Integer ratings;
|
||||
private Integer type;
|
||||
private String downloadUrl;
|
||||
private String version;
|
||||
private String author;
|
||||
private String effect;
|
||||
private String average;
|
||||
private String iframeUrl;
|
||||
//1本地软件 2远程软件
|
||||
private Integer isLocal;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class WpsUrlData {
|
||||
private String url;
|
||||
private String token;
|
||||
//1支持0不支持2非阿里云盘
|
||||
private Integer type;
|
||||
//是否是编辑
|
||||
private boolean edit;
|
||||
//type=2的时候才有意义,存储wps专用的fileId
|
||||
private String fileId;
|
||||
private boolean hasFileAuth;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class OfficeUrlData {
|
||||
private String url;
|
||||
private LocalDateTime expireTime;//时间戳
|
||||
//返回 1.文件链接 2.分享链接 3.扫码+阿里云 4.阿里云
|
||||
private Integer type;
|
||||
private Integer coordinationVal;
|
||||
|
||||
public static OfficeUrlData init(String url, LocalDateTime expireTime, Integer type, Integer coordinationVal) {
|
||||
OfficeUrlData that = new OfficeUrlData();
|
||||
that.setUrl(url);
|
||||
that.setType(type);
|
||||
that.setExpireTime(expireTime);
|
||||
that.setCoordinationVal(coordinationVal);
|
||||
return that;
|
||||
}
|
||||
|
||||
public static OfficeUrlData init(Integer type) {
|
||||
OfficeUrlData that = new OfficeUrlData();
|
||||
that.setType(type);
|
||||
return that;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class TransmissionFile implements Serializable {
|
||||
//{source:"{uio:1}/1.txt",path:"{uio:2}/path",name:"test/1.txt"}
|
||||
//源文件
|
||||
private String source;
|
||||
//目标目录
|
||||
private String path;
|
||||
//目标文件
|
||||
private String name;
|
||||
//文件名
|
||||
private String fileName;
|
||||
//缩略图
|
||||
private String thumbnail;
|
||||
//文件大小
|
||||
private long size;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class CopyMoveFile implements Serializable {
|
||||
private String sourceName;
|
||||
private String targetName;
|
||||
private String currentFileName;
|
||||
private double sd;//kb/s
|
||||
private double jd;//0.1等于10%
|
||||
private long loaded;//已上传量
|
||||
private long size;//总量
|
||||
private int status;//1执行中2执行成功3部分失败4前端实现
|
||||
private String exp;
|
||||
}
|
||||
|
||||
public static class BaseEntity implements Serializable {
|
||||
public Entity toEntity() {
|
||||
return Entity.parseWithUnderlineCase(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package cn.tenfell.webos.common.bean;
|
||||
|
||||
public interface StartInface {
|
||||
static String start(){
|
||||
return "19920117";
|
||||
}
|
||||
}
|
20
api/src/main/java/cn/tenfell/webos/common/bean/WebSSHData.java
Executable file
20
api/src/main/java/cn/tenfell/webos/common/bean/WebSSHData.java
Executable file
@ -0,0 +1,20 @@
|
||||
package cn.tenfell.webos.common.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Description: webssh数据传输
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/8
|
||||
*/
|
||||
@Data
|
||||
public class WebSSHData {
|
||||
//操作
|
||||
private String operate;
|
||||
private String host;
|
||||
//端口号默认为22
|
||||
private Integer port = 22;
|
||||
private String username;
|
||||
private String password;
|
||||
private String command = "";
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,303 @@
|
||||
package cn.tenfell.webos.common.filesystem;
|
||||
|
||||
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.modules.entity.IoTokenData;
|
||||
import cn.tenfell.webos.modules.entity.IoUserRecycle;
|
||||
import lombok.Data;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 文件系统接口
|
||||
*/
|
||||
public interface FileSystemInface {
|
||||
/**
|
||||
* 复制文件或者目录
|
||||
* 批量
|
||||
*
|
||||
* @param sourceParent 源文件或者目录 例如aliyundrive/userid/root/626cf8b5abbaf1e4e9e141ea834f6a81c837ef2a/626cf8cb52d147efb3954b85b5888abd6772a0fb
|
||||
* @param path 目标目录 aliyundrive/userid/root 则结果为aliyundrive/userid/root/626cf8cb52d147efb3954b85b5888abd6772a0fb
|
||||
* @return 1成功 0失败 其他taskId
|
||||
*/
|
||||
String copy(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, FileSystemInface.PlainPath path);
|
||||
|
||||
/**
|
||||
* 移动文件或者目录
|
||||
* 批量
|
||||
*
|
||||
* @param sourceParent 源文件或者目录 例如aliyundrive/userid/root/626cf8b5abbaf1e4e9e141ea834f6a81c837ef2a/626cf8cb52d147efb3954b85b5888abd6772a0fb
|
||||
* @param path 目标目录 aliyundrive/userid/root 则结果为aliyundrive/userid/root/626cf8cb52d147efb3954b85b5888abd6772a0fb
|
||||
* @return 1成功 0失败 其他taskId
|
||||
*/
|
||||
String move(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, FileSystemInface.PlainPath path);
|
||||
|
||||
/**
|
||||
* 重命名文件或者目录
|
||||
*
|
||||
* @param source 源文件或者目录 例如aliyundrive/userid/root/626cf8b5abbaf1e4e9e141ea834f6a81c837ef2a/626cf8cb52d147efb3954b85b5888abd6772a0fb
|
||||
* @param name 名称
|
||||
* @return 0失败 1成功
|
||||
*/
|
||||
boolean rename(FileSystemInface.PlainPath source, String name, Integer type);
|
||||
|
||||
/**
|
||||
* 上传文件 因第三方数据各个网盘做法不一样,依赖js实现
|
||||
* @param path 上传的目录
|
||||
* @param name 名称
|
||||
* @param expand 拓展数据
|
||||
* @return
|
||||
* */
|
||||
/**
|
||||
* 阿里云盘
|
||||
* 1.https://api.aliyundrive.com/adrive/v3/file/search 检查是否存在
|
||||
* 2.js端进行分块,每块10MB
|
||||
* 3.https://api.aliyundrive.com/adrive/v2/file/createWithFolders创建文件并获取1-20包的上传链接,如果md5存在直接秒传
|
||||
* 4.https://api.aliyundrive.com/v2/file/get_upload_url获取21-40包的上传链接
|
||||
* 5.按照顺序上传包,每上传完获取下20包,直到上传完毕
|
||||
* 6.https://api.aliyundrive.com/v2/file/complete执行完成上传,并返回fileId
|
||||
* 123网盘
|
||||
* 1.https://www.123pan.com/b/api/file/upload_request
|
||||
* 2.js端进行分块,每块10MB
|
||||
* 3.https://www.123pan.com/b/api/file/upload_request创建文件,如果md5存在直接秒传
|
||||
* 4.https://www.123pan.com/b/api/file/s3_repare_upload_parts_batch获取上传分包,第一次4包,其他每次2包
|
||||
* 5.按照顺序上传包,每上传完获取下2包,直到上传完毕
|
||||
* 6.https://www.123pan.com/a/api/file/s3_complete_multipart_upload批量上传预校验
|
||||
* 7.https://www.123pan.com/a/api/file/upload_complete执行完成上传
|
||||
*/
|
||||
/**
|
||||
* 预检方法,存在则跳过
|
||||
*
|
||||
* @param path
|
||||
* @param name
|
||||
* @param expand
|
||||
* @return 返回前端需要的参数
|
||||
*/
|
||||
String uploadPre(FileSystemInface.PlainPath path, String name, String expand);
|
||||
|
||||
/**
|
||||
* 获取上传地址
|
||||
*
|
||||
* @param path
|
||||
* @param name
|
||||
* @param expand
|
||||
* @return 返回前端需要的参数
|
||||
*/
|
||||
String uploadUrl(FileSystemInface.PlainPath path, String name, String expand);
|
||||
|
||||
/**
|
||||
* 完成方法执行
|
||||
* 分片上传后调用此方法
|
||||
*
|
||||
* @param path
|
||||
* @param name
|
||||
* @param expand
|
||||
* @return 1.成功 0.失败
|
||||
*/
|
||||
String uploadAfter(FileSystemInface.PlainPath path, String name, String expand);
|
||||
|
||||
/**
|
||||
* 后端上传逻辑
|
||||
*
|
||||
* @param path
|
||||
* @param name
|
||||
* @param in 二选一
|
||||
* @param file 二选一
|
||||
* @param consumer 进度条,已上传的值
|
||||
* @return 成功返回fileId 失败返回空
|
||||
*/
|
||||
String uploadByServer(FileSystemInface.PlainPath path, String name, InputStream in, File file, Consumer<Long> consumer);
|
||||
|
||||
/**
|
||||
* 获取下载地址
|
||||
*
|
||||
* @param path
|
||||
* @return 下载地址
|
||||
*/
|
||||
String downloadUrl(FileSystemInface.PlainPath path);
|
||||
|
||||
/**
|
||||
* 新建目录
|
||||
*
|
||||
* @return 返回fileId
|
||||
*/
|
||||
String createDir(FileSystemInface.PlainPath parentPath, String pathName);
|
||||
|
||||
/**
|
||||
* 文件分页数据
|
||||
*
|
||||
* @param parentPath
|
||||
* @param next
|
||||
* @return
|
||||
*/
|
||||
CommonBean.Page<CommonBean.PathInfo> listFiles(FileSystemInface.PlainPath parentPath, String next);
|
||||
|
||||
void refreshToken(String driveType, IoTokenData itd);
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @return 1成功 0失败 其他taskId
|
||||
*/
|
||||
String remove(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes);
|
||||
|
||||
/**
|
||||
* 获取地址对应的中文名
|
||||
*
|
||||
* @param plainPath
|
||||
* @return
|
||||
*/
|
||||
String pathName(PlainPath plainPath);
|
||||
|
||||
/**
|
||||
* 判断文件是否存在,不存在返回mainName,存在返回可能的mainName名称
|
||||
*
|
||||
* @param parentPath
|
||||
* @param mainName 主名称
|
||||
* @param ext
|
||||
* @return
|
||||
*/
|
||||
String availableMainName(PlainPath parentPath, String mainName, String ext);
|
||||
|
||||
/**
|
||||
* zip解压
|
||||
* 1解压成功 0解压失败 2前端实现
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
String unzip(PlainPath file);
|
||||
|
||||
/**
|
||||
* zip压缩
|
||||
*
|
||||
* @param files
|
||||
* @param dirPath
|
||||
* @return 1.成功 0.失败 2.前端实现
|
||||
*/
|
||||
String zip(List<PlainPath> files, PlainPath dirPath);
|
||||
|
||||
/**
|
||||
* 根据路径获取文件信息
|
||||
*
|
||||
* @param plainPath
|
||||
* @return
|
||||
*/
|
||||
|
||||
CommonBean.PathInfo fileInfo(PlainPath plainPath);
|
||||
|
||||
/**
|
||||
* 获取wps编辑地址
|
||||
* 只有阿里云盘返回
|
||||
* 其他网盘返回{type:2}即可
|
||||
*
|
||||
* @param plainPath
|
||||
* @param isEdit
|
||||
* @return
|
||||
*/
|
||||
CommonBean.WpsUrlData getWpsUrl(PlainPath plainPath, boolean isEdit);
|
||||
|
||||
/**
|
||||
* 根据目录,搜索目录下指定的文件
|
||||
*
|
||||
* @param parentPath
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
List<CommonBean.PathInfo> searchFile(PlainPath parentPath, String name);
|
||||
|
||||
/**
|
||||
* 读取文件部分内容
|
||||
* 如果要读取全部,设置length为0
|
||||
*
|
||||
* @param path
|
||||
* @param start
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
|
||||
InputStream getInputStream(PlainPath path, long start, long length);
|
||||
|
||||
/**
|
||||
* 秒传判断
|
||||
*
|
||||
* @param path 当前要传输的目录
|
||||
* @param name 当前文件要存放的目录 aaa/dd/1.txt
|
||||
* @param sourceCipherPath 源文件密文 {sio:1}/path/file
|
||||
* @param size 文件长度
|
||||
* @return 1秒传成功 0秒传失败
|
||||
*/
|
||||
String secondTransmission(PlainPath path, String name, String sourceCipherPath, long size);
|
||||
|
||||
/**
|
||||
* 同盘不同号,自己实现秒传能力
|
||||
*
|
||||
* @param driveType
|
||||
* @param file 包含从哪到哪
|
||||
* @return 成功返回fileId 失败返回空
|
||||
*/
|
||||
String sameDriveCopy(String driveType, CommonBean.TransmissionFile file);
|
||||
|
||||
/**
|
||||
* 获取文件的sha1
|
||||
* 不支持返回空
|
||||
* 请不要实时计算
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
String sha1(PlainPath path);
|
||||
|
||||
/**
|
||||
* 获取文件的md5
|
||||
* 不支持返回空
|
||||
* 请不要实时计算
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
String md5(PlainPath path);
|
||||
|
||||
@Data
|
||||
class PlainPath implements Serializable {
|
||||
private String driveType;//硬盘类型
|
||||
private String realPath;//真实目录
|
||||
private String tokenId;//当前配置数据
|
||||
private String cipherPath;//当前密文路径
|
||||
private String sioNo;//分享时候的编号
|
||||
private String uioNo;//用户io编号
|
||||
private String ioNo;//主io编号
|
||||
private String realFilePath;//真实文件目录(仅限local文件)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从回收站恢复
|
||||
* 本地网盘,realPath必填
|
||||
* 非本地网盘,从path恢复即可
|
||||
*
|
||||
* @param path
|
||||
* @param ioUserRecycle
|
||||
* @return
|
||||
*/
|
||||
|
||||
boolean restore(PlainPath path, IoUserRecycle ioUserRecycle);
|
||||
|
||||
|
||||
String getRootId(String driveType);
|
||||
|
||||
/**
|
||||
* 通用接口,此接口是万能接口
|
||||
*
|
||||
* @param path
|
||||
* @param ctx
|
||||
* @return
|
||||
*/
|
||||
R commonReq(PlainPath path, Context ctx);
|
||||
}
|
||||
|
@ -0,0 +1,746 @@
|
||||
package cn.tenfell.webos.common.filesystem;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.*;
|
||||
import cn.tenfell.webos.modules.entity.*;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class FileSystemUtil implements FileSystemInface {
|
||||
final private static Map<String, FileSystemInface> fileSystemMap = new ConcurrentHashMap<>();
|
||||
final public static FileSystemUtil ACTION = new FileSystemUtil();
|
||||
final public static String LOCAL_DRIVE_TYPE = "local";
|
||||
final public static String SERVER_DRIVE_TYPE = "server";
|
||||
final public static String ALI_PAN_DRIVE_TYPE = "aliyundrive";
|
||||
final public static String PAN123_DRIVE_TYPE = "pan123";
|
||||
final public static String PAN189_DRIVE_TYPE = "pan189";
|
||||
final public static String KODBOX_DRIVE_TYPE = "kodbox";
|
||||
|
||||
static {
|
||||
fileSystemMap.put(ALI_PAN_DRIVE_TYPE, new AliyunDriveFileSystem());
|
||||
fileSystemMap.put(PAN123_DRIVE_TYPE, new Pan123FileSystem());
|
||||
fileSystemMap.put(LOCAL_DRIVE_TYPE, new LocalFileSystem());
|
||||
fileSystemMap.put(SERVER_DRIVE_TYPE, new ServerFileSystem());
|
||||
fileSystemMap.put(PAN189_DRIVE_TYPE, new Pan189FileSystem());
|
||||
fileSystemMap.put(KODBOX_DRIVE_TYPE, new KodBoxFileSystem());
|
||||
}
|
||||
|
||||
public static FileSystemInface.PlainPath cipherPath2PlainPathByUser(String cipherPath, String shareCode, String sharePwd, SysUser user) {
|
||||
FileSystemInface.PlainPath plainPath = cipherPath2PlainPath(cipherPath);//权限验证
|
||||
if (user != null && user.getIsAdmin() != null && user.getIsAdmin() == 1) {
|
||||
return plainPath;
|
||||
}
|
||||
boolean hasAuth = false;
|
||||
if (StrUtil.isNotBlank(plainPath.getSioNo())) {
|
||||
//当前是sio 校验分享编码和密码,失败校验当前登录用户
|
||||
ShareFile sf = getShareFileByNo(plainPath.getSioNo());
|
||||
String errMsg = "";
|
||||
int authCount = 0;
|
||||
if (!StrUtil.equals(sf.getCode(), shareCode)) {
|
||||
if (StrUtil.isBlank(errMsg)) {
|
||||
errMsg = "此分享不存在";
|
||||
}
|
||||
} else {
|
||||
authCount++;
|
||||
}
|
||||
if (StrUtil.isNotBlank(sf.getPassword()) && !StrUtil.equals(sharePwd, sf.getPassword())) {
|
||||
if (StrUtil.isBlank(errMsg)) {
|
||||
errMsg = "权限不足,请刷新后重试";
|
||||
}
|
||||
} else {
|
||||
authCount++;
|
||||
}
|
||||
if (sf.getExpireTime() != null && LocalDate.now().isAfter(sf.getExpireTime())) {
|
||||
if (StrUtil.isBlank(errMsg)) {
|
||||
errMsg = "你来太晚了,当前分享已过期";
|
||||
}
|
||||
} else {
|
||||
authCount++;
|
||||
}
|
||||
hasAuth = authCount == 3;
|
||||
if (hasAuth) {
|
||||
String[] fsz = cipherPath.split("/");
|
||||
if (fsz.length > 1) {
|
||||
String[] files = sf.getFiles().split(";");
|
||||
Assert.isTrue(ArrayUtil.contains(files, fsz[1]), "权限不足");
|
||||
}
|
||||
}
|
||||
if (!hasAuth) {
|
||||
if (user != null) {
|
||||
hasAuth = StrUtil.equals(sf.getUserId(), user.getId());
|
||||
}
|
||||
}
|
||||
Assert.isTrue(hasAuth, StrUtil.isNotBlank(errMsg) ? errMsg : "权限不足,请刷新后重试");
|
||||
} else if (StrUtil.isNotBlank(plainPath.getUioNo())) {
|
||||
//当前是uio 校验当前登录用户
|
||||
IoUserDrive iud = getIoUserDriveByNo(plainPath.getUioNo());
|
||||
if (user != null) {
|
||||
if (user.getUserType() == 1) {
|
||||
hasAuth = StrUtil.equals(iud.getParentUserId(), user.getId());
|
||||
} else {
|
||||
hasAuth = StrUtil.equals(iud.getUserId(), user.getId());
|
||||
}
|
||||
}
|
||||
Assert.isTrue(hasAuth, "权限不足,请刷新后重试");
|
||||
} else {
|
||||
//当前是io 校验当前登录人
|
||||
if (StrUtil.isBlank(plainPath.getIoNo()) && user.getIsAdmin() == 1) {
|
||||
return plainPath;
|
||||
}
|
||||
IoDrive id = getIoDriveByNo(plainPath.getIoNo());
|
||||
if (user != null) {
|
||||
if (user.getUserType() == 1) {
|
||||
hasAuth = StrUtil.equals(id.getParentUserId(), user.getId());
|
||||
}
|
||||
}
|
||||
Assert.isTrue(hasAuth, "权限不足,请刷新后重试");
|
||||
}
|
||||
return plainPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 密文地址转明文地址
|
||||
*
|
||||
* @param cipherPath 密文地址 {io:1} {uio:1}这种
|
||||
* @param shareCode 分享的code
|
||||
* @param sharePwd 分享的密码
|
||||
* @return
|
||||
*/
|
||||
public static FileSystemInface.PlainPath cipherPath2PlainPathByLogin(String cipherPath, String shareCode, String sharePwd) {
|
||||
return cipherPath2PlainPathByUser(cipherPath, shareCode, sharePwd, LoginAuthUtil.getUser());
|
||||
}
|
||||
|
||||
public static ShareFile getShareFileByNo(String sio) {
|
||||
ShareFile sf = DbUtil.queryObject("select * from share_file where no = ?", ShareFile.class, sio);
|
||||
Assert.notNull(sf, "此分享不存在");
|
||||
return sf;
|
||||
}
|
||||
|
||||
public static IoUserDrive getIoUserDriveByNo(String uio) {
|
||||
IoUserDrive iud = DbUtil.queryObject("select * from io_user_drive where no = ?", IoUserDrive.class, uio);
|
||||
Assert.notNull(iud, "此网盘不存在");
|
||||
Assert.isTrue(iud.getValid() == 1, "此网盘已被停止使用");
|
||||
return iud;
|
||||
}
|
||||
|
||||
public static IoDrive getIoDriveByNo(String io) {
|
||||
IoDrive id = DbUtil.queryObject("select * from io_drive where no = ?", IoDrive.class, io);
|
||||
Assert.notNull(id, "此硬盘不存在");
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 密文地址转明文地址
|
||||
* 将以{io:1},{uio:1},{sio:1}开头的地址
|
||||
*
|
||||
* @param cipherPath
|
||||
* @return
|
||||
*/
|
||||
public static FileSystemInface.PlainPath cipherPath2PlainPath(String cipherPath) {
|
||||
//cipherPath 例如 {io:12}/abc {uio:13}/abc {sio:14}/abc
|
||||
Assert.notBlank(cipherPath, "地址不可为空");
|
||||
cipherPath = StrUtil.replace(cipherPath, "\\", "/");
|
||||
if (cipherPath.endsWith(":")) {
|
||||
cipherPath = cipherPath + "/";
|
||||
}
|
||||
String tmpPath = cipherPath;
|
||||
FileSystemInface.PlainPath pp = new FileSystemInface.PlainPath();
|
||||
if (StrUtil.startWith(tmpPath, "{sio:")) {
|
||||
String[] sz = tmpPath.split("/");
|
||||
String sioNo = sz[0].split("}")[0].split(":")[1];
|
||||
ShareFile sf = getShareFileByNo(sioNo);
|
||||
tmpPath = CommonUtil.replaceFirst(tmpPath, sz[0], sf.getPath());
|
||||
pp.setSioNo(sioNo);
|
||||
}
|
||||
if (StrUtil.startWith(tmpPath, "{uio:")) {
|
||||
String[] sz = tmpPath.split("/");
|
||||
String uioNo = sz[0].split("}")[0].split(":")[1];
|
||||
IoUserDrive iud = getIoUserDriveByNo(uioNo);
|
||||
Assert.isTrue(iud.getValid() == 1, "此网盘已被禁用");
|
||||
tmpPath = CommonUtil.replaceFirst(tmpPath, sz[0], iud.getPath());
|
||||
pp.setUioNo(uioNo);
|
||||
}
|
||||
if (StrUtil.startWith(tmpPath, "{io:")) {
|
||||
String[] sz = tmpPath.split("/");
|
||||
String ioNo = sz[0].split("}")[0].split(":")[1];
|
||||
pp.setIoNo(ioNo);
|
||||
IoDrive id = getIoDriveByNo(ioNo);
|
||||
String ioPath = StrUtil.replace(id.getPath(), "\\", "/");
|
||||
String realPath = CommonUtil.replaceFirst(tmpPath, sz[0], ioPath);
|
||||
pp.setTokenId(id.getTokenId());
|
||||
pp.setCipherPath(cipherPath);
|
||||
pp.setRealPath(StrUtil.replace(new File(realPath).getPath(), "\\", "/"));
|
||||
pp.setDriveType(id.getDriveType());
|
||||
if (StrUtil.equals(id.getDriveType(), FileSystemUtil.LOCAL_DRIVE_TYPE)) {
|
||||
if (id.getSecondTransmission() == 1) {
|
||||
//支持秒传,需要真实文件位置
|
||||
pp.setRealFilePath(id.getRealFilePath());
|
||||
} else {
|
||||
//不支持秒传,当服务器文件处理
|
||||
pp.setDriveType(FileSystemUtil.SERVER_DRIVE_TYPE);
|
||||
}
|
||||
}
|
||||
return pp;
|
||||
} else {
|
||||
//系统文件
|
||||
if (!FileUtil.exist(tmpPath)) {
|
||||
Assert.isTrue(false, "此路径不存在");
|
||||
}
|
||||
pp.setCipherPath(cipherPath);
|
||||
pp.setRealPath(StrUtil.replace(new File(tmpPath).getPath(), "\\", "/"));
|
||||
pp.setDriveType(FileSystemUtil.SERVER_DRIVE_TYPE);
|
||||
return pp;
|
||||
}
|
||||
}
|
||||
|
||||
private static FileSystemInface getInfaceByType(String type) {
|
||||
FileSystemInface inface = fileSystemMap.get(type);
|
||||
Assert.notNull(inface, "当前地址暂时还不支持");
|
||||
return inface;
|
||||
}
|
||||
|
||||
public static void serverConfirm(String taskId) {
|
||||
final CommonBean.CopyMoveFile cmf = CacheUtil.getValue("copymove:task:" + taskId);
|
||||
final SysUser user = CacheUtil.getValue("copymove:task-user:" + taskId);
|
||||
Assert.notNull(cmf, "当前任务数据不存在");
|
||||
Assert.notNull(user, "当前任务未指定用户数据");
|
||||
JSONObject exp = JSONUtil.parseObj(cmf.getExp());
|
||||
cmf.setExp(null);
|
||||
final List<CommonBean.TransmissionFile> files = exp.getBeanList("files", CommonBean.TransmissionFile.class);
|
||||
Assert.notEmpty(files, "需要传输的文件不可为空");
|
||||
CommonBean.TransmissionFile firstFile = files.get(0);
|
||||
cmf.setCurrentFileName("(剩余" + files.size() + "个)" + firstFile.getFileName());
|
||||
cmf.setSd(0);
|
||||
cmf.setJd(0);
|
||||
cmf.setLoaded(0);
|
||||
cmf.setSize(firstFile.getSize());
|
||||
cmf.setStatus(1);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
FiberUtil.run(() -> {
|
||||
final Supplier<Boolean> isStop = () -> {
|
||||
Integer flag = CacheUtil.getValue("copymove:task-stop:" + taskId);
|
||||
if (flag != null && flag == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
LoginAuthUtil.USER_LOCAL.set(user);
|
||||
final AtomicBoolean hasError = new AtomicBoolean(false);
|
||||
int fileIndex = 0;
|
||||
int count = files.size();
|
||||
int success = 0;
|
||||
for (CommonBean.TransmissionFile file : files) {
|
||||
if (isStop.get()) {
|
||||
cmf.setCurrentFileName(success + "个文件");
|
||||
cmf.setStatus(3);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
LoginAuthUtil.USER_LOCAL.set(null);
|
||||
return null;
|
||||
}
|
||||
fileIndex++;
|
||||
cmf.setCurrentFileName("(剩余" + (files.size() - fileIndex) + "个)" + file.getFileName());
|
||||
cmf.setSd(0);
|
||||
cmf.setJd(0);
|
||||
cmf.setLoaded(0);
|
||||
cmf.setSize(file.getSize());
|
||||
cmf.setStatus(1);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
long currentTime = System.currentTimeMillis();
|
||||
PlainPath source1 = FileSystemUtil.cipherPath2PlainPathByUser(file.getSource(), "", "", user);
|
||||
PlainPath path1 = FileSystemUtil.cipherPath2PlainPathByUser(file.getPath(), "", "", user);
|
||||
InputStream in = FileSystemUtil.ACTION.getInputStream(source1, 0, 0);
|
||||
String fileId = FileSystemUtil.ACTION.uploadByServer(path1, file.getName(), in, null,
|
||||
loaded -> {
|
||||
cmf.setSd(loaded / 1024.0 / ((System.currentTimeMillis() - currentTime) / 1000.0));
|
||||
cmf.setJd(loaded * 1.0 / cmf.getSize());
|
||||
cmf.setLoaded(loaded);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
}
|
||||
);
|
||||
if (StrUtil.isBlank(fileId)) {
|
||||
hasError.set(true);
|
||||
} else {
|
||||
success++;
|
||||
}
|
||||
}
|
||||
cmf.setCurrentFileName(count + "个文件");
|
||||
cmf.setStatus(hasError.get() ? 3 : 2);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
LoginAuthUtil.USER_LOCAL.set(null);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static void serverStop(String taskId) {
|
||||
CacheUtil.setValue("copymove:task-stop:" + taskId, 1, 3600);
|
||||
}
|
||||
|
||||
private String copyOrMove(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, FileSystemInface.PlainPath path, String method) {
|
||||
if (!isSameDrive(sourceParent, path)) {
|
||||
//通盘不同号 或者 不同盘
|
||||
return serverCopyOrMove(sourceParent, sourceChildren, path, method);
|
||||
}
|
||||
//通盘同号
|
||||
FileSystemInface inface = getInfaceByType(sourceParent.getDriveType());
|
||||
Assert.notNull(inface, "当前地址暂时还不支持");
|
||||
if (StrUtil.equals(method, "copy")) {
|
||||
return inface.copy(sourceParent, sourceChildren, sourceTypes, path);
|
||||
} else if (StrUtil.equals(method, "move")) {
|
||||
return inface.move(sourceParent, sourceChildren, sourceTypes, path);
|
||||
} else {
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
private String serverCopyOrMove(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, PlainPath path, String method) {
|
||||
String sourceDriveType = sourceParent.getDriveType();
|
||||
String pathDriveType = path.getDriveType();
|
||||
String taskId = "task" + IdUtil.fastSimpleUUID();
|
||||
final SysUser user = LoginAuthUtil.getUser();
|
||||
final CommonBean.CopyMoveFile cmf = new CommonBean.CopyMoveFile();
|
||||
String[] pathName1sz = FileSystemUtil.ACTION.pathName(sourceParent).split("/");
|
||||
String[] pathName2sz = FileSystemUtil.ACTION.pathName(path).split("/");
|
||||
cmf.setSourceName(pathName1sz[pathName1sz.length - 1]);
|
||||
cmf.setTargetName(pathName2sz[pathName2sz.length - 1]);
|
||||
cmf.setStatus(1);
|
||||
cmf.setSd(0d);
|
||||
cmf.setLoaded(0);
|
||||
cmf.setSize(0);
|
||||
cmf.setCurrentFileName("即将开始扫描文件");
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
FiberUtil.run(() -> {
|
||||
LoginAuthUtil.USER_LOCAL.set(user);
|
||||
final Supplier<Boolean> isStop = () -> {
|
||||
Integer flag = CacheUtil.getValue("copymove:task-stop:" + taskId);
|
||||
if (flag != null && flag == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
cmf.setCurrentFileName("正在扫描文件,请耐心等待");
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
List<CommonBean.CopyMoveInfo> list = new ArrayList<>();
|
||||
String sourceParentRealPath = sourceParent.getRealPath();
|
||||
String sourceParentCipherPath = sourceParent.getCipherPath();
|
||||
for (int i = 0; i < sourceChildren.size(); i++) {
|
||||
String sourceChild = sourceChildren.get(i);
|
||||
sourceParent.setRealPath(sourceParentRealPath + "/" + sourceChild);
|
||||
sourceParent.setCipherPath(sourceParentCipherPath + "/" + sourceChild);
|
||||
CommonBean.PathInfo info = FileSystemUtil.ACTION.fileInfo(sourceParent);
|
||||
if (info.getType() == 1) {
|
||||
//文件
|
||||
cmf.setCurrentFileName("(扫描中..)" + info.getName());
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
info.setPath(sourceParent.getRealPath());
|
||||
list.add(CommonBean.CopyMoveInfo.init(info, sourceParent.getCipherPath()));
|
||||
} else {
|
||||
//目录
|
||||
loopList(sourceParent, list, cmf, taskId);
|
||||
}
|
||||
}
|
||||
|
||||
List<CommonBean.TransmissionFile> files = new ArrayList<>();
|
||||
int index = 0;
|
||||
int count = list.size();
|
||||
int type = 3;//1同盘不同号,利用服务器实现秒传 2不同盘,与服务器文件相关的采用服务器上传 3不同盘,与服务器文件无关的采用前端处理
|
||||
if (StrUtil.equals(sourceDriveType, pathDriveType)) {
|
||||
type = 1;
|
||||
} else {
|
||||
if (StrUtil.equals(sourceDriveType, LOCAL_DRIVE_TYPE)
|
||||
|| StrUtil.equals(sourceDriveType, SERVER_DRIVE_TYPE)
|
||||
|| StrUtil.equals(pathDriveType, LOCAL_DRIVE_TYPE)
|
||||
|| StrUtil.equals(pathDriveType, SERVER_DRIVE_TYPE)) {
|
||||
type = 2;
|
||||
}
|
||||
}
|
||||
int success = 0;
|
||||
for (CommonBean.CopyMoveInfo cmInfo : list) {
|
||||
if (isStop.get()) {
|
||||
cmf.setCurrentFileName(success + "个文件");
|
||||
cmf.setStatus(3);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
LoginAuthUtil.USER_LOCAL.set(null);
|
||||
return null;
|
||||
}
|
||||
index++;
|
||||
cmf.setCurrentFileName("(扫描中)" + cmInfo.getInfo().getName());
|
||||
cmf.setJd(index * 1.0 / count);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
CommonBean.PathInfo fileInfo = cmInfo.getInfo();
|
||||
sourceParent.setRealPath(fileInfo.getPath());
|
||||
sourceParent.setCipherPath(cmInfo.getCipherPath());
|
||||
String pathName = FileSystemUtil.ACTION.pathName(sourceParent);
|
||||
String tmpPath2 = StrUtil.removeAll(fileInfo.getPath(), sourceParentRealPath);
|
||||
int length = tmpPath2.split("/").length;
|
||||
String[] sz = pathName.split("/");
|
||||
int cz = sz.length - length;
|
||||
List<String> nameList = new ArrayList<>();
|
||||
for (int i = 1; i < length; i++) {
|
||||
nameList.add(sz[cz + i]);
|
||||
}
|
||||
String name = CollUtil.join(nameList, "/");
|
||||
if (type == 1) {
|
||||
//同盘不同号,不验证秒传能力
|
||||
} else {
|
||||
//不同盘,验证秒传能力
|
||||
String st = FileSystemUtil.ACTION.secondTransmission(path, name, sourceParent.getCipherPath(), fileInfo.getSize());
|
||||
if (StrUtil.equals(st, "1")) {
|
||||
success++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
CommonBean.TransmissionFile tf = new CommonBean.TransmissionFile();
|
||||
tf.setName(name);
|
||||
tf.setPath(path.getCipherPath());
|
||||
tf.setSource(sourceParent.getCipherPath());
|
||||
tf.setSize(fileInfo.getSize());
|
||||
tf.setFileName(fileInfo.getName());
|
||||
tf.setThumbnail(fileInfo.getThumbnail());
|
||||
files.add(tf);
|
||||
}
|
||||
if (files.size() == 0) {
|
||||
cmf.setCurrentFileName(count + "个文件");
|
||||
cmf.setJd(1);
|
||||
cmf.setStatus(2);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
LoginAuthUtil.USER_LOCAL.set(null);
|
||||
return null;
|
||||
}
|
||||
if (type == 3) {
|
||||
//前端处理
|
||||
cmf.setStatus(4);
|
||||
cmf.setExp(JSONUtil.createObj().set("type", "2").set("files", files).toString());
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
CacheUtil.setValue("copymove:task-user:" + taskId, user, 3600);
|
||||
LoginAuthUtil.USER_LOCAL.set(null);
|
||||
return null;
|
||||
}
|
||||
final AtomicBoolean hasError = new AtomicBoolean(false);
|
||||
int fileIndex = 0;
|
||||
List<CommonBean.TransmissionFile> sameUploads = new ArrayList<>();
|
||||
for (CommonBean.TransmissionFile file : files) {
|
||||
if (isStop.get()) {
|
||||
cmf.setCurrentFileName(success + "个文件");
|
||||
cmf.setStatus(3);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
LoginAuthUtil.USER_LOCAL.set(null);
|
||||
return null;
|
||||
}
|
||||
fileIndex++;
|
||||
cmf.setCurrentFileName("(剩余" + (files.size() - fileIndex) + "个)" + file.getFileName());
|
||||
cmf.setSd(0);
|
||||
cmf.setJd(0);
|
||||
cmf.setLoaded(0);
|
||||
cmf.setSize(file.getSize());
|
||||
cmf.setStatus(1);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
String fileId;
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (type == 1) {
|
||||
//同盘不同号,利用服务器实现秒传
|
||||
fileId = FileSystemUtil.ACTION.sameDriveCopy(sourceDriveType, file);
|
||||
if (StrUtil.isBlank(fileId)) {
|
||||
sameUploads.add(file);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
//不同盘,与服务器文件相关的采用服务器上传
|
||||
PlainPath source1 = FileSystemUtil.cipherPath2PlainPathByUser(file.getSource(), "", "", user);
|
||||
PlainPath path1 = FileSystemUtil.cipherPath2PlainPathByUser(file.getPath(), "", "", user);
|
||||
InputStream in = FileSystemUtil.ACTION.getInputStream(source1, 0, 0);
|
||||
fileId = FileSystemUtil.ACTION.uploadByServer(path1, file.getName(), in, null,
|
||||
loaded -> {
|
||||
cmf.setSd(loaded / 1024.0 / ((System.currentTimeMillis() - currentTime) / 1000.0));
|
||||
cmf.setJd(loaded * 1.0 / cmf.getSize());
|
||||
cmf.setLoaded(loaded);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
}
|
||||
);
|
||||
}
|
||||
if (StrUtil.isBlank(fileId)) {
|
||||
hasError.set(true);
|
||||
} else {
|
||||
success++;
|
||||
}
|
||||
}
|
||||
if (sameUploads.size() > 0) {
|
||||
cmf.setStatus(4);
|
||||
cmf.setExp(JSONUtil.createObj().set("type", "2").set("files", files).toString());
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
CacheUtil.setValue("copymove:task-user:" + taskId, user, 3600);
|
||||
LoginAuthUtil.USER_LOCAL.set(null);
|
||||
return null;
|
||||
}
|
||||
cmf.setCurrentFileName(count + "个文件");
|
||||
cmf.setStatus(hasError.get() ? 3 : 2);
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
LoginAuthUtil.USER_LOCAL.set(null);
|
||||
return null;
|
||||
});
|
||||
return taskId;
|
||||
}
|
||||
|
||||
private void loopList(PlainPath parent, List<CommonBean.CopyMoveInfo> files, CommonBean.CopyMoveFile cmf, String taskId) {
|
||||
String next = "";
|
||||
String parentRealPath = parent.getRealPath();
|
||||
String parentCipherPath = parent.getCipherPath();
|
||||
while (true) {
|
||||
CommonBean.Page<CommonBean.PathInfo> page = FileSystemUtil.ACTION.listFiles(parent, next);
|
||||
if (CollUtil.isEmpty(page.getList())) {
|
||||
break;
|
||||
}
|
||||
page.getList().forEach(fileInfo -> {
|
||||
if (fileInfo.getType() == 1) {
|
||||
String path = fileInfo.getPath();
|
||||
fileInfo.setPath(parentRealPath + "/" + path);
|
||||
files.add(CommonBean.CopyMoveInfo.init(fileInfo, parentCipherPath + "/" + path));
|
||||
cmf.setCurrentFileName("(扫描中..)" + fileInfo.getName());
|
||||
CacheUtil.setValue("copymove:task:" + taskId, cmf, 3600);
|
||||
} else {
|
||||
parent.setRealPath(parentRealPath + "/" + fileInfo.getPath());
|
||||
parent.setCipherPath(parentCipherPath + "/" + fileInfo.getPath());
|
||||
loopList(parent, files, cmf, taskId);
|
||||
}
|
||||
});
|
||||
if (StrUtil.isBlank(page.getNext())) {
|
||||
break;
|
||||
}
|
||||
next = page.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSameDrive(FileSystemInface.PlainPath first, FileSystemInface.PlainPath... list) {
|
||||
if (first == null) {
|
||||
return false;
|
||||
}
|
||||
if (list == null) {
|
||||
return false;
|
||||
}
|
||||
for (FileSystemInface.PlainPath tmp : list) {
|
||||
if (!StrUtil.equals(tmp.getDriveType(), first.getDriveType())) {
|
||||
return false;
|
||||
}
|
||||
if (!StrUtil.equals(tmp.getDriveType(), LOCAL_DRIVE_TYPE) && !StrUtil.equals(tmp.getDriveType(), SERVER_DRIVE_TYPE)) {
|
||||
if (!StrUtil.equals(tmp.getTokenId(), first.getTokenId())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String copy(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, FileSystemInface.PlainPath path) {
|
||||
return copyOrMove(sourceParent, sourceChildren, sourceTypes, path, "copy");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String move(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, FileSystemInface.PlainPath path) {
|
||||
return copyOrMove(sourceParent, sourceChildren, sourceTypes, path, "move");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rename(FileSystemInface.PlainPath source, String name, Integer type) {
|
||||
return getInfaceByType(source.getDriveType()).rename(source, name, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadPre(FileSystemInface.PlainPath path, String name, String expand) {
|
||||
return getInfaceByType(path.getDriveType()).uploadPre(path, name, expand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadUrl(FileSystemInface.PlainPath path, String name, String expand) {
|
||||
return getInfaceByType(path.getDriveType()).uploadUrl(path, name, expand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadAfter(FileSystemInface.PlainPath path, String name, String expand) {
|
||||
return getInfaceByType(path.getDriveType()).uploadAfter(path, name, expand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadByServer(PlainPath path, String name, InputStream in, File file, Consumer<Long> consumer) {
|
||||
return getInfaceByType(path.getDriveType()).uploadByServer(path, name, in, file, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String downloadUrl(FileSystemInface.PlainPath path) {
|
||||
return getInfaceByType(path.getDriveType()).downloadUrl(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createDir(FileSystemInface.PlainPath path, String pathName) {
|
||||
return getInfaceByType(path.getDriveType()).createDir(path, pathName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.Page listFiles(FileSystemInface.PlainPath parentPath, String next) {
|
||||
return getInfaceByType(parentPath.getDriveType()).listFiles(parentPath, next);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshToken(String driveType, IoTokenData itd) {
|
||||
getInfaceByType(driveType).refreshToken(driveType, itd);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String remove(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes) {
|
||||
return getInfaceByType(sourceParent.getDriveType()).remove(sourceParent, sourceChildren, sourceTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String pathName(PlainPath plainPath) {
|
||||
String path = getInfaceByType(plainPath.getDriveType()).pathName(plainPath);
|
||||
String[] paths = path.split("/");
|
||||
if (StrUtil.isNotBlank(plainPath.getSioNo())) {
|
||||
//当前是sio
|
||||
ShareFile sf = getShareFileByNo(plainPath.getSioNo());
|
||||
paths[0] = sf.getName();
|
||||
} else if (StrUtil.isNotBlank(plainPath.getUioNo())) {
|
||||
//当前是uio
|
||||
IoUserDrive iud = getIoUserDriveByNo(plainPath.getUioNo());
|
||||
paths[0] = iud.getName();
|
||||
} else {
|
||||
//当前是io
|
||||
if (StrUtil.isBlank(plainPath.getIoNo()) && LoginAuthUtil.isSystem()) {
|
||||
return ArrayUtil.join(paths, "/");
|
||||
}
|
||||
IoDrive id = getIoDriveByNo(plainPath.getIoNo());
|
||||
paths[0] = id.getName();
|
||||
}
|
||||
return ArrayUtil.join(paths, "/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String availableMainName(PlainPath parentPath, String mainName, String ext) {
|
||||
return getInfaceByType(parentPath.getDriveType()).availableMainName(parentPath, mainName, ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String unzip(PlainPath file) {
|
||||
return getInfaceByType(file.getDriveType()).unzip(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String zip(List<PlainPath> files, PlainPath dirPath) {
|
||||
if (!isSameDrive(dirPath, ArrayUtil.toArray(files, PlainPath.class))) {
|
||||
return "2";
|
||||
}
|
||||
return getInfaceByType(dirPath.getDriveType()).zip(files, dirPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.PathInfo fileInfo(PlainPath plainPath) {
|
||||
return getInfaceByType(plainPath.getDriveType()).fileInfo(plainPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.WpsUrlData getWpsUrl(PlainPath plainPath, boolean isEdit) {
|
||||
return getInfaceByType(plainPath.getDriveType()).getWpsUrl(plainPath, isEdit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommonBean.PathInfo> searchFile(PlainPath parentPath, String name) {
|
||||
return getInfaceByType(parentPath.getDriveType()).searchFile(parentPath, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream(PlainPath path, long start, long length) {
|
||||
return getInfaceByType(path.getDriveType()).getInputStream(path, start, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String secondTransmission(PlainPath path, String name, String sourceCipherPath, long size) {
|
||||
return getInfaceByType(path.getDriveType()).secondTransmission(path, name, sourceCipherPath, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sameDriveCopy(String driveType, CommonBean.TransmissionFile file) {
|
||||
return getInfaceByType(driveType).sameDriveCopy(driveType, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sha1(PlainPath path) {
|
||||
String hash = getInfaceByType(path.getDriveType()).sha1(path);
|
||||
if (StrUtil.isNotBlank(hash)) {
|
||||
return hash.toLowerCase();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public interface HashSimpleInputStream {
|
||||
InputStream get(long start, long length);
|
||||
}
|
||||
|
||||
|
||||
public static String fileHashSimple(HashSimpleInputStream hsis, long size) {
|
||||
InputStream in;
|
||||
if (size > 1e4) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
long n = size / 50;
|
||||
for (int s = 0; s < 50; s++) {
|
||||
InputStream part = hsis.get(n * s, 200);
|
||||
IoUtil.copy(part, baos);
|
||||
IoUtil.close(part);
|
||||
}
|
||||
InputStream part = hsis.get(size - 200, 200);
|
||||
IoUtil.copy(part, baos);
|
||||
IoUtil.close(part);
|
||||
in = new ByteArrayInputStream(baos.toByteArray());
|
||||
} else {
|
||||
in = hsis.get(0, 0);
|
||||
}
|
||||
String hashSimple = SecureUtil.md5(in) + size;
|
||||
IoUtil.close(in);
|
||||
return hashSimple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String md5(PlainPath path) {
|
||||
String md5 = getInfaceByType(path.getDriveType()).md5(path);
|
||||
if (StrUtil.isNotBlank(md5)) {
|
||||
return md5.toLowerCase();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restore(PlainPath path, IoUserRecycle ioUserRecycle) {
|
||||
return getInfaceByType(path.getDriveType()).restore(path, ioUserRecycle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootId(String driveType) {
|
||||
return getInfaceByType(driveType).getRootId(driveType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R commonReq(PlainPath path, Context ctx) {
|
||||
return getInfaceByType(path.getDriveType()).commonReq(path, ctx);
|
||||
}
|
||||
}
|
@ -0,0 +1,665 @@
|
||||
package cn.tenfell.webos.common.filesystem;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.net.URLEncodeUtil;
|
||||
import cn.hutool.core.net.multipart.MultipartFormData;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.*;
|
||||
import cn.tenfell.webos.modules.entity.IoTokenData;
|
||||
import cn.tenfell.webos.modules.entity.IoUserRecycle;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 可道云挂载
|
||||
*/
|
||||
public class KodBoxFileSystem implements FileSystemInface {
|
||||
@Override
|
||||
public String copy(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, PlainPath path) {
|
||||
copyMoveDel(sourceParent, sourceChildren, sourceTypes, path, "copy");
|
||||
return "1";
|
||||
}
|
||||
|
||||
private void copyMoveDel(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, PlainPath path, String type) {
|
||||
String uri = "";
|
||||
if (StrUtil.equals(type, "copy")) {
|
||||
uri = "explorer/index/pathCopyTo";
|
||||
} else if (StrUtil.equals(type, "move")) {
|
||||
uri = "explorer/index/pathCuteTo";
|
||||
} else if (StrUtil.equals(type, "remove")) {
|
||||
uri = "explorer/index/pathDelete";
|
||||
}
|
||||
Assert.notBlank(uri, "此操作不支持");
|
||||
List<Dict> list = new ArrayList<>();
|
||||
for (int i = 0; i < sourceChildren.size(); i++) {
|
||||
String child = sourceChildren.get(i);
|
||||
String pathStr = realPath2kodPath(sourceParent.getRealPath() + "/" + child);
|
||||
list.add(Dict.create().set("path", pathStr).set("type", sourceTypes.get(i) == 2 ? "folder" : "file"));
|
||||
}
|
||||
Dict param = Dict.create().set("dataArr",
|
||||
JSONUtil.toJsonStr(list));
|
||||
if (StrUtil.equals(type, "copy") || StrUtil.equals(type, "move")) {
|
||||
param.set("path", realPath2kodPath(path.getRealPath()));
|
||||
}
|
||||
this.postData(sourceParent.getTokenId(),
|
||||
uri, param);
|
||||
this.clearCacheByParent(sourceParent.getTokenId(), realPath2kodPath(sourceParent.getRealPath()));
|
||||
if (StrUtil.equals(type, "copy") || StrUtil.equals(type, "move")) {
|
||||
this.clearCacheByParent(path.getTokenId(), realPath2kodPath(path.getRealPath()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String move(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, PlainPath path) {
|
||||
copyMoveDel(sourceParent, sourceChildren, sourceTypes, path, "move");
|
||||
return "1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rename(PlainPath source, String name, Integer type) {
|
||||
String path = realPath2kodPath(source.getRealPath());
|
||||
postData(source.getTokenId(), "explorer/index/pathRename", Dict.create().set("path", path).set("newName", name));
|
||||
this.clearCacheByParent(source.getTokenId(), realPath2parentKodPath(source.getRealPath()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadPre(PlainPath path, String name, String expand) {
|
||||
String parentPath = realPath2kodPath(path.getRealPath());
|
||||
Dict param = JSONUtil.toBean(expand, Dict.class);
|
||||
param.set("path", parentPath);
|
||||
JSONObject res = postData(path.getTokenId(), "explorer/upload/fileUpload", param);
|
||||
if (StrUtil.equals(param.getStr("checkType"), "matchMd5")) {
|
||||
this.clearCacheByParent(path.getTokenId(), realPath2kodPath(path.getRealPath()));
|
||||
if (res.get("info") instanceof String) {
|
||||
return "1";
|
||||
} else {
|
||||
return "2";
|
||||
}
|
||||
} else {
|
||||
return res.getJSONObject("info").toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadUrl(PlainPath path, String name, String expand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadAfter(PlainPath path, String name, String expand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadByServer(PlainPath path, String name, InputStream in, File tmpFile, Consumer<Long> consumer) {
|
||||
boolean needDel = true;
|
||||
if (tmpFile != null) {
|
||||
needDel = false;
|
||||
} else {
|
||||
tmpFile = FileUtil.writeFromStream(in, ProjectUtil.rootPath + "/tmpUpload/" + IdUtil.fastSimpleUUID() + ".cache");
|
||||
}
|
||||
try {
|
||||
Integer sjs = RandomUtil.randomInt(100) + 1;
|
||||
File file = tmpFile;
|
||||
long fileSize = file.length();
|
||||
int fpSize = 1024 * 1024 * 5;
|
||||
long fps = fileSize / fpSize;
|
||||
if (fileSize % fpSize != 0) {
|
||||
fps++;
|
||||
}
|
||||
long modifyTime = 0;
|
||||
try {
|
||||
BasicFileAttributes basicAttr = Files.readAttributes(Paths.get(file.getPath()), BasicFileAttributes.class);
|
||||
FileTime updateTime = basicAttr.lastModifiedTime();
|
||||
modifyTime = updateTime.toMillis();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
String hashSimple = FileSystemUtil.fileHashSimple((start, length) -> new ShardingInputStream(FileUtil.getInputStream(file), start, length), fileSize);
|
||||
String fileMd5 = SecureUtil.md5(file);
|
||||
JSONObject expand = JSONUtil.createObj()
|
||||
.set("fullPath", "/" + name)
|
||||
.set("name", FileUtil.getName(name))
|
||||
.set("checkType", "matchMd5")
|
||||
.set("checkHashSimple", hashSimple)
|
||||
.set("checkHashMd5", fileMd5)
|
||||
.set("size", fileSize)
|
||||
.set("modifyTime", modifyTime)
|
||||
.set("chunkSize", fpSize)
|
||||
.set("chunks", 0);
|
||||
//秒传检查
|
||||
String flag = uploadPre(path, name, expand.toString());
|
||||
if (StrUtil.equals(flag, "1")) {
|
||||
this.clearCacheByParent(path.getTokenId(), realPath2kodPath(path.getRealPath()));
|
||||
return "1";
|
||||
}
|
||||
//获取已经上传的数量
|
||||
expand = JSONUtil.createObj()
|
||||
.set("fullPath", "/" + name)
|
||||
.set("name", FileUtil.getName(name))
|
||||
.set("checkType", "checkHash")
|
||||
.set("checkHashSimple", hashSimple)
|
||||
.set("size", fileSize)
|
||||
.set("modifyTime", modifyTime)
|
||||
.set("chunkSize", fpSize)
|
||||
.set("chunks", 0);
|
||||
String resStr = uploadPre(path, name, expand.toString());
|
||||
JSONObject uploadPreRes = JSONUtil.parseObj(resStr);
|
||||
if (uploadPreRes.get("checkChunkArray") instanceof JSONArray) {
|
||||
uploadPreRes.set("checkChunkArray", JSONUtil.createObj());
|
||||
}
|
||||
JSONObject hasPushMap = uploadPreRes.getJSONObject("checkChunkArray");
|
||||
for (int i = 0; i < fps; i++) {
|
||||
if (consumer != null) {
|
||||
consumer.accept((long) i * fpSize);
|
||||
}
|
||||
int start = i * fpSize;
|
||||
long length = fpSize;
|
||||
if (fileSize - start < length) {
|
||||
length = fileSize - start;
|
||||
}
|
||||
File tmp = FileUtil.writeFromStream(new ShardingInputStream(FileUtil.getInputStream(file), start, length),
|
||||
ProjectUtil.rootPath + "/tmpUpload/" + IdUtil.fastSimpleUUID() + ".data");
|
||||
String uploadSimple = hasPushMap.getStr("part_" + i);
|
||||
if (StrUtil.isNotBlank(uploadSimple)) {
|
||||
String partHashSimple = FileSystemUtil.fileHashSimple((start1, length1) -> new ShardingInputStream(FileUtil.getInputStream(tmp), start1, length1), length);
|
||||
if (StrUtil.equals(uploadSimple, partHashSimple)) {
|
||||
FileUtil.del(tmp);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
try {
|
||||
Dict param = Dict.create()
|
||||
.set("id", "WU_FILE_" + sjs)
|
||||
.set("name", FileUtil.getName(name))
|
||||
.set("size", fileSize)
|
||||
.set("chunks", fps)
|
||||
.set("chunk", i)
|
||||
.set("fullPath", "/" + name)
|
||||
.set("modifyTime", modifyTime)
|
||||
.set("checkHashSimple", hashSimple)
|
||||
.set("chunkSize", fpSize)
|
||||
.set("file", tmp)
|
||||
.set("path", realPath2kodPath(path.getRealPath()));
|
||||
JSONObject res = postData(path.getTokenId(), "explorer/upload/fileUpload", param);
|
||||
if (res.get("info") != null) {
|
||||
if (consumer != null) {
|
||||
consumer.accept(fileSize);
|
||||
}
|
||||
this.clearCacheByParent(path.getTokenId(), realPath2kodPath(path.getRealPath()));
|
||||
return "1";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
} finally {
|
||||
FileUtil.del(tmp);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
} finally {
|
||||
if (needDel) {
|
||||
FileUtil.del(tmpFile);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String downloadUrl(PlainPath path) {
|
||||
String pathStr = realPath2kodPath(path.getRealPath());
|
||||
JSONObject res = this.postData(path.getTokenId(), "explorer/index/pathInfo", Dict.create()
|
||||
.set("dataArr", JSONUtil.createArray().set(Dict.create().set("path", pathStr)).toString())
|
||||
);
|
||||
String str = res.getJSONObject("data").getStr("downloadPath");
|
||||
return CommonUtil.getLastUrl(str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createDir(PlainPath parentPath, String pathName) {
|
||||
String path = realPath2kodPath(parentPath.getRealPath());
|
||||
JSONObject res = this.postData(parentPath.getTokenId(), "explorer/index/mkdir", Dict.create()
|
||||
.set("path", path + "/" + pathName)
|
||||
);
|
||||
this.clearCacheByParent(parentPath.getTokenId(), path);
|
||||
return realPath2kodPath(res.getStr("info"));
|
||||
}
|
||||
|
||||
private JSONObject postData(String tokenId, String uri, Dict data) {
|
||||
IoTokenData itd = TokenDataUtil.getTokenDataByIdOrToken(FileSystemUtil.KODBOX_DRIVE_TYPE, tokenId);
|
||||
JSONObject tokenData = JSONUtil.parseObj(itd.getTokenData());
|
||||
String accessToken = tokenData.getStr("accessToken");
|
||||
String host = tokenData.getStr("host");
|
||||
data.set("accessToken", accessToken);
|
||||
data.set("API_ROUTE", uri);
|
||||
String resStr = HttpUtil.post(host + "/index.php?" + uri, data);
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
Assert.isTrue(res.getBool("code", false), res.getStr("data"));
|
||||
return res;
|
||||
}
|
||||
|
||||
private CommonBean.PathInfo file2info(JSONObject item) {
|
||||
CommonBean.PathInfo pi = new CommonBean.PathInfo();
|
||||
String path = item.getStr("path");
|
||||
if (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
if (StrUtil.isBlank(path)) {
|
||||
//root代替斜杠
|
||||
path = "{root}";
|
||||
}
|
||||
String[] sz = path.split("/");
|
||||
pi.setName(item.getStr("name"));
|
||||
pi.setPath(sz[sz.length - 1]);
|
||||
if (item.getLong("createTime") != null) {
|
||||
pi.setCreatedAt(DateUtil.date(item.getLong("createTime") * 1000L).toString());
|
||||
}
|
||||
if (item.getLong("modifyTime") != null) {
|
||||
pi.setUpdatedAt(DateUtil.date(item.getLong("modifyTime") * 1000L).toString());
|
||||
}
|
||||
if (StrUtil.equals(item.getStr("type"), "file")) {
|
||||
pi.setSize(item.getLong("size"));
|
||||
pi.setType(1);
|
||||
if (item.getJSONObject("fileInfo") != null) {
|
||||
pi.setMd5(item.getJSONObject("fileInfo").getStr("hashMd5"));
|
||||
}
|
||||
pi.setExt(item.getStr("ext"));
|
||||
pi.setThumbnail(item.getStr("fileThumb"));
|
||||
} else {
|
||||
pi.setType(2);
|
||||
}
|
||||
return pi;
|
||||
}
|
||||
|
||||
private List<CommonBean.PathInfo> items2infos(JSONArray folderList, JSONArray fileList) {
|
||||
List<CommonBean.PathInfo> list = new ArrayList<>();
|
||||
if (folderList != null) {
|
||||
for (int i = 0; i < folderList.size(); i++) {
|
||||
JSONObject item = folderList.getJSONObject(i);
|
||||
CommonBean.PathInfo info = file2info(item);
|
||||
list.add(info);
|
||||
}
|
||||
}
|
||||
if (fileList != null) {
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
JSONObject item = fileList.getJSONObject(i);
|
||||
CommonBean.PathInfo info = file2info(item);
|
||||
list.add(info);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private String realPath2kodPath(String realPath) {
|
||||
String[] sz = realPath.split("\\{");
|
||||
String path = "{" + sz[sz.length - 1];
|
||||
path = CommonUtil.trimPath(path);
|
||||
if (StrUtil.startWith(path, "{root}")) {
|
||||
path = CommonUtil.replaceFirst(path, "{root}", "/").replaceAll("//", "/");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private String realPath2parentKodPath(String realPath) {
|
||||
return realPath2kodPath(CommonUtil.getParentPath(realPath));
|
||||
}
|
||||
|
||||
private void clearCacheByParent(String tokenId, String parentPath) {
|
||||
String key = "kodbox:filelist:" + tokenId + ":" + SecureUtil.md5(parentPath) + ":*";
|
||||
CacheUtil.delCacheData(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.Page<CommonBean.PathInfo> listFiles(PlainPath parentPath, String tmpNext) {
|
||||
String path = realPath2kodPath(parentPath.getRealPath());
|
||||
if (StrUtil.isBlank(tmpNext)) {
|
||||
tmpNext = "1";
|
||||
}
|
||||
final int current = Convert.toInt(tmpNext);
|
||||
String marker = SecureUtil.md5(current + "1");
|
||||
String redisKey = "kodbox:filelist:" + parentPath.getTokenId() + ":" + SecureUtil.md5(path) + ":" + marker;
|
||||
return CacheUtil.getCacheData(redisKey, () -> {
|
||||
JSONObject res = postData(parentPath.getTokenId(), "explorer/list/path",
|
||||
Dict.create()
|
||||
.set("path", path)
|
||||
.set("page", current)
|
||||
.set("pageNum", "500")
|
||||
);
|
||||
JSONObject data = res.getJSONObject("data");
|
||||
JSONArray folderList = data.getJSONArray("folderList");
|
||||
JSONArray fileList = data.getJSONArray("fileList");
|
||||
CommonBean.Page<CommonBean.PathInfo> page = new CommonBean.Page<>();
|
||||
int pageTotal = data.getJSONObject("pageInfo").getInt("pageTotal");
|
||||
if (pageTotal > current) {
|
||||
page.setNext(Convert.toStr(current + 1));
|
||||
} else {
|
||||
page.setNext("");
|
||||
}
|
||||
List<CommonBean.PathInfo> list = items2infos(folderList, fileList);
|
||||
page.setList(list);
|
||||
page.setType(1);
|
||||
return page;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
private static String usernamePassword2accessToken(String host, String username, String password) {
|
||||
String csrf = RandomUtil.randomString(16);
|
||||
HttpResponse resp = HttpUtil.createPost(host + "?user/index/loginSubmit").form(Dict.create()
|
||||
.set("name", username)
|
||||
.set("password", password)
|
||||
.set("rememberPassword", "0")
|
||||
.set("salt", "1")
|
||||
.set("CSRF_TOKEN", csrf)
|
||||
.set("API_ROUTE", "user/index/loginSubmit")
|
||||
).execute();
|
||||
String resStr = resp.body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
Assert.isTrue(res.getBool("code", false), "用户名或密码不正确");
|
||||
return res.getStr("info");
|
||||
}
|
||||
|
||||
private static Dict accessToken2user(String host, String accessToken) {
|
||||
String resStr = HttpUtil.createGet(host + "?user/view/options&accessToken=" + accessToken).execute().body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
JSONObject data = res.getJSONObject("data");
|
||||
if (data != null) {
|
||||
JSONObject user = data.getJSONObject("user");
|
||||
if (user != null) {
|
||||
String userId = user.getStr("userID");
|
||||
if (StrUtil.isNotBlank(userId)) {
|
||||
return Dict.create().set("userId", userId).set("accessToken", data.getJSONObject("kod").getStr("accessToken"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshToken(String driveType, IoTokenData itd) {
|
||||
JSONObject tokenData = JSONUtil.parseObj(itd.getTokenData());
|
||||
String host = tokenData.getStr("host");
|
||||
String username = tokenData.getStr("username");
|
||||
String password = tokenData.getStr("password");
|
||||
String accessToken = tokenData.getStr("accessToken");
|
||||
String userId = null;
|
||||
boolean has = false;
|
||||
if (StrUtil.isNotBlank(accessToken)) {
|
||||
Dict user = accessToken2user(host, accessToken);
|
||||
if (user == null) {
|
||||
accessToken = null;
|
||||
} else {
|
||||
accessToken = user.getStr("accessToken");
|
||||
userId = user.getStr("userId");
|
||||
has = true;
|
||||
}
|
||||
}
|
||||
if (StrUtil.isBlank(accessToken)) {
|
||||
accessToken = usernamePassword2accessToken(host, username, password);
|
||||
}
|
||||
Assert.notBlank(accessToken, "当前账号有误,获取token失败");
|
||||
if (!has) {
|
||||
Dict user = accessToken2user(host, accessToken);
|
||||
Assert.notNull(user, "当前账号有误,获取用户信息失败");
|
||||
accessToken = user.getStr("accessToken");
|
||||
userId = user.getStr("userId");
|
||||
}
|
||||
tokenData.set("userId", userId);
|
||||
tokenData.set("accessToken", accessToken);
|
||||
itd.setTokenData(tokenData.toString());
|
||||
itd.setDriveType(driveType);
|
||||
itd.setId(SecureUtil.md5(driveType + host + userId));
|
||||
itd.setExpireTime(LocalDateTime.now().plusMinutes(30L));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes) {
|
||||
copyMoveDel(sourceParent, sourceChildren, sourceTypes, null, "remove");
|
||||
return "1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String pathName(PlainPath plainPath) {
|
||||
Dict pathMap = Dict.create()
|
||||
.set("{block:driver}", "网络挂载")
|
||||
.set("{block:tools}", "工具")
|
||||
.set("{block:files}", "位置")
|
||||
.set("{block:fileType}", "文件类型")
|
||||
.set("{userRecycle}", "回收站")
|
||||
.set("{userShareLink}", "外链分享")
|
||||
.set("{userFileType:photo}", "我的相册")
|
||||
.set("{userShare}", "我的协作")
|
||||
.set("{userRencent}", "最近文档")
|
||||
.set("{block:fileTag}", "标签");
|
||||
String[] paths = plainPath.getCipherPath().split("/");
|
||||
String path = realPath2kodPath(plainPath.getRealPath());
|
||||
String redisKey = "kodbox:pathName:" + SecureUtil.md5(path);
|
||||
JSONArray items = CacheUtil.getCacheData(redisKey, () -> {
|
||||
JSONObject res = postData(plainPath.getTokenId(), "explorer/list/path",
|
||||
Dict.create()
|
||||
.set("path", path)
|
||||
.set("page", 1)
|
||||
.set("pageNum", "1")
|
||||
);
|
||||
JSONObject data = res.getJSONObject("data");
|
||||
JSONObject current = data.getJSONObject("current");
|
||||
JSONArray tmp = JSONUtil.createArray();
|
||||
String pathDisplay = current.getStr("pathDisplay");
|
||||
if (StrUtil.startWith(pathDisplay, "/")) {
|
||||
pathDisplay = "根路径" + pathDisplay;
|
||||
}
|
||||
String kodPath = current.getStr("path");
|
||||
kodPath = CommonUtil.trimPath(kodPath);
|
||||
String[] pathDisplays = pathDisplay.split("/");
|
||||
String[] kodPaths = kodPath.split("/");
|
||||
for (int i = 0; i < pathDisplays.length; i++) {
|
||||
String fileId = "";
|
||||
int pathIndex = i + kodPaths.length - pathDisplays.length;
|
||||
if (pathIndex >= 0) {
|
||||
fileId = kodPaths[pathIndex];
|
||||
}
|
||||
tmp.add(JSONUtil.createObj().set("fileId", fileId).set("name", pathDisplays[i]));
|
||||
}
|
||||
return tmp;
|
||||
}, 0);
|
||||
int length = paths.length;
|
||||
for (int i = length - 1; i > 0; i--) {
|
||||
int n = items.size() - paths.length + i;
|
||||
if (n <= 0) {
|
||||
if (pathMap.get(paths[i]) != null) {
|
||||
paths[i] = pathMap.getStr(paths[i]);
|
||||
continue;
|
||||
}
|
||||
if (n < 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
JSONObject item = items.getJSONObject(n);
|
||||
if (StrUtil.isNotBlank(item.getStr("fileId"))) {
|
||||
Assert.isTrue(item != null && StrUtil.equals(item.getStr("fileId"), paths[i]), "路径非法");
|
||||
}
|
||||
paths[i] = item.getStr("name");
|
||||
}
|
||||
return ArrayUtil.join(paths, "/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String availableMainName(PlainPath parentPath, String mainName, String ext) {
|
||||
int index = 0;
|
||||
while (true) {
|
||||
String tmpMainName = mainName;
|
||||
if (index > 0) {
|
||||
tmpMainName += "(" + index + ")";
|
||||
}
|
||||
String name = tmpMainName;
|
||||
if (StrUtil.isNotBlank(ext)) {
|
||||
name += "." + ext;
|
||||
}
|
||||
List<CommonBean.PathInfo> items = this.searchFile(parentPath, name);
|
||||
if (items.size() == 0) {
|
||||
return tmpMainName;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String unzip(PlainPath file) {
|
||||
String path = realPath2kodPath(file.getRealPath());
|
||||
String parentPath = realPath2kodPath(CommonUtil.getParentPath(file.getRealPath()));
|
||||
postData(file.getTokenId(), "explorer/index/unzip", Dict.create().set("path", path).set("pathTo", parentPath));
|
||||
clearCacheByParent(file.getTokenId(), parentPath);
|
||||
return "1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String zip(List<PlainPath> files, PlainPath dirPath) {
|
||||
List<Dict> list = new ArrayList<>();
|
||||
for (PlainPath file : files) {
|
||||
list.add(Dict.create()
|
||||
.set("path", realPath2kodPath(file.getRealPath()))
|
||||
);
|
||||
}
|
||||
Dict param = Dict.create().set("type", "zip").set("dataArr", JSONUtil.toJsonStr(list));
|
||||
JSONObject res = postData(dirPath.getTokenId(), "explorer/index/zip", param);
|
||||
String parentPath = realPath2kodPath(dirPath.getRealPath());
|
||||
String newPath = res.getStr("info");
|
||||
String newMainName = availableMainName(dirPath, "压缩文件", "zip");
|
||||
dirPath.setRealPath(dirPath.getRealPath() + "/" + newPath);
|
||||
boolean flag = rename(dirPath, newMainName + ".zip", 1);
|
||||
if (!flag) {
|
||||
clearCacheByParent(dirPath.getTokenId(), parentPath);
|
||||
}
|
||||
return "1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.PathInfo fileInfo(PlainPath plainPath) {
|
||||
String pathStr = realPath2kodPath(plainPath.getRealPath());
|
||||
JSONObject res = this.postData(plainPath.getTokenId(), "explorer/index/pathInfo",
|
||||
Dict.create().set("dataArr", JSONUtil.createArray().set(Dict.create().set("path", pathStr)).toString()));
|
||||
return file2info(res.getJSONObject("data"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.WpsUrlData getWpsUrl(PlainPath plainPath, boolean isEdit) {
|
||||
CommonBean.WpsUrlData data = new CommonBean.WpsUrlData();
|
||||
data.setType(2);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommonBean.PathInfo> searchFile(PlainPath parentPath, String name) {
|
||||
String path = "{search}/parentPath=" + URLEncodeUtil.encodeQuery(realPath2kodPath(parentPath.getRealPath())) + "@words=" + URLEncodeUtil.encodeQuery(name);
|
||||
JSONObject res = postData(parentPath.getTokenId(), "explorer/list/path", Dict.create().set("path", path).set("page", 1).set("pageNum", 500));
|
||||
JSONArray fileList = res.getJSONObject("data").getJSONArray("fileList");
|
||||
return items2infos(null, fileList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream(PlainPath path, long start, long length) {
|
||||
String url = downloadUrl(path);
|
||||
HttpRequest req = HttpUtil.createGet(url).setMaxRedirectCount(100);
|
||||
if (length > 0) {
|
||||
//局部
|
||||
req.header("Range", "bytes=" + start + "-" + (length + start - 1));
|
||||
} else {
|
||||
//全部
|
||||
}
|
||||
return req.executeAsync().bodyStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String secondTransmission(PlainPath path, String name, String sourceCipherPath, long size) {
|
||||
//秒传检查
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sameDriveCopy(String driveType, CommonBean.TransmissionFile file) {
|
||||
//同盘不同号,不支持
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sha1(PlainPath path) {
|
||||
//不支持
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String md5(PlainPath path) {
|
||||
CommonBean.PathInfo info = fileInfo(path);
|
||||
if (info != null) {
|
||||
return info.getMd5();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restore(PlainPath path, IoUserRecycle ioUserRecycle) {
|
||||
List<Dict> list = new ArrayList<>();
|
||||
list.add(Dict.create().set("path", realPath2kodPath(ioUserRecycle.getRemovePath())).set("type", ioUserRecycle.getType() == 2 ? "folder" : "file"));
|
||||
Dict param = Dict.create().set("dataArr",
|
||||
JSONUtil.toJsonStr(list));
|
||||
postData(path.getTokenId(), "explorer/index/recycleRestore", param);
|
||||
clearCacheByParent(path.getTokenId(), realPath2kodPath(CommonUtil.getParentPath(ioUserRecycle.getRemovePath())));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootId(String driveType) {
|
||||
return "{block:root}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public R commonReq(PlainPath path, Context ctx) {
|
||||
if (StrUtil.equals(ctx.param("API_ROUTE"), "explorer/upload/fileUpload")) {
|
||||
//分片上传
|
||||
String filePath = ProjectUtil.rootPath + "/tmpUpload/" + IdUtil.fastSimpleUUID() + ".data";
|
||||
try {
|
||||
MultipartFormData multiForm = ProjectContext.getMultipart(ctx);
|
||||
File file = multiForm.getFile("file").write(filePath);
|
||||
Dict param = Dict.parse(ctx.paramMap());
|
||||
param.set("file", file);
|
||||
param.remove("module");
|
||||
param.remove("action");
|
||||
param.set("path", realPath2kodPath(path.getRealPath()));
|
||||
JSONObject res = postData(path.getTokenId(), "explorer/upload/fileUpload", param);
|
||||
return R.okData(res);
|
||||
} catch (Exception e) {
|
||||
return R.error(e);
|
||||
} finally {
|
||||
FileUtil.del(filePath);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,610 @@
|
||||
package cn.tenfell.webos.common.filesystem;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.NioUtil;
|
||||
import cn.hutool.core.io.StreamProgress;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.CacheUtil;
|
||||
import cn.tenfell.webos.common.util.CommonUtil;
|
||||
import cn.tenfell.webos.common.util.ProjectUtil;
|
||||
import cn.tenfell.webos.common.util.ShardingInputStream;
|
||||
import cn.tenfell.webos.modules.action.UserRecycleAction;
|
||||
import cn.tenfell.webos.modules.entity.IoTokenData;
|
||||
import cn.tenfell.webos.modules.entity.IoUserRecycle;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class LocalFileSystem implements FileSystemInface {
|
||||
private static long nextClearTime = 0;
|
||||
|
||||
@Override
|
||||
public String copy(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, FileSystemInface.PlainPath path) {
|
||||
try {
|
||||
for (int i = 0; i < sourceChildren.size(); i++) {
|
||||
FileUtil.copy(new File(sourceParent.getRealPath() + "/" + sourceChildren.get(i)), new File(path.getRealPath()), true);
|
||||
}
|
||||
return "1";
|
||||
} catch (Exception e) {
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String move(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, FileSystemInface.PlainPath path) {
|
||||
try {
|
||||
for (int i = 0; i < sourceChildren.size(); i++) {
|
||||
FileUtil.move(new File(sourceParent.getRealPath() + "/" + sourceChildren.get(i)), new File(path.getRealPath()), true);
|
||||
}
|
||||
return "1";
|
||||
} catch (Exception e) {
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rename(FileSystemInface.PlainPath source, String name, Integer type) {
|
||||
try {
|
||||
FileUtil.rename(new File(source.getRealPath()), name, false);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static CommonBean.PathInfo localPathInfo(String fileRealPath, String name, String fileMd5) {
|
||||
if (!FileUtil.exist(fileRealPath)) {
|
||||
return null;
|
||||
}
|
||||
CommonBean.PathInfo pi = new CommonBean.PathInfo();
|
||||
pi.setName(name);
|
||||
pi.setPath(name);
|
||||
pi.setType(1);
|
||||
pi.setSize(new File(fileRealPath).length());
|
||||
String ext = FileUtil.extName(name).toLowerCase();
|
||||
pi.setExt(ext);
|
||||
pi.setHash(SecureUtil.sha1(new File(fileRealPath)));
|
||||
pi.setMd5(fileMd5);
|
||||
try {
|
||||
BasicFileAttributes basicAttr = Files.readAttributes(Paths.get(fileRealPath), BasicFileAttributes.class);
|
||||
FileTime createTime = basicAttr.creationTime();
|
||||
pi.setCreatedAt(DateTime.of(createTime.toMillis()).toString());
|
||||
FileTime updateTime = basicAttr.lastModifiedTime();
|
||||
pi.setUpdatedAt(DateTime.of(updateTime.toMillis()).toString());
|
||||
} catch (IOException e) {
|
||||
}
|
||||
return pi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadPre(FileSystemInface.PlainPath path, String name, String expand) {
|
||||
JSONObject data = JSONUtil.parseObj(expand);
|
||||
JSONObject res = JSONUtil.createObj();
|
||||
String fileMd5 = data.getStr("file_hash");
|
||||
String pre_hash = data.getStr("pre_hash");
|
||||
res.set("currentFp", 0);
|
||||
if (StrUtil.isBlank(pre_hash)) {
|
||||
pre_hash = IdUtil.fastSimpleUUID();
|
||||
} else {
|
||||
String filePath = ProjectUtil.rootPath + "/tmpUpload/" + pre_hash;
|
||||
if (FileUtil.exist(filePath)) {
|
||||
List<String> files = FileUtil.listFileNames(filePath);
|
||||
if (files != null) {
|
||||
int max = 0;
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
int current = Convert.toInt(files.get(i).replace(".data", ""), 0);
|
||||
if (current > max) {
|
||||
max = current;
|
||||
}
|
||||
}
|
||||
//减2是因为文件序号是从1开始,而前端currentFp是从0开始需要减少1
|
||||
//然后最后一个分片无法保证是完整的,不参与计算,需要继续减少1
|
||||
max = max - 2;
|
||||
if (max < 0) {
|
||||
max = 0;
|
||||
}
|
||||
res.set("currentFp", max);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (StrUtil.isNotBlank(fileMd5)) {
|
||||
String hashFilePath = getRealFileByHash(path.getRealFilePath(), fileMd5);
|
||||
if (FileUtil.exist(hashFilePath)) {
|
||||
res.set("has", true);
|
||||
createWebosFile(fileMd5, path, name);
|
||||
return res.toString();
|
||||
}
|
||||
}
|
||||
res.set("has", false);
|
||||
res.set("upload_id", pre_hash);
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadUrl(FileSystemInface.PlainPath path, String name, String expand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadAfter(FileSystemInface.PlainPath path, String name, String expand) {
|
||||
JSONObject data = JSONUtil.parseObj(expand);
|
||||
String upload_id = data.getStr("upload_id");
|
||||
String fileHash = data.getStr("file_hash");
|
||||
String tmpPath = ProjectUtil.rootPath + "/tmpUpload/" + IdUtil.fastSimpleUUID() + ".data";
|
||||
OutputStream os = FileUtil.getOutputStream(tmpPath);
|
||||
int fps = data.getInt("fps");
|
||||
for (int i = 1; i <= fps; i++) {
|
||||
String fpPath = ProjectUtil.rootPath + "/tmpUpload/" + upload_id + "/" + i + ".data";
|
||||
FileUtil.writeToStream(fpPath, os);
|
||||
}
|
||||
IoUtil.close(os);
|
||||
String nowHash = SecureUtil.md5(new File(tmpPath));
|
||||
String flag = "0";
|
||||
if (StrUtil.equals(nowHash, fileHash)) {
|
||||
flag = "1";
|
||||
removeTmpPathAndCreateWebosFile(tmpPath, path, name);
|
||||
} else {
|
||||
FileUtil.del(tmpPath);
|
||||
}
|
||||
FileUtil.del(ProjectUtil.rootPath + "/tmpUpload/" + upload_id);
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将临时文件移动到webos文件并生成预览文件
|
||||
* 1.
|
||||
*
|
||||
* @param tmpPath
|
||||
* @param path
|
||||
* @param name
|
||||
*/
|
||||
public void removeTmpPathAndCreateWebosFile(String tmpPath, PlainPath path, String name) {
|
||||
String fileMd5 = SecureUtil.md5(new File(tmpPath));
|
||||
String hashFilePath = getRealFileByHash(path.getRealFilePath(), fileMd5);
|
||||
if (FileUtil.exist(hashFilePath)) {
|
||||
String fileMd52 = SecureUtil.md5(new File(hashFilePath));
|
||||
if (StrUtil.equals(fileMd5, fileMd52)) {
|
||||
FileUtil.del(tmpPath);
|
||||
} else {
|
||||
FileUtil.del(hashFilePath);
|
||||
FileUtil.move(new File(tmpPath), new File(hashFilePath), true);
|
||||
}
|
||||
} else {
|
||||
FileUtil.move(new File(tmpPath), new File(hashFilePath), true);
|
||||
}
|
||||
createWebosFile(fileMd5, path, name);
|
||||
}
|
||||
|
||||
private static void createWebosFile(String fileMd5, PlainPath path, String name) {
|
||||
String hashFilePath = getRealFileByHash(path.getRealFilePath(), fileMd5);
|
||||
if (!FileUtil.exist(hashFilePath)) {
|
||||
return;
|
||||
}
|
||||
String webFilePath = path.getRealPath() + "/" + name;
|
||||
name = FileUtil.getName(webFilePath);
|
||||
CommonBean.PathInfo info = localPathInfo(hashFilePath, name, fileMd5);
|
||||
JSONObject jsonObject = JSONUtil.parseObj(info);
|
||||
jsonObject.remove("name");
|
||||
jsonObject.remove("path");
|
||||
jsonObject.remove("ext");
|
||||
FileUtil.writeUtf8String(jsonObject.toString(), webFilePath);
|
||||
FileUtil.writeUtf8String(info.getHash(), path.getRealFilePath() + "/webos_files_sha1" + File.separator + info.getHash() + ".webosfile");
|
||||
}
|
||||
|
||||
private static String getHashBySha1(PlainPath path, String sha1) {
|
||||
String sha1Path = path.getRealFilePath() + "/webos_files_sha1" + File.separator + sha1 + ".webosfile";
|
||||
if (!FileUtil.exist(sha1Path)) {
|
||||
return null;
|
||||
}
|
||||
return FileUtil.readUtf8String(sha1Path);
|
||||
}
|
||||
|
||||
private static String getRealFileByHash(String realFilePath, String fileMd5) {
|
||||
return realFilePath + "/webos_files" + File.separator + fileMd5 + ".webosfile";
|
||||
}
|
||||
|
||||
public String realFilePath(String realFilePath, String webosFilePath) {
|
||||
return LocalFileSystem.getRealFile(realFilePath, webosFilePath);
|
||||
}
|
||||
|
||||
|
||||
public static String getRealFile(String realFilePath, String webosFilePath) {
|
||||
JSONObject res = JSONUtil.parseObj(FileUtil.readUtf8String(webosFilePath));
|
||||
String fileMd5 = res.getStr("md5");
|
||||
if (StrUtil.isBlank(fileMd5)) {
|
||||
return null;
|
||||
}
|
||||
return getRealFileByHash(realFilePath, fileMd5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadByServer(PlainPath path, String name, InputStream in, File file, Consumer<Long> consumer) {
|
||||
OutputStream os = null;
|
||||
try {
|
||||
if (in == null) {
|
||||
in = FileUtil.getInputStream(file);
|
||||
}
|
||||
String tmpPath = ProjectUtil.rootPath + "/tmpUpload/" + IdUtil.fastSimpleUUID() + ".webosfile";
|
||||
os = FileUtil.getOutputStream(tmpPath);
|
||||
IoUtil.copy(in, os, NioUtil.DEFAULT_BUFFER_SIZE, new StreamProgress() {
|
||||
@Override
|
||||
public void start() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void progress(long total, long progressSize) {
|
||||
if (consumer != null) {
|
||||
consumer.accept(progressSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
if (consumer != null) {
|
||||
consumer.accept(new File(tmpPath).length());
|
||||
}
|
||||
}
|
||||
});
|
||||
IoUtil.close(os);
|
||||
removeTmpPathAndCreateWebosFile(tmpPath, path, name);
|
||||
return name;
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
} finally {
|
||||
IoUtil.close(in);
|
||||
if (os != null) {
|
||||
IoUtil.close(os);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String downloadUrl(FileSystemInface.PlainPath path) {
|
||||
String realPath = path.getRealPath();
|
||||
String[] sz = path.getRealPath().split("/");
|
||||
String name = sz[sz.length - 1];
|
||||
String tempId = IdUtil.fastSimpleUUID();
|
||||
CacheUtil.setValue("file_down:" + tempId, Dict.create().set("name", name).set("realPath", realPath).set("realFilePath", path.getRealFilePath()).set("type", path.getDriveType()), 14400);
|
||||
String fileDownUrl = "api?module=fileSystem&action=localFileDown&tempId=" + URLUtil.encodeAll(tempId);
|
||||
return fileDownUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createDir(FileSystemInface.PlainPath parentPath, String pathName) {
|
||||
File file = FileUtil.mkdir(parentPath.getRealPath() + "/" + pathName);
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
return pathName;
|
||||
}
|
||||
|
||||
public CommonBean.PathInfo fileToInfo(File file, String cipherPath) {
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
CommonBean.PathInfo pi;
|
||||
if (file.isFile()) {
|
||||
try {
|
||||
JSONObject res = JSONUtil.parseObj(FileUtil.readUtf8String(file));
|
||||
pi = JSONUtil.toBean(res, CommonBean.PathInfo.class);
|
||||
pi.setName(file.getName());
|
||||
pi.setPath(file.getName());
|
||||
pi.setExt(FileUtil.extName(file.getName()));
|
||||
if (CommonUtil.hasThumbnailByExt(pi.getExt())) {
|
||||
String fileViewerUrl = "api?module=fileSystem&action=localFileViewer";
|
||||
String thumbnail = fileViewerUrl + "&ext=" + pi.getExt() + "&path=" + URLUtil.encodeAll(cipherPath);
|
||||
pi.setThumbnail(thumbnail);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
pi = new CommonBean.PathInfo();
|
||||
pi.setName(file.getName());
|
||||
pi.setPath(file.getName());
|
||||
pi.setType(2);
|
||||
}
|
||||
try {
|
||||
BasicFileAttributes basicAttr = Files.readAttributes(Paths.get(file.getPath()), BasicFileAttributes.class);
|
||||
FileTime createTime = basicAttr.creationTime();
|
||||
pi.setCreatedAt(DateTime.of(createTime.toMillis()).toString());
|
||||
FileTime updateTime = basicAttr.lastModifiedTime();
|
||||
pi.setUpdatedAt(DateTime.of(updateTime.toMillis()).toString());
|
||||
} catch (IOException e) {
|
||||
}
|
||||
return pi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.Page<CommonBean.PathInfo> listFiles(FileSystemInface.PlainPath parentPath, String next) {
|
||||
File[] files = new File(parentPath.getRealPath()).listFiles();
|
||||
CommonBean.Page<CommonBean.PathInfo> page = new CommonBean.Page<>();
|
||||
List<CommonBean.PathInfo> list = new ArrayList<>();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
CommonBean.PathInfo info = fileToInfo(file, parentPath.getCipherPath() + "/" + file.getName());
|
||||
if (info != null) {
|
||||
list.add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
page.setList(list);
|
||||
page.setType(0);
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshToken(String driveType, IoTokenData itd) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes) {
|
||||
try {
|
||||
for (int i = 0; i < sourceChildren.size(); i++) {
|
||||
FileUtil.del(new File(sourceParent.getRealPath() + "/" + sourceChildren.get(i)));
|
||||
}
|
||||
return "1";
|
||||
} catch (Exception e) {
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String pathName(PlainPath plainPath) {
|
||||
String pathName = plainPath.getCipherPath();
|
||||
if (pathName.startsWith("/")) {
|
||||
pathName = "服务器" + pathName;
|
||||
}
|
||||
return pathName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String availableMainName(PlainPath parentPath, String mainName, String ext) {
|
||||
int index = 0;
|
||||
while (true) {
|
||||
String tmpMainName = mainName;
|
||||
if (index > 0) {
|
||||
tmpMainName += "(" + index + ")";
|
||||
}
|
||||
String name = tmpMainName;
|
||||
if (StrUtil.isNotBlank(ext)) {
|
||||
name += "." + ext;
|
||||
}
|
||||
if (!FileUtil.exist(parentPath.getRealPath() + "/" + name)) {
|
||||
return tmpMainName;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommonBean.PathInfo> searchFile(PlainPath parentPath, String name) {
|
||||
if (FileUtil.exist(parentPath.getRealPath() + "/" + name)) {
|
||||
CommonBean.PathInfo info = fileToInfo(new File(parentPath.getRealPath() + "/" + name), parentPath.getCipherPath() + "/" + name);
|
||||
if (info == null) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.newArrayList(info);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream(PlainPath path, long start, long length) {
|
||||
String filePath = realFilePath(path.getRealFilePath(), path.getRealPath());
|
||||
InputStream in = FileUtil.getInputStream(filePath);
|
||||
if (length == 0) {
|
||||
return in;
|
||||
}
|
||||
return new ShardingInputStream(in, start, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String secondTransmission(PlainPath path, String name, String sourceCipherPath, long size) {
|
||||
FileSystemInface.PlainPath source = FileSystemUtil.cipherPath2PlainPathByLogin(sourceCipherPath, "", "");
|
||||
String hash = FileSystemUtil.ACTION.md5(source);
|
||||
if (StrUtil.isBlank(hash)) {
|
||||
String sha1 = FileSystemUtil.ACTION.sha1(source);
|
||||
if (StrUtil.isNotBlank(sha1)) {
|
||||
hash = getHashBySha1(path, sha1);
|
||||
}
|
||||
}
|
||||
if (StrUtil.isBlank(hash)) {
|
||||
return "0";
|
||||
}
|
||||
String resStr = uploadPre(path, name, JSONUtil.createObj().set("file_hash", hash).toString());
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
return res.getBool("has") ? "1" : "0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sameDriveCopy(String driveType, CommonBean.TransmissionFile file) {
|
||||
//不需要实现,本地已经有FileSystemUtil处理过
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sha1(PlainPath path) {
|
||||
CommonBean.PathInfo info = fileInfo(path);
|
||||
if (info != null) {
|
||||
return info.getHash();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String md5(PlainPath path) {
|
||||
CommonBean.PathInfo info = fileInfo(path);
|
||||
if (info != null) {
|
||||
return info.getMd5();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restore(PlainPath path, IoUserRecycle ioUserRecycle) {
|
||||
String realPath = UserRecycleAction.id2realpath(ioUserRecycle.getId());
|
||||
String dir = FileUtil.getParent(path.getRealPath(), 1);
|
||||
boolean flag = false;
|
||||
try {
|
||||
ZipUtil.unzip(realPath, dir, CharsetUtil.CHARSET_UTF_8);
|
||||
flag = true;
|
||||
} catch (Exception e) {
|
||||
if (StrUtil.equals(e.getMessage(), "MALFORMED")) {
|
||||
try {
|
||||
ZipUtil.unzip(realPath, dir, CharsetUtil.CHARSET_GBK);
|
||||
flag = true;
|
||||
} catch (Exception e2) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
FileUtil.del(realPath);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootId(String driveType) {
|
||||
if (System.getProperty("os.name").toLowerCase().indexOf("window") != -1) {
|
||||
return "C:";
|
||||
} else {
|
||||
return "/";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R commonReq(PlainPath path, Context ctx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String unzip(PlainPath file) {
|
||||
try {
|
||||
String[] sz = file.getRealPath().split("/");
|
||||
String parent = ArrayUtil.join(ArrayUtil.remove(sz, sz.length - 1), "/");
|
||||
String zipPath = realFilePath(file.getRealFilePath(), file.getRealPath());
|
||||
String tmpDir = ProjectUtil.rootPath + "/tmpUpload/" + IdUtil.fastSimpleUUID();
|
||||
tmpDir = StrUtil.replace(tmpDir, "\\", "/");
|
||||
try {
|
||||
ZipUtil.unzip(zipPath, tmpDir, CharsetUtil.CHARSET_UTF_8);
|
||||
} catch (Exception e) {
|
||||
if (StrUtil.equals(e.getMessage(), "MALFORMED")) {
|
||||
ZipUtil.unzip(zipPath, tmpDir, CharsetUtil.CHARSET_GBK);
|
||||
} else {
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
List<File> list = FileUtil.loopFiles(tmpDir);
|
||||
for (File tmp : list) {
|
||||
String webosFilePath = parent + StrUtil.replace(StrUtil.replace(tmp.getAbsolutePath(), "\\", "/"), tmpDir, "");
|
||||
file.setRealPath(webosFilePath);
|
||||
removeTmpPathAndCreateWebosFile(tmp.getAbsolutePath(), file, "");
|
||||
}
|
||||
FileUtil.del(tmpDir);
|
||||
return "1";
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String zip(List<PlainPath> files, PlainPath dirPath) {
|
||||
try {
|
||||
String ext = "zip";
|
||||
String zipMainName = this.availableMainName(dirPath, "压缩文件", ext);
|
||||
String tmpPath = ProjectUtil.rootPath + "/tmpUpload/" + IdUtil.fastSimpleUUID() + "." + ext;
|
||||
String path = dirPath.getRealPath();
|
||||
OutputStream fos = new FileOutputStream(tmpPath);
|
||||
List<File> list = new ArrayList<>();
|
||||
for (PlainPath tmp : files) {
|
||||
String realPath = tmp.getRealPath();
|
||||
File obj = new File(realPath);
|
||||
if (!obj.exists()) {
|
||||
return "0";
|
||||
}
|
||||
if (obj.isDirectory()) {
|
||||
List<File> all = FileUtil.loopFiles(obj);
|
||||
if (all != null) {
|
||||
list.addAll(all);
|
||||
}
|
||||
} else if (obj.isFile()) {
|
||||
list.add(obj);
|
||||
}
|
||||
}
|
||||
String[] paths = new String[list.size()];
|
||||
InputStream[] ins = new InputStream[list.size()];
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
String filePath = list.get(i).getAbsolutePath();
|
||||
String realPath = realFilePath(dirPath.getRealFilePath(), filePath);
|
||||
ins[i] = FileUtil.getInputStream(realPath);
|
||||
paths[i] = StrUtil.replace(StrUtil.replace(filePath, "\\", "/"), path + "/", "");
|
||||
}
|
||||
ZipUtil.zip(fos, paths, ins);
|
||||
removeTmpPathAndCreateWebosFile(tmpPath, dirPath, zipMainName + "." + ext);
|
||||
return "1";
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.PathInfo fileInfo(PlainPath plainPath) {
|
||||
File file = new File(plainPath.getRealPath());
|
||||
return fileToInfo(file, plainPath.getCipherPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.WpsUrlData getWpsUrl(PlainPath plainPath, boolean isEdit) {
|
||||
CommonBean.WpsUrlData data = new CommonBean.WpsUrlData();
|
||||
data.setType(2);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 每隔2小时检查一下过期的上传文件缓存
|
||||
*/
|
||||
public static void clearExpireTmpUploadFile() {
|
||||
if (nextClearTime > System.currentTimeMillis()) {
|
||||
return;
|
||||
}
|
||||
String parent = ProjectUtil.rootPath + "/tmpUpload";
|
||||
File[] files = new File(parent).listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
try {
|
||||
BasicFileAttributes basicAttr = Files.readAttributes(Paths.get(file.getPath()), BasicFileAttributes.class);
|
||||
FileTime createTime = basicAttr.creationTime();
|
||||
if (System.currentTimeMillis() - createTime.toMillis() > 24 * 60 * 60 * 1000L) {
|
||||
FileUtil.del(file);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
nextClearTime = System.currentTimeMillis() + 2 * 60 * 60 * 1000;
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
package cn.tenfell.webos.common.filesystem;
|
||||
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.modules.entity.IoTokenData;
|
||||
import cn.tenfell.webos.modules.entity.IoUserRecycle;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Pan123FileSystem implements FileSystemInface {
|
||||
@Override
|
||||
public String copy(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, PlainPath path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String move(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, PlainPath path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rename(FileSystemInface.PlainPath source, String name, Integer type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadPre(FileSystemInface.PlainPath path, String name, String expand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadUrl(FileSystemInface.PlainPath path, String name, String expand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadAfter(FileSystemInface.PlainPath path, String name, String expand) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadByServer(PlainPath path, String name, InputStream in, File file, Consumer<Long> consumer) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String downloadUrl(FileSystemInface.PlainPath path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createDir(FileSystemInface.PlainPath parentPath, String pathName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.Page<CommonBean.PathInfo> listFiles(FileSystemInface.PlainPath parentPath, String next) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshToken(String driveType, IoTokenData itd) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String pathName(PlainPath plainPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String availableMainName(PlainPath parentPath, String mainName, String ext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String unzip(PlainPath file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String zip(List<PlainPath> files, PlainPath dirPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.PathInfo fileInfo(PlainPath plainPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.WpsUrlData getWpsUrl(PlainPath plainPath, boolean isEdit) {
|
||||
CommonBean.WpsUrlData data = new CommonBean.WpsUrlData();
|
||||
data.setType(2);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommonBean.PathInfo> searchFile(PlainPath parentPath, String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream(PlainPath path, long start, long length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String secondTransmission(PlainPath path, String name, String sourceCipherPath, long size) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sameDriveCopy(String driveType, CommonBean.TransmissionFile file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sha1(PlainPath path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String md5(PlainPath path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restore(PlainPath path, IoUserRecycle ioUserRecycle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootId(String driveType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R commonReq(PlainPath path, Context ctx) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,803 @@
|
||||
package cn.tenfell.webos.common.filesystem;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.NioUtil;
|
||||
import cn.hutool.core.io.StreamProgress;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.hutool.http.*;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.CacheUtil;
|
||||
import cn.tenfell.webos.common.util.ProjectUtil;
|
||||
import cn.tenfell.webos.common.util.ShardingInputStream;
|
||||
import cn.tenfell.webos.common.util.TokenDataUtil;
|
||||
import cn.tenfell.webos.modules.entity.IoTokenData;
|
||||
import cn.tenfell.webos.modules.entity.IoUserRecycle;
|
||||
import lombok.Data;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Pan189FileSystem implements FileSystemInface {
|
||||
String apiHost = "https://cloud.189.cn/api";
|
||||
|
||||
@Override
|
||||
public String copy(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, PlainPath path) {
|
||||
return copyOrMoveDelete(sourceParent, sourceChildren, sourceTypes, path, "COPY", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String move(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, PlainPath path) {
|
||||
return copyOrMoveDelete(sourceParent, sourceChildren, sourceTypes, path, "MOVE", null);
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class RestoreFile {
|
||||
private String name;
|
||||
private Integer type;
|
||||
|
||||
public static RestoreFile init(String name, Integer type) {
|
||||
RestoreFile rf = new RestoreFile();
|
||||
rf.setName(name);
|
||||
rf.setType(type);
|
||||
return rf;
|
||||
}
|
||||
}
|
||||
|
||||
private String copyOrMoveDelete(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, FileSystemInface.PlainPath path, String type, RestoreFile rf) {
|
||||
Pan189FileSystem that = this;
|
||||
String oldRealPath = sourceParent.getRealPath();
|
||||
String oldCipherPath = sourceParent.getCipherPath();
|
||||
String target_file_id = "";
|
||||
if (type.equals("COPY") || type.equals("MOVE")) {
|
||||
String[] szTarget = path.getRealPath().split("/");
|
||||
target_file_id = szTarget[szTarget.length - 1];
|
||||
}
|
||||
String[] sz = sourceParent.getRealPath().split("/");
|
||||
String source_file_id = sz[sz.length - 1];
|
||||
String sourceParentReal = sourceParent.getRealPath();
|
||||
String sourceParentCipher = sourceParent.getCipherPath();
|
||||
String uri = "/open/batch/createBatchTask.action";
|
||||
List<Dict> actionDataList = new ArrayList<>();
|
||||
for (int i = 0; i < sourceChildren.size(); i++) {
|
||||
String fileId = sourceChildren.get(i);
|
||||
sourceParent.setRealPath(sourceParentReal + "/" + fileId);
|
||||
sourceParent.setCipherPath(sourceParentCipher + "/" + fileId);
|
||||
if (rf == null) {
|
||||
CommonBean.PathInfo info = this.fileInfo(sourceParent);
|
||||
actionDataList.add(Dict.create()
|
||||
.set("fileId", fileId)
|
||||
.set("fileName", info.getName())
|
||||
.set("isFolder", sourceTypes.get(i) == 2 ? 1 : 0)
|
||||
);
|
||||
} else {
|
||||
actionDataList.add(Dict.create()
|
||||
.set("fileId", fileId)
|
||||
.set("fileName", rf.getName())
|
||||
.set("isFolder", rf.getType() != 1 ? 1 : 0)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
Dict data = Dict.create()
|
||||
.set("type", type)
|
||||
.set("targetFolderId", target_file_id)
|
||||
.set("taskInfos", JSONUtil.toJsonStr(actionDataList));
|
||||
JSONObject res = postData(uri, data, sourceParent.getTokenId(), 0);
|
||||
boolean flag = StrUtil.equals(res.getStr("res_code"), "0");
|
||||
if (flag) {
|
||||
String taskId = res.getStr("taskId");
|
||||
int count = 0;
|
||||
while (true) {
|
||||
uri = "/batch/checkBatchTask.action";
|
||||
data = Dict.create()
|
||||
.set("type", type)
|
||||
.set("taskId", taskId);
|
||||
res = postData(uri, data, sourceParent.getTokenId(), 0);
|
||||
flag = StrUtil.equals(res.getStr("res_code"), "0");
|
||||
if (res.getInt("taskStatus", 1) == 4) {
|
||||
break;
|
||||
}
|
||||
ThreadUtil.sleep(1000L);
|
||||
count++;
|
||||
if (count > 10) {
|
||||
sourceParent.setRealPath(oldRealPath);
|
||||
sourceParent.setCipherPath(oldCipherPath);
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
that.clearCacheByParent(sourceParent.getTokenId(), source_file_id);
|
||||
if (type.equals("COPY") || type.equals("MOVE")) {
|
||||
that.clearCacheByParent(path.getTokenId(), target_file_id);
|
||||
}
|
||||
}
|
||||
sourceParent.setRealPath(oldRealPath);
|
||||
sourceParent.setCipherPath(oldCipherPath);
|
||||
return flag ? "1" : "0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rename(PlainPath source, String name, Integer type) {
|
||||
String[] sz = source.getRealPath().split("/");
|
||||
String file_id = sz[sz.length - 1];
|
||||
String uri = "/open/file/renameFile.action";
|
||||
Dict map = Dict.create().set("fileId", file_id).set("destFileName", name);
|
||||
if (type == 2) {
|
||||
uri = "/open/file/renameFolder.action";
|
||||
map = Dict.create().set("folderId", file_id).set("destFolderName", name);
|
||||
}
|
||||
JSONObject res = postData(uri, map, source.getTokenId(), 0);
|
||||
boolean flag = StrUtil.equals(res.getStr("res_code"), "0");
|
||||
if (flag) {
|
||||
this.clearCacheByParent(source.getTokenId(), sz[sz.length - 2]);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
private void clearCacheByParent(String tokenId, String parent_file_id) {
|
||||
String key = "pan189:filelist:" + tokenId + ":" + parent_file_id + ":*";
|
||||
CacheUtil.delCacheData(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadPre(PlainPath path, String name, String expand) {
|
||||
JSONObject exp = JSONUtil.parseObj(expand);
|
||||
Integer currentType = exp.getInt("currentType");
|
||||
JSONObject data = exp.getJSONObject("data");
|
||||
if (currentType == 1) {
|
||||
//获取上传id
|
||||
Dict parentData = this.getParentPathIdAndName(path, name);
|
||||
String parentFolderId = parentData.getStr("parentFolderId");
|
||||
Dict param = data.toBean(Dict.class);
|
||||
param.set("parentFolderId", parentFolderId);
|
||||
JSONObject res = jmGetData("/person/initMultiUpload",
|
||||
param,
|
||||
path.getTokenId());
|
||||
return res.getJSONObject("data").toString();
|
||||
} else if (currentType == 2) {
|
||||
//查询已上传分片数量
|
||||
} else if (currentType == 3) {
|
||||
//秒传检查
|
||||
Dict param = data.toBean(Dict.class);
|
||||
JSONObject res = jmGetData("/person/checkTransSecond",
|
||||
param,
|
||||
path.getTokenId());
|
||||
return res.getJSONObject("data").toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadUrl(PlainPath path, String name, String expand) {
|
||||
Dict data = JSONUtil.toBean(expand, Dict.class);
|
||||
JSONObject putUrlRes = jmGetData("/person/getMultiUploadUrls", data, path.getTokenId());
|
||||
return putUrlRes.getJSONObject("uploadUrls").toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadAfter(PlainPath path, String name, String expand) {
|
||||
Dict data = JSONUtil.toBean(expand, Dict.class);
|
||||
JSONObject res = jmGetData("/person/commitMultiUploadFile", data, path.getTokenId());
|
||||
JSONObject resFile = res.getJSONObject("file");
|
||||
String fileId = resFile.getStr("userFileId");
|
||||
if (StrUtil.isNotBlank(fileId)) {
|
||||
Dict parentData = this.getParentPathIdAndName(path, name);
|
||||
String parentFolderId = parentData.getStr("parentFolderId");
|
||||
String lastName = parentData.getStr("name");
|
||||
if (!StrUtil.equals(lastName, resFile.getStr("fileName"))) {
|
||||
this.coverFile(path.getTokenId(), parentFolderId, fileId, lastName);
|
||||
}
|
||||
String[] sz = path.getRealPath().split("/");
|
||||
this.clearCacheByParent(path.getTokenId(), sz[sz.length - 1]);
|
||||
return "1";
|
||||
} else {
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
private Dict getParentPathIdAndName(PlainPath path, String name) {
|
||||
String[] sz = path.getRealPath().split("/");
|
||||
String parentFolderId = sz[sz.length - 1];
|
||||
String[] pathSz = name.split("/");
|
||||
String lastName = pathSz[pathSz.length - 1];
|
||||
if (pathSz.length > 1) {
|
||||
List<String> dirs = new ArrayList<>();
|
||||
for (int i = 0; i < pathSz.length - 1; i++) {
|
||||
List<String> tmp = new ArrayList<>();
|
||||
for (int j = 0; j <= i; j++) {
|
||||
tmp.add(pathSz[j]);
|
||||
}
|
||||
dirs.add(CollUtil.join(tmp, "/"));
|
||||
}
|
||||
JSONObject map = this.createDirPl(path, dirs);
|
||||
parentFolderId = map.getStr(dirs.get(dirs.size() - 1));
|
||||
}
|
||||
return Dict.create().set("parentFolderId", parentFolderId).set("name", lastName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadByServer(PlainPath path, String name, InputStream in, File file, Consumer<Long> consumer) {
|
||||
boolean needDel = true;
|
||||
if (file != null) {
|
||||
needDel = false;
|
||||
} else {
|
||||
file = FileUtil.writeFromStream(in, ProjectUtil.rootPath + "/tmpUpload/" + IdUtil.fastSimpleUUID() + ".cache");
|
||||
}
|
||||
try {
|
||||
Dict parentData = this.getParentPathIdAndName(path, name);
|
||||
String parentFolderId = parentData.getStr("parentFolderId");
|
||||
String lastName = parentData.getStr("name");
|
||||
long size = file.length();
|
||||
int tenMSize = 10485760;
|
||||
long fpSize = 0;
|
||||
if (size >= 0 && size < 1000 * tenMSize) {
|
||||
//10M
|
||||
fpSize = tenMSize * 1;
|
||||
} else if (1000 * tenMSize >= 0 && size < 2000 * tenMSize) {
|
||||
//20M
|
||||
fpSize = tenMSize * 2;
|
||||
} else if (size >= 2000 * tenMSize && size < 10000 * tenMSize) {
|
||||
//50M
|
||||
fpSize = tenMSize * 5;
|
||||
} else {
|
||||
long n = size / 2000 / tenMSize;
|
||||
if (size % (2000 * tenMSize) != 0) {
|
||||
n++;
|
||||
}
|
||||
fpSize = tenMSize * n;
|
||||
}
|
||||
long fpSl = size / fpSize;
|
||||
if (size % fpSize != 0) {
|
||||
fpSl++;
|
||||
}
|
||||
String fileMd5 = SecureUtil.md5(file);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
List<Dict> fps = new ArrayList<>();
|
||||
String sliceMd5;
|
||||
if (fpSl > 1) {
|
||||
for (int i = 1; i <= fpSl; i++) {
|
||||
long length = fpSize;
|
||||
if (size - (i - 1) * fpSize < length) {
|
||||
length = size - (i - 1) * fpSize;
|
||||
}
|
||||
if (i > 1) {
|
||||
sb.append("\n");
|
||||
}
|
||||
InputStream fp = new ShardingInputStream(FileUtil.getInputStream(file), (i - 1) * fpSize, length);
|
||||
String fpMd5 = SecureUtil.md5(fp);
|
||||
sb.append(fpMd5.toUpperCase());
|
||||
IoUtil.close(fp);
|
||||
fps.add(Dict.create().set("index", i).set("fpMd5", fpMd5));
|
||||
}
|
||||
sliceMd5 = SecureUtil.md5(sb.toString());
|
||||
} else {
|
||||
sliceMd5 = fileMd5;
|
||||
fps.add(Dict.create().set("index", 1).set("fpMd5", fileMd5.toUpperCase()));
|
||||
}
|
||||
JSONObject param = JSONUtil.createObj().set("currentType", 1).set("data", Dict.create()
|
||||
.set("fileName", name)
|
||||
.set("fileSize", file.length())
|
||||
.set("sliceSize", fpSize)
|
||||
.set("fileMd5", fileMd5)
|
||||
.set("sliceMd5", sliceMd5));
|
||||
JSONObject data = JSONUtil.parseObj(uploadPre(path, name, param.toString()));
|
||||
if (data.getInt("fileDataExists") == 0) {
|
||||
//文件不存在需要上传
|
||||
for (int n = 0; n < fps.size(); n++) {
|
||||
if (consumer != null) {
|
||||
consumer.accept((long) n * fpSize);
|
||||
}
|
||||
Dict fpData = fps.get(n);
|
||||
int i = fpData.getInt("index");
|
||||
String fpMd5 = fpData.getStr("fpMd5");
|
||||
long length = fpSize;
|
||||
if (size - (i - 1) * fpSize < length) {
|
||||
length = size - (i - 1) * fpSize;
|
||||
}
|
||||
InputStream fp = new ShardingInputStream(FileUtil.getInputStream(file), (i - 1) * fpSize, length);
|
||||
JSONObject urlsParam = JSONUtil.createObj()
|
||||
.set("uploadFileId", data.getStr("uploadFileId"))
|
||||
.set("partInfo", i + "-" + Base64.encode(HexUtil.decodeHex(fpMd5)));
|
||||
JSONObject putUrlData = JSONUtil.parseObj(uploadUrl(path, name, urlsParam.toString())).getJSONObject("partNumber_" + i);
|
||||
String putUrl = putUrlData.getStr("requestURL");
|
||||
String putHeader = putUrlData.getStr("requestHeader");
|
||||
String[] phsz = putHeader.split("&");
|
||||
Map<String, String> headerMap = new HashMap<>();
|
||||
for (String ph : phsz) {
|
||||
String[] isz = ph.split("=", 2);
|
||||
headerMap.put(isz[0], isz[1]);
|
||||
}
|
||||
headerMap.put("content-length", Convert.toStr(length));
|
||||
putFile(putUrl, fp, headerMap, (long) n * fpSize, consumer);
|
||||
}
|
||||
}
|
||||
//文件上传完成可以进行提交
|
||||
Dict compParam = Dict.create()
|
||||
.set("uploadFileId", data.getStr("uploadFileId"))
|
||||
.set("lazyCheck", 0)
|
||||
.set("fileMd5", fileMd5)
|
||||
.set("sliceMd5", sb.toString());
|
||||
JSONObject res = jmGetData("/person/commitMultiUploadFile", compParam, path.getTokenId());
|
||||
if (consumer != null) {
|
||||
consumer.accept(size);
|
||||
}
|
||||
JSONObject resFile = res.getJSONObject("file");
|
||||
String fileId = resFile.getStr("userFileId");
|
||||
if (StrUtil.isNotBlank(fileId)) {
|
||||
if (!StrUtil.equals(resFile.getStr("fileName"), lastName)) {
|
||||
//不相等,需要删掉旧文件,新文件改名成旧文件
|
||||
this.coverFile(path.getTokenId(), parentFolderId, fileId, lastName);
|
||||
}
|
||||
String[] sz = path.getRealPath().split("/");
|
||||
this.clearCacheByParent(path.getTokenId(), sz[sz.length - 1]);
|
||||
}
|
||||
return fileId;
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
} finally {
|
||||
if (needDel) {
|
||||
FileUtil.del(file);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private void coverFile(String tokenId, String parentFolderId, String newFileId, String oldName) {
|
||||
JSONObject res = postData("/open/file/searchFiles.action", Dict.create()
|
||||
.set("folderId", parentFolderId)
|
||||
.set("pageSize", 60)
|
||||
.set("pageNum", 1)
|
||||
.set("fileName", oldName)
|
||||
.set("fileType", 1)
|
||||
, tokenId, 0);
|
||||
JSONArray fileList = res.getJSONArray("fileList");
|
||||
String oldFileId = "";
|
||||
if (fileList != null) {
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
JSONObject file = fileList.getJSONObject(i);
|
||||
if (StrUtil.equals(file.getStr("name"), oldName)) {
|
||||
oldFileId = file.getStr("id");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
PlainPath path = new PlainPath();
|
||||
path.setTokenId(tokenId);
|
||||
if (StrUtil.isNotBlank(oldFileId)) {
|
||||
path.setRealPath(parentFolderId);
|
||||
Assert.isTrue(StrUtil.equals(this.remove(path, CollUtil.newArrayList(oldFileId), CollUtil.newArrayList(1)), "1"), "删除文件失败");
|
||||
}
|
||||
path.setRealPath(parentFolderId + "/" + newFileId);
|
||||
Assert.isTrue(this.rename(path, oldName, 1), "重命名文件失败");
|
||||
}
|
||||
|
||||
private void putFile(String url, InputStream in, Map<String, String> headerMap, long start, Consumer<Long> consumer) {
|
||||
OutputStream out = null;
|
||||
try {
|
||||
byte[] fpData = IoUtil.readBytes(in);
|
||||
in = new ByteArrayInputStream(fpData);
|
||||
HttpConnection connection = HttpConnection.create(url, null)
|
||||
.setConnectTimeout(HttpGlobalConfig.getTimeout())
|
||||
.setReadTimeout(HttpGlobalConfig.getTimeout())
|
||||
.setInstanceFollowRedirects(false)
|
||||
.setMethod(Method.PUT);
|
||||
HttpURLConnection conn = (HttpURLConnection) ReflectUtil.getFieldValue(connection, "conn");
|
||||
headerMap.forEach((k, v) -> connection.header(k, v, true));
|
||||
conn.setFixedLengthStreamingMode(Convert.toLong(headerMap.get("content-length")));
|
||||
out = connection.getOutputStream();
|
||||
IoUtil.copy(in, out, NioUtil.DEFAULT_BUFFER_SIZE, new StreamProgress() {
|
||||
@Override
|
||||
public void start() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void progress(long total, long progressSize) {
|
||||
if (consumer != null) {
|
||||
consumer.accept(start + progressSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
}
|
||||
});
|
||||
|
||||
connection.responseCode();
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
} finally {
|
||||
IoUtil.close(in);
|
||||
if (out != null) {
|
||||
IoUtil.close(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String downloadUrl(PlainPath path) {
|
||||
Pan189FileSystem that = this;
|
||||
String[] sz = path.getRealPath().split("/");
|
||||
String file_id = sz[sz.length - 1];
|
||||
return CacheUtil.getCacheData("pan189:downurl:" + file_id, () -> {
|
||||
JSONObject res = that.postData("/open/file/getFileDownloadUrl.action", Dict.create().set("fileId", file_id).set("forcedGet", "1"), path.getTokenId(), 0);
|
||||
String url = res.getStr("fileDownloadUrl");
|
||||
HttpResponse response = HttpUtil.createGet(url).execute();
|
||||
String tmpUrl = response.header("Location");
|
||||
if (StrUtil.isNotBlank(tmpUrl)) {
|
||||
url = tmpUrl;
|
||||
}
|
||||
return url;
|
||||
}, 120);
|
||||
}
|
||||
|
||||
private JSONObject createDirPl(PlainPath path, List<String> dirs) {
|
||||
String[] sz = path.getRealPath().split("/");
|
||||
String parentFileId = sz[sz.length - 1];
|
||||
JSONObject res = this.postData("/portal/createFolders.action",
|
||||
Dict.create().set("opScene", 1).
|
||||
set("folderList", JSONUtil.createObj()
|
||||
.set("parentId", parentFileId)
|
||||
.set("paths", dirs)
|
||||
.toString()
|
||||
),
|
||||
path.getTokenId(),
|
||||
0
|
||||
);
|
||||
this.clearCacheByParent(path.getTokenId(), parentFileId);
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createDir(PlainPath parentPath, String pathName) {
|
||||
return this.createDirPl(parentPath, CollUtil.newArrayList(pathName)).getStr(pathName);
|
||||
}
|
||||
|
||||
public JSONObject postData(String uri, Dict map, String tokenId, int secondOut) {
|
||||
String cookie = getCookie(tokenId);
|
||||
String param = "";
|
||||
if (map != null) {
|
||||
param = JSONUtil.toJsonStr(map);
|
||||
}
|
||||
Supplier<JSONObject> supplier = () -> {
|
||||
String resstr = HttpUtil.createPost(apiHost + uri)
|
||||
.header("accept", "application/json;charset=UTF-8").cookie(cookie)
|
||||
.form(map)
|
||||
.header("referer", "https://cloud.189.cn/")
|
||||
.execute().body();
|
||||
if (StrUtil.isBlank(resstr)) {
|
||||
return null;
|
||||
}
|
||||
JSONObject json = JSONUtil.parseObj(resstr);
|
||||
Assert.isFalse(StrUtil.equals(json.getStr("errorCode"), "InvalidSessionKey"), "当前登录已失效,请重新扫码登录");
|
||||
return json;
|
||||
};
|
||||
if (secondOut > 0) {
|
||||
return CacheUtil.getCacheData("pan189:postdata:" + SecureUtil.md5(uri + param + tokenId), supplier, secondOut);
|
||||
} else {
|
||||
return supplier.get();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.Page<CommonBean.PathInfo> listFiles(PlainPath parentPath, String tmpNext) {
|
||||
if (StrUtil.isBlank(tmpNext)) {
|
||||
tmpNext = "1";
|
||||
}
|
||||
final int current = Convert.toInt(tmpNext);
|
||||
String[] sz = parentPath.getRealPath().split("/");
|
||||
String parent_file_id = sz[sz.length - 1];
|
||||
String marker = SecureUtil.md5(current + "1");
|
||||
String redisKey = "pan189:filelist:" + parentPath.getTokenId() + ":" + parent_file_id + ":" + marker;
|
||||
CommonBean.Page<CommonBean.PathInfo> page = CacheUtil.getCacheData(redisKey, () -> {
|
||||
int pageSize = 60;
|
||||
JSONObject res = postData("/open/file/listFiles.action?pageSize=" + pageSize + "&pageNum=" + current + "&mediaType=0&folderId=" + parent_file_id + "&iconOption=5&orderBy=lastOpTime&descending=true", null,
|
||||
parentPath.getTokenId()
|
||||
, 0);
|
||||
CommonBean.Page<CommonBean.PathInfo> page1 = new CommonBean.Page<>();
|
||||
JSONObject fileListAO = res.getJSONObject("fileListAO");
|
||||
int count = fileListAO.getInt("count");
|
||||
int pages = count / pageSize;
|
||||
if (count % pageSize != 0) {
|
||||
pages++;
|
||||
}
|
||||
if (pages > current) {
|
||||
page1.setNext(Convert.toStr(current + 1));
|
||||
} else {
|
||||
page1.setNext("");
|
||||
}
|
||||
List<CommonBean.PathInfo> list = items2infos(fileListAO.getJSONArray("folderList"), fileListAO.getJSONArray("fileList"));
|
||||
page1.setList(list);
|
||||
page1.setType(1);
|
||||
return page1;
|
||||
}, 120);
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshToken(String driveType, IoTokenData itd) {
|
||||
String resStr = HttpUtil.createGet("https://cloud.189.cn/api/open/user/getUserInfoForPortal.action")
|
||||
.header("accept", "application/json;charset=UTF-8")
|
||||
.cookie(itd.getTokenData())
|
||||
.execute()
|
||||
.body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
Assert.isTrue(res.getInt("res_code", -1) == 0, "当前cookie有误");
|
||||
itd.setDriveType(driveType);
|
||||
itd.setId(SecureUtil.md5(driveType + res.getStr("loginName")));
|
||||
itd.setExpireTime(LocalDateTime.now().plusMinutes(30L));
|
||||
}
|
||||
|
||||
private CommonBean.PathInfo file2info(JSONObject item) {
|
||||
CommonBean.PathInfo pi = new CommonBean.PathInfo();
|
||||
pi.setName(item.getStr("name"));
|
||||
pi.setPath(item.getStr("id"));
|
||||
pi.setCreatedAt(DateUtil.parse(item.getStr("createDate")).toString());
|
||||
pi.setUpdatedAt(DateUtil.parse(item.getStr("lastOpTime")).toString());
|
||||
if (StrUtil.equals(item.getStr("type"), "file")) {
|
||||
pi.setSize(item.getLong("size"));
|
||||
pi.setType(1);
|
||||
pi.setMd5(item.getStr("md5"));
|
||||
pi.setExt(FileUtil.extName(pi.getName()));
|
||||
JSONObject icon = item.getJSONObject("icon");
|
||||
if (icon != null) {
|
||||
pi.setThumbnail(icon.getStr("smallUrl"));
|
||||
}
|
||||
} else {
|
||||
pi.setType(2);
|
||||
}
|
||||
return pi;
|
||||
}
|
||||
|
||||
private List<CommonBean.PathInfo> items2infos(JSONArray folderList, JSONArray fileList) {
|
||||
List<CommonBean.PathInfo> list = new ArrayList<>();
|
||||
if (folderList != null) {
|
||||
for (int i = 0; i < folderList.size(); i++) {
|
||||
JSONObject item = folderList.getJSONObject(i);
|
||||
item.set("type", "folder");
|
||||
CommonBean.PathInfo info = file2info(item);
|
||||
list.add(info);
|
||||
}
|
||||
}
|
||||
if (fileList != null) {
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
JSONObject item = fileList.getJSONObject(i);
|
||||
item.set("type", "file");
|
||||
CommonBean.PathInfo info = file2info(item);
|
||||
list.add(info);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private String getCookie(String tokenId) {
|
||||
IoTokenData itd = TokenDataUtil.getTokenDataByIdOrToken(FileSystemUtil.PAN189_DRIVE_TYPE, tokenId);
|
||||
return itd.getTokenData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(FileSystemInface.PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes) {
|
||||
return copyOrMoveDelete(sourceParent, sourceChildren, sourceTypes, null, "DELETE", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String pathName(PlainPath plainPath) {
|
||||
String[] sz = plainPath.getRealPath().split("/");
|
||||
String fileId = sz[sz.length - 1];
|
||||
JSONObject res = postData("/portal/listFiles.action", Dict.create().set("fileId", fileId), plainPath.getTokenId(), 30);
|
||||
JSONArray items = res.getJSONArray("path");
|
||||
String[] paths = plainPath.getCipherPath().split("/");
|
||||
int length = paths.length;
|
||||
for (int i = length - 1; i > 0; i--) {
|
||||
int n = items.size() - paths.length + i;
|
||||
JSONObject item = items.getJSONObject(n);
|
||||
Assert.isTrue(item != null && StrUtil.equals(item.getStr("fileId"), paths[i]), "路径非法");
|
||||
paths[i] = item.getStr("fileName");
|
||||
}
|
||||
return ArrayUtil.join(paths, "/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String availableMainName(PlainPath parentPath, String mainName, String ext) {
|
||||
int index = 0;
|
||||
while (true) {
|
||||
String tmpMainName = mainName;
|
||||
if (index > 0) {
|
||||
tmpMainName += "(" + index + ")";
|
||||
}
|
||||
String name = tmpMainName;
|
||||
if (StrUtil.isNotBlank(ext)) {
|
||||
name += "." + ext;
|
||||
}
|
||||
List<CommonBean.PathInfo> items = this.searchFile(parentPath, name);
|
||||
if (items.size() == 0) {
|
||||
return tmpMainName;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String unzip(PlainPath file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String zip(List<PlainPath> files, PlainPath dirPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.PathInfo fileInfo(PlainPath plainPath) {
|
||||
String[] sz = plainPath.getRealPath().split("/");
|
||||
String file_id = sz[sz.length - 1];
|
||||
JSONObject item = postData("/open/file/getFileInfo.action", Dict.create().set("fileId", file_id), plainPath.getTokenId(), 0);
|
||||
Assert.isTrue(StrUtil.equals(item.getStr("res_code"), "0"), "当前文件(夹)信息不存在,请稍后重试");
|
||||
CommonBean.PathInfo pi = new CommonBean.PathInfo();
|
||||
pi.setMd5(item.getStr("md5"));
|
||||
pi.setName(item.getStr("name"));
|
||||
pi.setPath(item.getStr("id"));
|
||||
pi.setCreatedAt(DateUtil.parse(item.getStr("createDate")).toString());
|
||||
pi.setUpdatedAt(DateUtil.parse(item.getStr("lastOpTimeStr")).toString());
|
||||
if (StrUtil.isNotBlank(pi.getMd5())) {
|
||||
pi.setSize(item.getLong("size"));
|
||||
pi.setType(1);
|
||||
pi.setExt(FileUtil.extName(pi.getName()));
|
||||
JSONObject icon = item.getJSONObject("icon");
|
||||
if (icon != null) {
|
||||
pi.setThumbnail(icon.getStr("smallUrl"));
|
||||
}
|
||||
} else {
|
||||
pi.setType(2);
|
||||
}
|
||||
return pi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.WpsUrlData getWpsUrl(PlainPath plainPath, boolean isEdit) {
|
||||
CommonBean.WpsUrlData data = new CommonBean.WpsUrlData();
|
||||
data.setType(2);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommonBean.PathInfo> searchFile(PlainPath parentPath, String name) {
|
||||
String[] sz = parentPath.getRealPath().split("/");
|
||||
String file_id = sz[sz.length - 1];
|
||||
JSONObject res = this.postData("/open/file/searchFiles.action", Dict.create()
|
||||
.set("folderId", file_id)
|
||||
.set("fileName", name)
|
||||
.set("pageNum", "1")
|
||||
.set("pageSize", "100")
|
||||
, parentPath.getTokenId(), 0);
|
||||
JSONArray fileList = res.getJSONArray("fileList");
|
||||
JSONArray folderList = res.getJSONArray("folderList");
|
||||
return items2infos(folderList, fileList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream(PlainPath path, long start, long length) {
|
||||
String url = downloadUrl(path);
|
||||
HttpRequest req = HttpUtil.createGet(url).setMaxRedirectCount(100);
|
||||
req.header("Referer", "https://cloud.189.cn/", true);
|
||||
if (length > 0) {
|
||||
//局部
|
||||
req.header("Range", "bytes=" + start + "-" + (length + start - 1));
|
||||
} else {
|
||||
//全部
|
||||
}
|
||||
return req.executeAsync().bodyStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String secondTransmission(PlainPath path, String name, String sourceCipherPath, long size) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
private JSONObject getPkData(String config) {
|
||||
return this.postData("/security/generateRsaKey.action", null, config, 3600);
|
||||
}
|
||||
|
||||
private String getSessionKey(String config) {
|
||||
JSONObject res = this.postData("/portal/v2/getUserBriefInfo.action", null, config, 3600);
|
||||
return res.getStr("sessionKey");
|
||||
}
|
||||
|
||||
private JSONObject jmGetData(String uri, Dict data, String config) {
|
||||
List<String> list = new ArrayList<>();
|
||||
data.forEach((s, o) -> list.add(s + "=" + o.toString()));
|
||||
String fStr = CollUtil.join(list, "&");
|
||||
long time = System.currentTimeMillis();
|
||||
String key = IdUtil.fastSimpleUUID();
|
||||
JSONObject pkData = getPkData(config);
|
||||
String paramStr = SecureUtil.aes(key.substring(0, 16).getBytes(StandardCharsets.UTF_8)).encryptHex(fStr, CharsetUtil.CHARSET_UTF_8);
|
||||
RSA rsa = SecureUtil.rsa(null, pkData.getStr("pubKey"));
|
||||
String etx = Base64.encode(rsa.encrypt(key, KeyType.PublicKey));
|
||||
String method = "GET";
|
||||
String sk = getSessionKey(config);
|
||||
String v = "SessionKey=" + sk + "&Operate=" + method + "&RequestURI=" + uri + "&Date=" + time + "¶ms=" + paramStr;
|
||||
String signature = SecureUtil.hmacSha1(key).digestHex(v);
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("EncryptionText", etx);
|
||||
headers.put("PkId", pkData.getStr("pkId"));
|
||||
headers.put("SessionKey", sk);
|
||||
headers.put("Signature", signature);
|
||||
headers.put("X-Request-Date", Convert.toStr(time));
|
||||
headers.put("X-Request-ID", IdUtil.fastSimpleUUID());
|
||||
String resStr = HttpUtil.createGet("https://upload.cloud.189.cn" + uri + "?params=" + paramStr).headerMap(headers, true).execute().body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
Assert.isTrue(StrUtil.equals(res.getStr("code"), "SUCCESS"), "执行失败{}", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sameDriveCopy(String driveType, CommonBean.TransmissionFile file) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sha1(PlainPath path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String md5(PlainPath path) {
|
||||
return fileInfo(path).getMd5();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restore(PlainPath path, IoUserRecycle ioUserRecycle) {
|
||||
String[] realPaths = path.getRealPath().split("/");
|
||||
String fileId = realPaths[realPaths.length - 1];
|
||||
String parentId = realPaths[realPaths.length - 2];
|
||||
String realPathParent = ArrayUtil.join(ArrayUtil.remove(realPaths, realPaths.length - 1), "/");
|
||||
String[] cipherPaths = path.getCipherPath().split("/");
|
||||
String cipherPathParent = ArrayUtil.join(ArrayUtil.remove(cipherPaths, cipherPaths.length - 1), "/");
|
||||
path.setRealPath(realPathParent);
|
||||
path.setCipherPath(cipherPathParent);
|
||||
List<String> children = CollUtil.newArrayList(fileId);
|
||||
String res = copyOrMoveDelete(path, children, CollUtil.newArrayList(ioUserRecycle.getType()), null, "RESTORE", RestoreFile.init(ioUserRecycle.getName(), ioUserRecycle.getType()));
|
||||
boolean flag = StrUtil.equals(res, "1");
|
||||
if (flag) {
|
||||
this.clearCacheByParent(path.getTokenId(), parentId);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootId(String driveType) {
|
||||
return "-11";
|
||||
}
|
||||
|
||||
@Override
|
||||
public R commonReq(PlainPath path, Context ctx) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
package cn.tenfell.webos.common.filesystem;
|
||||
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.modules.entity.IoTokenData;
|
||||
import cn.tenfell.webos.modules.entity.IoUserRecycle;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 天翼云盘合作版
|
||||
*/
|
||||
public class Pan189hzFileSystem implements FileSystemInface {
|
||||
private static final String USER_APP_ID = "8148776600";
|
||||
private static final String USER_APP_SECRET = "dRrp4P7QrVeGGbwsNOD4MhBXmBM3N7pR";
|
||||
private static final String PAN_APP_KEY = "1130133156402";
|
||||
private static final String PAN_APP_SECRET = "e3a44b7a3ce1f2598f57e05dfbff3f0b";
|
||||
|
||||
@Override
|
||||
public String copy(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, PlainPath path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String move(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes, PlainPath path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rename(PlainPath source, String name, Integer type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadPre(PlainPath path, String name, String expand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadUrl(PlainPath path, String name, String expand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadAfter(PlainPath path, String name, String expand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadByServer(PlainPath path, String name, InputStream in, File file, Consumer<Long> consumer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String downloadUrl(PlainPath path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createDir(PlainPath parentPath, String pathName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.Page<CommonBean.PathInfo> listFiles(PlainPath parentPath, String next) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshToken(String driveType, IoTokenData itd) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(PlainPath sourceParent, List<String> sourceChildren, List<Integer> sourceTypes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String pathName(PlainPath plainPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String availableMainName(PlainPath parentPath, String mainName, String ext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String unzip(PlainPath file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String zip(List<PlainPath> files, PlainPath dirPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.PathInfo fileInfo(PlainPath plainPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonBean.WpsUrlData getWpsUrl(PlainPath plainPath, boolean isEdit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommonBean.PathInfo> searchFile(PlainPath parentPath, String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream(PlainPath path, long start, long length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String secondTransmission(PlainPath path, String name, String sourceCipherPath, long size) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sameDriveCopy(String driveType, CommonBean.TransmissionFile file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sha1(PlainPath path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String md5(PlainPath path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restore(PlainPath path, IoUserRecycle ioUserRecycle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootId(String driveType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R commonReq(PlainPath path, Context ctx) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package cn.tenfell.webos.common.filesystem;
|
||||
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.util.CommonUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
|
||||
/**
|
||||
* 服务器文件
|
||||
*/
|
||||
public class ServerFileSystem extends LocalFileSystem {
|
||||
@Override
|
||||
public void removeTmpPathAndCreateWebosFile(String tmpPath, PlainPath path, String name) {
|
||||
FileUtil.copy(tmpPath, path.getRealPath() + "/" + name, true);
|
||||
FileUtil.del(tmpPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String realFilePath(String realFilePath, String webosRealFilePath) {
|
||||
return webosRealFilePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public CommonBean.PathInfo fileToInfo(File file, String cipherPath) {
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
CommonBean.PathInfo pi = new CommonBean.PathInfo();
|
||||
if (file.isFile()) {
|
||||
pi.setName(file.getName());
|
||||
pi.setPath(file.getName());
|
||||
pi.setExt(FileUtil.extName(file.getName()));
|
||||
pi.setSize(file.length());
|
||||
pi.setType(1);
|
||||
if (CommonUtil.hasThumbnailByExt(pi.getExt())) {
|
||||
String fileViewerUrl = "api?module=fileSystem&action=localFileViewer";
|
||||
String thumbnail = fileViewerUrl + "&ext=" + pi.getExt() + "&path=" + URLUtil.encodeAll(cipherPath);
|
||||
pi.setThumbnail(thumbnail);
|
||||
}
|
||||
} else {
|
||||
pi.setName(file.getName());
|
||||
pi.setPath(file.getName());
|
||||
pi.setType(2);
|
||||
}
|
||||
try {
|
||||
BasicFileAttributes basicAttr = Files.readAttributes(Paths.get(file.getPath()), BasicFileAttributes.class);
|
||||
FileTime createTime = basicAttr.creationTime();
|
||||
pi.setCreatedAt(DateTime.of(createTime.toMillis()).toString());
|
||||
FileTime updateTime = basicAttr.lastModifiedTime();
|
||||
pi.setUpdatedAt(DateTime.of(updateTime.toMillis()).toString());
|
||||
} catch (IOException e) {
|
||||
}
|
||||
return pi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String secondTransmission(PlainPath path, String name, String sourceCipherPath, long size) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sha1(PlainPath path) {
|
||||
return SecureUtil.sha1(FileUtil.file(path.getRealPath()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String md5(PlainPath path) {
|
||||
return SecureUtil.md5(FileUtil.file(path.getRealPath()));
|
||||
}
|
||||
}
|
109
api/src/main/java/cn/tenfell/webos/common/server/R.java
Normal file
109
api/src/main/java/cn/tenfell/webos/common/server/R.java
Normal file
@ -0,0 +1,109 @@
|
||||
package cn.tenfell.webos.common.server;
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.tenfell.webos.common.util.ProjectUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class R extends HashMap<String, Object> {
|
||||
private static final Long serialVersionUID = 1L;
|
||||
|
||||
public static String getExceptionMessage(Throwable e, boolean isAll) {
|
||||
String msg;
|
||||
Throwable temp = ExceptionUtil.getRootCause(e);
|
||||
if (isAll) {
|
||||
msg = ExceptionUtil.stacktraceToString(temp);
|
||||
} else {
|
||||
msg = temp.getMessage();
|
||||
if (StrUtil.isBlank(msg)) {
|
||||
msg = temp.getClass().getName();
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
//操作成功
|
||||
public static R ok() {
|
||||
return okData(null);
|
||||
}
|
||||
|
||||
public static <T> R okData(T data) {
|
||||
return ok(data, "操作成功");
|
||||
}
|
||||
|
||||
public static <T> R ok(T data, String msg) {
|
||||
return restResult(data, 0, msg, null);
|
||||
}
|
||||
|
||||
public static R error(Throwable e) {
|
||||
String error = null;
|
||||
if (ProjectUtil.startConfig.getBool("debug")) {
|
||||
error = getExceptionMessage(e, true);
|
||||
}
|
||||
String msg = getExceptionMessage(e, false);
|
||||
return restResult(null, -1, msg, error);
|
||||
}
|
||||
|
||||
//操作失败
|
||||
public static R failed() {
|
||||
return failed("操作失败");
|
||||
}
|
||||
|
||||
public static R failed(String msg) {
|
||||
return failed(msg, -1);
|
||||
}
|
||||
|
||||
public static R failed(String msg, int status) {
|
||||
return restResult(null, status, msg, null);
|
||||
}
|
||||
|
||||
//权限不足
|
||||
public static R noAuth() {
|
||||
return failed("权限不足", 403);
|
||||
}
|
||||
|
||||
public static R lock() {
|
||||
return failed("已锁定", 408);
|
||||
}
|
||||
|
||||
|
||||
//未找到
|
||||
public static R noPath() {
|
||||
return failed("此路径找不到", 404);
|
||||
}
|
||||
|
||||
//未登录
|
||||
public static R noLogin() {
|
||||
return noLogin("请登录后重试");
|
||||
}
|
||||
|
||||
public static R noLogin(String msg) {
|
||||
return failed(msg, 401);
|
||||
}
|
||||
|
||||
private static <T> R restResult(T data, int osStatus, String msg, String error) {
|
||||
R r = new R();
|
||||
//0成功 -1失败 401未登录 403权限不足 404未找到 407系统未安装
|
||||
r.set("code", osStatus)
|
||||
.set(data != null, "data", data)
|
||||
.set(error != null, "error", error)
|
||||
.set("msg", msg);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static R noInstall() {
|
||||
return failed("当前系统未安装,请先安装", 407);
|
||||
}
|
||||
|
||||
private R set(String attr, Object object) {
|
||||
return set(true, attr, object);
|
||||
}
|
||||
|
||||
private R set(boolean flag, String attr, Object object) {
|
||||
if (flag) {
|
||||
this.put(attr, object);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
21
api/src/main/java/cn/tenfell/webos/common/ssh/ConstantPool.java
Executable file
21
api/src/main/java/cn/tenfell/webos/common/ssh/ConstantPool.java
Executable file
@ -0,0 +1,21 @@
|
||||
package cn.tenfell.webos.common.ssh;
|
||||
|
||||
/**
|
||||
* @Description: 常量池
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/8
|
||||
*/
|
||||
public class ConstantPool {
|
||||
/**
|
||||
* 随机生成uuid的key名
|
||||
*/
|
||||
public static final String USER_UUID_KEY = "user_uuid";
|
||||
/**
|
||||
* 发送指令:连接
|
||||
*/
|
||||
public static final String WEBSSH_OPERATE_CONNECT = "connect";
|
||||
/**
|
||||
* 发送指令:命令
|
||||
*/
|
||||
public static final String WEBSSH_OPERATE_COMMAND = "command";
|
||||
}
|
17
api/src/main/java/cn/tenfell/webos/common/ssh/SSHConnectInfo.java
Executable file
17
api/src/main/java/cn/tenfell/webos/common/ssh/SSHConnectInfo.java
Executable file
@ -0,0 +1,17 @@
|
||||
package cn.tenfell.webos.common.ssh;
|
||||
|
||||
import com.jcraft.jsch.Channel;
|
||||
import com.jcraft.jsch.JSch;
|
||||
import lombok.Data;
|
||||
import org.noear.solon.core.message.Session;
|
||||
/**
|
||||
* @Description: ssh连接信息
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/8
|
||||
*/
|
||||
@Data
|
||||
public class SSHConnectInfo {
|
||||
private Session webSocketSession;
|
||||
private JSch jSch;
|
||||
private Channel channel;
|
||||
}
|
48
api/src/main/java/cn/tenfell/webos/common/ssh/WebSSHService.java
Executable file
48
api/src/main/java/cn/tenfell/webos/common/ssh/WebSSHService.java
Executable file
@ -0,0 +1,48 @@
|
||||
package cn.tenfell.webos.common.ssh;
|
||||
|
||||
import org.noear.solon.core.message.Session;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @Description: WebSSH的业务逻辑
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/7
|
||||
*/
|
||||
public interface WebSSHService {
|
||||
/**
|
||||
* @Description: 初始化ssh连接
|
||||
* @Param:
|
||||
* @return:
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/7
|
||||
*/
|
||||
public void initConnection(Session session);
|
||||
|
||||
/**
|
||||
* @Description: 处理客户段发的数据
|
||||
* @Param:
|
||||
* @return:
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/7
|
||||
*/
|
||||
public void recvHandle(String buffer, Session session);
|
||||
|
||||
/**
|
||||
* @Description: 数据写回前端 for websocket
|
||||
* @Param:
|
||||
* @return:
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/7
|
||||
*/
|
||||
public void sendMessage(Session session, byte[] buffer) throws IOException;
|
||||
|
||||
/**
|
||||
* @Description: 关闭连接
|
||||
* @Param:
|
||||
* @return:
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/7
|
||||
*/
|
||||
public void close(Session session);
|
||||
}
|
184
api/src/main/java/cn/tenfell/webos/common/ssh/WebSSHServiceImpl.java
Executable file
184
api/src/main/java/cn/tenfell/webos/common/ssh/WebSSHServiceImpl.java
Executable file
@ -0,0 +1,184 @@
|
||||
package cn.tenfell.webos.common.ssh;
|
||||
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.tenfell.webos.common.bean.WebSSHData;
|
||||
import com.jcraft.jsch.Channel;
|
||||
import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.Session;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* @Description: WebSSH业务逻辑实现
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/8
|
||||
*/
|
||||
public class WebSSHServiceImpl implements WebSSHService {
|
||||
//存放ssh连接信息的map
|
||||
private static Map<String, Object> sshMap = new ConcurrentHashMap<>();
|
||||
//线程池
|
||||
private ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
|
||||
/**
|
||||
* @Description: 初始化连接
|
||||
* @Param: [session]
|
||||
* @return: void
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/7
|
||||
*/
|
||||
@Override
|
||||
public void initConnection(org.noear.solon.core.message.Session session) {
|
||||
JSch jSch = new JSch();
|
||||
SSHConnectInfo sshConnectInfo = new SSHConnectInfo();
|
||||
sshConnectInfo.setJSch(jSch);
|
||||
sshConnectInfo.setWebSocketSession(session);
|
||||
String uuid = session.sessionId();
|
||||
//将这个ssh连接信息放入map中
|
||||
sshMap.put(uuid, sshConnectInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description: 处理客户端发送的数据
|
||||
* @Param: [buffer, session]
|
||||
* @return: void
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/7
|
||||
*/
|
||||
@Override
|
||||
public void recvHandle(String buffer, org.noear.solon.core.message.Session session) {
|
||||
WebSSHData webSSHData = JSONUtil.toBean(buffer,WebSSHData.class);
|
||||
String userId = session.sessionId();
|
||||
if (ConstantPool.WEBSSH_OPERATE_CONNECT.equals(webSSHData.getOperate())) {
|
||||
//找到刚才存储的ssh连接对象
|
||||
SSHConnectInfo tmpInfo = (SSHConnectInfo) sshMap.get(userId);
|
||||
if(tmpInfo == null){
|
||||
ThreadUtil.sleep(1000);
|
||||
tmpInfo = (SSHConnectInfo) sshMap.get(userId);
|
||||
}
|
||||
SSHConnectInfo sshConnectInfo = tmpInfo;
|
||||
//启动线程异步处理
|
||||
WebSSHData finalWebSSHData = webSSHData;
|
||||
executorService.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
connectToSSH(sshConnectInfo, finalWebSSHData, session);
|
||||
} catch (JSchException | IOException e) {
|
||||
e.printStackTrace();
|
||||
close(session);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (ConstantPool.WEBSSH_OPERATE_COMMAND.equals(webSSHData.getOperate())) {
|
||||
String command = webSSHData.getCommand();
|
||||
SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(userId);
|
||||
if (sshConnectInfo != null) {
|
||||
try {
|
||||
transToSSH(sshConnectInfo.getChannel(), command);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
close(session);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
close(session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(org.noear.solon.core.message.Session session, byte[] buffer) throws IOException {
|
||||
session.send(StrUtil.str(buffer, CharsetUtil.CHARSET_UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(org.noear.solon.core.message.Session session) {
|
||||
String userId = session.sessionId();
|
||||
SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(userId);
|
||||
if (sshConnectInfo != null) {
|
||||
//断开连接
|
||||
if (sshConnectInfo.getChannel() != null) sshConnectInfo.getChannel().disconnect();
|
||||
//map中移除
|
||||
sshMap.remove(userId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description: 使用jsch连接终端
|
||||
* @Param: [cloudSSH, webSSHData, webSocketSession]
|
||||
* @return: void
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/7
|
||||
*/
|
||||
private void connectToSSH(SSHConnectInfo sshConnectInfo, WebSSHData webSSHData, org.noear.solon.core.message.Session webSocketSession) throws JSchException, IOException {
|
||||
Session session = null;
|
||||
Properties config = new Properties();
|
||||
config.put("StrictHostKeyChecking", "no");
|
||||
//获取jsch的会话
|
||||
session = sshConnectInfo.getJSch().getSession(webSSHData.getUsername(), webSSHData.getHost(), webSSHData.getPort());
|
||||
session.setConfig(config);
|
||||
//设置密码
|
||||
session.setPassword(webSSHData.getPassword());
|
||||
//连接 超时时间30s
|
||||
session.connect(30000);
|
||||
|
||||
//开启shell通道
|
||||
Channel channel = session.openChannel("shell");
|
||||
|
||||
//通道连接 超时时间3s
|
||||
channel.connect(3000);
|
||||
|
||||
//设置channel
|
||||
sshConnectInfo.setChannel(channel);
|
||||
|
||||
//转发消息
|
||||
transToSSH(channel, "\r");
|
||||
|
||||
//读取终端返回的信息流
|
||||
InputStream inputStream = channel.getInputStream();
|
||||
try {
|
||||
//循环读取
|
||||
byte[] buffer = new byte[1024];
|
||||
int i = 0;
|
||||
//如果没有数据来,线程会一直阻塞在这个地方等待数据。
|
||||
while ((i = inputStream.read(buffer)) != -1) {
|
||||
sendMessage(webSocketSession, Arrays.copyOfRange(buffer, 0, i));
|
||||
}
|
||||
|
||||
} finally {
|
||||
//断开连接后关闭会话
|
||||
session.disconnect();
|
||||
channel.disconnect();
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description: 将消息转发到终端
|
||||
* @Param: [channel, data]
|
||||
* @return: void
|
||||
* @Author: NoCortY
|
||||
* @Date: 2020/3/7
|
||||
*/
|
||||
private void transToSSH(Channel channel, String command) throws IOException {
|
||||
if (channel != null) {
|
||||
OutputStream outputStream = channel.getOutputStream();
|
||||
outputStream.write(command.getBytes());
|
||||
outputStream.flush();
|
||||
}
|
||||
}
|
||||
}
|
242
api/src/main/java/cn/tenfell/webos/common/util/BeanProxy.java
Normal file
242
api/src/main/java/cn/tenfell/webos/common/util/BeanProxy.java
Normal file
@ -0,0 +1,242 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.lang.func.VoidFunc1;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.Db;
|
||||
import cn.hutool.db.transaction.TransactionLevel;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.hutool.log.Log;
|
||||
import cn.tenfell.webos.common.annt.*;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
import lombok.Data;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 代理类,用于实现事务,权限控制
|
||||
*/
|
||||
public class BeanProxy {
|
||||
private static final Log log = Log.get();
|
||||
private static boolean hasInit = false;
|
||||
private static final Map<String, BeanDefine> pool = new ConcurrentHashMap<>(32);
|
||||
|
||||
@Data
|
||||
private static class BeanDefine {
|
||||
private Object bean;
|
||||
private String name;
|
||||
private Class type;
|
||||
private Map<String, MethodDefine> methodPool;
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class MethodDefine {
|
||||
private Method method;
|
||||
private int type;
|
||||
private boolean needLogin;
|
||||
private boolean needTransactional;
|
||||
private TransactionLevel transactionLevel;
|
||||
private Set<String> auths;
|
||||
}
|
||||
|
||||
public static synchronized void init(Set<Class<?>> beans) {
|
||||
if (hasInit) {
|
||||
return;
|
||||
}
|
||||
for (Class clazz : beans) {
|
||||
BeanAction beanAction = (BeanAction) clazz.getAnnotation(BeanAction.class);
|
||||
if (beanAction == null || StrUtil.isBlank(beanAction.val())) {
|
||||
continue;
|
||||
}
|
||||
if (pool.get(beanAction.val()) != null) {
|
||||
throw new RuntimeException(beanAction.val() + "模块已经注册过,不允许重复注册");
|
||||
}
|
||||
BeanDefine proxyBean = new BeanDefine();
|
||||
proxyBean.setBean(ReflectUtil.newInstanceIfPossible(clazz));
|
||||
proxyBean.setName(beanAction.val());
|
||||
proxyBean.setType(clazz);
|
||||
Map<String, MethodDefine> methodPool = new ConcurrentHashMap<>();
|
||||
Method[] methods = clazz.getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
if (!Modifier.isPublic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
Action action = method.getAnnotation(Action.class);
|
||||
if (action == null) {
|
||||
continue;
|
||||
}
|
||||
if (methodPool.get(action.val()) != null) {
|
||||
throw new RuntimeException(action.val() + "动作已经注册过,不允许重复注册");
|
||||
}
|
||||
MethodDefine methodDefine = new MethodDefine();
|
||||
methodDefine.setMethod(method);
|
||||
methodDefine.setType(action.type());
|
||||
Login login = method.getAnnotation(Login.class);
|
||||
methodDefine.setNeedLogin(login == null || login.val());
|
||||
Transactional transactional = method.getAnnotation(Transactional.class);
|
||||
methodDefine.setNeedTransactional(transactional != null);
|
||||
if (transactional != null) {
|
||||
methodDefine.setTransactionLevel(transactional.level());
|
||||
}
|
||||
Auth auth = method.getAnnotation(Auth.class);
|
||||
if (auth != null && auth.val().length > 0) {
|
||||
Set<String> auths = new HashSet<>();
|
||||
String[] methodAuths = auth.val();
|
||||
for (String methodAuth : methodAuths) {
|
||||
auths.add(methodAuth);
|
||||
}
|
||||
methodDefine.setAuths(auths);
|
||||
}
|
||||
methodDefine.setNeedTransactional(transactional != null);
|
||||
methodPool.put(action.val(), methodDefine);
|
||||
}
|
||||
proxyBean.setMethodPool(methodPool);
|
||||
pool.put(beanAction.val(), proxyBean);
|
||||
}
|
||||
hasInit = true;
|
||||
}
|
||||
|
||||
public static Object invoke(String module, String action, Context ctx) {
|
||||
try {
|
||||
return invokeThrow(module, action, ctx);
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
return R.failed("服务器异常");
|
||||
}
|
||||
}
|
||||
|
||||
public static Object invokeThrow(String module, String action, Context ctx) throws Exception{
|
||||
if (!ProjectUtil.hasInstall && !StrUtil.equals(module, "install")) {
|
||||
return R.noInstall();
|
||||
}
|
||||
if (StrUtil.isBlank(module) || StrUtil.isBlank(action)) {
|
||||
return R.noPath();
|
||||
}
|
||||
BeanDefine bean = pool.get(module);
|
||||
if (bean == null) {
|
||||
return R.noPath();
|
||||
}
|
||||
MethodDefine method = bean.methodPool.get(action);
|
||||
if (method == null) {
|
||||
return R.noPath();
|
||||
}
|
||||
if (method.isNeedLogin() && !isLogin()) {
|
||||
return R.noLogin();
|
||||
}
|
||||
if (!hasAuth(method.getAuths())) {
|
||||
return R.noAuth();
|
||||
}
|
||||
Class[] paramTypes = method.getMethod().getParameterTypes();
|
||||
if (paramTypes.length > 1) {
|
||||
return R.failed("参数错误");
|
||||
}
|
||||
Supplier invoke;
|
||||
if (paramTypes.length > 0) {
|
||||
Object param;
|
||||
Class clazz = paramTypes[0];
|
||||
if (method.getType() == 0) {
|
||||
param = ctx;
|
||||
} else if (method.getType() == 1) {
|
||||
String body = ctx.body();
|
||||
if (StrUtil.isNotBlank(body)) {
|
||||
if (ClassUtil.isAssignable(Collection.class, clazz)) {
|
||||
//集合
|
||||
Class entityClass = (Class) ((ParameterizedTypeImpl) method.getMethod().getGenericParameterTypes()[0]).getActualTypeArguments()[0];
|
||||
param = JSONUtil.toList(body, entityClass);
|
||||
} else {
|
||||
//非集合
|
||||
param = JSONUtil.toBean(body, clazz);
|
||||
}
|
||||
} else {
|
||||
param = null;
|
||||
}
|
||||
} else if (method.getType() == 2) {
|
||||
Map<String, String> params = ctx.paramMap();
|
||||
param = BeanUtil.fillBeanWithMap(params, ReflectUtil.newInstanceIfPossible(clazz), true);
|
||||
} else {
|
||||
return R.failed("此类型暂不支持");
|
||||
}
|
||||
invoke = () -> {
|
||||
try {
|
||||
return method.getMethod().invoke(bean.getBean(), param);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
invoke = () -> {
|
||||
try {
|
||||
return method.getMethod().invoke(bean.getBean());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
try {
|
||||
if (method.isNeedTransactional()) {
|
||||
//事务模式
|
||||
AtomicReference<Object> ares = new AtomicReference<>();
|
||||
DbUtil.get().tx(method.getTransactionLevel(), (VoidFunc1<Db>) db -> {
|
||||
DbUtil.set(db);
|
||||
ares.set(invoke.get());
|
||||
});
|
||||
return ares.get();
|
||||
} else {
|
||||
//非事务模式
|
||||
return invoke.get();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (ProjectUtil.startConfig.getBool("debug")) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
}
|
||||
return errorHandle(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLogin() {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
return user != null;
|
||||
}
|
||||
|
||||
public static boolean hasAuth(Set<String> methodAuths) {
|
||||
if (methodAuths == null || methodAuths.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
if (!isLogin()) {
|
||||
return false;
|
||||
}
|
||||
List<String> userAuths = LoginAuthUtil.getUserAuths();
|
||||
if (CollUtil.isEmpty(userAuths)) {
|
||||
return false;
|
||||
}
|
||||
for (String methodAuth : methodAuths) {
|
||||
if (userAuths.contains(methodAuth)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static R errorHandle(Throwable error) {
|
||||
Throwable root = ExceptionUtil.getRootCause(error);
|
||||
if (!(root instanceof IllegalArgumentException)) {
|
||||
log.error(root);
|
||||
}
|
||||
return R.error(root);
|
||||
}
|
||||
}
|
294
api/src/main/java/cn/tenfell/webos/common/util/CacheUtil.java
Normal file
294
api/src/main/java/cn/tenfell/webos/common/util/CacheUtil.java
Normal file
@ -0,0 +1,294 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.cache.Cache;
|
||||
import cn.hutool.cache.file.LRUFileCache;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.SerializeUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.nosql.redis.RedisDS;
|
||||
import cn.hutool.setting.Setting;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.ScanParams;
|
||||
import redis.clients.jedis.ScanResult;
|
||||
import redis.clients.jedis.params.SetParams;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CacheUtil {
|
||||
private static RedisDS redisDS;
|
||||
//0redis 1file
|
||||
private static int type;
|
||||
private static FileCache fileCache;
|
||||
private static long nextClearTime = 0;
|
||||
|
||||
private static Jedis getJedis() {
|
||||
return redisDS.getJedis();
|
||||
}
|
||||
|
||||
public static <T> void setValue(String key, T value) {
|
||||
setValue(key, value, 0);
|
||||
}
|
||||
|
||||
public static <T> void setValue(String key, T value, int secondOut) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
if (type == 0) {
|
||||
Jedis jedis = getJedis();
|
||||
try {
|
||||
byte[] bytes = SerializeUtil.serialize(value);
|
||||
if (secondOut > 0) {
|
||||
jedis.set(key.getBytes(CharsetUtil.CHARSET_UTF_8), bytes, new SetParams().px(secondOut * 1000L));
|
||||
} else {
|
||||
jedis.set(key.getBytes(CharsetUtil.CHARSET_UTF_8), bytes);
|
||||
}
|
||||
} finally {
|
||||
jedis.close();
|
||||
}
|
||||
} else {
|
||||
fileCache.setValue(key, value, secondOut * 1000L);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T getCacheData(String key, Supplier<T> supplier, int secondOut) {
|
||||
if (secondOut > 0) {
|
||||
Dict data = getValue(key);
|
||||
if (data == null) {
|
||||
data = Dict.create().set("data", supplier.get());
|
||||
setValue(key, data, secondOut);
|
||||
}
|
||||
return data.getBean("data");
|
||||
} else {
|
||||
return supplier.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 检索key最后的字符串必须为:*
|
||||
*
|
||||
* @param keyx
|
||||
* @return
|
||||
*/
|
||||
|
||||
public static List<String> scan(String keyx) {
|
||||
if (type == 0) {
|
||||
List<String> list = new ArrayList<>();
|
||||
ScanParams params = new ScanParams();
|
||||
params.match(keyx);
|
||||
params.count(1000);
|
||||
String cursor = "0";
|
||||
Jedis jedis = getJedis();
|
||||
try {
|
||||
while (true) {
|
||||
ScanResult scanResult = jedis.scan(cursor, params);
|
||||
List<String> elements = scanResult.getResult();
|
||||
if (elements != null && elements.size() > 0) {
|
||||
list.addAll(elements);
|
||||
}
|
||||
cursor = scanResult.getCursor();
|
||||
if ("0".equals(cursor)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
jedis.close();
|
||||
}
|
||||
return list;
|
||||
} else {
|
||||
return fileCache.scan(keyx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 支持以:*结尾的key
|
||||
*
|
||||
* @param keyx
|
||||
*/
|
||||
public static void delCacheData(String keyx) {
|
||||
if (keyx.endsWith(":*")) {
|
||||
List<String> keys = scan(keyx);
|
||||
for (String key : keys) {
|
||||
delValue(key);
|
||||
}
|
||||
} else {
|
||||
delValue(keyx);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T getValue(String key) {
|
||||
if (type == 0) {
|
||||
Jedis jedis = getJedis();
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = jedis.get(key.getBytes(CharsetUtil.CHARSET_UTF_8));
|
||||
if (bytes == null || bytes.length == 0) {
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
jedis.close();
|
||||
}
|
||||
return SerializeUtil.deserialize(bytes);
|
||||
} else {
|
||||
return (T) fileCache.getValue(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static void delValue(String key) {
|
||||
try {
|
||||
if (type == 0) {
|
||||
Jedis jedis = getJedis();
|
||||
try {
|
||||
jedis.del(key.getBytes(CharsetUtil.CHARSET_UTF_8));
|
||||
} finally {
|
||||
jedis.close();
|
||||
}
|
||||
} else {
|
||||
fileCache.remove(key);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void changeRedisCache(String host, Integer port, Integer database, String password) {
|
||||
CacheUtil.type = 0;
|
||||
Setting redisConfig = new Setting();
|
||||
redisConfig.setByGroup("host", "main", StrUtil.isNotBlank(host) ? host : null);
|
||||
redisConfig.setByGroup("port", "main", port == null ? null : Convert.toStr(port));
|
||||
redisConfig.setByGroup("password", "main", StrUtil.isNotBlank(password) ? password : null);
|
||||
redisConfig.setByGroup("database", "main", database == null ? null : Convert.toStr(database));
|
||||
redisConfig.setByGroup("timeout", "main", "2000");
|
||||
if (redisDS != null) {
|
||||
redisDS.close();
|
||||
}
|
||||
if (fileCache != null) {
|
||||
fileCache.clear();
|
||||
}
|
||||
redisDS = RedisDS.create(redisConfig, "main");
|
||||
}
|
||||
|
||||
public static void changeFileCache(String path) {
|
||||
CacheUtil.type = 1;
|
||||
if (redisDS != null) {
|
||||
redisDS.close();
|
||||
}
|
||||
if (fileCache != null) {
|
||||
fileCache.clear();
|
||||
}
|
||||
fileCache = new FileCache(StrUtil.format("{}{}", ProjectUtil.rootPath, path));
|
||||
}
|
||||
|
||||
/**
|
||||
* 每隔2小时检查一下本地过期缓存
|
||||
*/
|
||||
public static void clearExpireCache() {
|
||||
if (type == 0) {
|
||||
return;
|
||||
}
|
||||
if (nextClearTime > System.currentTimeMillis()) {
|
||||
return;
|
||||
}
|
||||
List<String> keys = fileCache.scan("*");
|
||||
for (String key : keys) {
|
||||
getValue(key);
|
||||
}
|
||||
nextClearTime = System.currentTimeMillis() + 2 * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
private static class FileCache {
|
||||
private LRUFileCache lruFileCache;
|
||||
private String rootPath;
|
||||
|
||||
public FileCache(String rootPath) {
|
||||
this.rootPath = rootPath;
|
||||
//每隔60秒去文件读
|
||||
this.lruFileCache = new LRUFileCache(10 * 1024 * 1024, 5 * 1024 * 1024, 60000);
|
||||
}
|
||||
|
||||
public Object getValue(String key) {
|
||||
try {
|
||||
File file = key2file(key);
|
||||
byte[] sz = this.lruFileCache.getFileBytes(file);
|
||||
if (sz == null || sz.length == 0) {
|
||||
return null;
|
||||
}
|
||||
Dict dict = SerializeUtil.deserialize(sz);
|
||||
long time = dict.getLong("time");
|
||||
Object data = dict.get("data");
|
||||
if (time == 0) {
|
||||
return data;
|
||||
} else {
|
||||
if (System.currentTimeMillis() > time) {
|
||||
//过期
|
||||
FileUtil.del(file);
|
||||
getCache().remove(file);
|
||||
return null;
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue(String key, Object data, long timeout) {
|
||||
Dict dict = Dict.create().set("data", data);
|
||||
if (timeout > 0) {
|
||||
dict.set("time", System.currentTimeMillis() + timeout);
|
||||
} else {
|
||||
dict.set("time", 0);
|
||||
}
|
||||
File file = key2file(key);
|
||||
byte[] cache = SerializeUtil.serialize(dict);
|
||||
//先覆盖文件
|
||||
FileUtil.writeBytes(cache, file);
|
||||
//后覆盖内存
|
||||
getCache().put(file, cache);
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
File file = key2file(key);
|
||||
FileUtil.del(file);
|
||||
getCache().remove(file);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
FileUtil.del(this.rootPath);
|
||||
this.lruFileCache.clear();
|
||||
}
|
||||
|
||||
private File key2file(String key) {
|
||||
File file = new File(this.rootPath + "/" + StrUtil.replace(key, ":", "/") + "cache.data");
|
||||
return file;
|
||||
}
|
||||
|
||||
private Cache<File, byte[]> getCache() {
|
||||
return (Cache<File, byte[]>) ReflectUtil.getFieldValue(lruFileCache, "cache");
|
||||
}
|
||||
|
||||
public List<String> scan(String key) {
|
||||
String pathPrefix = StrUtil.replace(this.rootPath, "\\", "/") + "/";
|
||||
String path = pathPrefix + StrUtil.replace(key, ":", "/");
|
||||
path = path.replace("/*", "");
|
||||
List<File> files = FileUtil.loopFiles(path);
|
||||
List<String> keys = new ArrayList<>();
|
||||
for (File file : files) {
|
||||
String tmp = StrUtil.replace(file.getAbsolutePath(), "\\", "/");
|
||||
tmp = StrUtil.removeAny(tmp, "cache.data", pathPrefix);
|
||||
tmp = StrUtil.replace(tmp, "/", ":");
|
||||
keys.add(tmp);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
}
|
145
api/src/main/java/cn/tenfell/webos/common/util/CommonUtil.java
Normal file
145
api/src/main/java/cn/tenfell/webos/common/util/CommonUtil.java
Normal file
@ -0,0 +1,145 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.Method;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemInface;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.common.filesystem.LocalFileSystem;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class CommonUtil {
|
||||
private static long nextClearTime = 0;
|
||||
private static List<String> imageExt = CollUtil.newArrayList("jpeg", "png", "gif", "bmp", "jpg", "tiff", "svg", "ico");
|
||||
|
||||
public static boolean isImage(String ext) {
|
||||
return imageExt.contains(ext.toLowerCase());
|
||||
}
|
||||
|
||||
public static String getTbPath(String ext, String path) {
|
||||
if (isImage(ext)) {
|
||||
String md5 = SecureUtil.md5(path);
|
||||
String thumbnail = ProjectUtil.rootPath + "/fileThumbnail" + File.separator + md5 + ".jpg";
|
||||
if (FileUtil.exist(thumbnail)) {
|
||||
return thumbnail;
|
||||
}
|
||||
String realPath;
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPath(path);
|
||||
if (StrUtil.equals(plainPath.getDriveType(), FileSystemUtil.LOCAL_DRIVE_TYPE)) {
|
||||
realPath = LocalFileSystem.getRealFile(plainPath.getRealFilePath(), plainPath.getRealPath());
|
||||
} else if (StrUtil.equals(plainPath.getDriveType(), FileSystemUtil.SERVER_DRIVE_TYPE)) {
|
||||
realPath = plainPath.getRealPath();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
BufferedImage img = ImgUtil.read(realPath);
|
||||
int width = img.getWidth();
|
||||
if (width <= 128) {
|
||||
return realPath;
|
||||
}
|
||||
int height = 128 * img.getHeight() / width;
|
||||
Image nimg = ImgUtil.scale(img, 128, height, Color.white);
|
||||
ImgUtil.write(nimg, new File(thumbnail));
|
||||
return thumbnail;
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static boolean hasThumbnailByExt(String ext) {
|
||||
if (isImage(ext)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static void clearExpireTmpFile() {
|
||||
if (nextClearTime > System.currentTimeMillis()) {
|
||||
return;
|
||||
}
|
||||
String parent = ProjectUtil.rootPath + "/fileThumbnail";
|
||||
File[] files = new File(parent).listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
try {
|
||||
BasicFileAttributes basicAttr = Files.readAttributes(Paths.get(file.getPath()), BasicFileAttributes.class);
|
||||
FileTime createTime = basicAttr.creationTime();
|
||||
if (System.currentTimeMillis() - createTime.toMillis() > 2 * 60 * 60 * 1000L) {
|
||||
FileUtil.del(file);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
nextClearTime = System.currentTimeMillis() + 2 * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
public static String trimPath(String path) {
|
||||
path = path.trim();
|
||||
if (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static String getParentPath(String path) {
|
||||
String[] sz = trimPath(path).split("/");
|
||||
sz = ArrayUtil.remove(sz, sz.length - 1);
|
||||
String targetPath = ArrayUtil.join(sz, "/");
|
||||
if(path.startsWith("/")){
|
||||
targetPath = "/"+targetPath;
|
||||
}
|
||||
return targetPath;
|
||||
}
|
||||
|
||||
public static String getLastUrl(String url) {
|
||||
url = url.trim();
|
||||
HttpResponse resp = HttpUtil.createRequest(Method.HEAD, url).execute();
|
||||
if (resp.getStatus() == 302) {
|
||||
String newUrl = resp.header("Location");
|
||||
if (StrUtil.isBlank(newUrl)) {
|
||||
return url;
|
||||
}
|
||||
newUrl = newUrl.trim();
|
||||
if (newUrl.startsWith("//")) {
|
||||
String p = url.substring(0, 4);
|
||||
String last = url.substring(4, 5);
|
||||
if (last.equals(":")) {
|
||||
last = "";
|
||||
}
|
||||
newUrl = p + last + ":" + newUrl;
|
||||
}
|
||||
return getLastUrl(newUrl);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public static String replaceFirst(String str, String old, String newString) {
|
||||
return Pattern.compile(old, Pattern.LITERAL).matcher(
|
||||
str).replaceFirst(Matcher.quoteReplacement(newString));
|
||||
}
|
||||
}
|
300
api/src/main/java/cn/tenfell/webos/common/util/DbUtil.java
Normal file
300
api/src/main/java/cn/tenfell/webos/common/util/DbUtil.java
Normal file
@ -0,0 +1,300 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.db.Db;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.hutool.db.ds.DSFactory;
|
||||
import cn.hutool.db.ds.GlobalDSFactory;
|
||||
import cn.hutool.db.sql.SqlLog;
|
||||
import cn.hutool.setting.Setting;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class DbUtil {
|
||||
private static boolean hasInit = false;
|
||||
private static ThreadLocal<Db> THREAD_LOCAL_DB = new ThreadLocal<>();
|
||||
//默认数据库缓存时间30分钟
|
||||
private static int DEFAULT_CACHE_TIME = 1800;
|
||||
|
||||
public static Db get() {
|
||||
Db db = THREAD_LOCAL_DB.get();
|
||||
if (db == null) {
|
||||
db = Db.use();
|
||||
THREAD_LOCAL_DB.set(db);
|
||||
}
|
||||
Assert.notNull(db,"数据库不可为空");
|
||||
return db;
|
||||
}
|
||||
|
||||
public static void set(Db db) {
|
||||
THREAD_LOCAL_DB.set(db);
|
||||
}
|
||||
|
||||
public static void initMysql(String host, Integer port, String database, String user, String password) {
|
||||
if (hasInit) {
|
||||
return;
|
||||
}
|
||||
initSqlPool(StrUtil.format("jdbc:mysql://{}:{}/{}?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8", host, port, database), user, password, "com.mysql.jdbc.Driver");
|
||||
}
|
||||
|
||||
private static void initSqlPool(String url, String user, String password, String driver) {
|
||||
Setting setting = new Setting();
|
||||
setting.set("url", url);
|
||||
if (StrUtil.isNotBlank(user)) {
|
||||
setting.set("username", user);
|
||||
}
|
||||
if (StrUtil.isNotBlank(password)) {
|
||||
setting.set("password", password);
|
||||
}
|
||||
if (StrUtil.isNotBlank(driver)) {
|
||||
setting.set("driver", driver);
|
||||
}
|
||||
setting.set("autoCommit", "true");
|
||||
setting.set("connectionTimeout", "30000");
|
||||
setting.set("idleTimeout", "600000");
|
||||
setting.set("maxLifetime", "1800000");
|
||||
setting.set("connectionTestQuery", "SELECT 1");
|
||||
setting.set("minimumIdle", "1");
|
||||
setting.set("maximumPoolSize", "5");
|
||||
if (ProjectUtil.startConfig.getBool("showSql")) {
|
||||
setting.set(SqlLog.KEY_SHOW_SQL, "true");
|
||||
setting.set(SqlLog.KEY_SHOW_PARAMS, "true");
|
||||
setting.set(SqlLog.KEY_FORMAT_SQL, "true");
|
||||
}
|
||||
GlobalDSFactory.set(DSFactory.create(setting));
|
||||
hasInit = true;
|
||||
}
|
||||
|
||||
public static void initSqlite(String path) {
|
||||
if (hasInit) {
|
||||
return;
|
||||
}
|
||||
initSqlPool(StrUtil.format("jdbc:sqlite:{}{}", ProjectUtil.rootPath, path), null, null, null);
|
||||
}
|
||||
|
||||
private interface ActionExec<T> {
|
||||
T action() throws Exception;
|
||||
}
|
||||
|
||||
private static <T> T tryAction(ActionExec<T> exec, String key, int secondOut, Class clazz) {
|
||||
try {
|
||||
if (StrUtil.isNotBlank(key) && secondOut > 0) {
|
||||
return CacheUtil.getCacheData(redisKey(clazz, key), () -> {
|
||||
try {
|
||||
return exec.action();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, secondOut);
|
||||
} else {
|
||||
T t = exec.action();
|
||||
if (secondOut == -1) {
|
||||
DbUtil.removeCache(clazz);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String redisKey(Class clazz, String cacheKey) {
|
||||
if (!StrUtil.equals(cacheKey, "*")) {
|
||||
cacheKey = SecureUtil.md5(cacheKey);
|
||||
}
|
||||
String redisKey = "sql:" + clazz.getSimpleName().toLowerCase() + ":" + cacheKey;
|
||||
return redisKey;
|
||||
}
|
||||
|
||||
private static String cacheKey(String sql, Object... params) {
|
||||
StringBuilder sb = new StringBuilder(sql);
|
||||
if (params != null) {
|
||||
for (Object param : params) {
|
||||
sb.append(param);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static Entity queryOne(String sql, Class clazz, Object... params) {
|
||||
return tryAction(() -> DbUtil.get().queryOne(sql, params), cacheKey(sql, params), DEFAULT_CACHE_TIME, clazz);
|
||||
}
|
||||
|
||||
public static <T> T queryObject(String sql, Class<T> clazz, Object... params) {
|
||||
Entity item = tryAction(() -> DbUtil.get().queryOne(sql, params), cacheKey(sql, params), DEFAULT_CACHE_TIME, clazz);
|
||||
if (item != null) {
|
||||
return item.toBean(clazz);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Long queryLong(String sql, Class clazz, Object... params) {
|
||||
Number number = tryAction(() -> DbUtil.get().queryNumber(sql, params), cacheKey(sql, params), DEFAULT_CACHE_TIME, clazz);
|
||||
if (number == null) {
|
||||
return 0L;
|
||||
}
|
||||
return number.longValue();
|
||||
}
|
||||
|
||||
public static int delete(String sql, Class clazz, Object... params) {
|
||||
return tryAction(() -> DbUtil.get().execute(sql, params), null, -1, clazz);
|
||||
}
|
||||
|
||||
public static String queryString(String sql, Class clazz, Object... params) {
|
||||
return tryAction(() -> DbUtil.get().queryString(sql, params), cacheKey(sql, params), DEFAULT_CACHE_TIME, clazz);
|
||||
}
|
||||
|
||||
public static List<Entity> query(String sql, Class clazz, Object... params) {
|
||||
return tryAction(() -> DbUtil.get().query(sql, params), cacheKey(sql, params), DEFAULT_CACHE_TIME, clazz);
|
||||
}
|
||||
|
||||
public static <T> List<T> queryList(String sql, Class<T> clazz, Object... params) {
|
||||
List<Entity> tmpList = query(sql, clazz, params);
|
||||
if (tmpList == null) {
|
||||
return null;
|
||||
}
|
||||
List<T> list = new ArrayList<>();
|
||||
for (Entity tmp : tmpList) {
|
||||
list.add(tmp.toBean(clazz));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static boolean insert(Entity entity, Class clazz) {
|
||||
return tryAction(() -> DbUtil.get().insert(entity) > 0, null, -1, clazz);
|
||||
}
|
||||
|
||||
public static boolean insertObject(Object object) {
|
||||
Entity entity = Entity.parse(object, true, false);
|
||||
return insert(entity, object.getClass());
|
||||
}
|
||||
|
||||
public static boolean[] insertColl(Collection<Entity> colls, Class clazz) {
|
||||
return tryAction(() -> {
|
||||
int[] items = DbUtil.get().insert(colls);
|
||||
boolean[] flags = new boolean[items.length];
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
flags[i] = items[i] > 0;
|
||||
}
|
||||
return flags;
|
||||
}, null, -1, clazz);
|
||||
}
|
||||
|
||||
public static boolean[] insertObjectColl(Collection<Object> objects) {
|
||||
List<Entity> list = new ArrayList<>();
|
||||
Class clazz = null;
|
||||
for (Object obj : objects) {
|
||||
Entity entity = Entity.parse(obj, true, false);
|
||||
list.add(entity);
|
||||
if (clazz == null) {
|
||||
clazz = obj.getClass();
|
||||
}
|
||||
}
|
||||
if (clazz == null) {
|
||||
return new boolean[0];
|
||||
}
|
||||
return insertColl(list, clazz);
|
||||
}
|
||||
|
||||
public static int update(Entity entity, Entity where, Class clazz) {
|
||||
return tryAction(() -> DbUtil.get().update(entity, where), null, -1, clazz);
|
||||
}
|
||||
|
||||
public static int updateObject(Object obj, Entity where) {
|
||||
Entity entity = Entity.parse(obj, true, false);
|
||||
return update(entity, where, obj.getClass());
|
||||
}
|
||||
|
||||
public static int upsert(Entity entity, Class clazz, String... keys) {
|
||||
return tryAction(() -> DbUtil.get().upsert(entity, keys), null, -1, clazz);
|
||||
}
|
||||
|
||||
public static int upsertObject(Object obj, String... keys) {
|
||||
Entity entity = Entity.parse(obj, true, false);
|
||||
return upsert(entity, obj.getClass(), keys);
|
||||
}
|
||||
|
||||
/*public static void removeCache(String sql,Class clazz, Object... params) {
|
||||
String cacheKey = cacheKey(sql, params);
|
||||
String redisKey = redisKey(clazz,cacheKey);
|
||||
CacheUtil.delCacheData(redisKey);
|
||||
}*/
|
||||
public static void removeCache(Class clazz) {
|
||||
String redisKey = redisKey(clazz, "*");
|
||||
CacheUtil.delCacheData(redisKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param queryData 如 select *
|
||||
* @param whereSql 如 from t1 where n = ?
|
||||
* @param currentPage
|
||||
* @param pageSize
|
||||
* @param params
|
||||
* @return
|
||||
*/
|
||||
public static CommonBean.PageRes<Entity> page(String queryData, String whereSql, Class clazz, int currentPage, int pageSize, Object... params) {
|
||||
List<Object> keyList = CollUtil.newArrayList(params);
|
||||
keyList.add(currentPage);
|
||||
keyList.add(pageSize);
|
||||
int offset = (currentPage - 1) * pageSize;
|
||||
String dataSql = queryData + " " + whereSql + " limit " + pageSize + " offset " + offset;
|
||||
String countSql = "SELECT count(1) " + whereSql;
|
||||
CommonBean.PageRes<Entity> res = tryAction(() -> {
|
||||
List<Entity> data = DbUtil.query(dataSql, clazz, params);
|
||||
long count = DbUtil.queryLong(countSql, clazz, params);
|
||||
CommonBean.PageRes<Entity> tmp = new CommonBean.PageRes<>();
|
||||
tmp.setCount(count);
|
||||
tmp.setData(data);
|
||||
long pages = count / pageSize;
|
||||
if (count % pageSize != 0) {
|
||||
pages += 1L;
|
||||
}
|
||||
tmp.setPages(pages);
|
||||
return tmp;
|
||||
}, cacheKey(dataSql, ArrayUtil.toArray(keyList, Object.class)), DEFAULT_CACHE_TIME, clazz);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static <T> CommonBean.PageRes<T> pageObject(String queryData, String whereSql, Class<T> clazz, int currentPage, int pageSize, Object... params) {
|
||||
CommonBean.PageRes<Entity> pageResult = page(queryData, whereSql, clazz, currentPage, pageSize, params);
|
||||
CommonBean.PageRes<T> objRes = new CommonBean.PageRes<>();
|
||||
objRes.setPages(pageResult.getPages());
|
||||
objRes.setCount(pageResult.getCount());
|
||||
objRes.setData(new ArrayList<>());
|
||||
for (Entity entity : pageResult.getData()) {
|
||||
objRes.getData().add(entity.toBean(clazz));
|
||||
}
|
||||
return objRes;
|
||||
}
|
||||
|
||||
public static R commonEdit(Object data) {
|
||||
boolean flag;
|
||||
String id = (String) ReflectUtil.getFieldValue(data, "id");
|
||||
if (StrUtil.isNotBlank(id)) {
|
||||
flag = DbUtil.updateObject(data, Entity.create().set("id", id)) > 0;
|
||||
} else {
|
||||
ReflectUtil.setFieldValue(data, "id", IdUtil.fastSimpleUUID());
|
||||
flag = DbUtil.insertObject(data);
|
||||
}
|
||||
Assert.isTrue(flag, "操作失败");
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
public static void execSql(String sql) {
|
||||
try {
|
||||
DbUtil.get().execute(sql);
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 模拟协程,节省内存开销
|
||||
*/
|
||||
public class FiberUtil {
|
||||
private static final ExecutorService es;
|
||||
|
||||
static {
|
||||
es = ThreadUtil.newExecutor(10, 20);
|
||||
}
|
||||
|
||||
public static void run(Supplier action, FiberCallBack callback) {
|
||||
es.execute(() -> {
|
||||
if (callback != null) {
|
||||
FiberResult result = new FiberResult();
|
||||
try {
|
||||
Object obj = action.get();
|
||||
result.setRes(obj);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
result.setError(e);
|
||||
}
|
||||
callback.result(result);
|
||||
} else {
|
||||
try {
|
||||
action.get();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void run(Supplier action) {
|
||||
run(action, null);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class FiberResult {
|
||||
private Exception error;
|
||||
private Object res;
|
||||
}
|
||||
|
||||
public interface FiberCallBack {
|
||||
void result(FiberResult result);
|
||||
}
|
||||
}
|
@ -0,0 +1,293 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.RuntimeUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FrameStartUtil {
|
||||
public static byte[] logo;
|
||||
|
||||
public static void pringInfo() {
|
||||
try {
|
||||
startFrame();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
FileUtil.writeUtf8String(ExceptionUtil.stacktraceToString(e), "startError.txt");
|
||||
} catch (Exception e2) {
|
||||
}
|
||||
}
|
||||
certificatePringInfo();
|
||||
boolean isWin = System.getProperty("os.name").toLowerCase().indexOf("window") != -1;
|
||||
if (isWin) {
|
||||
desktopKjfs();
|
||||
}
|
||||
}
|
||||
|
||||
private static void desktopKjfs() {
|
||||
String iconPath = ProjectUtil.rootPath + "/webos.ico";
|
||||
Map<String, Image> data = iconMap();
|
||||
Image img = data.get("i64");
|
||||
if (img == null) {
|
||||
img = data.get("i32");
|
||||
}
|
||||
convertPngToIco(img, iconPath);
|
||||
createShortcut(ProjectUtil.rootPath + "/auto-restart.bat", iconPath, "腾飞Webos服务端");
|
||||
}
|
||||
|
||||
public static void convertPngToIco(Image img, String icoPath) {
|
||||
try {
|
||||
BufferedImage bimg = ImgUtil.toBufferedImage(img);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(bimg, "png", baos);
|
||||
byte[] pngBytes = baos.toByteArray();
|
||||
ByteBuffer bb = ByteBuffer.allocate(6 + 16 + pngBytes.length);
|
||||
bb.order(ByteOrder.LITTLE_ENDIAN);
|
||||
bb.putShort((short) 0); // 保留字段,必须为0
|
||||
bb.putShort((short) 1); // 图像类型,1 = 图标
|
||||
bb.putShort((short) 1); // 图像数量,1 为单图像 ICO
|
||||
bb.put((byte) bimg.getWidth()); // 宽度,0 表示 256 像素
|
||||
bb.put((byte) bimg.getHeight()); // 高度,0 表示 256 像素
|
||||
bb.put((byte) 0); // 颜色数,0 表示无调色板
|
||||
bb.put((byte) 0); // 保留字段
|
||||
bb.putShort((short) 1); // 颜色平面
|
||||
bb.putShort((short) 32); // 每像素位数
|
||||
bb.putInt(pngBytes.length); // 图像数据大小
|
||||
bb.putInt(6 + 16); // 图像数据偏移
|
||||
bb.put(pngBytes);
|
||||
try (RandomAccessFile raf = new RandomAccessFile(icoPath, "rw");
|
||||
FileChannel fc = raf.getChannel()) {
|
||||
((Buffer)bb).flip();
|
||||
fc.write(bb);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void createShortcut(String scriptPath, String iconPath, String shortcutName) {
|
||||
String vbscriptPath = ProjectUtil.rootPath + "/create-desktop.vbs";
|
||||
String vbscript = "Set oWS = WScript.CreateObject(\"WScript.Shell\")\n" +
|
||||
"strDesktop = oWS.SpecialFolders(\"Desktop\")\n" +
|
||||
"set oLink = oWS.CreateShortcut(strDesktop & \"\\" + shortcutName + ".lnk\")\n" +
|
||||
"oLink.TargetPath = \"" + scriptPath + "\"\n" +
|
||||
"oLink.IconLocation = \"" + iconPath + ",0\"\n" +
|
||||
"oLink.Save\n" +
|
||||
"Set oLink = Nothing\n" +
|
||||
"Set oWS = Nothing";
|
||||
FileUtil.writeString(vbscript, vbscriptPath, CharsetUtil.CHARSET_GBK);
|
||||
try {
|
||||
RuntimeUtil.exec("wscript", "\"" + vbscriptPath + "\"").waitFor();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
//FileUtil.del(vbscriptPath);
|
||||
}
|
||||
|
||||
private static void browserUrl(String url) {
|
||||
if (Desktop.isDesktopSupported()) {
|
||||
try {
|
||||
URI uri = new URI(url);
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
desktop.browse(uri);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void openDir(String dir) {
|
||||
if (Desktop.isDesktopSupported()) {
|
||||
try {
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
desktop.open(new File(dir));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Image> iconMapData;
|
||||
|
||||
private synchronized static Map<String, Image> iconMap() {
|
||||
if (iconMapData != null) {
|
||||
return iconMapData;
|
||||
}
|
||||
iconMapData = new HashMap<>();
|
||||
try {
|
||||
if (logo == null) {
|
||||
try {
|
||||
logo = IoUtil.readBytes(FrameStartUtil.class.getResource("/logo.png").openStream());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
if (logo == null) {
|
||||
logo = new byte[0];
|
||||
}
|
||||
Color color = new Color(0, 0, 0, 0);
|
||||
InputStream in = new ByteArrayInputStream(logo);
|
||||
Image i64 = ImgUtil.scale(ImgUtil.read(in), 128, 128, color);
|
||||
Image i32 = ImgUtil.scale(i64, 32, 32, color);
|
||||
Image i16 = ImgUtil.scale(i32, 16, 16, color);
|
||||
iconMapData.put("i64", i64);
|
||||
iconMapData.put("i32", i32);
|
||||
iconMapData.put("i16", i16);
|
||||
return iconMapData;
|
||||
} catch (Exception e) {
|
||||
iconMapData.put("i32", new BufferedImage(32, 32, 1));
|
||||
iconMapData.put("i16", new BufferedImage(16, 16, 1));
|
||||
return iconMapData;
|
||||
} finally {
|
||||
logo = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void startFrame() {
|
||||
JFrame win = new JFrame();
|
||||
win.setSize(400, 300);
|
||||
win.setLocationRelativeTo(null);
|
||||
win.setVisible(true);
|
||||
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
win.setResizable(false);
|
||||
String contact = "QQ185085781";
|
||||
String softName = "腾飞软件";
|
||||
String systemName = "腾飞Webos";
|
||||
win.setTitle(systemName);
|
||||
win.setLayout(new GridLayout(3, 1));
|
||||
JLabel hy = new JLabel("欢迎使用" + softName + ",客户服务联系" + contact, JLabel.CENTER);
|
||||
win.add(hy);
|
||||
JLabel portError = new JLabel("正在探测端口...", JLabel.CENTER);
|
||||
win.add(portError);
|
||||
int port = ProjectUtil.startConfig.getInt("port");
|
||||
JPanel jPanel = new JPanel();
|
||||
JButton acBtn = new JButton("打开网站首页");
|
||||
acBtn.setSize(100, 30);
|
||||
jPanel.add(acBtn);
|
||||
acBtn.addActionListener(e -> {
|
||||
String localIp = "127.0.0.1";
|
||||
browserUrl("http://" + localIp + ":" + port);
|
||||
});
|
||||
JButton ycBtn = new JButton("隐藏到托盘区");
|
||||
ycBtn.setSize(100, 30);
|
||||
jPanel.add(ycBtn);
|
||||
ycBtn.addActionListener(e -> win.setVisible(false));
|
||||
win.add(jPanel);
|
||||
try {
|
||||
boolean isWin = System.getProperty("os.name").toLowerCase().indexOf("window") != -1;
|
||||
String path = new File(ProjectUtil.rootPath).getParent();
|
||||
PopupMenu pm = new PopupMenu();
|
||||
MenuItem m1 = new MenuItem(isWin ? "Show" : "显示");
|
||||
m1.addActionListener(e -> {
|
||||
win.setVisible(true);
|
||||
win.setAlwaysOnTop(true);
|
||||
});
|
||||
MenuItem m3 = new MenuItem(isWin ? "Open Install Directory" : "打开安装目录");
|
||||
m3.addActionListener(e -> {
|
||||
openDir(path);
|
||||
});
|
||||
MenuItem m4 = new MenuItem(isWin ? "Restart Service" : "重启服务");
|
||||
m4.addActionListener(e -> {
|
||||
ProjectUtil.restartServer();
|
||||
});
|
||||
MenuItem m2 = new MenuItem(isWin ? "Exit" : "退出");
|
||||
m2.addActionListener(e -> System.exit(0));
|
||||
pm.add(m1);
|
||||
pm.add(m3);
|
||||
pm.addSeparator();
|
||||
pm.add(m4);
|
||||
pm.add(m2);
|
||||
Map<String, Image> iconMapData = iconMap();
|
||||
SystemTray systemTray = SystemTray.getSystemTray();
|
||||
win.setIconImage(iconMapData.get("i32"));
|
||||
TrayIcon trayIcon = new TrayIcon(iconMapData.get("i16"), systemName, pm);
|
||||
systemTray.add(trayIcon);
|
||||
trayIcon.addActionListener(e -> {
|
||||
win.setVisible(true);
|
||||
win.setAlwaysOnTop(true);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
}
|
||||
boolean use = isPortUsing(port);
|
||||
portError.setText(use ? (port + "端口已被占用") : (port + "端口未被占用"));
|
||||
if (!use) {
|
||||
ThreadUtil.execute(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
ThreadUtil.sleep(2000);
|
||||
JSONObject res = JSONUtil.parseObj(HttpUtil.get("http://127.0.0.1:" + port + "/webos/api"));
|
||||
if (res.getInt("code", -1) >= 0) {
|
||||
portError.setText(port + "服务正常启动");
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPortUsing(int port) {
|
||||
boolean flag = false;
|
||||
try {
|
||||
ServerSocket ss = new ServerSocket(port);
|
||||
ss.close();
|
||||
} catch (Exception e) {
|
||||
flag = true;
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
private static void certificatePringInfo() {
|
||||
String contact = "QQ185085781";
|
||||
String softName = "腾飞软件";
|
||||
String sysName = System.getProperty("os.name");
|
||||
if (sysName.contains("Windows")) {
|
||||
System.out.println();
|
||||
System.out.println("=========================================================");
|
||||
System.out.println(" ");
|
||||
System.out.println(" 欢迎使用" + softName + ",客户服务联系" + contact + " ");
|
||||
System.out.println(" ");
|
||||
System.out.println("=========================================================");
|
||||
System.out.println();
|
||||
} else {
|
||||
String[] color = new String[]{"\u001b[31m", "\u001b[32m", "\u001b[33m", "\u001b[34m", "\u001b[35m",
|
||||
"\u001b[36m", "\u001b[90m", "\u001b[92m", "\u001b[93m", "\u001b[94m", "\u001b[95m", "\u001b[96m"};
|
||||
System.out.println();
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 57; ++i) {
|
||||
System.out.print(color[i % color.length] + "=\u001b[0m");
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
System.out.println(" ");
|
||||
System.out.println(" \u001b[31m欢迎\u001b[92m使用\u001b[95m" + softName
|
||||
+ "\u001b[0m,\u001b[37m客户\u001b[0m服务\u001b[91m联系\u001b[93m" + contact + " ");
|
||||
System.out.println(" ");
|
||||
for (i = 56; i >= 0; --i) {
|
||||
System.out.print(color[i % color.length] + "=\u001b[0m");
|
||||
}
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,237 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.core.codec.Base64Decoder;
|
||||
import cn.hutool.core.codec.Base64Encoder;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.modules.entity.SysMenu;
|
||||
import cn.tenfell.webos.modules.entity.SysRoleMenu;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
import cn.tenfell.webos.modules.entity.SysUserRole;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LoginAuthUtil {
|
||||
//主要用于异步线程中获得用户信息
|
||||
public static ThreadLocal<SysUser> USER_LOCAL = new ThreadLocal<>();
|
||||
public static String userCacheSql = "select * from sys_user where id = ? and valid = 1";
|
||||
private static JwtKeyCache keyCache;
|
||||
|
||||
private static List<String> getJwtKeys() {
|
||||
if (keyCache == null || keyCache.getCreateTime() == null || System.currentTimeMillis() - keyCache.getCreateTime() > 10 * 60 * 1000) {
|
||||
if (keyCache == null) {
|
||||
keyCache = new JwtKeyCache();
|
||||
}
|
||||
long eachTwo = System.currentTimeMillis() / 1000 / 60 / 60 / 2;
|
||||
int expireTime = 7200;
|
||||
String key1 = CacheUtil.getCacheData("jwt:key-" + eachTwo, IdUtil::simpleUUID, expireTime);
|
||||
String key2 = CacheUtil.getCacheData("jwt:key-" + (eachTwo + 1L), IdUtil::simpleUUID, expireTime * 2);
|
||||
keyCache.setKeys(CollUtil.newArrayList(key1, key2));
|
||||
keyCache.setCreateTime(System.currentTimeMillis());
|
||||
}
|
||||
return keyCache.getKeys();
|
||||
}
|
||||
|
||||
public static CommonBean.AccessToken createToken(String userId,String pwd ,String haw, Integer userType) {
|
||||
if(StrUtil.isBlank(haw)){
|
||||
haw = createPageCheck(pwd);
|
||||
Assert.notBlank(haw,"请刷新页面重试");
|
||||
}
|
||||
JSONObject mapObj = new JSONObject();
|
||||
mapObj.set("userId", userId);
|
||||
mapObj.set("userType", userType);
|
||||
mapObj.set("haw", haw);
|
||||
Long expireTime = System.currentTimeMillis() + 7200 * 1000L;
|
||||
mapObj.set("expireTime", expireTime);
|
||||
List<String> keys = getJwtKeys();
|
||||
String token = createTokenStr(mapObj, keys.get(1));
|
||||
String uuid = IdUtil.fastSimpleUUID();
|
||||
CacheUtil.setValue("jwt:user-" + uuid, mapObj, 7200);
|
||||
CacheUtil.delValue("jwt:user_lock:" + userId);
|
||||
CacheUtil.setValue("jwt:user-page-id-"+userId,haw,14400);
|
||||
return CommonBean.AccessToken.builder().webosToken(token).expireTime(expireTime).refreshToken(uuid).build();
|
||||
}
|
||||
|
||||
private static String createTokenStr(JSONObject mapObj, String key) {
|
||||
String str = Base64Encoder.encode(mapObj.toString());
|
||||
String str2 = SecureUtil.sha1(str + key);
|
||||
return str + "." + str2;
|
||||
}
|
||||
|
||||
private static boolean verifyToken(String token, String key) {
|
||||
String[] tokenSz = token.split("\\.");
|
||||
if (tokenSz.length < 2) {
|
||||
return false;
|
||||
}
|
||||
String str = tokenSz[0];
|
||||
String str2 = tokenSz[1];
|
||||
String str2p = SecureUtil.sha1(str + key);
|
||||
return str2p.equals(str2);
|
||||
}
|
||||
|
||||
public static SysUser getUserByToken(String token) {
|
||||
List<String> keys = getJwtKeys();
|
||||
if (verifyToken(token, keys.get(1)) || verifyToken(token, keys.get(0))) {
|
||||
//说明是合法的
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
JSONObject mapObj = JSONUtil.parseObj(Base64Decoder.decodeStr(token.split("\\.")[0]));
|
||||
Long expireTime = mapObj.getLong("expireTime");
|
||||
if (System.currentTimeMillis() > expireTime) {
|
||||
return null;
|
||||
}
|
||||
String userId = mapObj.getStr("userId");
|
||||
String haw = mapObj.getStr("haw");
|
||||
SysUser cacheUser = DbUtil.queryObject(userCacheSql, SysUser.class, userId);
|
||||
if (cacheUser == null) {
|
||||
return null;
|
||||
}
|
||||
String cacheHaw = CacheUtil.getValue("jwt:user-page-id-"+userId);
|
||||
if(StrUtil.isBlank(cacheHaw)){
|
||||
return null;
|
||||
}
|
||||
if(!StrUtil.equals(cacheHaw,haw)){
|
||||
return null;
|
||||
}
|
||||
String pageId = createPageCheck(cacheUser.getPassword());
|
||||
if(StrUtil.isNotBlank(pageId)){
|
||||
if(!StrUtil.equals(cacheHaw,pageId)){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return cacheUser;
|
||||
}
|
||||
|
||||
public static R checkLock() {
|
||||
SysUser user = getUser();
|
||||
if (user != null) {
|
||||
String lock = CacheUtil.getValue("jwt:user_lock:" + user.getId());
|
||||
if (StrUtil.equals(lock, "1")) {
|
||||
//此用户已锁定
|
||||
return R.lock();
|
||||
}
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
public static SysUser getUser() {
|
||||
SysUser user = USER_LOCAL.get();
|
||||
if (user == null) {
|
||||
try {
|
||||
String token = ProjectUtil.getContext().getCtx().header("webos-token");
|
||||
if (StrUtil.isBlank(token)) {
|
||||
return null;
|
||||
}
|
||||
user = getUserByToken(token);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public static boolean isSystem() {
|
||||
List<String> auths = getUserAuths();
|
||||
if (CollUtil.isEmpty(auths)) {
|
||||
return false;
|
||||
}
|
||||
return auths.contains("system");
|
||||
}
|
||||
|
||||
public static boolean isMain() {
|
||||
List<String> auths = getUserAuths();
|
||||
if (CollUtil.isEmpty(auths)) {
|
||||
return false;
|
||||
}
|
||||
return auths.contains("main_auth");
|
||||
}
|
||||
|
||||
public static List<String> getUserAuths() {
|
||||
SysUser user = getUser();
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
if (true) {
|
||||
//因不考虑角色关系,所以用户只分主用户和子用户
|
||||
List<String> list = CollUtil.newArrayList(user.getUserType() == 1 ? "main_auth" : "child_auth");
|
||||
if (user.getUserType() == 1 && user.getIsAdmin() == 1) {
|
||||
list.add("system");
|
||||
}
|
||||
return list;
|
||||
}
|
||||
List<SysUserRole> sysUserRoles = DbUtil.queryList("select * from sys_user_role where user_id = ?", SysUserRole.class, user.getId());
|
||||
if (CollUtil.isEmpty(sysUserRoles)) {
|
||||
return null;
|
||||
}
|
||||
List<String> roleIds = CollUtil.getFieldValues(sysUserRoles, "roleId", String.class);
|
||||
String roleIdsStr = "'" + CollUtil.join(roleIds, "','") + "'";
|
||||
List<SysRoleMenu> sysRoleMenus = DbUtil.queryList("select * from sys_role_menu where role_id in ( " + roleIdsStr + " )", SysRoleMenu.class);
|
||||
if (CollUtil.isEmpty(sysRoleMenus)) {
|
||||
return null;
|
||||
}
|
||||
List<String> menuIds = CollUtil.getFieldValues(sysRoleMenus, "menuId", String.class);
|
||||
String menuIdsStr = "'" + CollUtil.join(menuIds, "','") + "'";
|
||||
List<SysMenu> menus = DbUtil.queryList("select * from sys_menu where id in ( " + menuIdsStr + " )", SysMenu.class);
|
||||
if (CollUtil.isEmpty(menus)) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.getFieldValues(menus, "authKey", String.class);
|
||||
}
|
||||
|
||||
private static String createPageCheck(String userPwd){
|
||||
String pageId = ProjectUtil.getContext().getCtx().header("page-id");
|
||||
if(StrUtil.isBlank(pageId)){
|
||||
return ProjectUtil.getContext().getCtx().attr("page-id");
|
||||
}
|
||||
return SecureUtil.md5(pageId+userPwd);
|
||||
}
|
||||
public static CommonBean.AccessToken refreshToken(String refreshToken) {
|
||||
JSONObject mapObj = CacheUtil.getValue("jwt:user-" + refreshToken);
|
||||
if (mapObj == null) {
|
||||
return null;
|
||||
}
|
||||
String userId = mapObj.getStr("userId");
|
||||
String haw = mapObj.getStr("haw");
|
||||
return createToken(userId, null,haw, mapObj.getInt("userType"));
|
||||
}
|
||||
|
||||
final private static String PASSWORD_KEY = "kCK!EKRhjli0d0ce";
|
||||
|
||||
public static String encodePassword(String password) {
|
||||
String sjs = RandomUtil.randomString(6);
|
||||
String ep = SecureUtil.hmacSha256(PASSWORD_KEY + sjs).digestBase64(password, false);
|
||||
return ep + "." + sjs;
|
||||
}
|
||||
|
||||
public static boolean checkPassword(String password, String encodePassword) {
|
||||
String[] mmsz = encodePassword.split("\\.");
|
||||
String sjs = mmsz[1];
|
||||
String ep = SecureUtil.hmacSha256(PASSWORD_KEY + sjs).digestBase64(password, false);
|
||||
return encodePassword.equals(ep + "." + sjs);
|
||||
}
|
||||
|
||||
public static void toLock() {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
if (user != null) {
|
||||
CacheUtil.setValue("jwt:user_lock:" + user.getId(), "1", 4 * 60 * 60);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class JwtKeyCache {
|
||||
private Long createTime;
|
||||
private List<String> keys;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.net.multipart.MultipartFormData;
|
||||
import cn.hutool.core.net.multipart.UploadSetting;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import lombok.Data;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Data
|
||||
public class ProjectContext {
|
||||
private Context ctx;
|
||||
public static MultipartFormData getMultipart(Context ctx) throws IORuntimeException {
|
||||
final MultipartFormData formData = new MultipartFormData(new UploadSetting());
|
||||
try {
|
||||
formData.parseRequestStream(ctx.bodyAsStream(), CharsetUtil.charset("utf-8"));
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
return formData;
|
||||
}
|
||||
public static ProjectContext init(Context ctx) {
|
||||
ProjectContext context = new ProjectContext();
|
||||
context.setCtx(ctx);
|
||||
return context;
|
||||
}
|
||||
}
|
497
api/src/main/java/cn/tenfell/webos/common/util/ProjectUtil.java
Normal file
497
api/src/main/java/cn/tenfell/webos/common/util/ProjectUtil.java
Normal file
@ -0,0 +1,497 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.noear.solon.Solon;
|
||||
import org.noear.solon.core.handle.MethodType;
|
||||
import org.noear.solon.core.message.Listener;
|
||||
import org.noear.solon.core.message.Message;
|
||||
import org.noear.solon.core.message.Session;
|
||||
import org.noear.solon.web.staticfiles.StaticMappings;
|
||||
import org.noear.solon.web.staticfiles.StaticMimes;
|
||||
import org.noear.solon.web.staticfiles.repository.FileStaticRepository;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.RuntimeUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.hutool.json.JSONConfig;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.hutool.log.Log;
|
||||
import cn.hutool.log.LogFactory;
|
||||
import cn.tenfell.webos.WebOsApp;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.filesystem.LocalFileSystem;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.ssh.WebSSHService;
|
||||
import cn.tenfell.webos.common.ssh.WebSSHServiceImpl;
|
||||
import cn.tenfell.webos.common.webdav.WebdavUtil;
|
||||
|
||||
public class ProjectUtil {
|
||||
private static Log log = LogFactory.get();
|
||||
public static String jmPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyCKDubntmD36NAS0EqUY6re+GVCVZr2y/poNdC0AG9sTYURtt6qmfF2SBgJydceyYKV8AZ7zV3QKpa2ieqmTBLRsB4JIEQovzcQyNVWY90XPd10LG6f9WshWdCArwBp2VchKhL86fm5pypvqtIih+kFGfOoocQ+NAafOwVrD3JwIDAQAB";
|
||||
public static JSONObject softVersion;
|
||||
private static final ThreadLocal<ProjectContext> currentContext = new ThreadLocal<>();
|
||||
// 配置文件路径
|
||||
public static String projectConfigPath;
|
||||
public static JSONObject startConfig;
|
||||
public static String rootPath;
|
||||
// 系统是否安装
|
||||
public static boolean hasInstall = false;
|
||||
public static JSONConfig jsonConfig;
|
||||
public static String webPath;
|
||||
|
||||
public static ProjectContext getContext() {
|
||||
return currentContext.get();
|
||||
}
|
||||
|
||||
public static void showConsoleErr(Throwable e) {
|
||||
authRestart(e);
|
||||
if (!startConfig.getBool("debug")) {
|
||||
return;
|
||||
}
|
||||
log.error(e);
|
||||
}
|
||||
|
||||
private static void settingKaijiAction() {
|
||||
boolean isWin = System.getProperty("os.name").toLowerCase().indexOf("window") != -1;
|
||||
if (!isWin) {
|
||||
// linux
|
||||
String jb = "cd " + ProjectUtil.rootPath + " && sh restart.sh";
|
||||
String rc = "/etc/rc.d/rc.local";
|
||||
String cmd = "grep -q \"" + jb + "\" " + rc + " || echo \"" + jb + "\" >> " + rc;
|
||||
FileUtil.writeUtf8String(cmd, ProjectUtil.rootPath + "/linuxauto.sh");
|
||||
RuntimeUtil.exec(null, new File(ProjectUtil.rootPath), "sh linuxauto.sh");
|
||||
FileUtil.del(ProjectUtil.rootPath + "/linuxauto.sh");
|
||||
String autoPath = "/etc/profile.d/" + ProjectUtil.startConfig.getStr("projectName") + ".sh";
|
||||
if (FileUtil.exist(autoPath)) {
|
||||
FileUtil.del(autoPath);
|
||||
}
|
||||
} else {
|
||||
// win系统
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("title tenfell-webos\n");
|
||||
String jarPath = ProjectUtil.rootPath + "/webos.jar";
|
||||
sb.append("java -javaagent:" + jarPath + " -jar " + jarPath + "\n");
|
||||
FileUtil.writeUtf8String(sb.toString(), ProjectUtil.rootPath + "/winauto.bat");
|
||||
}
|
||||
}
|
||||
|
||||
public static void authRestart(Throwable e) {
|
||||
if(e != null){
|
||||
e = ExceptionUtil.getRootCause(e);
|
||||
if (!"InaccessibleObjectException".equals(e.getClass().getSimpleName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
int version = Convert.toInt(System.getProperty("java.version").split("_")[0].split("\\.")[0], 1);
|
||||
if (version < 9) {
|
||||
return;
|
||||
}
|
||||
String oldStr = "-Dfile.encoding=UTF-8 -jar";
|
||||
String[] psz = { "java.lang", "java.util", "java.nio", "sun.nio.ch", "jdk.internal.loader", "java.net",
|
||||
"sun.net.www.protocol.https" };
|
||||
StringBuilder newStr = new StringBuilder("-Dfile.encoding=UTF-8 ");
|
||||
for (String p : psz) {
|
||||
newStr.append("--add-opens java.base/").append(p).append("=ALL-UNNAMED ");
|
||||
}
|
||||
newStr.append("-jar");
|
||||
List<Dict> paths = CollUtil.newArrayList(
|
||||
Dict.create().set("path", "restart.sh").set("charset", CharsetUtil.UTF_8),
|
||||
Dict.create().set("path", "restart.bat").set("charset", CharsetUtil.UTF_8),
|
||||
Dict.create().set("path", "start.bat").set("charset", CharsetUtil.UTF_8));
|
||||
boolean has = false;
|
||||
for (Dict item : paths) {
|
||||
if (!FileUtil.exist(ProjectUtil.rootPath + "/" + item.getStr("path"))) {
|
||||
continue;
|
||||
}
|
||||
String str = FileUtil.readString(ProjectUtil.rootPath + "/" + item.getStr("path"),
|
||||
CharsetUtil.charset(item.getStr("charset")));
|
||||
if (str.indexOf(oldStr) == -1) {
|
||||
continue;
|
||||
}
|
||||
has = true;
|
||||
str = str.replace(oldStr, newStr);
|
||||
FileUtil.writeString(str, ProjectUtil.rootPath + "/" + item.getStr("path"), item.getStr("charset"));
|
||||
}
|
||||
if (!has) {
|
||||
return;
|
||||
}
|
||||
restartServer();
|
||||
}
|
||||
|
||||
public static void restartServer() {
|
||||
new Thread(() -> {
|
||||
boolean isWin = System.getProperty("os.name").toLowerCase().indexOf("window") != -1;
|
||||
if (isWin) {
|
||||
// win
|
||||
RuntimeUtil.exec(null, new File(ProjectUtil.rootPath), "restart.bat");
|
||||
} else {
|
||||
// linux系统或mac系统
|
||||
RuntimeUtil.exec(null, new File(ProjectUtil.rootPath), "sh restart.sh");
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
String configPath = StrUtil.replace(WebOsApp.class.getResource("").getPath(), "\\", "/");
|
||||
rootPath = StrUtil.replace(FileUtil.getParent(configPath, 4), "\\", "/");
|
||||
FileUtil.writeUtf8String(Convert.toStr(RuntimeUtil.getPid()), rootPath + "/pidfile.txt");
|
||||
try {
|
||||
if(!ArrayList.class.getDeclaredField("size").trySetAccessible()){
|
||||
authRestart(null);
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
} catch (SecurityException e) {
|
||||
}
|
||||
projectConfigPath = rootPath + "/project_config.json";
|
||||
hasInstall = FileUtil.exist(projectConfigPath);
|
||||
String startConfigPath = rootPath + "/start_config.json";
|
||||
if (FileUtil.exist(startConfigPath)) {
|
||||
startConfig = JSONUtil.parseObj(FileUtil.readUtf8String(startConfigPath));
|
||||
} else {
|
||||
startConfig = JSONUtil.createObj();
|
||||
}
|
||||
boolean needSave = false;
|
||||
if (startConfig.getBool("debug") == null) {
|
||||
startConfig.set("debug", false);
|
||||
needSave = true;
|
||||
}
|
||||
if (startConfig.getStr("defaultPwd") == null) {
|
||||
startConfig.set("defaultPwd", "123456");
|
||||
needSave = true;
|
||||
}
|
||||
if (startConfig.getBool("showSql") == null) {
|
||||
startConfig.set("showSql", false);
|
||||
needSave = true;
|
||||
}
|
||||
if (startConfig.getBool("rangeFilter") == null) {
|
||||
startConfig.set("rangeFilter", true);
|
||||
needSave = true;
|
||||
}
|
||||
if (startConfig.getInt("port", 0) == 0) {
|
||||
startConfig.set("port", 8088);
|
||||
needSave = true;
|
||||
}
|
||||
if (StrUtil.isBlank(startConfig.getStr("zlPublic")) || StrUtil.isBlank(startConfig.getStr("zlPrivate"))) {
|
||||
RSA rsa = SecureUtil.rsa();
|
||||
startConfig.set("zlPrivate", rsa.getPrivateKeyBase64());
|
||||
startConfig.set("zlPublic", rsa.getPublicKeyBase64());
|
||||
needSave = true;
|
||||
}
|
||||
if (StrUtil.isBlank(startConfig.getStr("projectName"))) {
|
||||
startConfig.set("projectName", "webos" + IdUtil.getSnowflakeNextId());
|
||||
needSave = true;
|
||||
}
|
||||
if (needSave) {
|
||||
FileUtil.writeUtf8String(startConfig.toString(), startConfigPath);
|
||||
}
|
||||
noErrorMethod(ProjectUtil::settingKaijiAction);
|
||||
noErrorMethod(FrameStartUtil::pringInfo);
|
||||
log.info("webos started in jdk:{}", System.getProperty("java.version"));
|
||||
jsonConfig = new JSONConfig();
|
||||
jsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
AtomicReference<String> webRootPath = new AtomicReference<>(startConfig.getStr("webRootPath"));
|
||||
if (StrUtil.isNotBlank(webRootPath.get())) {
|
||||
// 如果项目移动了,尝试自动修复
|
||||
String checkfile = webRootPath.get() + "/common/sdk/check.json";
|
||||
if (!FileUtil.exist(checkfile)) {
|
||||
// 不存在的时候
|
||||
String tmpPath = CommonUtil.getParentPath(ProjectUtil.rootPath);
|
||||
tmpPath = tmpPath + "/web";
|
||||
if (FileUtil.exist(tmpPath + "/common/sdk/check.json")) {
|
||||
startConfig.set("webRootPath", tmpPath);
|
||||
needSave = true;
|
||||
webRootPath.set(tmpPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (StrUtil.isNotBlank(webRootPath.get())) {
|
||||
FileUtil.writeBytes(FrameStartUtil.logo, webRootPath.get() + "/imgs/logo.png");
|
||||
FileUtil.writeBytes(FrameStartUtil.logo, webRootPath.get() + "/common/smart-ui/expand/logo_32.png");
|
||||
}
|
||||
int port = startConfig.getInt("port");
|
||||
Solon.start(ProjectUtil.class, ArrayUtil.append(new String[0], "--server.port=" + port), app -> {
|
||||
app.filter((ctx, chain) -> {
|
||||
try {
|
||||
if (ctx.uri().getPath().startsWith("/webdav")) {
|
||||
ctx.attrSet("page-id", "webdav");
|
||||
currentContext.set(ProjectContext.init(ctx));
|
||||
WebdavUtil.servlet(ctx);
|
||||
ctx.setHandled(true);
|
||||
} else {
|
||||
MethodType.valueOf(ctx.method());
|
||||
chain.doFilter(ctx);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("");
|
||||
}
|
||||
});
|
||||
app.http("/api", ctx -> {
|
||||
try {
|
||||
String module = ctx.param("module");
|
||||
String action = ctx.param("action");
|
||||
if (ctx.method().equals("OPTIONS")) {
|
||||
return;
|
||||
}
|
||||
currentContext.set(ProjectContext.init(ctx));
|
||||
Object resData = BeanProxy.invoke(module, action, ctx);
|
||||
if (resData == null) {
|
||||
|
||||
} else if (resData instanceof InputStream) {
|
||||
InputStream in = (InputStream) resData;
|
||||
try {
|
||||
IoUtil.copy(in, ctx.outputStream());
|
||||
} catch (Exception e) {
|
||||
|
||||
} finally {
|
||||
IoUtil.close(in);
|
||||
}
|
||||
} else if (resData.getClass().isArray()
|
||||
&& ClassUtil.isAssignable(Byte.class, resData.getClass().getComponentType())) {
|
||||
ctx.output((byte[]) resData);
|
||||
} else {
|
||||
ctx.output(JSONUtil.toJsonStr(resData, jsonConfig));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
showConsoleErr(e);
|
||||
}
|
||||
});
|
||||
app.http("/init", ctx -> {
|
||||
String path = ctx.param("path");
|
||||
if (StrUtil.isNotBlank(path)) {
|
||||
// 设置
|
||||
R r;
|
||||
JSONObject tmpConfig;
|
||||
if (FileUtil.exist(startConfigPath)) {
|
||||
tmpConfig = JSONUtil.parseObj(FileUtil.readUtf8String(startConfigPath));
|
||||
} else {
|
||||
tmpConfig = JSONUtil.createObj();
|
||||
}
|
||||
if (StrUtil.isNotBlank(tmpConfig.getStr("webRootPath"))) {
|
||||
r = R.failed("已经设置,请删除start_config.json后重试");
|
||||
} else {
|
||||
path = StrUtil.replace(path, "\\", "/");
|
||||
if (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
String checkfile = path + "/common/sdk/check.json";
|
||||
if (FileUtil.exist(checkfile)) {
|
||||
startConfig.set("webRootPath", path);
|
||||
webRootPath.set(path);
|
||||
FileUtil.writeUtf8String(startConfig.toString(), startConfigPath);
|
||||
StaticMappings.add("/", false, new FileStaticRepository(webRootPath.get()));
|
||||
r = R.ok();
|
||||
} else {
|
||||
r = R.failed("此目录并非前端程序目录,请检查后重试");
|
||||
}
|
||||
}
|
||||
ctx.output(JSONUtil.toJsonStr(r, jsonConfig));
|
||||
} else {
|
||||
String html = IoUtil.readUtf8(WebOsApp.class.getResource("/install/index.html").openStream());
|
||||
String webPath = CommonUtil.getParentPath(ProjectUtil.rootPath) + "/web";
|
||||
html = html.replace("{{path}}", webPath);
|
||||
ctx.outputAsHtml(html);
|
||||
}
|
||||
});
|
||||
app.enableWebSocket(true);
|
||||
final WebSSHService sshService = new WebSSHServiceImpl();
|
||||
app.ws("/websocket", new Listener() {
|
||||
@Override
|
||||
public void onOpen(Session session) {
|
||||
sshService.initConnection(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(Session session, Message message) {
|
||||
message.setHandled(true);
|
||||
sshService.recvHandle(message.bodyAsString(), session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session) {
|
||||
sshService.close(session);
|
||||
}
|
||||
});
|
||||
app.get("/", ctx -> {
|
||||
if (StrUtil.isNotBlank(webRootPath.get())) {
|
||||
ctx.redirect("/index.html");
|
||||
} else {
|
||||
ctx.redirect("/init");
|
||||
}
|
||||
});
|
||||
if (StrUtil.isNotBlank(webRootPath.get())) {
|
||||
StaticMappings.add("/", false, new FileStaticRepository(webRootPath.get()));
|
||||
}
|
||||
StaticMimes.add(".webp", "image/webp");
|
||||
});
|
||||
log.info("webos started on 【0.0.0.0:{}】", port);
|
||||
Set<Class<?>> beans = ClassUtil.scanPackageByAnnotation("cn.tenfell.webos", BeanAction.class);
|
||||
BeanProxy.init(beans);
|
||||
initOsConfig();
|
||||
}
|
||||
|
||||
public static R install() {
|
||||
try {
|
||||
hasInstall = FileUtil.exist(projectConfigPath);
|
||||
initOsConfig();
|
||||
// 写入sql文件
|
||||
JSONObject config = JSONUtil.parseObj(FileUtil.readString(projectConfigPath, CharsetUtil.CHARSET_UTF_8));
|
||||
InputStream in = null;
|
||||
if (StrUtil.equals(config.getStr("sqlType"), "mysql")) {
|
||||
in = WebOsApp.class.getResource("/config/mysql.sql").openStream();
|
||||
} else if (StrUtil.equals(config.getStr("sqlType"), "sqlite")) {
|
||||
in = WebOsApp.class.getResource("/config/sqlite.sql").openStream();
|
||||
}
|
||||
Assert.notNull(in, "文件流不存在");
|
||||
String content = IoUtil.readUtf8(in);
|
||||
List<String> list = SqlUtil.splitSqlScript(content);
|
||||
int[] data = DbUtil.get().executeBatch(list);
|
||||
int count = 0;
|
||||
for (int i : data) {
|
||||
count += i;
|
||||
}
|
||||
Assert.isTrue(count > 0, "数据库安装失败");
|
||||
return R.ok();
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
uninstall();
|
||||
return R.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void uninstall() {
|
||||
FileUtil.del(projectConfigPath);
|
||||
hasInstall = false;
|
||||
}
|
||||
|
||||
public static void initOsConfig() {
|
||||
if (!hasInstall) {
|
||||
return;
|
||||
}
|
||||
String webRootPath = startConfig.getStr("webRootPath");
|
||||
Assert.notBlank(webRootPath, "请先经过init初始化前端位置");
|
||||
// 初始化配置文件
|
||||
JSONObject config = JSONUtil.parseObj(FileUtil.readString(projectConfigPath, CharsetUtil.CHARSET_UTF_8));
|
||||
webPath = StrUtil.replace(webRootPath, "\\", "/");
|
||||
if (webPath.endsWith("/")) {
|
||||
webPath = webPath.substring(0, webPath.length() - 1);
|
||||
}
|
||||
// 初始化数据库
|
||||
if (StrUtil.equals(config.getStr("sqlType"), "mysql")) {
|
||||
JSONObject mysql = config.getJSONObject("mysql");
|
||||
DbUtil.initMysql(mysql.getStr("host"), mysql.getInt("port"), mysql.getStr("database"), mysql.getStr("user"),
|
||||
mysql.getStr("password"));
|
||||
} else if (StrUtil.equals(config.getStr("sqlType"), "sqlite")) {
|
||||
JSONObject sqlite = config.getJSONObject("sqlite");
|
||||
DbUtil.initSqlite(sqlite.getStr("path"));
|
||||
} else {
|
||||
Assert.isTrue(false, "数据库配置失败");
|
||||
}
|
||||
log.info("db is ok,current db is 【{}】", config.getStr("sqlType"));
|
||||
// 初始化缓存
|
||||
if (StrUtil.equals(config.getStr("cacheType"), "redis")) {
|
||||
JSONObject redis = config.getJSONObject("redis");
|
||||
CacheUtil.changeRedisCache(redis.getStr("host"), redis.getInt("port"), redis.getInt("database"),
|
||||
redis.getStr("password"));
|
||||
} else if (StrUtil.equals(config.getStr("cacheType"), "file")) {
|
||||
JSONObject file = config.getJSONObject("file");
|
||||
CacheUtil.changeFileCache(file.getStr("path"));
|
||||
} else {
|
||||
Assert.isTrue(false, "缓存配置失败");
|
||||
}
|
||||
log.info("cache is ok,current cache is 【{}】", config.getStr("cacheType"));
|
||||
try {
|
||||
InputStream in = WebOsApp.class.getResource("/version.json").openStream();
|
||||
softVersion = JSONUtil.parseObj(IoUtil.readUtf8(in));
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
String path = ProjectUtil.rootPath + "/updateAction.json";
|
||||
JSONObject map;
|
||||
if (FileUtil.exist(path)) {
|
||||
map = JSONUtil.parseObj(FileUtil.readUtf8String(path));
|
||||
} else {
|
||||
map = JSONUtil.createObj();
|
||||
}
|
||||
if (softVersion != null) {
|
||||
String version_num = softVersion.getStr("version_num");
|
||||
if (!map.getBool(version_num, false)) {
|
||||
String old_version_num = softVersion.getStr("old_version_num");
|
||||
if (map.getBool(old_version_num, false)) {
|
||||
try {
|
||||
updateAction();
|
||||
map.set(version_num, true);
|
||||
} catch (Exception e) {
|
||||
map.set(version_num, false);
|
||||
}
|
||||
} else {
|
||||
map.set(version_num, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
FileUtil.writeUtf8String(map.toString(), path);
|
||||
FiberUtil.run(() -> {
|
||||
while (true) {
|
||||
scheduledTask();
|
||||
ThreadUtil.sleep(300000L);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void log(String format, Object... arguments) {
|
||||
log.info(format, arguments);
|
||||
}
|
||||
|
||||
private interface Func {
|
||||
void action();
|
||||
}
|
||||
|
||||
private static void noErrorMethod(Func func) {
|
||||
try {
|
||||
func.action();
|
||||
} catch (Exception e) {
|
||||
showConsoleErr(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时任务入口
|
||||
* 所有定时任务均从此处进
|
||||
* 每隔5分钟执行一次
|
||||
*/
|
||||
public static void scheduledTask() {
|
||||
// 刷新token
|
||||
noErrorMethod(TokenDataUtil::refreshToken);
|
||||
// 清除本地缓存
|
||||
noErrorMethod(CacheUtil::clearExpireCache);
|
||||
// 清除上传缓存
|
||||
noErrorMethod(LocalFileSystem::clearExpireTmpUploadFile);
|
||||
// 清除缩略缓存
|
||||
noErrorMethod(CommonUtil::clearExpireTmpFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新必备的方法
|
||||
*/
|
||||
private static void updateAction() {
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* 分片流
|
||||
*/
|
||||
public class ShardingInputStream extends InputStream {
|
||||
private InputStream in;
|
||||
private long length;
|
||||
private long hasLength;
|
||||
|
||||
public ShardingInputStream(InputStream in, long start, long length) {
|
||||
this.in = in;
|
||||
this.length = length;
|
||||
this.hasLength = length;
|
||||
if (start > 0) {
|
||||
long at = start;
|
||||
while (at > 0) {
|
||||
try {
|
||||
long amt = this.in.skip(at);
|
||||
at -= amt;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (this.hasLength <= 0) {
|
||||
return -1;
|
||||
}
|
||||
int r = in.read();
|
||||
this.hasLength -= 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte b[]) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
throw new IOException("此流不支持跳过");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte b[], int off, int len) throws IOException {
|
||||
if (b == null) {
|
||||
throw new NullPointerException();
|
||||
} else if (off < 0 || len < 0 || len > b.length - off) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (this.hasLength <= 0) {
|
||||
return -1;
|
||||
}
|
||||
if (this.hasLength < len) {
|
||||
len = (int) this.hasLength;
|
||||
}
|
||||
int c = in.read(b, off, len);
|
||||
this.hasLength -= c;
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() {
|
||||
return (int) this.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
IoUtil.close(this.in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
throw new RuntimeException("此流不支持标记");
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() {
|
||||
throw new RuntimeException("此流不支持重置");
|
||||
}
|
||||
}
|
||||
|
135
api/src/main/java/cn/tenfell/webos/common/util/SqlUtil.java
Normal file
135
api/src/main/java/cn/tenfell/webos/common/util/SqlUtil.java
Normal file
@ -0,0 +1,135 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SqlUtil {
|
||||
public static List<String> splitSqlScript(String sql) {
|
||||
List<String> list = new ArrayList<>();
|
||||
splitSqlScript(sql, ";", list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void splitSqlScript(String script, String separator, List<String> statements) {
|
||||
splitSqlScript(null, script, separator, "--", "/*",
|
||||
"*/", statements);
|
||||
}
|
||||
|
||||
public static void splitSqlScript(String resource, String script,
|
||||
String separator, String commentPrefix, String blockCommentStartDelimiter,
|
||||
String blockCommentEndDelimiter, List<String> statements) {
|
||||
|
||||
Assert.isTrue(hasText(commentPrefix), "'commentPrefix' must not be null or empty");
|
||||
splitSqlScript(resource, script, separator, new String[]{commentPrefix},
|
||||
blockCommentStartDelimiter, blockCommentEndDelimiter, statements);
|
||||
}
|
||||
|
||||
public static void splitSqlScript(String resource, String script,
|
||||
String separator, String[] commentPrefixes, String blockCommentStartDelimiter,
|
||||
String blockCommentEndDelimiter, List<String> statements) {
|
||||
|
||||
Assert.isTrue(hasText(script), "'script' must not be null or empty");
|
||||
Assert.notNull(separator, "'separator' must not be null");
|
||||
Assert.notEmpty(commentPrefixes, "'commentPrefixes' must not be null or empty");
|
||||
for (String commentPrefix : commentPrefixes) {
|
||||
Assert.isTrue(hasText(commentPrefix), "'commentPrefixes' must not contain null or empty elements");
|
||||
}
|
||||
Assert.isTrue(hasText(blockCommentStartDelimiter), "'blockCommentStartDelimiter' must not be null or empty");
|
||||
Assert.isTrue(hasText(blockCommentEndDelimiter), "'blockCommentEndDelimiter' must not be null or empty");
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean inSingleQuote = false;
|
||||
boolean inDoubleQuote = false;
|
||||
boolean inEscape = false;
|
||||
|
||||
for (int i = 0; i < script.length(); i++) {
|
||||
char c = script.charAt(i);
|
||||
if (inEscape) {
|
||||
inEscape = false;
|
||||
sb.append(c);
|
||||
continue;
|
||||
}
|
||||
// MySQL style escapes
|
||||
if (c == '\\') {
|
||||
inEscape = true;
|
||||
sb.append(c);
|
||||
continue;
|
||||
}
|
||||
if (!inDoubleQuote && (c == '\'')) {
|
||||
inSingleQuote = !inSingleQuote;
|
||||
} else if (!inSingleQuote && (c == '"')) {
|
||||
inDoubleQuote = !inDoubleQuote;
|
||||
}
|
||||
if (!inSingleQuote && !inDoubleQuote) {
|
||||
if (script.startsWith(separator, i)) {
|
||||
// We've reached the end of the current statement
|
||||
if (sb.length() > 0) {
|
||||
statements.add(sb.toString());
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
i += separator.length() - 1;
|
||||
continue;
|
||||
} else if (startsWithAny(script, commentPrefixes, i)) {
|
||||
// Skip over any content from the start of the comment to the EOL
|
||||
int indexOfNextNewline = script.indexOf('\n', i);
|
||||
if (indexOfNextNewline > i) {
|
||||
i = indexOfNextNewline;
|
||||
continue;
|
||||
} else {
|
||||
// If there's no EOL, we must be at the end of the script, so stop here.
|
||||
break;
|
||||
}
|
||||
} else if (script.startsWith(blockCommentStartDelimiter, i)) {
|
||||
// Skip over any block comments
|
||||
int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i);
|
||||
if (indexOfCommentEnd > i) {
|
||||
i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1;
|
||||
continue;
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Missing block comment end delimiter: " + blockCommentEndDelimiter);
|
||||
}
|
||||
} else if (c == ' ' || c == '\r' || c == '\n' || c == '\t') {
|
||||
// Avoid multiple adjacent whitespace characters
|
||||
if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') {
|
||||
c = ' ';
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
if (hasText(sb)) {
|
||||
statements.add(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean startsWithAny(String script, String[] prefixes, int offset) {
|
||||
for (String prefix : prefixes) {
|
||||
if (script.startsWith(prefix, offset)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasText(CharSequence str) {
|
||||
return str != null && str.length() > 0 && containsText(str);
|
||||
}
|
||||
|
||||
private static boolean containsText(CharSequence str) {
|
||||
int strLen = str.length();
|
||||
|
||||
for (int i = 0; i < strLen; ++i) {
|
||||
if (!Character.isWhitespace(str.charAt(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.modules.entity.IoTokenData;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public class TokenDataUtil {
|
||||
public static IoTokenData getTokenDataByIdOrToken(String driveType, String idOrToken) {
|
||||
if (StrUtil.equals(driveType, FileSystemUtil.LOCAL_DRIVE_TYPE)) {
|
||||
return new IoTokenData();
|
||||
}
|
||||
if (StrUtil.equals(driveType, FileSystemUtil.SERVER_DRIVE_TYPE)) {
|
||||
Assert.isTrue(false, "此磁盘不可作为用户盘使用");
|
||||
}
|
||||
Assert.notBlank(idOrToken, "id或token不可为空");
|
||||
IoTokenData itd = DbUtil.queryObject("select * from io_token_data where id = ?", IoTokenData.class, idOrToken);
|
||||
if (itd == null) {
|
||||
itd = new IoTokenData();
|
||||
itd.setTokenData(idOrToken);
|
||||
itd.setDriveType(driveType);
|
||||
FileSystemUtil.ACTION.refreshToken(driveType, itd);
|
||||
Assert.notBlank(itd.getId(), "当前磁盘暂未支持,请联系管理员");
|
||||
itd.setErrCount(0);
|
||||
DbUtil.upsertObject(itd, "id");
|
||||
}
|
||||
return itd;
|
||||
}
|
||||
|
||||
public static void refreshToken() {
|
||||
LocalDateTime now = LocalDateTime.now().plusMinutes(20L);
|
||||
List<IoTokenData> list = DbUtil.queryList("select * from io_token_data where expire_time <= ?", IoTokenData.class, now);
|
||||
for (IoTokenData itd : list) {
|
||||
try {
|
||||
if (itd.getErrCount() != null && itd.getErrCount() > 10) {
|
||||
DbUtil.delete("delete from io_token_data where id = ?", IoTokenData.class, itd.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
FileSystemUtil.ACTION.refreshToken(itd.getDriveType(), itd);
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
if (itd.getErrCount() == null) {
|
||||
itd.setErrCount(0);
|
||||
}
|
||||
itd.setErrCount(itd.getErrCount() + 1);
|
||||
}
|
||||
DbUtil.upsertObject(itd, "id");
|
||||
} catch (Exception e2) {
|
||||
ProjectUtil.showConsoleErr(e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package cn.tenfell.webos.common.util;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 校验工具类
|
||||
*/
|
||||
public class ValidaUtil {
|
||||
public static ValidaCheck init(Object obj) {
|
||||
ValidaCheck check = new ValidaCheck();
|
||||
check.setData(obj);
|
||||
return check;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ValidaCheck {
|
||||
private Object data;
|
||||
|
||||
public ValidaCheck notBlank(String field, String name, boolean ignore) {
|
||||
if (ignore) {
|
||||
return this;
|
||||
}
|
||||
this.notBlank(field, name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidaCheck notBlank(String field, String name) {
|
||||
Object val = ValidaUtil.getFieldValue(this.data, field);
|
||||
Assert.notNull(val, StrUtil.format("{}不可为空", name));
|
||||
Assert.notBlank(val.toString(), StrUtil.format("{}不可为空", name));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidaCheck notNull(String field, String name) {
|
||||
Object val = ValidaUtil.getFieldValue(this.data, field);
|
||||
Assert.notNull(val, StrUtil.format("{}不可为空", name));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidaCheck greater(String field, double val2, String name) {
|
||||
Object val = ValidaUtil.getFieldValue(this.data, field);
|
||||
Assert.notNull(val, StrUtil.format("{}不可为空", name));
|
||||
try {
|
||||
Double dv = Convert.toDouble(val);
|
||||
Assert.notNull(dv, StrUtil.format("{}必须为数值", name));
|
||||
Assert.isTrue(dv > val2, "{}必须大于{}", name, val2);
|
||||
} catch (Exception e) {
|
||||
Assert.isTrue(false, StrUtil.format("{}必须为数值", name));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getFieldValue(Object obj, String field) {
|
||||
if (obj instanceof Map) {
|
||||
return ((Map) obj).get(field);
|
||||
} else {
|
||||
return ReflectUtil.getFieldValue(obj, field);
|
||||
}
|
||||
}
|
||||
}
|
297
api/src/main/java/cn/tenfell/webos/common/webdav/WebdavUtil.java
Normal file
297
api/src/main/java/cn/tenfell/webos/common/webdav/WebdavUtil.java
Normal file
@ -0,0 +1,297 @@
|
||||
package cn.tenfell.webos.common.webdav;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.tenfell.webos.common.webdav.sf.*;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.UnauthenticatedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.WebdavException;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.ResourceLocks;
|
||||
import cn.tenfell.webos.common.webdav.sf.methods.*;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.security.Principal;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class WebdavUtil {
|
||||
private static Map<String, IMethodExecutor> webdavMap = new ConcurrentHashMap<>();
|
||||
private static IWebdavStore store;
|
||||
private static final boolean READ_ONLY = false;
|
||||
public static void init(String dftIndexFile,
|
||||
String insteadOf404, int nocontentLenghHeaders,
|
||||
boolean lazyFolderCreationOnPut){
|
||||
ResourceLocks _resLocks = new ResourceLocks();
|
||||
IMimeTyper mimeTyper = new IMimeTyper() {
|
||||
public String getMimeType(ITransaction transaction, String path) {
|
||||
String retVal= store.getStoredObject(transaction, path).getMimeType();
|
||||
return retVal;
|
||||
}
|
||||
};
|
||||
register("GET", new DoGet(store, dftIndexFile, insteadOf404, _resLocks,
|
||||
mimeTyper, nocontentLenghHeaders));
|
||||
register("HEAD", new DoHead(store, dftIndexFile, insteadOf404,
|
||||
_resLocks, mimeTyper, nocontentLenghHeaders));
|
||||
DoDelete doDelete = (DoDelete) register("DELETE", new DoDelete(store,
|
||||
_resLocks, READ_ONLY));
|
||||
DoCopy doCopy = (DoCopy) register("COPY", new DoCopy(store, _resLocks,
|
||||
doDelete, READ_ONLY));
|
||||
register("LOCK", new DoLock(store, _resLocks, READ_ONLY));
|
||||
register("UNLOCK", new DoUnlock(store, _resLocks, READ_ONLY));
|
||||
register("MOVE", new DoMove(store, _resLocks, doDelete, doCopy, READ_ONLY));
|
||||
register("MKCOL", new DoMkcol(store, _resLocks, READ_ONLY));
|
||||
register("OPTIONS", new DoOptions(store, _resLocks));
|
||||
register("PUT", new DoPut(store, _resLocks, READ_ONLY,
|
||||
lazyFolderCreationOnPut));
|
||||
register("PROPFIND", new DoPropfind(store, _resLocks, mimeTyper));
|
||||
register("PROPPATCH", new DoProppatch(store, _resLocks, READ_ONLY));
|
||||
register("*NO*IMPL*", new DoNotImplemented(READ_ONLY));
|
||||
}
|
||||
public static IMethodExecutor register(String methodName, IMethodExecutor method){
|
||||
webdavMap.put(methodName,method);
|
||||
return method;
|
||||
}
|
||||
static {
|
||||
store = new WebosStore();
|
||||
init(null,null,-1,false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将uri地址转化成中文地址
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public static String cnPath(String path){
|
||||
path = URLUtil.decode(path);
|
||||
String prefix = prefixPath();
|
||||
path = path.substring(prefix.length());
|
||||
if(path.startsWith("/")){
|
||||
path = path.substring(1);
|
||||
}
|
||||
if(path.endsWith("/")){
|
||||
path = path.substring(0,path.length()-1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
public static void servlet(Context ctx) throws Exception{
|
||||
/*if(ctx.uri().getPath().indexOf("/._")!=-1){
|
||||
return;
|
||||
}*/
|
||||
IMethodExecutor inface = webdavMap.get(ctx.method());
|
||||
if(inface == null){
|
||||
inface = webdavMap.get("*NO*IMPL*");
|
||||
}
|
||||
HttpServletRequest req = ctx2req(ctx);
|
||||
HttpServletResponse resp = ctx2resp(ctx);
|
||||
ITransaction transaction = null;
|
||||
boolean needRollback = false;
|
||||
try {
|
||||
Principal userPrincipal = req.getUserPrincipal();
|
||||
transaction =store.begin(userPrincipal, req, resp);
|
||||
needRollback = true;
|
||||
store.checkAuthentication(transaction);
|
||||
resp.setStatus(WebdavStatus.SC_OK);
|
||||
try {
|
||||
inface.execute(transaction, req, resp);
|
||||
store.commit(transaction);
|
||||
/** Clear not consumed data
|
||||
*
|
||||
* Clear input stream if available otherwise later access
|
||||
* include current input. These cases occure if the client
|
||||
* sends a request with body to an not existing resource.
|
||||
*/
|
||||
if (req.getContentLength() != 0 && req.getInputStream().available() > 0) {
|
||||
while (req.getInputStream().available() > 0) {
|
||||
req.getInputStream().read();
|
||||
}
|
||||
}
|
||||
needRollback = false;
|
||||
} catch (IOException e) {
|
||||
java.io.StringWriter sw = new java.io.StringWriter();
|
||||
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
store.rollback(transaction);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} catch (UnauthenticatedException e) {
|
||||
ctx.headerAdd("WWW-Authenticate","Basic realm=\"webos\"");
|
||||
resp.sendError(e.getCode());
|
||||
} catch (WebdavException e) {
|
||||
java.io.StringWriter sw = new java.io.StringWriter();
|
||||
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
throw new RuntimeException(e);
|
||||
} catch (Exception e) {
|
||||
java.io.StringWriter sw = new java.io.StringWriter();
|
||||
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
} finally {
|
||||
if (needRollback)
|
||||
store.rollback(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpServletResponse ctx2resp(Context ctx) {
|
||||
return new HttpServletResponse() {
|
||||
@Override
|
||||
public void sendError(int status) {
|
||||
ctx.status(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendError(int status, String statusText) {
|
||||
ctx.status(status);
|
||||
ctx.output(statusText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(int status) {
|
||||
ctx.status(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Writer getWriter() {
|
||||
try{
|
||||
return IoUtil.getUtf8Writer(ctx.outputStream());
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHeader(String key, String val) {
|
||||
ctx.headerAdd(key,val);
|
||||
}
|
||||
@Override
|
||||
public void setContentType(String s) {
|
||||
ctx.contentType(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
try{
|
||||
return ctx.outputStream();
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCharacterEncoding(String str) {
|
||||
ctx.charset(str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeRedirectURL(String url) {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendRedirect(String url) {
|
||||
ctx.redirect(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDateHeader(String s, long lastModified) {
|
||||
ctx.headerSet(s,new Date(lastModified).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLength(int length) {
|
||||
ctx.contentLength(length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(String s, String s1) {
|
||||
ctx.headerSet(s,s1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static HttpServletRequest ctx2req(Context ctx) {
|
||||
return new HttpServletRequest() {
|
||||
@Override
|
||||
public String getHeader(String key) {
|
||||
return ctx.header(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerName() {
|
||||
return ctx.uri().getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContextPath() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathInfo() {
|
||||
return ctx.uri().getRawPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServletPath() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String s) {
|
||||
return ctx.attr(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestURI() {
|
||||
return ctx.uri().getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return ctx.method();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
return ctx.contentLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
try{
|
||||
return ctx.bodyAsStream();
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getUserPrincipal() {
|
||||
String authorization = ctx.header("authorization");
|
||||
if(StrUtil.isBlank(authorization)){
|
||||
return null;
|
||||
}
|
||||
String[] sz = authorization.split(" ");
|
||||
if(sz.length != 2){
|
||||
return null;
|
||||
}
|
||||
return () -> sz[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
return ctx.getLocale();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static String prefixPath() {
|
||||
return "/webdav";
|
||||
}
|
||||
}
|
179
api/src/main/java/cn/tenfell/webos/common/webdav/WebosStore.java
Normal file
179
api/src/main/java/cn/tenfell/webos/common/webdav/WebosStore.java
Normal file
@ -0,0 +1,179 @@
|
||||
package cn.tenfell.webos.common.webdav;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.common.util.CacheUtil;
|
||||
import cn.tenfell.webos.common.util.LoginAuthUtil;
|
||||
import cn.tenfell.webos.common.webdav.sf.*;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.UnauthenticatedException;
|
||||
import cn.tenfell.webos.modules.action.UserAction;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
|
||||
public class WebosStore implements IWebdavStore {
|
||||
@Override
|
||||
public void destroy() {
|
||||
System.out.println("destroy");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITransaction begin(Principal principal, HttpServletRequest req, HttpServletResponse resp) {
|
||||
return new Transaction(principal,req,resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAuthentication(ITransaction transaction) {
|
||||
if(transaction.getPrincipal() == null){
|
||||
throw new UnauthenticatedException(401);
|
||||
}
|
||||
String userPwd = transaction.getPrincipal().getName();
|
||||
if(StrUtil.isBlank(userPwd)){
|
||||
throw new UnauthenticatedException(401);
|
||||
}
|
||||
SysUser user = CacheUtil.getValue("webdav:login:"+userPwd);
|
||||
boolean login = false;
|
||||
if(user == null){
|
||||
String[] userPwds = Base64.decodeStr(userPwd).split(":",3);
|
||||
SysUser param = new SysUser();
|
||||
if(userPwds.length == 2){
|
||||
//主账户
|
||||
param.setUsername(userPwds[0]);
|
||||
param.setPassword(userPwds[1]);
|
||||
param.setUserType(1);
|
||||
}else if(userPwds.length == 3){
|
||||
//子账户
|
||||
param.setParentUserNo(userPwds[0]);
|
||||
param.setUsername(userPwds[1]);
|
||||
param.setPassword(userPwds[2]);
|
||||
param.setUserType(2);
|
||||
}else{
|
||||
throw new UnauthenticatedException(401);
|
||||
}
|
||||
CommonBean.AccessToken token;
|
||||
try{
|
||||
token = UserAction.userLogin(param);
|
||||
}catch (Exception e){
|
||||
throw new UnauthenticatedException(401);
|
||||
}
|
||||
if(token == null){
|
||||
throw new UnauthenticatedException(401);
|
||||
}
|
||||
String accessToken = token.getWebosToken();
|
||||
user = LoginAuthUtil.getUserByToken(accessToken);
|
||||
if(user == null){
|
||||
throw new UnauthenticatedException(401);
|
||||
}
|
||||
CacheUtil.setValue("webdav:login:"+userPwd,user,60*60*24*7);
|
||||
CacheUtil.setValue("webdav:token:"+userPwd,token,60*60*4);
|
||||
login = true;
|
||||
}
|
||||
if(!login){
|
||||
CommonBean.AccessToken token = CacheUtil.getValue("webdav:token:"+userPwd);
|
||||
if(token != null && System.currentTimeMillis() >= token.getExpireTime() - 15*60*1000){
|
||||
token = LoginAuthUtil.refreshToken(token.getRefreshToken());
|
||||
if(token != null){
|
||||
CacheUtil.setValue("webdav:login:"+userPwd,user,60*60*24*7);
|
||||
CacheUtil.setValue("webdav:token:"+userPwd,token,60*60*4);
|
||||
}else{
|
||||
CacheUtil.delValue("webdav:token:"+userPwd);
|
||||
}
|
||||
}
|
||||
}
|
||||
LoginAuthUtil.USER_LOCAL.set(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit(ITransaction transaction) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(ITransaction transaction) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createFolder(ITransaction transaction, String folderUri) {
|
||||
String cnPath = WebdavUtil.cnPath(folderUri);
|
||||
WebosWebDavUtil.createFolder(cnPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createResource(ITransaction transaction, String resourceUri) {
|
||||
System.out.println("createResource:"+resourceUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceContent(ITransaction transaction, String resourceUri) {
|
||||
String cnPath = WebdavUtil.cnPath(resourceUri);
|
||||
return WebosWebDavUtil.getIn(cnPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long setResourceContent(ITransaction transaction, String resourceUri, InputStream content, String contentType, String characterEncoding) {
|
||||
String cnPath = WebdavUtil.cnPath(resourceUri);
|
||||
return WebosWebDavUtil.putFile(cnPath,content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getChildrenNames(ITransaction transaction, String folderUri) {
|
||||
String cnPath = WebdavUtil.cnPath(folderUri);
|
||||
List<CommonBean.PathInfo> list = WebosWebDavUtil.fileList(cnPath);
|
||||
if(CollUtil.isEmpty(list)){
|
||||
return new String[0];
|
||||
}
|
||||
return ArrayUtil.toArray(CollUtil.getFieldValues(list,"name",String.class),String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getResourceLength(ITransaction transaction, String path) {
|
||||
String cnPath = WebdavUtil.cnPath(path);
|
||||
return WebosWebDavUtil.fileLength(cnPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeObject(ITransaction transaction, String uri) {
|
||||
String cnPath = WebdavUtil.cnPath(uri);
|
||||
WebosWebDavUtil.del(cnPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveObject(ITransaction transaction, String destinationPath, String sourcePath) {
|
||||
Console.log("moveObject:{};{}",destinationPath,sourcePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredObject getStoredObject(ITransaction transaction, String uri) {
|
||||
String cnPath = WebdavUtil.cnPath(uri);
|
||||
CommonBean.PathInfo info = WebosWebDavUtil.fileInfo(cnPath);
|
||||
if(info != null){
|
||||
StoredObject stored = new StoredObject();
|
||||
stored.setFolder(info.getType() == 2);
|
||||
if(StrUtil.isNotBlank(info.getUpdatedAt())){
|
||||
stored.setLastModified(DateUtil.parseDateTime(info.getUpdatedAt()).toJdkDate());
|
||||
}
|
||||
if(StrUtil.isNotBlank(info.getCreatedAt())){
|
||||
stored.setCreationDate(DateUtil.parseDateTime(info.getCreatedAt()).toJdkDate());
|
||||
}
|
||||
stored.setResourceLength(info.getSize());
|
||||
if(stored.isResource()){
|
||||
stored.setMimeType("application/octet-stream");
|
||||
if(stored.getResourceLength() == 0){
|
||||
stored.setNullResource(true);
|
||||
}
|
||||
}else{
|
||||
stored.setMimeType("application/octet-stream");
|
||||
}
|
||||
return stored;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,229 @@
|
||||
package cn.tenfell.webos.common.webdav;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.tenfell.webos.WebOsApp;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemInface;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.common.util.CacheUtil;
|
||||
import cn.tenfell.webos.common.util.CommonUtil;
|
||||
import cn.tenfell.webos.common.util.LoginAuthUtil;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.AccessDeniedException;
|
||||
import cn.tenfell.webos.modules.action.FileSystemAction;
|
||||
import cn.tenfell.webos.modules.action.IoDriveAction;
|
||||
import cn.tenfell.webos.modules.action.IoUserDriveAction;
|
||||
import cn.tenfell.webos.modules.entity.IoDrive;
|
||||
import cn.tenfell.webos.modules.entity.IoUserDrive;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WebosWebDavUtil {
|
||||
private static CommonBean.PathInfo systemInfo(String path){
|
||||
CommonBean.PathInfo info = new CommonBean.PathInfo();
|
||||
info.setType(2);
|
||||
info.setName(path);
|
||||
info.setCreatedAt(DateUtil.now());
|
||||
info.setUpdatedAt(info.getCreatedAt());
|
||||
return info;
|
||||
}
|
||||
public static List<CommonBean.PathInfo> fileList(String cnParentPath){
|
||||
if(StrUtil.equals(cnParentPath,"")){
|
||||
List<CommonBean.PathInfo> infos = CollUtil.newArrayList(systemInfo("此电脑"));
|
||||
if(LoginAuthUtil.isMain()){
|
||||
infos.add(systemInfo("磁盘管理"));
|
||||
}
|
||||
return infos;
|
||||
}else if(StrUtil.equals(cnParentPath,"此电脑")){
|
||||
try{
|
||||
CommonBean.PageRes<IoUserDrive> iudPage = IoUserDriveAction.userDriveList(Dict.create().set("current",1).set("pageSize",9999));
|
||||
if(iudPage == null || CollUtil.isEmpty(iudPage.getData())){
|
||||
return null;
|
||||
}
|
||||
List<CommonBean.PathInfo> list = new ArrayList<>();
|
||||
for (IoUserDrive one:iudPage.getData()){
|
||||
CommonBean.PathInfo info = new CommonBean.PathInfo();
|
||||
info.setType(2);
|
||||
info.setName(one.getName());
|
||||
info.setCreatedAt(LocalDateTimeUtil.format(one.getCreatedTime(),"yyyy-MM-dd HH:mm:ss"));
|
||||
info.setUpdatedAt(LocalDateTimeUtil.format(one.getUpdatedTime(),"yyyy-MM-dd HH:mm:ss"));
|
||||
info.setPath("{uio:"+one.getNo()+"}");
|
||||
setCnPathCache(cnParentPath+"/"+info.getName(),info.getPath());
|
||||
list.add(info);
|
||||
}
|
||||
return list;
|
||||
}catch (Exception e){
|
||||
|
||||
}
|
||||
}else if(StrUtil.equals(cnParentPath,"磁盘管理")){
|
||||
try{
|
||||
CommonBean.PageRes<IoDrive> idPage = IoDriveAction.mainUserDrive(Dict.create().set("current",1).set("pageSize",9999));
|
||||
if(idPage == null || CollUtil.isEmpty(idPage.getData())){
|
||||
return null;
|
||||
}
|
||||
List<CommonBean.PathInfo> list = new ArrayList<>();
|
||||
for (IoDrive one:idPage.getData()){
|
||||
CommonBean.PathInfo info = new CommonBean.PathInfo();
|
||||
info.setType(2);
|
||||
info.setName(one.getName());
|
||||
info.setCreatedAt(LocalDateTimeUtil.format(one.getCreatedTime(),"yyyy-MM-dd HH:mm:ss"));
|
||||
info.setUpdatedAt(LocalDateTimeUtil.format(one.getUpdatedTime(),"yyyy-MM-dd HH:mm:ss"));
|
||||
info.setPath("{io:"+one.getNo()+"}");
|
||||
setCnPathCache(cnParentPath+"/"+info.getName(),info.getPath());
|
||||
list.add(info);
|
||||
}
|
||||
return list;
|
||||
}catch (Exception e){
|
||||
|
||||
}
|
||||
}else{
|
||||
String webosPath = cnPathToWebosPath(cnParentPath);
|
||||
if(StrUtil.isBlank(webosPath)){
|
||||
return null;
|
||||
}
|
||||
try{
|
||||
Integer type = 0;
|
||||
String next = "";
|
||||
List<CommonBean.PathInfo> fileList = new ArrayList<>();
|
||||
while(true){
|
||||
Dict param = Dict.create()
|
||||
.set("parentPath",webosPath)
|
||||
.set("type",type)
|
||||
.set("next",next);
|
||||
CommonBean.Page<CommonBean.PathInfo> data = FileSystemAction.fileListPage(param);
|
||||
if(data == null || CollUtil.isEmpty(data.getList())){
|
||||
break;
|
||||
}
|
||||
if(CollUtil.isNotEmpty(data.getList())){
|
||||
fileList.addAll(data.getList());
|
||||
}
|
||||
if(data.getType() == 0){
|
||||
break;
|
||||
}
|
||||
if(StrUtil.isBlank(data.getNext())){
|
||||
break;
|
||||
}
|
||||
type = data.getType();
|
||||
next = data.getNext();
|
||||
}
|
||||
for(CommonBean.PathInfo info:fileList){
|
||||
setCnPathCache(cnParentPath+"/"+info.getName(),info.getPath());
|
||||
}
|
||||
return fileList;
|
||||
}catch (Exception e){
|
||||
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static CommonBean.PathInfo fileInfo(String cnPath){
|
||||
if(StrUtil.equalsAny(cnPath,"","此电脑","磁盘管理")){
|
||||
if(StrUtil.equals(cnPath,"")){
|
||||
return systemInfo("根目录");
|
||||
}
|
||||
return systemInfo(cnPath);
|
||||
}
|
||||
String webosPath = cnPathToWebosPath(cnPath);
|
||||
if(StrUtil.isNotBlank(webosPath)){
|
||||
try{
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(webosPath, null,null);
|
||||
CommonBean.PathInfo info = FileSystemUtil.ACTION.fileInfo(plainPath);
|
||||
if(info != null){
|
||||
String[] sz = cnPath.split("/");
|
||||
info.setName(sz[sz.length-1]);
|
||||
}
|
||||
return info;
|
||||
}catch (Exception e){
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static String cnPathToWebosPath(String cnPath){
|
||||
return CacheUtil.getValue("webdav:path:"+ LoginAuthUtil.getUser().getId()+":"+ SecureUtil.md5(cnPath));
|
||||
}
|
||||
private static void setCnPathCache(String cnPath,String webosPath){
|
||||
CacheUtil.setValue("webdav:path:"+ LoginAuthUtil.getUser().getId()+":"+ SecureUtil.md5(cnPath),webosPath,60*60*24*2);
|
||||
}
|
||||
|
||||
public static InputStream getIn(String cnPath) {
|
||||
return FileSystemUtil.ACTION.getInputStream(cnPath2PlainPath(cnPath),0,0);
|
||||
}
|
||||
|
||||
public static long putFile(String cnPath, InputStream content) {
|
||||
ParentSplit p = cnPath2ParentSplit(cnPath);
|
||||
String fId = FileSystemUtil.ACTION.uploadByServer(p.getParentPath(),p.getName(),content,null,null);
|
||||
return StrUtil.isNotBlank(fId)?1:0;
|
||||
}
|
||||
|
||||
public static void del(String cnPath) {
|
||||
try{
|
||||
String path = cnPathToWebosPath(cnPath);
|
||||
FileSystemInface.PlainPath filePath = FileSystemUtil.cipherPath2PlainPathByLogin(path, null,null);
|
||||
CommonBean.PathInfo info = FileSystemUtil.ACTION.fileInfo(filePath);
|
||||
if(info == null){
|
||||
return;
|
||||
}
|
||||
String[] sz = path.split("/");
|
||||
String current = sz[sz.length-1];
|
||||
sz = ArrayUtil.remove(sz, sz.length - 1);
|
||||
String parentPath = ArrayUtil.join(sz, "/");
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(parentPath, null,null);
|
||||
String res = FileSystemUtil.ACTION.remove(plainPath,CollUtil.newArrayList(current),CollUtil.newArrayList(info.getType()));
|
||||
Assert.isTrue(StrUtil.equals("1",res));
|
||||
}catch (Exception e){
|
||||
throw new AccessDeniedException("权限不足");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static long fileLength(String cnPath) {
|
||||
CommonBean.PathInfo info = FileSystemUtil.ACTION.fileInfo(cnPath2PlainPath(cnPath));
|
||||
return info!=null?info.getSize():0;
|
||||
}
|
||||
|
||||
public static void createFolder(String cnPath) {
|
||||
ParentSplit p = cnPath2ParentSplit(cnPath);
|
||||
FileSystemUtil.ACTION.createDir(p.getParentPath(),p.getName());
|
||||
}
|
||||
private static ParentSplit cnPath2ParentSplit(String cnPath){
|
||||
try{
|
||||
String[] sz = cnPath.split("/");
|
||||
String name = sz[sz.length-1];
|
||||
sz = ArrayUtil.remove(sz, sz.length - 1);
|
||||
String cnParentPath = ArrayUtil.join(sz, "/");
|
||||
String path = cnPathToWebosPath(cnParentPath);
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, null,null);
|
||||
ParentSplit p = new ParentSplit();
|
||||
p.setParentPath(plainPath);
|
||||
p.setName(name);
|
||||
return p;
|
||||
}catch (Exception e){
|
||||
throw new AccessDeniedException("权限不足");
|
||||
}
|
||||
}
|
||||
private static FileSystemInface.PlainPath cnPath2PlainPath(String cnPath){
|
||||
try{
|
||||
String webosPath = cnPathToWebosPath(cnPath);
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(webosPath, null,null);
|
||||
return plainPath;
|
||||
}catch (Exception e){
|
||||
throw new AccessDeniedException("权限不足");
|
||||
}
|
||||
}
|
||||
@Data
|
||||
private static class ParentSplit{
|
||||
private String name;
|
||||
private FileSystemInface.PlainPath parentPath;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package cn.tenfell.webos.common.webdav.sf;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.security.Principal;
|
||||
import java.util.Locale;
|
||||
|
||||
public interface HttpServletRequest {
|
||||
String getHeader(String key);
|
||||
|
||||
String getServerName();
|
||||
|
||||
String getContextPath();
|
||||
|
||||
String getPathInfo();
|
||||
|
||||
String getServletPath();
|
||||
|
||||
String getAttribute(String s);
|
||||
|
||||
String getRequestURI();
|
||||
|
||||
String getMethod();
|
||||
|
||||
long getContentLength();
|
||||
|
||||
InputStream getInputStream();
|
||||
|
||||
Principal getUserPrincipal();
|
||||
|
||||
Locale getLocale();
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package cn.tenfell.webos.common.webdav.sf;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
public interface HttpServletResponse {
|
||||
void sendError(int status);
|
||||
|
||||
void sendError(int code, String statusText);
|
||||
|
||||
void setStatus(int scMultiStatus);
|
||||
|
||||
Writer getWriter();
|
||||
|
||||
void addHeader(String allow, String methodsAllowed);
|
||||
|
||||
void setContentType(String s);
|
||||
|
||||
OutputStream getOutputStream();
|
||||
|
||||
void setCharacterEncoding(String utf8);
|
||||
|
||||
String encodeRedirectURL(String s);
|
||||
|
||||
void sendRedirect(String encodeRedirectURL);
|
||||
|
||||
void setDateHeader(String s, long lastModified);
|
||||
|
||||
void setContentLength(int resourceLength);
|
||||
|
||||
void setHeader(String s, String s1);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IMethodExecutor {
|
||||
|
||||
void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException;
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cn.tenfell.webos.common.webdav.sf;
|
||||
|
||||
public interface IMimeTyper {
|
||||
|
||||
/**
|
||||
* Detect the mime type of this object
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
String getMimeType(ITransaction transaction, String path);
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package cn.tenfell.webos.common.webdav.sf;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
public interface ITransaction {
|
||||
|
||||
Principal getPrincipal();
|
||||
|
||||
HttpServletRequest getRequest();
|
||||
|
||||
HttpServletResponse getResponse();
|
||||
}
|
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* $Header: /Users/ak/temp/cvs2svn/webdav-servlet/src/main/java/net/sf/webdav/IWebdavStore.java,v 1.1 2008-08-05 07:38:42 bauhardt Exp $
|
||||
* $Revision: 1.1 $
|
||||
* $Date: 2008-08-05 07:38:42 $
|
||||
*
|
||||
* ====================================================================
|
||||
*
|
||||
* Copyright 2004 The Apache Software Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package cn.tenfell.webos.common.webdav.sf;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.WebdavException;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* Interface for simple implementation of any store for the WebdavServlet
|
||||
* <p>
|
||||
* based on the BasicWebdavStore from Oliver Zeigermann, that was part of the
|
||||
* Webdav Construcktion Kit from slide
|
||||
*
|
||||
*/
|
||||
public interface IWebdavStore {
|
||||
|
||||
/**
|
||||
* Life cycle method, called by WebdavServlet's destroy() method. Should be used to clean up resources.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Indicates that a new request or transaction with this store involved has
|
||||
* been started. The request will be terminated by either {@link #commit()}
|
||||
* or {@link #rollback()}. If only non-read methods have been called, the
|
||||
* request will be terminated by a {@link #commit()}. This method will be
|
||||
* called by (@link WebdavStoreAdapter} at the beginning of each request.
|
||||
*
|
||||
*
|
||||
* @param principal
|
||||
* the principal that started this request or <code>null</code> if
|
||||
* there is non available
|
||||
*
|
||||
* @throws WebdavException
|
||||
*/
|
||||
ITransaction begin(Principal principal, HttpServletRequest req, HttpServletResponse resp);
|
||||
|
||||
/**
|
||||
* Checks if authentication information passed in is valid. If not throws an
|
||||
* exception.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
*/
|
||||
void checkAuthentication(ITransaction transaction);
|
||||
|
||||
/**
|
||||
* Indicates that all changes done inside this request shall be made
|
||||
* permanent and any transactions, connections and other temporary resources
|
||||
* shall be terminated.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
*
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
void commit(ITransaction transaction);
|
||||
|
||||
/**
|
||||
* Indicates that all changes done inside this request shall be undone and
|
||||
* any transactions, connections and other temporary resources shall be
|
||||
* terminated.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
*
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
void rollback(ITransaction transaction);
|
||||
|
||||
/**
|
||||
* Creates a folder at the position specified by <code>folderUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param folderUri
|
||||
* URI of the folder
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
void createFolder(ITransaction transaction, String folderUri);
|
||||
|
||||
/**
|
||||
* Creates a content resource at the position specified by
|
||||
* <code>resourceUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param resourceUri
|
||||
* URI of the content resource
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
void createResource(ITransaction transaction, String resourceUri);
|
||||
|
||||
/**
|
||||
* Gets the content of the resource specified by <code>resourceUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param resourceUri
|
||||
* URI of the content resource
|
||||
* @return input stream you can read the content of the resource from
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
InputStream getResourceContent(ITransaction transaction, String resourceUri);
|
||||
|
||||
/**
|
||||
* Sets / stores the content of the resource specified by
|
||||
* <code>resourceUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param resourceUri
|
||||
* URI of the resource where the content will be stored
|
||||
* @param content
|
||||
* input stream from which the content will be read from
|
||||
* @param contentType
|
||||
* content type of the resource or <code>null</code> if unknown
|
||||
* @param characterEncoding
|
||||
* character encoding of the resource or <code>null</code> if unknown
|
||||
* or not applicable
|
||||
* @return lenght of resource
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
long setResourceContent(ITransaction transaction, String resourceUri,
|
||||
InputStream content, String contentType, String characterEncoding);
|
||||
|
||||
/**
|
||||
* Gets the names of the children of the folder specified by
|
||||
* <code>folderUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param folderUri
|
||||
* URI of the folder
|
||||
* @return a (possibly empty) list of children, or <code>null</code> if the
|
||||
* uri points to a file
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
String[] getChildrenNames(ITransaction transaction, String folderUri);
|
||||
|
||||
/**
|
||||
* Gets the length of the content resource specified by
|
||||
* <code>resourceUri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param resourceUri
|
||||
* URI of the content resource
|
||||
* @return length of the resource in bytes, <code>-1</code> declares this
|
||||
* value as invalid and asks the adapter to try to set it from the
|
||||
* properties if possible
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
long getResourceLength(ITransaction transaction, String path);
|
||||
|
||||
/**
|
||||
* Removes the object specified by <code>uri</code>.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param uri
|
||||
* URI of the object, i.e. content resource or folder
|
||||
* @throws WebdavException
|
||||
* if something goes wrong on the store level
|
||||
*/
|
||||
void removeObject(ITransaction transaction, String uri);
|
||||
|
||||
boolean moveObject(ITransaction transaction, String destinationPath, String sourcePath);
|
||||
|
||||
/**
|
||||
* Gets the storedObject specified by <code>uri</code>
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param uri
|
||||
* URI
|
||||
* @return StoredObject
|
||||
*/
|
||||
StoredObject getStoredObject(ITransaction transaction, String uri);
|
||||
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
*
|
||||
* Copyright 2004 The Apache Software Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package cn.tenfell.webos.common.webdav.sf;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class StoredObject {
|
||||
|
||||
private boolean isFolder;
|
||||
private Date lastModified;
|
||||
private Date creationDate;
|
||||
private long contentLength;
|
||||
private String mimeType;
|
||||
|
||||
private boolean isNullRessource;
|
||||
|
||||
/**
|
||||
* Determines whether the StoredObject is a folder or a resource
|
||||
*
|
||||
* @return true if the StoredObject is a collection
|
||||
*/
|
||||
public boolean isFolder() {
|
||||
return (isFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the StoredObject is a folder or a resource
|
||||
*
|
||||
* @return true if the StoredObject is a resource
|
||||
*/
|
||||
public boolean isResource() {
|
||||
return (!isFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new StoredObject as a collection or resource
|
||||
*
|
||||
* @param f
|
||||
* true - collection ; false - resource
|
||||
*/
|
||||
public void setFolder(boolean f) {
|
||||
this.isFolder = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date of the last modification
|
||||
*
|
||||
* @return last modification Date
|
||||
*/
|
||||
public Date getLastModified() {
|
||||
return (lastModified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date of the last modification
|
||||
*
|
||||
* @param d
|
||||
* date of the last modification
|
||||
*/
|
||||
public void setLastModified(Date d) {
|
||||
this.lastModified = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date of the creation
|
||||
*
|
||||
* @return creation Date
|
||||
*/
|
||||
public Date getCreationDate() {
|
||||
return (creationDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date of the creation
|
||||
*
|
||||
* @param d
|
||||
* date of the creation
|
||||
*/
|
||||
public void setCreationDate(Date c) {
|
||||
this.creationDate = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length of the resource content
|
||||
*
|
||||
* @return length of the resource content
|
||||
*/
|
||||
public long getResourceLength() {
|
||||
return (contentLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of the resource content
|
||||
*
|
||||
* @param l
|
||||
* the length of the resource content
|
||||
*/
|
||||
public void setResourceLength(long l) {
|
||||
this.contentLength = l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of the resource
|
||||
*
|
||||
* @return true if the resource is in lock-null state
|
||||
*/
|
||||
public boolean isNullResource() {
|
||||
return isNullRessource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a StoredObject as a lock-null resource
|
||||
*
|
||||
* @param f
|
||||
* true to set the resource as lock-null resource
|
||||
*/
|
||||
public void setNullResource(boolean f) {
|
||||
this.isNullRessource = f;
|
||||
this.isFolder = false;
|
||||
this.creationDate = null;
|
||||
this.lastModified = null;
|
||||
// this.content = null;
|
||||
this.contentLength = 0;
|
||||
this.mimeType= null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the myme type from the store object.
|
||||
* Can also return NULL if the store does not handle
|
||||
* mime type stuff.
|
||||
* In that case the mime type is determined by the servletcontext
|
||||
*
|
||||
* @return the mimeType
|
||||
*/
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mime type of this object
|
||||
*
|
||||
* @param mimeType the mimeType to set
|
||||
*/
|
||||
public void setMimeType(String mimeType) {
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cn.tenfell.webos.common.webdav.sf;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
public class Transaction implements ITransaction {
|
||||
private final Principal principal;
|
||||
private final HttpServletRequest request;
|
||||
private final HttpServletResponse response;
|
||||
|
||||
public Transaction(Principal principal, HttpServletRequest request, HttpServletResponse response) {
|
||||
this.principal = principal;
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getPrincipal() {
|
||||
return principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpServletRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpServletResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
}
|
@ -0,0 +1,271 @@
|
||||
package cn.tenfell.webos.common.webdav.sf;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* Wraps the HttpServletResponse class to abstract the specific protocol used.
|
||||
* To support other protocols we would only need to modify this class and the
|
||||
* WebDavRetCode classes.
|
||||
*
|
||||
* @author Marc Eaddy
|
||||
* @version 1.0, 16 Nov 1997
|
||||
*/
|
||||
public class WebdavStatus {
|
||||
|
||||
// ----------------------------------------------------- Instance Variables
|
||||
|
||||
/**
|
||||
* This Hashtable contains the mapping of HTTP and WebDAV status codes to
|
||||
* descriptive text. This is a static variable.
|
||||
*/
|
||||
private static Hashtable<Integer, String> _mapStatusCodes = new Hashtable<Integer, String>();
|
||||
|
||||
// ------------------------------------------------------ HTTP Status Codes
|
||||
|
||||
/**
|
||||
* Status code (200) indicating the request succeeded normally.
|
||||
*/
|
||||
public static final int SC_OK = 200;
|
||||
|
||||
/**
|
||||
* Status code (201) indicating the request succeeded and created a new
|
||||
* resource on the server.
|
||||
*/
|
||||
public static final int SC_CREATED = 201;
|
||||
|
||||
/**
|
||||
* Status code (202) indicating that a request was accepted for processing,
|
||||
* but was not completed.
|
||||
*/
|
||||
public static final int SC_ACCEPTED = 202;
|
||||
|
||||
/**
|
||||
* Status code (204) indicating that the request succeeded but that there
|
||||
* was no new information to return.
|
||||
*/
|
||||
public static final int SC_NO_CONTENT = 204;
|
||||
|
||||
/**
|
||||
* Status code (301) indicating that the resource has permanently moved to a
|
||||
* new location, and that future references should use a new URI with their
|
||||
* requests.
|
||||
*/
|
||||
public static final int SC_MOVED_PERMANENTLY = 301;
|
||||
|
||||
/**
|
||||
* Status code (302) indicating that the resource has temporarily moved to
|
||||
* another location, but that future references should still use the
|
||||
* original URI to access the resource.
|
||||
*/
|
||||
public static final int SC_MOVED_TEMPORARILY = 302;
|
||||
|
||||
/**
|
||||
* Status code (304) indicating that a conditional GET operation found that
|
||||
* the resource was available and not modified.
|
||||
*/
|
||||
public static final int SC_NOT_MODIFIED = 304;
|
||||
|
||||
/**
|
||||
* Status code (400) indicating the request sent by the client was
|
||||
* syntactically incorrect.
|
||||
*/
|
||||
public static final int SC_BAD_REQUEST = 400;
|
||||
|
||||
/**
|
||||
* Status code (401) indicating that the request requires HTTP
|
||||
* authentication.
|
||||
*/
|
||||
public static final int SC_UNAUTHORIZED = 401;
|
||||
|
||||
/**
|
||||
* Status code (403) indicating the server understood the request but
|
||||
* refused to fulfill it.
|
||||
*/
|
||||
public static final int SC_FORBIDDEN = 403;
|
||||
|
||||
/**
|
||||
* Status code (404) indicating that the requested resource is not
|
||||
* available.
|
||||
*/
|
||||
public static final int SC_NOT_FOUND = 404;
|
||||
|
||||
/**
|
||||
* Status code (500) indicating an error inside the HTTP service which
|
||||
* prevented it from fulfilling the request.
|
||||
*/
|
||||
public static final int SC_INTERNAL_SERVER_ERROR = 500;
|
||||
|
||||
/**
|
||||
* Status code (501) indicating the HTTP service does not support the
|
||||
* functionality needed to fulfill the request.
|
||||
*/
|
||||
public static final int SC_NOT_IMPLEMENTED = 501;
|
||||
|
||||
/**
|
||||
* Status code (502) indicating that the HTTP server received an invalid
|
||||
* response from a server it consulted when acting as a proxy or gateway.
|
||||
*/
|
||||
public static final int SC_BAD_GATEWAY = 502;
|
||||
|
||||
/**
|
||||
* Status code (503) indicating that the HTTP service is temporarily
|
||||
* overloaded, and unable to handle the request.
|
||||
*/
|
||||
public static final int SC_SERVICE_UNAVAILABLE = 503;
|
||||
|
||||
/**
|
||||
* Status code (100) indicating the client may continue with its request.
|
||||
* This interim response is used to inform the client that the initial part
|
||||
* of the request has been received and has not yet been rejected by the
|
||||
* server.
|
||||
*/
|
||||
public static final int SC_CONTINUE = 100;
|
||||
|
||||
/**
|
||||
* Status code (405) indicating the method specified is not allowed for the
|
||||
* resource.
|
||||
*/
|
||||
public static final int SC_METHOD_NOT_ALLOWED = 405;
|
||||
|
||||
/**
|
||||
* Status code (409) indicating that the request could not be completed due
|
||||
* to a conflict with the current state of the resource.
|
||||
*/
|
||||
public static final int SC_CONFLICT = 409;
|
||||
|
||||
/**
|
||||
* Status code (412) indicating the precondition given in one or more of the
|
||||
* request-header fields evaluated to false when it was tested on the
|
||||
* server.
|
||||
*/
|
||||
public static final int SC_PRECONDITION_FAILED = 412;
|
||||
|
||||
/**
|
||||
* Status code (413) indicating the server is refusing to process a request
|
||||
* because the request entity is larger than the server is willing or able
|
||||
* to process.
|
||||
*/
|
||||
public static final int SC_REQUEST_TOO_LONG = 413;
|
||||
|
||||
/**
|
||||
* Status code (415) indicating the server is refusing to service the
|
||||
* request because the entity of the request is in a format not supported by
|
||||
* the requested resource for the requested method.
|
||||
*/
|
||||
public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
|
||||
|
||||
// -------------------------------------------- Extended WebDav status code
|
||||
|
||||
/**
|
||||
* Status code (207) indicating that the response requires providing status
|
||||
* for multiple independent operations.
|
||||
*/
|
||||
public static final int SC_MULTI_STATUS = 207;
|
||||
|
||||
// This one colides with HTTP 1.1
|
||||
// "207 Parital Update OK"
|
||||
|
||||
/**
|
||||
* Status code (418) indicating the entity body submitted with the PATCH
|
||||
* method was not understood by the resource.
|
||||
*/
|
||||
public static final int SC_UNPROCESSABLE_ENTITY = 418;
|
||||
|
||||
// This one colides with HTTP 1.1
|
||||
// "418 Reauthentication Required"
|
||||
|
||||
/**
|
||||
* Status code (419) indicating that the resource does not have sufficient
|
||||
* space to record the state of the resource after the execution of this
|
||||
* method.
|
||||
*/
|
||||
public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
|
||||
|
||||
// This one colides with HTTP 1.1
|
||||
// "419 Proxy Reauthentication Required"
|
||||
|
||||
/**
|
||||
* Status code (420) indicating the method was not executed on a particular
|
||||
* resource within its scope because some part of the method's execution
|
||||
* failed causing the entire method to be aborted.
|
||||
*/
|
||||
public static final int SC_METHOD_FAILURE = 420;
|
||||
|
||||
/**
|
||||
* Status code (423) indicating the destination resource of a method is
|
||||
* locked, and either the request did not contain a valid Lock-Info header,
|
||||
* or the Lock-Info header identifies a lock held by another principal.
|
||||
*/
|
||||
public static final int SC_LOCKED = 423;
|
||||
|
||||
// ------------------------------------------------------------ Initializer
|
||||
|
||||
static {
|
||||
// HTTP 1.0 Status Code
|
||||
addStatusCodeMap(SC_OK, "OK");
|
||||
addStatusCodeMap(SC_CREATED, "Created");
|
||||
addStatusCodeMap(SC_ACCEPTED, "Accepted");
|
||||
addStatusCodeMap(SC_NO_CONTENT, "No Content");
|
||||
addStatusCodeMap(SC_MOVED_PERMANENTLY, "Moved Permanently");
|
||||
addStatusCodeMap(SC_MOVED_TEMPORARILY, "Moved Temporarily");
|
||||
addStatusCodeMap(SC_NOT_MODIFIED, "Not Modified");
|
||||
addStatusCodeMap(SC_BAD_REQUEST, "Bad Request");
|
||||
addStatusCodeMap(SC_UNAUTHORIZED, "Unauthorized");
|
||||
addStatusCodeMap(SC_FORBIDDEN, "Forbidden");
|
||||
addStatusCodeMap(SC_NOT_FOUND, "Not Found");
|
||||
addStatusCodeMap(SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
|
||||
addStatusCodeMap(SC_NOT_IMPLEMENTED, "Not Implemented");
|
||||
addStatusCodeMap(SC_BAD_GATEWAY, "Bad Gateway");
|
||||
addStatusCodeMap(SC_SERVICE_UNAVAILABLE, "Service Unavailable");
|
||||
addStatusCodeMap(SC_CONTINUE, "Continue");
|
||||
addStatusCodeMap(SC_METHOD_NOT_ALLOWED, "Method Not Allowed");
|
||||
addStatusCodeMap(SC_CONFLICT, "Conflict");
|
||||
addStatusCodeMap(SC_PRECONDITION_FAILED, "Precondition Failed");
|
||||
addStatusCodeMap(SC_REQUEST_TOO_LONG, "Request Too Long");
|
||||
addStatusCodeMap(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
|
||||
// WebDav Status Codes
|
||||
addStatusCodeMap(SC_MULTI_STATUS, "Multi-Status");
|
||||
addStatusCodeMap(SC_UNPROCESSABLE_ENTITY, "Unprocessable Entity");
|
||||
addStatusCodeMap(SC_INSUFFICIENT_SPACE_ON_RESOURCE,
|
||||
"Insufficient Space On Resource");
|
||||
addStatusCodeMap(SC_METHOD_FAILURE, "Method Failure");
|
||||
addStatusCodeMap(SC_LOCKED, "Locked");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------- Public Methods
|
||||
|
||||
/**
|
||||
* Returns the HTTP status text for the HTTP or WebDav status code specified
|
||||
* by looking it up in the static mapping. This is a static function.
|
||||
*
|
||||
* @param nHttpStatusCode
|
||||
* [IN] HTTP or WebDAV status code
|
||||
* @return A string with a short descriptive phrase for the HTTP status code
|
||||
* (e.g., "OK").
|
||||
*/
|
||||
public static String getStatusText(int nHttpStatusCode) {
|
||||
Integer intKey = new Integer(nHttpStatusCode);
|
||||
|
||||
if (!_mapStatusCodes.containsKey(intKey)) {
|
||||
return "";
|
||||
} else {
|
||||
return _mapStatusCodes.get(intKey);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- Private Methods
|
||||
|
||||
/**
|
||||
* Adds a new status code -> status text mapping. This is a static method
|
||||
* because the mapping is a static variable.
|
||||
*
|
||||
* @param nKey
|
||||
* [IN] HTTP or WebDAV status code
|
||||
* @param strVal
|
||||
* [IN] HTTP status text
|
||||
*/
|
||||
private static void addStatusCodeMap(int nKey, String strVal) {
|
||||
_mapStatusCodes.put(new Integer(nKey), strVal);
|
||||
}
|
||||
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.tenfell.webos.common.webdav.sf.exceptions;
|
||||
|
||||
public class AccessDeniedException extends WebdavException {
|
||||
|
||||
public AccessDeniedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public AccessDeniedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AccessDeniedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public AccessDeniedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package cn.tenfell.webos.common.webdav.sf.exceptions;
|
||||
|
||||
public class LockFailedException extends WebdavException {
|
||||
|
||||
public LockFailedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public LockFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public LockFailedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public LockFailedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.tenfell.webos.common.webdav.sf.exceptions;
|
||||
|
||||
public class ObjectAlreadyExistsException extends WebdavException {
|
||||
|
||||
public ObjectAlreadyExistsException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ObjectAlreadyExistsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ObjectAlreadyExistsException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ObjectAlreadyExistsException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.tenfell.webos.common.webdav.sf.exceptions;
|
||||
|
||||
public class ObjectNotFoundException extends WebdavException {
|
||||
|
||||
public ObjectNotFoundException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ObjectNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ObjectNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ObjectNotFoundException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.tenfell.webos.common.webdav.sf.exceptions;
|
||||
|
||||
public class UnauthenticatedException extends WebdavException {
|
||||
private final int code;
|
||||
|
||||
public UnauthenticatedException(int code) {
|
||||
super();
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.tenfell.webos.common.webdav.sf.exceptions;
|
||||
|
||||
public class WebdavException extends RuntimeException {
|
||||
|
||||
public WebdavException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public WebdavException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public WebdavException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public WebdavException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.tenfell.webos.common.webdav.sf.fromcatalina;
|
||||
|
||||
/**
|
||||
* Encode an MD5 digest into a String.
|
||||
* <p>
|
||||
* The 128 bit MD5 hash is converted into a 32 character long String. Each
|
||||
* character of the String is the hexadecimal representation of 4 bits of the
|
||||
* digest.
|
||||
*
|
||||
* @author Remy Maucherat
|
||||
* @version $Revision: 1.2 $ $Date: 2008-08-05 07:38:45 $
|
||||
*/
|
||||
|
||||
public final class MD5Encoder {
|
||||
|
||||
// ----------------------------------------------------- Instance Variables
|
||||
|
||||
private static final char[] HEXADECIMAL = { '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
|
||||
// --------------------------------------------------------- Public Methods
|
||||
|
||||
/**
|
||||
* Encodes the 128 bit (16 bytes) MD5 into a 32 character String.
|
||||
*
|
||||
* @param binaryData
|
||||
* Array containing the digest
|
||||
* @return Encoded MD5, or null if encoding failed
|
||||
*/
|
||||
public String encode(byte[] binaryData) {
|
||||
|
||||
if (binaryData.length != 16)
|
||||
return null;
|
||||
|
||||
char[] buffer = new char[32];
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int low = (int) (binaryData[i] & 0x0f);
|
||||
int high = (int) ((binaryData[i] & 0xf0) >> 4);
|
||||
buffer[i * 2] = HEXADECIMAL[high];
|
||||
buffer[i * 2 + 1] = HEXADECIMAL[low];
|
||||
}
|
||||
|
||||
return new String(buffer);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package cn.tenfell.webos.common.webdav.sf.fromcatalina;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
public class XMLHelper {
|
||||
|
||||
public static Node findSubElement(Node parent, String localName) {
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
Node child = parent.getFirstChild();
|
||||
while (child != null) {
|
||||
if ((child.getNodeType() == Node.ELEMENT_NODE)
|
||||
&& (child.getLocalName().equals(localName))) {
|
||||
return child;
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Vector<String> getPropertiesFromXML(Node propNode) {
|
||||
Vector<String> properties;
|
||||
properties = new Vector<String>();
|
||||
NodeList childList = propNode.getChildNodes();
|
||||
|
||||
for (int i = 0; i < childList.getLength(); i++) {
|
||||
Node currentNode = childList.item(i);
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String nodeName = currentNode.getLocalName();
|
||||
String namespace = currentNode.getNamespaceURI();
|
||||
// href is a live property which is handled differently
|
||||
properties.addElement(namespace + ":" + nodeName);
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.tenfell.webos.common.webdav.sf.fromcatalina;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* XMLWriter helper class.
|
||||
*
|
||||
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
|
||||
*/
|
||||
public class XMLWriter {
|
||||
|
||||
// -------------------------------------------------------------- Constants
|
||||
|
||||
/**
|
||||
* Opening tag.
|
||||
*/
|
||||
public static final int OPENING = 0;
|
||||
|
||||
/**
|
||||
* Closing tag.
|
||||
*/
|
||||
public static final int CLOSING = 1;
|
||||
|
||||
/**
|
||||
* Element with no content.
|
||||
*/
|
||||
public static final int NO_CONTENT = 2;
|
||||
|
||||
// ----------------------------------------------------- Instance Variables
|
||||
|
||||
/**
|
||||
* Buffer.
|
||||
*/
|
||||
protected StringBuffer _buffer = new StringBuffer();
|
||||
|
||||
/**
|
||||
* Writer.
|
||||
*/
|
||||
protected Writer _writer = null;
|
||||
|
||||
/**
|
||||
* Namespaces to be declared in the root element
|
||||
*/
|
||||
protected Map<String, String> _namespaces;
|
||||
|
||||
/**
|
||||
* Is true until the root element is written
|
||||
*/
|
||||
protected boolean _isRootElement = true;
|
||||
|
||||
// ----------------------------------------------------------- Constructors
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public XMLWriter(Map<String, String> namespaces) {
|
||||
_namespaces = namespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public XMLWriter(Writer writer, Map<String, String> namespaces) {
|
||||
_writer = writer;
|
||||
_namespaces = namespaces;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------- Public Methods
|
||||
|
||||
/**
|
||||
* Retrieve generated XML.
|
||||
*
|
||||
* @return String containing the generated XML
|
||||
*/
|
||||
public String toString() {
|
||||
return _buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write property to the XML.
|
||||
*
|
||||
* @param name
|
||||
* Property name
|
||||
* @param value
|
||||
* Property value
|
||||
*/
|
||||
public void writeProperty(String name, String value) {
|
||||
writeElement(name, OPENING);
|
||||
_buffer.append(value);
|
||||
writeElement(name, CLOSING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write property to the XML.
|
||||
*
|
||||
* @param name
|
||||
* Property name
|
||||
*/
|
||||
public void writeProperty(String name) {
|
||||
writeElement(name, NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an element.
|
||||
*
|
||||
* @param name
|
||||
* Element name
|
||||
* @param type
|
||||
* Element type
|
||||
*/
|
||||
public void writeElement(String name, int type) {
|
||||
StringBuffer nsdecl = new StringBuffer();
|
||||
|
||||
if (_isRootElement) {
|
||||
for (Iterator<String> iter = _namespaces.keySet().iterator(); iter
|
||||
.hasNext();) {
|
||||
String fullName = (String) iter.next();
|
||||
String abbrev = (String) _namespaces.get(fullName);
|
||||
nsdecl.append(" xmlns:").append(abbrev).append("=\"").append(
|
||||
fullName).append("\"");
|
||||
}
|
||||
_isRootElement = false;
|
||||
}
|
||||
|
||||
int pos = name.lastIndexOf(':');
|
||||
if (pos >= 0) {
|
||||
// lookup prefix for namespace
|
||||
String fullns = name.substring(0, pos);
|
||||
String prefix = (String) _namespaces.get(fullns);
|
||||
if (prefix == null) {
|
||||
// there is no prefix for this namespace
|
||||
name = name.substring(pos + 1);
|
||||
nsdecl.append(" xmlns=\"").append(fullns).append("\"");
|
||||
} else {
|
||||
// there is a prefix
|
||||
name = prefix + ":" + name.substring(pos + 1);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"All XML elements must have a namespace");
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case OPENING:
|
||||
_buffer.append("<" + name + nsdecl + ">");
|
||||
break;
|
||||
case CLOSING:
|
||||
_buffer.append("</" + name + ">\n");
|
||||
break;
|
||||
case NO_CONTENT:
|
||||
default:
|
||||
_buffer.append("<" + name + nsdecl + "/>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write text.
|
||||
*
|
||||
* @param text
|
||||
* Text to append
|
||||
*/
|
||||
public void writeText(String text) {
|
||||
_buffer.append(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data.
|
||||
*
|
||||
* @param data
|
||||
* Data to append
|
||||
*/
|
||||
public void writeData(String data) {
|
||||
_buffer.append("<![CDATA[" + data + "]]>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Write XML Header.
|
||||
*/
|
||||
public void writeXMLHeader() {
|
||||
_buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data and reinitializes buffer.
|
||||
*/
|
||||
public void sendData() throws IOException {
|
||||
if (_writer != null) {
|
||||
_writer.write(_buffer.toString());
|
||||
_writer.flush();
|
||||
_buffer = new StringBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
package cn.tenfell.webos.common.webdav.sf.locking;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.ITransaction;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
|
||||
public interface IResourceLocks {
|
||||
|
||||
/**
|
||||
* Tries to lock the resource at "path".
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* what resource to lock
|
||||
* @param owner
|
||||
* the owner of the lock
|
||||
* @param exclusive
|
||||
* if the lock should be exclusive (or shared)
|
||||
* @param depth
|
||||
* depth
|
||||
* @param timeout
|
||||
* Lock Duration in seconds.
|
||||
* @return true if the resource at path was successfully locked, false if an
|
||||
* existing lock prevented this
|
||||
* @throws LockFailedException
|
||||
*/
|
||||
boolean lock(ITransaction transaction, String path, String owner,
|
||||
boolean exclusive, int depth, int timeout, boolean temporary)
|
||||
throws LockFailedException;
|
||||
|
||||
/**
|
||||
* Unlocks all resources at "path" (and all subfolders if existing)<p/> that
|
||||
* have the same owner.
|
||||
*
|
||||
* @param transaction
|
||||
* @param id
|
||||
* id to the resource to unlock
|
||||
* @param owner
|
||||
* who wants to unlock
|
||||
*/
|
||||
boolean unlock(ITransaction transaction, String id, String owner);
|
||||
|
||||
/**
|
||||
* Unlocks all resources at "path" (and all subfolders if existing)<p/> that
|
||||
* have the same owner.
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* what resource to unlock
|
||||
* @param owner
|
||||
* who wants to unlock
|
||||
*/
|
||||
void unlockTemporaryLockedObjects(ITransaction transaction, String path,
|
||||
String owner);
|
||||
|
||||
/**
|
||||
* Deletes LockedObjects, where timeout has reached.
|
||||
*
|
||||
* @param transaction
|
||||
* @param temporary
|
||||
* Check timeout on temporary or real locks
|
||||
*/
|
||||
void checkTimeouts(ITransaction transaction, boolean temporary);
|
||||
|
||||
/**
|
||||
* Tries to lock the resource at "path" exclusively.
|
||||
*
|
||||
* @param transaction
|
||||
* Transaction
|
||||
* @param path
|
||||
* what resource to lock
|
||||
* @param owner
|
||||
* the owner of the lock
|
||||
* @param depth
|
||||
* depth
|
||||
* @param timeout
|
||||
* Lock Duration in seconds.
|
||||
* @return true if the resource at path was successfully locked, false if an
|
||||
* existing lock prevented this
|
||||
* @throws LockFailedException
|
||||
*/
|
||||
boolean exclusiveLock(ITransaction transaction, String path, String owner,
|
||||
int depth, int timeout) throws LockFailedException;
|
||||
|
||||
/**
|
||||
* Tries to lock the resource at "path" shared.
|
||||
*
|
||||
* @param transaction
|
||||
* Transaction
|
||||
* @param path
|
||||
* what resource to lock
|
||||
* @param owner
|
||||
* the owner of the lock
|
||||
* @param depth
|
||||
* depth
|
||||
* @param timeout
|
||||
* Lock Duration in seconds.
|
||||
* @return true if the resource at path was successfully locked, false if an
|
||||
* existing lock prevented this
|
||||
* @throws LockFailedException
|
||||
*/
|
||||
boolean sharedLock(ITransaction transaction, String path, String owner,
|
||||
int depth, int timeout) throws LockFailedException;
|
||||
|
||||
/**
|
||||
* Gets the LockedObject corresponding to specified id.
|
||||
*
|
||||
* @param transaction
|
||||
* @param id
|
||||
* LockToken to requested resource
|
||||
* @return LockedObject or null if no LockedObject on specified path exists
|
||||
*/
|
||||
LockedObject getLockedObjectByID(ITransaction transaction, String id);
|
||||
|
||||
/**
|
||||
* Gets the LockedObject on specified path.
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* Path to requested resource
|
||||
* @return LockedObject or null if no LockedObject on specified path exists
|
||||
*/
|
||||
LockedObject getLockedObjectByPath(ITransaction transaction, String path);
|
||||
|
||||
/**
|
||||
* Gets the LockedObject corresponding to specified id (locktoken).
|
||||
*
|
||||
* @param transaction
|
||||
* @param id
|
||||
* LockToken to requested resource
|
||||
* @return LockedObject or null if no LockedObject on specified path exists
|
||||
*/
|
||||
LockedObject getTempLockedObjectByID(ITransaction transaction, String id);
|
||||
|
||||
/**
|
||||
* Gets the LockedObject on specified path.
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* Path to requested resource
|
||||
* @return LockedObject or null if no LockedObject on specified path exists
|
||||
*/
|
||||
LockedObject getTempLockedObjectByPath(ITransaction transaction, String path);
|
||||
|
||||
}
|
@ -0,0 +1,422 @@
|
||||
package cn.tenfell.webos.common.webdav.sf.locking;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* a helper class for ResourceLocks, represents the Locks
|
||||
*
|
||||
* @author re
|
||||
*
|
||||
*/
|
||||
public class LockedObject {
|
||||
|
||||
private ResourceLocks _resourceLocks;
|
||||
|
||||
private String _path;
|
||||
|
||||
private String _id;
|
||||
|
||||
/**
|
||||
* Describing the depth of a locked collection. If the locked resource is
|
||||
* not a collection, depth is 0 / doesn't matter.
|
||||
*/
|
||||
protected int _lockDepth;
|
||||
|
||||
/**
|
||||
* Describing the timeout of a locked object (ms)
|
||||
*/
|
||||
protected long _expiresAt;
|
||||
|
||||
/**
|
||||
* owner of the lock. shared locks can have multiple owners. is null if no
|
||||
* owner is present
|
||||
*/
|
||||
// protected String[] _owner = null;
|
||||
protected String[] _owner = null;
|
||||
|
||||
/**
|
||||
* children of that lock
|
||||
*/
|
||||
protected LockedObject[] _children = null;
|
||||
|
||||
protected LockedObject _parent = null;
|
||||
|
||||
/**
|
||||
* weather the lock is exclusive or not. if owner=null the exclusive value
|
||||
* doesn't matter
|
||||
*/
|
||||
protected boolean _exclusive = false;
|
||||
|
||||
/**
|
||||
* weather the lock is a write or read lock
|
||||
*/
|
||||
protected String _type = null;
|
||||
|
||||
/**
|
||||
* @param _resourceLocks
|
||||
* the resourceLocks where locks are stored
|
||||
* @param path
|
||||
* the path to the locked object
|
||||
* @param temporary
|
||||
* indicates if the LockedObject should be temporary or not
|
||||
*/
|
||||
public LockedObject(ResourceLocks resLocks, String path, boolean temporary) {
|
||||
_path = path;
|
||||
_id = UUID.randomUUID().toString();
|
||||
_resourceLocks = resLocks;
|
||||
|
||||
if (!temporary) {
|
||||
_resourceLocks._locks.put(path, this);
|
||||
_resourceLocks._locksByID.put(_id, this);
|
||||
} else {
|
||||
_resourceLocks._tempLocks.put(path, this);
|
||||
_resourceLocks._tempLocksByID.put(_id, this);
|
||||
}
|
||||
_resourceLocks._cleanupCounter++;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a new owner to a lock
|
||||
*
|
||||
* @param owner
|
||||
* string that represents the owner
|
||||
* @return true if the owner was added, false otherwise
|
||||
*/
|
||||
public boolean addLockedObjectOwner(String owner) {
|
||||
|
||||
if (_owner == null) {
|
||||
_owner = new String[1];
|
||||
} else {
|
||||
|
||||
int size = _owner.length;
|
||||
String[] newLockObjectOwner = new String[size + 1];
|
||||
|
||||
// check if the owner is already here (that should actually not
|
||||
// happen)
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (_owner[i].equals(owner)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
System.arraycopy(_owner, 0, newLockObjectOwner, 0, size);
|
||||
_owner = newLockObjectOwner;
|
||||
}
|
||||
|
||||
_owner[_owner.length - 1] = owner;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* tries to remove the owner from the lock
|
||||
*
|
||||
* @param owner
|
||||
* string that represents the owner
|
||||
*/
|
||||
public void removeLockedObjectOwner(String owner) {
|
||||
|
||||
try {
|
||||
if (_owner != null) {
|
||||
int size = _owner.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
// check every owner if it is the requested one
|
||||
if (_owner[i].equals(owner)) {
|
||||
// remove the owner
|
||||
size -= 1;
|
||||
String[] newLockedObjectOwner = new String[size];
|
||||
for (int j = 0; j < size; j++) {
|
||||
if (j < i) {
|
||||
newLockedObjectOwner[j] = _owner[j];
|
||||
} else {
|
||||
newLockedObjectOwner[j] = _owner[j + 1];
|
||||
}
|
||||
}
|
||||
_owner = newLockedObjectOwner;
|
||||
|
||||
}
|
||||
}
|
||||
if (_owner.length == 0) {
|
||||
_owner = null;
|
||||
}
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
System.out.println("LockedObject.removeLockedObjectOwner()");
|
||||
System.out.println(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a new child lock to this lock
|
||||
*
|
||||
* @param newChild
|
||||
* new child
|
||||
*/
|
||||
public void addChild(LockedObject newChild) {
|
||||
if (_children == null) {
|
||||
_children = new LockedObject[0];
|
||||
}
|
||||
int size = _children.length;
|
||||
LockedObject[] newChildren = new LockedObject[size + 1];
|
||||
System.arraycopy(_children, 0, newChildren, 0, size);
|
||||
newChildren[size] = newChild;
|
||||
_children = newChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes this Lock object. assumes that it has no children and no owners
|
||||
* (does not check this itself)
|
||||
*
|
||||
*/
|
||||
public void removeLockedObject() {
|
||||
if (this != _resourceLocks._root && !this.getPath().equals("/")) {
|
||||
|
||||
int size = _parent._children.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (_parent._children[i].equals(this)) {
|
||||
LockedObject[] newChildren = new LockedObject[size - 1];
|
||||
for (int i2 = 0; i2 < (size - 1); i2++) {
|
||||
if (i2 < i) {
|
||||
newChildren[i2] = _parent._children[i2];
|
||||
} else {
|
||||
newChildren[i2] = _parent._children[i2 + 1];
|
||||
}
|
||||
}
|
||||
if (newChildren.length != 0) {
|
||||
_parent._children = newChildren;
|
||||
} else {
|
||||
_parent._children = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// removing from hashtable
|
||||
_resourceLocks._locksByID.remove(getID());
|
||||
_resourceLocks._locks.remove(getPath());
|
||||
|
||||
// now the garbage collector has some work to do
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes this Lock object. assumes that it has no children and no owners
|
||||
* (does not check this itself)
|
||||
*
|
||||
*/
|
||||
public void removeTempLockedObject() {
|
||||
if (this != _resourceLocks._tempRoot) {
|
||||
// removing from tree
|
||||
if (_parent != null && _parent._children != null) {
|
||||
int size = _parent._children.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (_parent._children[i].equals(this)) {
|
||||
LockedObject[] newChildren = new LockedObject[size - 1];
|
||||
for (int i2 = 0; i2 < (size - 1); i2++) {
|
||||
if (i2 < i) {
|
||||
newChildren[i2] = _parent._children[i2];
|
||||
} else {
|
||||
newChildren[i2] = _parent._children[i2 + 1];
|
||||
}
|
||||
}
|
||||
if (newChildren.length != 0) {
|
||||
_parent._children = newChildren;
|
||||
} else {
|
||||
_parent._children = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// removing from hashtable
|
||||
_resourceLocks._tempLocksByID.remove(getID());
|
||||
_resourceLocks._tempLocks.remove(getPath());
|
||||
|
||||
// now the garbage collector has some work to do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if a lock of the given exclusivity can be placed, only considering
|
||||
* children up to "depth"
|
||||
*
|
||||
* @param exclusive
|
||||
* wheather the new lock should be exclusive
|
||||
* @param depth
|
||||
* the depth to which should be checked
|
||||
* @return true if the lock can be placed
|
||||
*/
|
||||
public boolean checkLocks(boolean exclusive, int depth) {
|
||||
if (checkParents(exclusive) && checkChildren(exclusive, depth)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* helper of checkLocks(). looks if the parents are locked
|
||||
*
|
||||
* @param exclusive
|
||||
* wheather the new lock should be exclusive
|
||||
* @return true if no locks at the parent path are forbidding a new lock
|
||||
*/
|
||||
private boolean checkParents(boolean exclusive) {
|
||||
if (_path.equals("/")) {
|
||||
return true;
|
||||
} else {
|
||||
if (_owner == null) {
|
||||
// no owner, checking parents
|
||||
return _parent != null && _parent.checkParents(exclusive);
|
||||
} else {
|
||||
// there already is a owner
|
||||
return !(_exclusive || exclusive)
|
||||
&& _parent.checkParents(exclusive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* helper of checkLocks(). looks if the children are locked
|
||||
*
|
||||
* @param exclusive
|
||||
* wheather the new lock should be exclusive
|
||||
* @return true if no locks at the children paths are forbidding a new lock
|
||||
* @param depth
|
||||
* depth
|
||||
*/
|
||||
private boolean checkChildren(boolean exclusive, int depth) {
|
||||
if (_children == null) {
|
||||
// a file
|
||||
|
||||
return _owner == null || !(_exclusive || exclusive);
|
||||
} else {
|
||||
// a folder
|
||||
|
||||
if (_owner == null) {
|
||||
// no owner, checking children
|
||||
|
||||
if (depth != 0) {
|
||||
boolean canLock = true;
|
||||
int limit = _children.length;
|
||||
for (int i = 0; i < limit; i++) {
|
||||
if (!_children[i].checkChildren(exclusive, depth - 1)) {
|
||||
canLock = false;
|
||||
}
|
||||
}
|
||||
return canLock;
|
||||
} else {
|
||||
// depth == 0 -> we don't care for children
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// there already is a owner
|
||||
return !(_exclusive || exclusive);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new timeout for the LockedObject
|
||||
*
|
||||
* @param timeout
|
||||
*/
|
||||
public void refreshTimeout(int timeout) {
|
||||
_expiresAt = System.currentTimeMillis() + (timeout * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timeout for the LockedObject
|
||||
*
|
||||
* @return timeout
|
||||
*/
|
||||
public long getTimeoutMillis() {
|
||||
return (_expiresAt - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the lock has expired.
|
||||
*
|
||||
* @return true if timeout has passed
|
||||
*/
|
||||
public boolean hasExpired() {
|
||||
if (_expiresAt != 0) {
|
||||
return (System.currentTimeMillis() > _expiresAt);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the LockID (locktoken) for the LockedObject
|
||||
*
|
||||
* @return locktoken
|
||||
*/
|
||||
public String getID() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the owners for the LockedObject
|
||||
*
|
||||
* @return owners
|
||||
*/
|
||||
public String[] getOwner() {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path for the LockedObject
|
||||
*
|
||||
* @return path
|
||||
*/
|
||||
public String getPath() {
|
||||
return _path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the exclusivity for the LockedObject
|
||||
*
|
||||
* @param exclusive
|
||||
*/
|
||||
public void setExclusive(boolean exclusive) {
|
||||
_exclusive = exclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exclusivity for the LockedObject
|
||||
*
|
||||
* @return exclusivity
|
||||
*/
|
||||
public boolean isExclusive() {
|
||||
return _exclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exclusivity for the LockedObject
|
||||
*
|
||||
* @return exclusivity
|
||||
*/
|
||||
public boolean isShared() {
|
||||
return !_exclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the lock
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
public String getType() {
|
||||
return _type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the depth of the lock
|
||||
*
|
||||
* @return depth
|
||||
*/
|
||||
public int getLockDepth() {
|
||||
return _lockDepth;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,384 @@
|
||||
/*
|
||||
* Copyright 2005-2006 webdav-servlet group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.tenfell.webos.common.webdav.sf.locking;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.ITransaction;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* simple locking management for concurrent data access, NOT the webdav locking.
|
||||
* ( could that be used instead? )
|
||||
*
|
||||
* IT IS ACTUALLY USED FOR DOLOCK
|
||||
*
|
||||
* @author re
|
||||
*/
|
||||
public class ResourceLocks implements IResourceLocks {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(ResourceLocks.class);
|
||||
|
||||
/**
|
||||
* after creating this much LockedObjects, a cleanup deletes unused
|
||||
* LockedObjects
|
||||
*/
|
||||
private final int _cleanupLimit = 100000;
|
||||
|
||||
protected int _cleanupCounter = 0;
|
||||
|
||||
/**
|
||||
* keys: path value: LockedObject from that path
|
||||
*/
|
||||
protected Hashtable<String, LockedObject> _locks = new Hashtable<String, LockedObject>();
|
||||
|
||||
/**
|
||||
* keys: id value: LockedObject from that id
|
||||
*/
|
||||
protected Hashtable<String, LockedObject> _locksByID = new Hashtable<String, LockedObject>();
|
||||
|
||||
/**
|
||||
* keys: path value: Temporary LockedObject from that path
|
||||
*/
|
||||
protected Hashtable<String, LockedObject> _tempLocks = new Hashtable<String, LockedObject>();
|
||||
|
||||
/**
|
||||
* keys: id value: Temporary LockedObject from that id
|
||||
*/
|
||||
protected Hashtable<String, LockedObject> _tempLocksByID = new Hashtable<String, LockedObject>();
|
||||
|
||||
// REMEMBER TO REMOVE UNUSED LOCKS FROM THE HASHTABLE AS WELL
|
||||
|
||||
protected LockedObject _root = null;
|
||||
|
||||
protected LockedObject _tempRoot = null;
|
||||
|
||||
private boolean _temporary = true;
|
||||
|
||||
public ResourceLocks() {
|
||||
_root = new LockedObject(this, "/", true);
|
||||
_tempRoot = new LockedObject(this, "/", false);
|
||||
}
|
||||
|
||||
public synchronized boolean lock(ITransaction transaction, String path,
|
||||
String owner, boolean exclusive, int depth, int timeout,
|
||||
boolean temporary) throws LockFailedException {
|
||||
|
||||
LockedObject lo = null;
|
||||
|
||||
if (temporary) {
|
||||
lo = generateTempLockedObjects(transaction, path);
|
||||
lo._type = "read";
|
||||
} else {
|
||||
lo = generateLockedObjects(transaction, path);
|
||||
lo._type = "write";
|
||||
}
|
||||
|
||||
if (lo.checkLocks(exclusive, depth)) {
|
||||
|
||||
lo._exclusive = exclusive;
|
||||
lo._lockDepth = depth;
|
||||
lo._expiresAt = System.currentTimeMillis() + (timeout * 1000);
|
||||
if (lo._parent != null) {
|
||||
lo._parent._expiresAt = lo._expiresAt;
|
||||
if (lo._parent.equals(_root)) {
|
||||
LockedObject rootLo = getLockedObjectByPath(transaction,
|
||||
_root.getPath());
|
||||
rootLo._expiresAt = lo._expiresAt;
|
||||
} else if (lo._parent.equals(_tempRoot)) {
|
||||
LockedObject tempRootLo = getTempLockedObjectByPath(
|
||||
transaction, _tempRoot.getPath());
|
||||
tempRootLo._expiresAt = lo._expiresAt;
|
||||
}
|
||||
}
|
||||
if (lo.addLockedObjectOwner(owner)) {
|
||||
return true;
|
||||
} else {
|
||||
LOG.trace("Couldn't set owner \"" + owner
|
||||
+ "\" to resource at '" + path + "'");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// can not lock
|
||||
LOG.trace("Lock resource at " + path + " failed because"
|
||||
+ "\na parent or child resource is currently locked");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean unlock(ITransaction transaction, String id,
|
||||
String owner) {
|
||||
|
||||
if (_locksByID.containsKey(id)) {
|
||||
String path = _locksByID.get(id).getPath();
|
||||
if (_locks.containsKey(path)) {
|
||||
LockedObject lo = _locks.get(path);
|
||||
lo.removeLockedObjectOwner(owner);
|
||||
|
||||
if (lo._children == null && lo._owner == null)
|
||||
lo.removeLockedObject();
|
||||
|
||||
} else {
|
||||
// there is no lock at that path. someone tried to unlock it
|
||||
// anyway. could point to a problem
|
||||
LOG
|
||||
.trace("cn.tenfell.webos.common.webdav.sf.locking.ResourceLocks.unlock(): no lock for path "
|
||||
+ path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cleanupCounter > _cleanupLimit) {
|
||||
_cleanupCounter = 0;
|
||||
cleanLockedObjects(transaction, _root, !_temporary);
|
||||
}
|
||||
}
|
||||
checkTimeouts(transaction, !_temporary);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public synchronized void unlockTemporaryLockedObjects(
|
||||
ITransaction transaction, String path, String owner) {
|
||||
if (_tempLocks.containsKey(path)) {
|
||||
LockedObject lo = _tempLocks.get(path);
|
||||
lo.removeLockedObjectOwner(owner);
|
||||
|
||||
} else {
|
||||
// there is no lock at that path. someone tried to unlock it
|
||||
// anyway. could point to a problem
|
||||
LOG
|
||||
.trace("cn.tenfell.webos.common.webdav.sf.locking.ResourceLocks.unlock(): no lock for path "
|
||||
+ path);
|
||||
}
|
||||
|
||||
if (_cleanupCounter > _cleanupLimit) {
|
||||
_cleanupCounter = 0;
|
||||
cleanLockedObjects(transaction, _tempRoot, _temporary);
|
||||
}
|
||||
|
||||
checkTimeouts(transaction, _temporary);
|
||||
|
||||
}
|
||||
|
||||
public void checkTimeouts(ITransaction transaction, boolean temporary) {
|
||||
if (!temporary) {
|
||||
Enumeration<LockedObject> lockedObjects = _locks.elements();
|
||||
while (lockedObjects.hasMoreElements()) {
|
||||
LockedObject currentLockedObject = lockedObjects.nextElement();
|
||||
|
||||
if (currentLockedObject._expiresAt < System.currentTimeMillis()) {
|
||||
currentLockedObject.removeLockedObject();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Enumeration<LockedObject> lockedObjects = _tempLocks.elements();
|
||||
while (lockedObjects.hasMoreElements()) {
|
||||
LockedObject currentLockedObject = lockedObjects.nextElement();
|
||||
|
||||
if (currentLockedObject._expiresAt < System.currentTimeMillis()) {
|
||||
currentLockedObject.removeTempLockedObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean exclusiveLock(ITransaction transaction, String path,
|
||||
String owner, int depth, int timeout) throws LockFailedException {
|
||||
return lock(transaction, path, owner, true, depth, timeout, false);
|
||||
}
|
||||
|
||||
public boolean sharedLock(ITransaction transaction, String path,
|
||||
String owner, int depth, int timeout) throws LockFailedException {
|
||||
return lock(transaction, path, owner, false, depth, timeout, false);
|
||||
}
|
||||
|
||||
public LockedObject getLockedObjectByID(ITransaction transaction, String id) {
|
||||
if (_locksByID.containsKey(id)) {
|
||||
return _locksByID.get(id);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public LockedObject getLockedObjectByPath(ITransaction transaction,
|
||||
String path) {
|
||||
if (_locks.containsKey(path)) {
|
||||
return (LockedObject) this._locks.get(path);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public LockedObject getTempLockedObjectByID(ITransaction transaction,
|
||||
String id) {
|
||||
if (_tempLocksByID.containsKey(id)) {
|
||||
return _tempLocksByID.get(id);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public LockedObject getTempLockedObjectByPath(ITransaction transaction,
|
||||
String path) {
|
||||
if (_tempLocks.containsKey(path)) {
|
||||
return (LockedObject) this._tempLocks.get(path);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generates real LockedObjects for the resource at path and its parent
|
||||
* folders. does not create new LockedObjects if they already exist
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* path to the (new) LockedObject
|
||||
* @return the LockedObject for path.
|
||||
*/
|
||||
private LockedObject generateLockedObjects(ITransaction transaction,
|
||||
String path) {
|
||||
if (!_locks.containsKey(path)) {
|
||||
LockedObject returnObject = new LockedObject(this, path,
|
||||
!_temporary);
|
||||
String parentPath = getParentPath(path);
|
||||
if (parentPath != null) {
|
||||
LockedObject parentLockedObject = generateLockedObjects(
|
||||
transaction, parentPath);
|
||||
parentLockedObject.addChild(returnObject);
|
||||
returnObject._parent = parentLockedObject;
|
||||
}
|
||||
return returnObject;
|
||||
} else {
|
||||
// there is already a LockedObject on the specified path
|
||||
return (LockedObject) this._locks.get(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* generates temporary LockedObjects for the resource at path and its parent
|
||||
* folders. does not create new LockedObjects if they already exist
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* path to the (new) LockedObject
|
||||
* @return the LockedObject for path.
|
||||
*/
|
||||
private LockedObject generateTempLockedObjects(ITransaction transaction,
|
||||
String path) {
|
||||
if (!_tempLocks.containsKey(path)) {
|
||||
LockedObject returnObject = new LockedObject(this, path, _temporary);
|
||||
String parentPath = getParentPath(path);
|
||||
if (parentPath != null) {
|
||||
LockedObject parentLockedObject = generateTempLockedObjects(
|
||||
transaction, parentPath);
|
||||
parentLockedObject.addChild(returnObject);
|
||||
returnObject._parent = parentLockedObject;
|
||||
}
|
||||
return returnObject;
|
||||
} else {
|
||||
// there is already a LockedObject on the specified path
|
||||
return (LockedObject) this._tempLocks.get(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes unused LockedObjects and resets the counter. works recursively
|
||||
* starting at the given LockedObject
|
||||
*
|
||||
* @param transaction
|
||||
* @param lo
|
||||
* LockedObject
|
||||
* @param temporary
|
||||
* Clean temporary or real locks
|
||||
*
|
||||
* @return if cleaned
|
||||
*/
|
||||
private boolean cleanLockedObjects(ITransaction transaction,
|
||||
LockedObject lo, boolean temporary) {
|
||||
|
||||
if (lo._children == null) {
|
||||
if (lo._owner == null) {
|
||||
if (temporary) {
|
||||
lo.removeTempLockedObject();
|
||||
} else {
|
||||
lo.removeLockedObject();
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
boolean canDelete = true;
|
||||
int limit = lo._children.length;
|
||||
for (int i = 0; i < limit; i++) {
|
||||
if (!cleanLockedObjects(transaction, lo._children[i], temporary)) {
|
||||
canDelete = false;
|
||||
} else {
|
||||
|
||||
// because the deleting shifts the array
|
||||
i--;
|
||||
limit--;
|
||||
}
|
||||
}
|
||||
if (canDelete) {
|
||||
if (lo._owner == null) {
|
||||
if (temporary) {
|
||||
lo.removeTempLockedObject();
|
||||
} else {
|
||||
lo.removeLockedObject();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* creates the parent path from the given path by removing the last '/' and
|
||||
* everything after that
|
||||
*
|
||||
* @param path
|
||||
* the path
|
||||
* @return parent path
|
||||
*/
|
||||
private String getParentPath(String path) {
|
||||
int slash = path.lastIndexOf('/');
|
||||
if (slash == -1) {
|
||||
return null;
|
||||
} else {
|
||||
if (slash == 0) {
|
||||
// return "root" if parent path is empty string
|
||||
return "/";
|
||||
} else {
|
||||
return path.substring(0, slash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,564 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.tenfell.webos.common.webdav.sf.*;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.fromcatalina.XMLWriter;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.IResourceLocks;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.LockedObject;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AbstractMethod implements IMethodExecutor {
|
||||
|
||||
private static final ThreadLocal<DateFormat> thLastmodifiedDateFormat = new ThreadLocal<DateFormat>();
|
||||
private static final ThreadLocal<DateFormat> thCreationDateFormat = new ThreadLocal<DateFormat>();
|
||||
private static final ThreadLocal<DateFormat> thLocalDateFormat = new ThreadLocal<DateFormat>();
|
||||
|
||||
|
||||
/**
|
||||
* Default depth is infite.
|
||||
*/
|
||||
protected static final int INFINITY = 3;
|
||||
|
||||
/**
|
||||
* Simple date format for the creation date ISO 8601 representation
|
||||
* (partial).
|
||||
*/
|
||||
protected static final String CREATION_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
|
||||
/**
|
||||
* Simple date format for the last modified date. (RFC 822 updated by RFC
|
||||
* 1123)
|
||||
*/
|
||||
protected static final String LAST_MODIFIED_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";
|
||||
|
||||
protected static final String LOCAL_DATE_FORMAT = "dd/MM/yy' 'HH:mm:ss";
|
||||
|
||||
/**
|
||||
* size of the io-buffer
|
||||
*/
|
||||
protected static int BUF_SIZE = 65536;
|
||||
|
||||
/**
|
||||
* Default lock timeout value.
|
||||
*/
|
||||
protected static final int DEFAULT_TIMEOUT = 3600;
|
||||
|
||||
/**
|
||||
* Maximum lock timeout.
|
||||
*/
|
||||
protected static final int MAX_TIMEOUT = 604800;
|
||||
|
||||
/**
|
||||
* Boolean value to temporary lock resources (for method locks)
|
||||
*/
|
||||
protected static final boolean TEMPORARY = true;
|
||||
|
||||
/**
|
||||
* Timeout for temporary locks
|
||||
*/
|
||||
protected static final int TEMP_TIMEOUT = 10;
|
||||
|
||||
|
||||
public static String lastModifiedDateFormat(final Date date) {
|
||||
DateFormat df = thLastmodifiedDateFormat.get();
|
||||
if( df == null ) {
|
||||
df = new SimpleDateFormat(LAST_MODIFIED_DATE_FORMAT, Locale.US);
|
||||
df.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
thLastmodifiedDateFormat.set( df );
|
||||
}
|
||||
return df.format(date);
|
||||
}
|
||||
|
||||
public static String creationDateFormat(final Date date) {
|
||||
DateFormat df = thCreationDateFormat.get();
|
||||
if( df == null ) {
|
||||
df = new SimpleDateFormat(CREATION_DATE_FORMAT);
|
||||
df.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
thCreationDateFormat.set( df );
|
||||
}
|
||||
return df.format(date);
|
||||
}
|
||||
|
||||
public static String getLocalDateFormat(final Date date, final Locale loc) {
|
||||
DateFormat df = thLocalDateFormat.get();
|
||||
if( df == null ) {
|
||||
df = new SimpleDateFormat(LOCAL_DATE_FORMAT, loc);
|
||||
}
|
||||
return df.format(date);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses and normalizes the destination header.
|
||||
*
|
||||
* @param req
|
||||
* Servlet request
|
||||
* @param resp
|
||||
* Servlet response
|
||||
* @return destinationPath
|
||||
* @throws IOException
|
||||
* if an error occurs while sending response
|
||||
*/
|
||||
protected String parseDestinationHeader(HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException {
|
||||
String destinationPath = req.getHeader("Destination");
|
||||
|
||||
if (destinationPath == null) {
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove url encoding from destination
|
||||
destinationPath = URLUtil.decode(destinationPath);
|
||||
|
||||
int protocolIndex = destinationPath.indexOf("://");
|
||||
if (protocolIndex >= 0) {
|
||||
// if the Destination URL contains the protocol, we can safely
|
||||
// trim everything upto the first "/" character after "://"
|
||||
int firstSeparator = destinationPath
|
||||
.indexOf("/", protocolIndex + 4);
|
||||
if (firstSeparator < 0) {
|
||||
destinationPath = "/";
|
||||
} else {
|
||||
destinationPath = destinationPath.substring(firstSeparator);
|
||||
}
|
||||
} else {
|
||||
String hostName = req.getServerName();
|
||||
if ((hostName != null) && (destinationPath.startsWith(hostName))) {
|
||||
destinationPath = destinationPath.substring(hostName.length());
|
||||
}
|
||||
|
||||
int portIndex = destinationPath.indexOf(":");
|
||||
if (portIndex >= 0) {
|
||||
destinationPath = destinationPath.substring(portIndex);
|
||||
}
|
||||
|
||||
if (destinationPath.startsWith(":")) {
|
||||
int firstSeparator = destinationPath.indexOf("/");
|
||||
if (firstSeparator < 0) {
|
||||
destinationPath = "/";
|
||||
} else {
|
||||
destinationPath = destinationPath.substring(firstSeparator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize destination path (remove '.' and' ..')
|
||||
destinationPath = normalize(destinationPath);
|
||||
|
||||
String contextPath = req.getContextPath();
|
||||
if ((contextPath != null) && (destinationPath.startsWith(contextPath))) {
|
||||
destinationPath = destinationPath.substring(contextPath.length());
|
||||
}
|
||||
|
||||
String pathInfo = req.getPathInfo();
|
||||
if (pathInfo != null) {
|
||||
String servletPath = req.getServletPath();
|
||||
if ((servletPath != null)
|
||||
&& (destinationPath.startsWith(servletPath))) {
|
||||
destinationPath = destinationPath.substring(servletPath
|
||||
.length());
|
||||
}
|
||||
}
|
||||
|
||||
return destinationPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a context-relative path, beginning with a "/", that represents the
|
||||
* canonical version of the specified path after ".." and "." elements are
|
||||
* resolved out. If the specified path attempts to go outside the boundaries
|
||||
* of the current context (i.e. too many ".." path elements are present),
|
||||
* return <code>null</code> instead.
|
||||
*
|
||||
* @param path
|
||||
* Path to be normalized
|
||||
* @return normalized path
|
||||
*/
|
||||
protected String normalize(String path) {
|
||||
|
||||
if (path == null)
|
||||
return null;
|
||||
|
||||
// Create a place for the normalized path
|
||||
String normalized = path;
|
||||
|
||||
if (normalized.equals("/."))
|
||||
return "/";
|
||||
|
||||
// Normalize the slashes and add leading slash if necessary
|
||||
if (normalized.indexOf('\\') >= 0)
|
||||
normalized = normalized.replace('\\', '/');
|
||||
if (!normalized.startsWith("/"))
|
||||
normalized = "/" + normalized;
|
||||
|
||||
// Resolve occurrences of "//" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("//");
|
||||
if (index < 0)
|
||||
break;
|
||||
normalized = normalized.substring(0, index)
|
||||
+ normalized.substring(index + 1);
|
||||
}
|
||||
|
||||
// Resolve occurrences of "/./" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("/./");
|
||||
if (index < 0)
|
||||
break;
|
||||
normalized = normalized.substring(0, index)
|
||||
+ normalized.substring(index + 2);
|
||||
}
|
||||
|
||||
// Resolve occurrences of "/../" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("/../");
|
||||
if (index < 0)
|
||||
break;
|
||||
if (index == 0)
|
||||
return (null); // Trying to go outside our context
|
||||
int index2 = normalized.lastIndexOf('/', index - 1);
|
||||
normalized = normalized.substring(0, index2)
|
||||
+ normalized.substring(index + 3);
|
||||
}
|
||||
|
||||
// Return the normalized path that we have completed
|
||||
return (normalized);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the relative path associated with this servlet.
|
||||
*
|
||||
* @param request
|
||||
* The servlet request we are processing
|
||||
*/
|
||||
protected String getRelativePath(HttpServletRequest request) {
|
||||
|
||||
// Are we being processed by a RequestDispatcher.include()?
|
||||
if (request.getAttribute("javax.servlet.include.request_uri") != null) {
|
||||
String result = (String) request
|
||||
.getAttribute("javax.servlet.include.path_info");
|
||||
// if (result == null)
|
||||
// result = (String) request
|
||||
// .getAttribute("javax.servlet.include.servlet_path");
|
||||
if ((result == null) || (result.equals("")))
|
||||
result = "/";
|
||||
return (result);
|
||||
}
|
||||
|
||||
// No, extract the desired path directly from the request
|
||||
String result = request.getPathInfo();
|
||||
// if (result == null) {
|
||||
// result = request.getServletPath();
|
||||
// }
|
||||
if ((result == null) || (result.equals(""))) {
|
||||
result = "/";
|
||||
}
|
||||
return (result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* creates the parent path from the given path by removing the last '/' and
|
||||
* everything after that
|
||||
*
|
||||
* @param path
|
||||
* the path
|
||||
* @return parent path
|
||||
*/
|
||||
protected String getParentPath(String path) {
|
||||
int slash = path.lastIndexOf('/');
|
||||
if (slash != -1) {
|
||||
return path.substring(0, slash);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* removes a / at the end of the path string, if present
|
||||
*
|
||||
* @param path
|
||||
* the path
|
||||
* @return the path without trailing /
|
||||
*/
|
||||
protected String getCleanPath(String path) {
|
||||
|
||||
if (path.endsWith("/") && path.length() > 1)
|
||||
path = path.substring(0, path.length() - 1);
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JAXP document builder instance.
|
||||
*/
|
||||
protected DocumentBuilder getDocumentBuilder(){
|
||||
DocumentBuilder documentBuilder = null;
|
||||
DocumentBuilderFactory documentBuilderFactory = null;
|
||||
try {
|
||||
documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
documentBuilderFactory.setNamespaceAware(true);
|
||||
documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new RuntimeException("jaxp failed");
|
||||
}
|
||||
return documentBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* reads the depth header from the request and returns it as a int
|
||||
*
|
||||
* @param req
|
||||
* @return the depth from the depth header
|
||||
*/
|
||||
protected int getDepth(HttpServletRequest req) {
|
||||
int depth = INFINITY;
|
||||
String depthStr = req.getHeader("Depth");
|
||||
if (depthStr != null) {
|
||||
if (depthStr.equals("0")) {
|
||||
depth = 0;
|
||||
} else if (depthStr.equals("1")) {
|
||||
depth = 1;
|
||||
}
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL rewriter.
|
||||
*
|
||||
* @param path
|
||||
* Path which has to be rewiten
|
||||
* @return the rewritten path
|
||||
*/
|
||||
protected String rewriteUrl(String path) {
|
||||
return URLUtil.encode(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ETag associated with a file.
|
||||
* StoredObject to get resourceLength, lastModified and a hashCode of
|
||||
* StoredObject
|
||||
* @return the ETag
|
||||
*/
|
||||
protected String getETag(StoredObject so) {
|
||||
|
||||
String resourceLength = "";
|
||||
String lastModified = "";
|
||||
|
||||
if (so != null && so.isResource()) {
|
||||
resourceLength = new Long(so.getResourceLength()).toString();
|
||||
lastModified = new Long(so.getLastModified().getTime()).toString();
|
||||
}
|
||||
|
||||
return "W/\"" + resourceLength + "-" + lastModified + "\"";
|
||||
|
||||
}
|
||||
|
||||
protected String[] getLockIdFromIfHeader(HttpServletRequest req) {
|
||||
String[] ids = new String[2];
|
||||
String id = req.getHeader("If");
|
||||
|
||||
if (id != null && !id.equals("")) {
|
||||
if (id.indexOf(">)") == id.lastIndexOf(">)")) {
|
||||
id = id.substring(id.indexOf("(<"), id.indexOf(">)"));
|
||||
|
||||
if (id.indexOf("locktoken:") != -1) {
|
||||
id = id.substring(id.indexOf(':') + 1);
|
||||
}
|
||||
ids[0] = id;
|
||||
} else {
|
||||
String firstId = id.substring(id.indexOf("(<"), id
|
||||
.indexOf(">)"));
|
||||
if (firstId.indexOf("locktoken:") != -1) {
|
||||
firstId = firstId.substring(firstId.indexOf(':') + 1);
|
||||
}
|
||||
ids[0] = firstId;
|
||||
|
||||
String secondId = id.substring(id.lastIndexOf("(<"), id
|
||||
.lastIndexOf(">)"));
|
||||
if (secondId.indexOf("locktoken:") != -1) {
|
||||
secondId = secondId.substring(secondId.indexOf(':') + 1);
|
||||
}
|
||||
ids[1] = secondId;
|
||||
}
|
||||
|
||||
} else {
|
||||
ids = null;
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
protected String getLockIdFromLockTokenHeader(HttpServletRequest req) {
|
||||
String id = req.getHeader("Lock-Token");
|
||||
|
||||
if (id != null) {
|
||||
id = id.substring(id.indexOf(":") + 1, id.indexOf(">"));
|
||||
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if locks on resources at the given path exists and if so checks
|
||||
* the If-Header to make sure the If-Header corresponds to the locked
|
||||
* resource. Returning true if no lock exists or the If-Header is
|
||||
* corresponding to the locked resource
|
||||
*
|
||||
* @param req
|
||||
* Servlet request
|
||||
* @param resp
|
||||
* Servlet response
|
||||
* @param resourceLocks
|
||||
* @param path
|
||||
* path to the resource
|
||||
* List of error to be displayed
|
||||
* @return true if no lock on a resource with the given path exists or if
|
||||
* the If-Header corresponds to the locked resource
|
||||
* @throws IOException
|
||||
* @throws LockFailedException
|
||||
*/
|
||||
protected boolean checkLocks(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp,
|
||||
IResourceLocks resourceLocks, String path) throws IOException,
|
||||
LockFailedException {
|
||||
|
||||
LockedObject loByPath = resourceLocks.getLockedObjectByPath(
|
||||
transaction, path);
|
||||
if (loByPath != null) {
|
||||
|
||||
if (loByPath.isShared())
|
||||
return true;
|
||||
|
||||
// the resource is locked
|
||||
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||
String lockToken = null;
|
||||
if (lockTokens != null)
|
||||
lockToken = lockTokens[0];
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
if (lockToken != null) {
|
||||
LockedObject loByIf = resourceLocks.getLockedObjectByID(
|
||||
transaction, lockToken);
|
||||
if (loByIf == null) {
|
||||
// no locked resource to the given lockToken
|
||||
return false;
|
||||
}
|
||||
if (!loByIf.equals(loByPath)) {
|
||||
loByIf = null;
|
||||
return false;
|
||||
}
|
||||
loByIf = null;
|
||||
}
|
||||
|
||||
}
|
||||
loByPath = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a multistatus element containing a complete error report to the
|
||||
* client. If the errorList contains only one error, send the error
|
||||
* directly without wrapping it in a multistatus message.
|
||||
*
|
||||
* @param req
|
||||
* Servlet request
|
||||
* @param resp
|
||||
* Servlet response
|
||||
* @param errorList
|
||||
* List of error to be displayed
|
||||
*/
|
||||
protected void sendReport(HttpServletRequest req, HttpServletResponse resp,
|
||||
Hashtable<String, Integer> errorList) throws IOException {
|
||||
|
||||
if (errorList.size() == 1) {
|
||||
int code = errorList.elements().nextElement();
|
||||
if (WebdavStatus.getStatusText(code) != "") {
|
||||
resp.sendError(code, WebdavStatus.getStatusText(code));
|
||||
} else {
|
||||
resp.sendError(code);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
|
||||
|
||||
String absoluteUri = req.getRequestURI();
|
||||
// String relativePath = getRelativePath(req);
|
||||
|
||||
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||
namespaces.put("DAV:", "D");
|
||||
|
||||
XMLWriter generatedXML = new XMLWriter(namespaces);
|
||||
generatedXML.writeXMLHeader();
|
||||
|
||||
generatedXML.writeElement("DAV::multistatus", XMLWriter.OPENING);
|
||||
|
||||
Enumeration<String> pathList = errorList.keys();
|
||||
while (pathList.hasMoreElements()) {
|
||||
|
||||
String errorPath = (String) pathList.nextElement();
|
||||
int errorCode = ((Integer) errorList.get(errorPath)).intValue();
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
String toAppend = null;
|
||||
if (absoluteUri.endsWith(errorPath)) {
|
||||
toAppend = absoluteUri;
|
||||
|
||||
} else if (absoluteUri.contains(errorPath)) {
|
||||
|
||||
int endIndex = absoluteUri.indexOf(errorPath)
|
||||
+ errorPath.length();
|
||||
toAppend = absoluteUri.substring(0, endIndex);
|
||||
}
|
||||
if (!toAppend.startsWith("/") && !toAppend.startsWith("http:"))
|
||||
toAppend = "/" + toAppend;
|
||||
generatedXML.writeText(errorPath);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText("HTTP/1.1 " + errorCode + " "
|
||||
+ WebdavStatus.getStatusText(errorCode));
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.CLOSING);
|
||||
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::multistatus", XMLWriter.CLOSING);
|
||||
|
||||
Writer writer = resp.getWriter();
|
||||
writer.write(generatedXML.toString());
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.StoredObject;
|
||||
|
||||
public abstract class DeterminableMethod extends AbstractMethod {
|
||||
|
||||
private static final String NULL_RESOURCE_METHODS_ALLOWED = "OPTIONS, MKCOL, PUT, PROPFIND, LOCK, UNLOCK";
|
||||
|
||||
private static final String RESOURCE_METHODS_ALLOWED = "OPTIONS, GET, HEAD, POST, DELETE, TRACE"
|
||||
+ ", PROPPATCH, COPY, MOVE, LOCK, UNLOCK, PROPFIND";
|
||||
|
||||
private static final String FOLDER_METHOD_ALLOWED = ", PUT";
|
||||
|
||||
private static final String LESS_ALLOWED_METHODS = "OPTIONS, MKCOL, PUT";
|
||||
|
||||
/**
|
||||
* Determines the methods normally allowed for the resource.
|
||||
*
|
||||
* @param so
|
||||
* StoredObject representing the resource
|
||||
* @return all allowed methods, separated by commas
|
||||
*/
|
||||
protected static String determineMethodsAllowed(StoredObject so) {
|
||||
|
||||
try {
|
||||
if (so != null) {
|
||||
if (so.isNullResource()) {
|
||||
|
||||
return NULL_RESOURCE_METHODS_ALLOWED;
|
||||
|
||||
} else if (so.isFolder()) {
|
||||
return RESOURCE_METHODS_ALLOWED + FOLDER_METHOD_ALLOWED;
|
||||
}
|
||||
// else resource
|
||||
return RESOURCE_METHODS_ALLOWED;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// we do nothing, just return less allowed methods
|
||||
}
|
||||
|
||||
return LESS_ALLOWED_METHODS;
|
||||
}
|
||||
}
|
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.*;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.*;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.ResourceLocks;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class DoCopy extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoCopy.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
private DoDelete _doDelete;
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoCopy(IWebdavStore store, ResourceLocks resourceLocks,
|
||||
DoDelete doDelete, boolean readOnly) {
|
||||
_store = store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_doDelete = doDelete;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
String path = getRelativePath(req);
|
||||
if (!_readOnly) {
|
||||
|
||||
String tempLockOwner = "doCopy" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
try {
|
||||
if (!copyResource(transaction, req, resp))
|
||||
return;
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (ObjectAlreadyExistsException e) {
|
||||
resp.sendError(WebdavStatus.SC_CONFLICT, req
|
||||
.getRequestURI());
|
||||
} catch (ObjectNotFoundException e) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
path, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a resource.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param req
|
||||
* Servlet request
|
||||
* @param resp
|
||||
* Servlet response
|
||||
* @return true if the copy is successful
|
||||
* @throws WebdavException
|
||||
* if an error in the underlying store occurs
|
||||
* @throws IOException
|
||||
* when an error occurs while sending the response
|
||||
* @throws LockFailedException
|
||||
*/
|
||||
public boolean copyResource(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws WebdavException, IOException, LockFailedException {
|
||||
|
||||
// Parsing destination header
|
||||
String destinationPath = parseDestinationHeader(req, resp);
|
||||
|
||||
if (destinationPath == null)
|
||||
return false;
|
||||
|
||||
String path = getRelativePath(req);
|
||||
|
||||
if (path.equals(destinationPath)) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return false;
|
||||
}
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
String parentDestinationPath = getParentPath(getCleanPath(destinationPath));
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks,
|
||||
parentDestinationPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return false; // parentDestination is locked
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, destinationPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return false; // destination is locked
|
||||
}
|
||||
|
||||
// Parsing overwrite header
|
||||
|
||||
boolean overwrite = true;
|
||||
String overwriteHeader = req.getHeader("Overwrite");
|
||||
|
||||
if (overwriteHeader != null) {
|
||||
overwrite = overwriteHeader.equalsIgnoreCase("T");
|
||||
}
|
||||
|
||||
// Overwriting the destination
|
||||
String lockOwner = "copyResource" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
|
||||
if (_resourceLocks.lock(transaction, destinationPath, lockOwner, false,
|
||||
0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||
StoredObject copySo, destinationSo = null;
|
||||
try {
|
||||
copySo = _store.getStoredObject(transaction, path);
|
||||
// Retrieve the resources
|
||||
if (copySo == null) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (copySo.isNullResource()) {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(copySo);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
return false;
|
||||
}
|
||||
|
||||
errorList = new Hashtable<String, Integer>();
|
||||
|
||||
destinationSo = _store.getStoredObject(transaction,
|
||||
destinationPath);
|
||||
|
||||
if (overwrite) {
|
||||
|
||||
// Delete destination resource, if it exists
|
||||
if (destinationSo != null) {
|
||||
_doDelete.deleteResource(transaction, destinationPath,
|
||||
errorList, req, resp);
|
||||
|
||||
} else {
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
}
|
||||
} else {
|
||||
|
||||
// If the destination exists, then it's a conflict
|
||||
if (destinationSo != null) {
|
||||
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||
return false;
|
||||
} else {
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
}
|
||||
|
||||
}
|
||||
copy(transaction, path, destinationPath, errorList, req, resp);
|
||||
|
||||
if (!errorList.isEmpty()) {
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
destinationPath, lockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* copies the specified resource(s) to the specified destination.
|
||||
* preconditions must be handled by the caller. Standard status codes must
|
||||
* be handled by the caller. a multi status report in case of errors is
|
||||
* created here.
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param sourcePath
|
||||
* path from where to read
|
||||
* @param destinationPath
|
||||
* path where to write
|
||||
* @param req
|
||||
* HttpServletRequest
|
||||
* @param resp
|
||||
* HttpServletResponse
|
||||
* @throws WebdavException
|
||||
* if an error in the underlying store occurs
|
||||
* @throws IOException
|
||||
*/
|
||||
private void copy(ITransaction transaction, String sourcePath,
|
||||
String destinationPath, Hashtable<String, Integer> errorList,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws WebdavException, IOException {
|
||||
|
||||
StoredObject sourceSo = _store.getStoredObject(transaction, sourcePath);
|
||||
if (sourceSo.isResource()) {
|
||||
_store.createResource(transaction, destinationPath);
|
||||
long resourceLength = _store.setResourceContent(transaction,
|
||||
destinationPath, _store.getResourceContent(transaction,
|
||||
sourcePath), null, null);
|
||||
|
||||
if (resourceLength != -1) {
|
||||
StoredObject destinationSo = _store.getStoredObject(
|
||||
transaction, destinationPath);
|
||||
destinationSo.setResourceLength(resourceLength);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (sourceSo.isFolder()) {
|
||||
copyFolder(transaction, sourcePath, destinationPath, errorList,
|
||||
req, resp);
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* helper method of copy() recursively copies the FOLDER at source path to
|
||||
* destination path
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param sourcePath
|
||||
* where to read
|
||||
* @param destinationPath
|
||||
* where to write
|
||||
* @param errorList
|
||||
* all errors that ocurred
|
||||
* @param req
|
||||
* HttpServletRequest
|
||||
* @param resp
|
||||
* HttpServletResponse
|
||||
* @throws WebdavException
|
||||
* if an error in the underlying store occurs
|
||||
*/
|
||||
private void copyFolder(ITransaction transaction, String sourcePath,
|
||||
String destinationPath, Hashtable<String, Integer> errorList,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws WebdavException {
|
||||
|
||||
_store.createFolder(transaction, destinationPath);
|
||||
boolean infiniteDepth = true;
|
||||
String depth = req.getHeader("Depth");
|
||||
if (depth != null) {
|
||||
if (depth.equals("0")) {
|
||||
infiniteDepth = false;
|
||||
}
|
||||
}
|
||||
if (infiniteDepth) {
|
||||
String[] children = _store
|
||||
.getChildrenNames(transaction, sourcePath);
|
||||
children = children == null ? new String[] {} : children;
|
||||
|
||||
StoredObject childSo;
|
||||
for (int i = children.length - 1; i >= 0; i--) {
|
||||
children[i] = "/" + children[i];
|
||||
try {
|
||||
childSo = _store.getStoredObject(transaction,
|
||||
(sourcePath + children[i]));
|
||||
if (childSo.isResource()) {
|
||||
_store.createResource(transaction, destinationPath
|
||||
+ children[i]);
|
||||
long resourceLength = _store.setResourceContent(
|
||||
transaction, destinationPath + children[i],
|
||||
_store.getResourceContent(transaction,
|
||||
sourcePath + children[i]), null, null);
|
||||
|
||||
if (resourceLength != -1) {
|
||||
StoredObject destinationSo = _store
|
||||
.getStoredObject(transaction,
|
||||
destinationPath + children[i]);
|
||||
destinationSo.setResourceLength(resourceLength);
|
||||
}
|
||||
|
||||
} else {
|
||||
copyFolder(transaction, sourcePath + children[i],
|
||||
destinationPath + children[i], errorList, req,
|
||||
resp);
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
errorList.put(destinationPath + children[i], new Integer(
|
||||
WebdavStatus.SC_FORBIDDEN));
|
||||
} catch (ObjectNotFoundException e) {
|
||||
errorList.put(destinationPath + children[i], new Integer(
|
||||
WebdavStatus.SC_NOT_FOUND));
|
||||
} catch (ObjectAlreadyExistsException e) {
|
||||
errorList.put(destinationPath + children[i], new Integer(
|
||||
WebdavStatus.SC_CONFLICT));
|
||||
} catch (WebdavException e) {
|
||||
errorList.put(destinationPath + children[i], new Integer(
|
||||
WebdavStatus.SC_INTERNAL_SERVER_ERROR));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.ITransaction;
|
||||
import cn.tenfell.webos.common.webdav.sf.IWebdavStore;
|
||||
import cn.tenfell.webos.common.webdav.sf.StoredObject;
|
||||
import cn.tenfell.webos.common.webdav.sf.WebdavStatus;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.*;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.ResourceLocks;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletRequest;
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class DoDelete extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoDelete.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoDelete(IWebdavStore store, ResourceLocks resourceLocks,
|
||||
boolean readOnly) {
|
||||
_store = store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (!_readOnly) {
|
||||
String path = getRelativePath(req);
|
||||
String parentPath = getParentPath(getCleanPath(path));
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // parent is locked
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, path)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // resource is locked
|
||||
}
|
||||
|
||||
String tempLockOwner = "doDelete" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
try {
|
||||
errorList = new Hashtable<String, Integer>();
|
||||
deleteResource(transaction, path, errorList, req, resp);
|
||||
if (!errorList.isEmpty()) {
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (ObjectAlreadyExistsException e) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
path, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes the recources at "path"
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param path
|
||||
* the folder to be deleted
|
||||
* @param errorList
|
||||
* all errors that ocurred
|
||||
* @param req
|
||||
* HttpServletRequest
|
||||
* @param resp
|
||||
* HttpServletResponse
|
||||
* @throws WebdavException
|
||||
* if an error in the underlying store occurs
|
||||
* @throws IOException
|
||||
* when an error occurs while sending the response
|
||||
*/
|
||||
public void deleteResource(ITransaction transaction, String path,
|
||||
Hashtable<String, Integer> errorList, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, WebdavException {
|
||||
|
||||
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||
|
||||
if (!_readOnly) {
|
||||
|
||||
StoredObject so = _store.getStoredObject(transaction, path);
|
||||
if (so != null) {
|
||||
|
||||
if (so.isResource()) {
|
||||
_store.removeObject(transaction, path);
|
||||
} else {
|
||||
if (so.isFolder()) {
|
||||
deleteFolder(transaction, path, errorList, req, resp);
|
||||
_store.removeObject(transaction, path);
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||
}
|
||||
so = null;
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* helper method of deleteResource() deletes the folder and all of its
|
||||
* contents
|
||||
*
|
||||
* @param transaction
|
||||
* indicates that the method is within the scope of a WebDAV
|
||||
* transaction
|
||||
* @param path
|
||||
* the folder to be deleted
|
||||
* @param errorList
|
||||
* all errors that ocurred
|
||||
* @param req
|
||||
* HttpServletRequest
|
||||
* @param resp
|
||||
* HttpServletResponse
|
||||
* @throws WebdavException
|
||||
* if an error in the underlying store occurs
|
||||
*/
|
||||
private void deleteFolder(ITransaction transaction, String path,
|
||||
Hashtable<String, Integer> errorList, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws WebdavException {
|
||||
|
||||
String[] children = _store.getChildrenNames(transaction, path);
|
||||
children = children == null ? new String[] {} : children;
|
||||
StoredObject so = null;
|
||||
for (int i = children.length - 1; i >= 0; i--) {
|
||||
children[i] = "/" + children[i];
|
||||
try {
|
||||
so = _store.getStoredObject(transaction, path + children[i]);
|
||||
if (so.isResource()) {
|
||||
_store.removeObject(transaction, path + children[i]);
|
||||
|
||||
} else {
|
||||
deleteFolder(transaction, path + children[i], errorList,
|
||||
req, resp);
|
||||
|
||||
_store.removeObject(transaction, path + children[i]);
|
||||
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
errorList.put(path + children[i], new Integer(
|
||||
WebdavStatus.SC_FORBIDDEN));
|
||||
} catch (ObjectNotFoundException e) {
|
||||
errorList.put(path + children[i], new Integer(
|
||||
WebdavStatus.SC_NOT_FOUND));
|
||||
} catch (WebdavException e) {
|
||||
errorList.put(path + children[i], new Integer(
|
||||
WebdavStatus.SC_INTERNAL_SERVER_ERROR));
|
||||
}
|
||||
}
|
||||
so = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.tenfell.webos.common.webdav.sf.*;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.ResourceLocks;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
public class DoGet extends DoHead {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoGet.class);
|
||||
|
||||
public DoGet(IWebdavStore store, String dftIndexFile, String insteadOf404,
|
||||
ResourceLocks resourceLocks, IMimeTyper mimeTyper,
|
||||
int contentLengthHeader) {
|
||||
super(store, dftIndexFile, insteadOf404, resourceLocks, mimeTyper,
|
||||
contentLengthHeader);
|
||||
|
||||
}
|
||||
|
||||
protected void doBody(ITransaction transaction, HttpServletResponse resp,
|
||||
String path) {
|
||||
|
||||
try {
|
||||
StoredObject so = _store.getStoredObject(transaction, path);
|
||||
if (so.isNullResource()) {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
OutputStream out = resp.getOutputStream();
|
||||
InputStream in = _store.getResourceContent(transaction, path);
|
||||
try {
|
||||
if (in != null) {
|
||||
LOG.debug("开始 {}, ", path);
|
||||
IoUtil.copy(in, out);
|
||||
LOG.debug("结束 {}", path);
|
||||
}
|
||||
} finally {
|
||||
// flushing causes a IOE if a file is opened on the webserver
|
||||
// client disconnected before server finished sending response
|
||||
try {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("{} Closing InputStream causes Exception!\n", path
|
||||
,e);
|
||||
}
|
||||
try {
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
LOG.warn("{} Flushing OutputStream causes Exception!\n", path
|
||||
,e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("{} doBody causes Exception!\n", path
|
||||
,e);
|
||||
LOG.trace(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected void folderBody(ITransaction transaction, String path,
|
||||
HttpServletResponse resp, HttpServletRequest req)
|
||||
throws IOException {
|
||||
|
||||
StoredObject so = _store.getStoredObject(transaction, path);
|
||||
if (so == null) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
} else {
|
||||
|
||||
if (so.isNullResource()) {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (so.isFolder()) {
|
||||
// TODO some folder response (for browsers, DAV tools
|
||||
// use propfind) in html?
|
||||
Locale locale = req.getLocale();
|
||||
DateFormat shortDF= getDateTimeFormat(req.getLocale());
|
||||
resp.setContentType("text/html");
|
||||
resp.setCharacterEncoding("UTF8");
|
||||
OutputStream out = resp.getOutputStream();
|
||||
String[] children = _store.getChildrenNames(transaction, path);
|
||||
// Make sure it's not null
|
||||
children = children == null ? new String[] {} : children;
|
||||
// Sort by name
|
||||
Arrays.sort(children);
|
||||
StringBuilder childrenTemp = new StringBuilder();
|
||||
childrenTemp.append("<html><head><title>Content of folder");
|
||||
childrenTemp.append(path);
|
||||
childrenTemp.append("</title><style type=\"text/css\">");
|
||||
childrenTemp.append(getCSS());
|
||||
childrenTemp.append("</style></head>");
|
||||
childrenTemp.append("<body>");
|
||||
childrenTemp.append(getHeader(transaction, path, resp, req));
|
||||
childrenTemp.append("<table>");
|
||||
childrenTemp.append("<tr><th>Name</th><th>Size</th><th>Created</th><th>Modified</th></tr>");
|
||||
childrenTemp.append("<tr>");
|
||||
childrenTemp.append("<td colspan=\"4\"><a href=\"../\">Parent</a></td></tr>");
|
||||
boolean isEven= false;
|
||||
for (String child : children)
|
||||
{
|
||||
isEven= !isEven;
|
||||
childrenTemp.append("<tr class=\"");
|
||||
childrenTemp.append(isEven ? "even" : "odd");
|
||||
childrenTemp.append("\">");
|
||||
childrenTemp.append("<td>");
|
||||
childrenTemp.append("<a href=\"");
|
||||
childrenTemp.append(URLUtil.encode(child));
|
||||
StoredObject obj= _store.getStoredObject(transaction, path+"/"+child);
|
||||
if (obj == null)
|
||||
{
|
||||
LOG.error("Should not return null for "+path+"/"+child);
|
||||
}
|
||||
if (obj != null && obj.isFolder())
|
||||
{
|
||||
childrenTemp.append("/");
|
||||
}
|
||||
childrenTemp.append("\">");
|
||||
childrenTemp.append(child);
|
||||
childrenTemp.append("</a></td>");
|
||||
if (obj != null && obj.isFolder())
|
||||
{
|
||||
childrenTemp.append("<td>Folder</td>");
|
||||
}
|
||||
else
|
||||
{
|
||||
childrenTemp.append("<td>");
|
||||
if (obj != null )
|
||||
{
|
||||
childrenTemp.append(obj.getResourceLength());
|
||||
}
|
||||
else
|
||||
{
|
||||
childrenTemp.append("Unknown");
|
||||
}
|
||||
childrenTemp.append(" Bytes</td>");
|
||||
}
|
||||
if (obj != null && obj.getCreationDate() != null)
|
||||
{
|
||||
childrenTemp.append("<td>");
|
||||
childrenTemp.append(shortDF.format(obj.getCreationDate()));
|
||||
childrenTemp.append("</td>");
|
||||
}
|
||||
else
|
||||
{
|
||||
childrenTemp.append("<td></td>");
|
||||
}
|
||||
if (obj != null && obj.getLastModified() != null)
|
||||
{
|
||||
childrenTemp.append("<td>");
|
||||
childrenTemp.append(shortDF.format(obj.getLastModified()));
|
||||
childrenTemp.append("</td>");
|
||||
}
|
||||
else
|
||||
{
|
||||
childrenTemp.append("<td></td>");
|
||||
}
|
||||
childrenTemp.append("</tr>");
|
||||
}
|
||||
childrenTemp.append("</table>");
|
||||
childrenTemp.append(getFooter(transaction, path, resp, req));
|
||||
childrenTemp.append("</body></html>");
|
||||
out.write(childrenTemp.toString().getBytes("UTF-8"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CSS styles used to display the HTML representation
|
||||
* of the webdav content.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String getCSS()
|
||||
{
|
||||
// The default styles to use
|
||||
String retVal= "body {\n"+
|
||||
" font-family: Arial, Helvetica, sans-serif;\n"+
|
||||
"}\n"+
|
||||
"h1 {\n"+
|
||||
" font-size: 1.5em;\n"+
|
||||
"}\n"+
|
||||
"th {\n"+
|
||||
" background-color: #9DACBF;\n"+
|
||||
"}\n"+
|
||||
"table {\n"+
|
||||
" border-top-style: solid;\n"+
|
||||
" border-right-style: solid;\n"+
|
||||
" border-bottom-style: solid;\n"+
|
||||
" border-left-style: solid;\n"+
|
||||
"}\n"+
|
||||
"td {\n"+
|
||||
" margin: 0px;\n"+
|
||||
" padding-top: 2px;\n"+
|
||||
" padding-right: 5px;\n"+
|
||||
" padding-bottom: 2px;\n"+
|
||||
" padding-left: 5px;\n"+
|
||||
"}\n"+
|
||||
"tr.even {\n"+
|
||||
" background-color: #CCCCCC;\n"+
|
||||
"}\n"+
|
||||
"tr.odd {\n"+
|
||||
" background-color: #FFFFFF;\n"+
|
||||
"}\n"+
|
||||
"";
|
||||
try
|
||||
{
|
||||
// Try loading one via class loader and use that one instead
|
||||
ClassLoader cl = getClass().getClassLoader();
|
||||
InputStream iStream = cl.getResourceAsStream("webdav.css");
|
||||
if(iStream != null)
|
||||
{
|
||||
// Found css via class loader, use that one
|
||||
StringBuffer out = new StringBuffer();
|
||||
byte[] b = new byte[4096];
|
||||
for (int n; (n = iStream.read(b)) != -1;)
|
||||
{
|
||||
out.append(new String(b, 0, n));
|
||||
}
|
||||
retVal= out.toString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOG.error("Error in reading webdav.css", ex);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the header to be displayed in front of the folder content
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* @param resp
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
protected String getHeader(ITransaction transaction, String path,
|
||||
HttpServletResponse resp, HttpServletRequest req)
|
||||
{
|
||||
return "<h1>Content of folder "+path+"</h1>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the footer to be displayed after the folder content
|
||||
*
|
||||
* @param transaction
|
||||
* @param path
|
||||
* @param resp
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
protected String getFooter(ITransaction transaction, String path,
|
||||
HttpServletResponse resp, HttpServletRequest req)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this as the Date/Time format for displaying Creation + Modification dates
|
||||
*
|
||||
* @param browserLocale
|
||||
* @return DateFormat used to display creation and modification dates
|
||||
*/
|
||||
protected DateFormat getDateTimeFormat(Locale browserLocale)
|
||||
{
|
||||
return SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.MEDIUM, browserLocale);
|
||||
}
|
||||
}
|
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.*;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.AccessDeniedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.ObjectAlreadyExistsException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.WebdavException;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.ResourceLocks;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DoHead extends AbstractMethod {
|
||||
|
||||
protected String _dftIndexFile;
|
||||
protected IWebdavStore _store;
|
||||
protected String _insteadOf404;
|
||||
protected ResourceLocks _resourceLocks;
|
||||
protected IMimeTyper _mimeTyper;
|
||||
protected int _contentLength;
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoHead.class);
|
||||
|
||||
public DoHead(IWebdavStore store, String dftIndexFile, String insteadOf404,
|
||||
ResourceLocks resourceLocks, IMimeTyper mimeTyper,
|
||||
int contentLengthHeader) {
|
||||
_store = store;
|
||||
_dftIndexFile = dftIndexFile;
|
||||
_insteadOf404 = insteadOf404;
|
||||
_resourceLocks = resourceLocks;
|
||||
_mimeTyper = mimeTyper;
|
||||
_contentLength = contentLengthHeader;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
|
||||
// determines if the uri exists.
|
||||
|
||||
boolean bUriExists = false;
|
||||
|
||||
String path = getRelativePath(req);
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
StoredObject so;
|
||||
try {
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
if (so == null) {
|
||||
if (this._insteadOf404 != null && !_insteadOf404.trim().equals("")) {
|
||||
path = this._insteadOf404;
|
||||
so = _store.getStoredObject(transaction, this._insteadOf404);
|
||||
}
|
||||
} else
|
||||
bUriExists = true;
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (so != null) {
|
||||
if (so.isFolder()) {
|
||||
if (_dftIndexFile != null && !_dftIndexFile.trim().equals("")) {
|
||||
resp.sendRedirect(resp.encodeRedirectURL(req
|
||||
.getRequestURI()
|
||||
+ this._dftIndexFile));
|
||||
return;
|
||||
}
|
||||
} else if (so.isNullResource()) {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
String tempLockOwner = "doGet" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
try {
|
||||
|
||||
String eTagMatch = req.getHeader("If-None-Match");
|
||||
if (eTagMatch != null) {
|
||||
if (eTagMatch.equals(getETag(so))) {
|
||||
resp.setStatus(WebdavStatus.SC_NOT_MODIFIED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (so.isResource()) {
|
||||
// path points to a file but ends with / or \
|
||||
if (path.endsWith("/") || (path.endsWith("\\"))) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND,
|
||||
req.getRequestURI());
|
||||
} else {
|
||||
|
||||
// setting headers
|
||||
long lastModified = so.getLastModified().getTime();
|
||||
resp.setDateHeader("last-modified", lastModified);
|
||||
|
||||
String eTag = getETag(so);
|
||||
resp.addHeader("ETag", eTag);
|
||||
|
||||
long resourceLength = so.getResourceLength();
|
||||
|
||||
if (_contentLength == 1) {
|
||||
if (resourceLength > 0) {
|
||||
if (resourceLength <= Integer.MAX_VALUE) {
|
||||
resp
|
||||
.setContentLength((int) resourceLength);
|
||||
} else {
|
||||
resp.setHeader("content-length", ""
|
||||
+ resourceLength);
|
||||
// is "content-length" the right header?
|
||||
// is long a valid format?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String mimeType = _mimeTyper.getMimeType(transaction, path);
|
||||
if (mimeType != null) {
|
||||
resp.setContentType(mimeType);
|
||||
} else {
|
||||
int lastSlash = path.replace('\\', '/')
|
||||
.lastIndexOf('/');
|
||||
int lastDot = path.indexOf(".", lastSlash);
|
||||
if (lastDot == -1) {
|
||||
resp.setContentType("text/html");
|
||||
}
|
||||
}
|
||||
|
||||
doBody(transaction, resp, path);
|
||||
}
|
||||
} else {
|
||||
folderBody(transaction, path, resp, req);
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (ObjectAlreadyExistsException e) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
path, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
folderBody(transaction, path, resp, req);
|
||||
}
|
||||
|
||||
if (!bUriExists)
|
||||
resp.setStatus(WebdavStatus.SC_NOT_FOUND);
|
||||
|
||||
}
|
||||
|
||||
protected void folderBody(ITransaction transaction, String path,
|
||||
HttpServletResponse resp, HttpServletRequest req)
|
||||
throws IOException {
|
||||
// no body for HEAD
|
||||
}
|
||||
|
||||
protected void doBody(ITransaction transaction, HttpServletResponse resp,
|
||||
String path) throws IOException {
|
||||
// no body for HEAD
|
||||
}
|
||||
}
|
@ -0,0 +1,587 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.*;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.WebdavException;
|
||||
import cn.tenfell.webos.common.webdav.sf.fromcatalina.XMLWriter;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.IResourceLocks;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.LockedObject;
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class DoLock extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoLock.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private IResourceLocks _resourceLocks;
|
||||
private boolean _readOnly;
|
||||
|
||||
private boolean _macLockRequest = false;
|
||||
|
||||
private boolean _exclusive = false;
|
||||
private String _type = null;
|
||||
private String _lockOwner = null;
|
||||
|
||||
private String _path = null;
|
||||
private String _parentPath = null;
|
||||
|
||||
private String _userAgent = null;
|
||||
|
||||
public DoLock(IWebdavStore store, IResourceLocks resourceLocks,
|
||||
boolean readOnly) {
|
||||
_store = store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (_readOnly) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
} else {
|
||||
_path = getRelativePath(req);
|
||||
_parentPath = getParentPath(getCleanPath(_path));
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, _path)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // resource is locked
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, _parentPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // parent is locked
|
||||
}
|
||||
|
||||
// Mac OS Finder (whether 10.4.x or 10.5) can't store files
|
||||
// because executing a LOCK without lock information causes a
|
||||
// SC_BAD_REQUEST
|
||||
_userAgent = req.getHeader("User-Agent");
|
||||
if (_userAgent != null && _userAgent.indexOf("Darwin") != -1) {
|
||||
_macLockRequest = true;
|
||||
|
||||
String timeString = new Long(System.currentTimeMillis())
|
||||
.toString();
|
||||
_lockOwner = _userAgent.concat(timeString);
|
||||
}
|
||||
|
||||
String tempLockOwner = "doLock" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
if (_resourceLocks.lock(transaction, _path, tempLockOwner, false,
|
||||
0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||
try {
|
||||
if (req.getHeader("If") != null) {
|
||||
doRefreshLock(transaction, req, resp);
|
||||
} else {
|
||||
doLock(transaction, req, resp);
|
||||
}
|
||||
} catch (LockFailedException e) {
|
||||
resp.sendError(WebdavStatus.SC_LOCKED);
|
||||
LOG.error("Lockfailed exception", e);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
_path, tempLockOwner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doLock(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
|
||||
StoredObject so = _store.getStoredObject(transaction, _path);
|
||||
|
||||
if (so != null) {
|
||||
doLocking(transaction, req, resp);
|
||||
} else {
|
||||
// resource doesn't exist, null-resource lock
|
||||
doNullResourceLock(transaction, req, resp);
|
||||
}
|
||||
|
||||
so = null;
|
||||
_exclusive = false;
|
||||
_type = null;
|
||||
_lockOwner = null;
|
||||
|
||||
}
|
||||
|
||||
private void doLocking(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException {
|
||||
|
||||
// Tests if LockObject on requested path exists, and if so, tests
|
||||
// exclusivity
|
||||
LockedObject lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||
_path);
|
||||
if (lo != null) {
|
||||
if (lo.isExclusive()) {
|
||||
sendLockFailError(transaction, req, resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
// Thats the locking itself
|
||||
executeLock(transaction, req, resp);
|
||||
|
||||
}catch (LockFailedException e) {
|
||||
sendLockFailError(transaction, req, resp);
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
lo = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void doNullResourceLock(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws IOException {
|
||||
|
||||
StoredObject parentSo, nullSo = null;
|
||||
|
||||
try {
|
||||
parentSo = _store.getStoredObject(transaction, _parentPath);
|
||||
if (_parentPath != null && parentSo == null) {
|
||||
_store.createFolder(transaction, _parentPath);
|
||||
} else if (_parentPath != null && parentSo != null
|
||||
&& parentSo.isResource()) {
|
||||
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
nullSo = _store.getStoredObject(transaction, _path);
|
||||
if (nullSo == null) {
|
||||
// resource doesn't exist
|
||||
_store.createResource(transaction, _path);
|
||||
|
||||
// Transmit expects 204 response-code, not 201
|
||||
if (_userAgent != null && _userAgent.indexOf("Transmit") != -1) {
|
||||
LOG
|
||||
.trace("DoLock.execute() : do workaround for user agent '"
|
||||
+ _userAgent + "'");
|
||||
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||
} else {
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
}
|
||||
|
||||
} else {
|
||||
// resource already exists, could not execute null-resource lock
|
||||
sendLockFailError(transaction, req, resp);
|
||||
return;
|
||||
}
|
||||
nullSo = _store.getStoredObject(transaction, _path);
|
||||
if (nullSo == null) {
|
||||
nullSo = new StoredObject();
|
||||
}
|
||||
// define the newly created resource as null-resource
|
||||
nullSo.setNullResource(true);
|
||||
|
||||
// Thats the locking itself
|
||||
executeLock(transaction, req, resp);
|
||||
|
||||
} catch (LockFailedException e) {
|
||||
sendLockFailError(transaction, req, resp);
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
LOG.error("Webdav exception", e);
|
||||
} catch (Exception e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
LOG.error("Servlet exception", e);
|
||||
} finally {
|
||||
parentSo = null;
|
||||
nullSo = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void doRefreshLock(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws IOException, LockFailedException {
|
||||
|
||||
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||
String lockToken = null;
|
||||
if (lockTokens != null)
|
||||
lockToken = lockTokens[0];
|
||||
|
||||
if (lockToken != null) {
|
||||
// Getting LockObject of specified lockToken in If header
|
||||
LockedObject refreshLo = _resourceLocks.getLockedObjectByID(
|
||||
transaction, lockToken);
|
||||
if (refreshLo != null) {
|
||||
int timeout = getTimeout(transaction, req);
|
||||
|
||||
refreshLo.refreshTimeout(timeout);
|
||||
// sending success response
|
||||
generateXMLReport(transaction, resp, refreshLo);
|
||||
|
||||
refreshLo = null;
|
||||
} else {
|
||||
// no LockObject to given lockToken
|
||||
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------- helper methods
|
||||
|
||||
/**
|
||||
* Executes the LOCK
|
||||
*/
|
||||
private void executeLock(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws LockFailedException, IOException {
|
||||
|
||||
// Mac OS lock request workaround
|
||||
if (_macLockRequest) {
|
||||
LOG.trace("DoLock.execute() : do workaround for user agent '"
|
||||
+ _userAgent + "'");
|
||||
|
||||
doMacLockRequestWorkaround(transaction, req, resp);
|
||||
} else {
|
||||
// Getting LockInformation from request
|
||||
if (getLockInformation(transaction, req, resp)) {
|
||||
int depth = getDepth(req);
|
||||
int lockDuration = getTimeout(transaction, req);
|
||||
|
||||
boolean lockSuccess = false;
|
||||
if (_exclusive) {
|
||||
lockSuccess = _resourceLocks.exclusiveLock(transaction,
|
||||
_path, _lockOwner, depth, lockDuration);
|
||||
} else {
|
||||
lockSuccess = _resourceLocks.sharedLock(transaction, _path,
|
||||
_lockOwner, depth, lockDuration);
|
||||
}
|
||||
|
||||
if (lockSuccess) {
|
||||
// Locks successfully placed - return information about
|
||||
LockedObject lo = _resourceLocks.getLockedObjectByPath(
|
||||
transaction, _path);
|
||||
if (lo != null) {
|
||||
generateXMLReport(transaction, resp, lo);
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
sendLockFailError(transaction, req, resp);
|
||||
|
||||
throw new LockFailedException();
|
||||
}
|
||||
} else {
|
||||
// information for LOCK could not be read successfully
|
||||
resp.setContentType("text/xml; charset=UTF-8");
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get the LockInformation from LOCK request
|
||||
*/
|
||||
private boolean getLockInformation(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws IOException {
|
||||
|
||||
Node lockInfoNode = null;
|
||||
DocumentBuilder documentBuilder = null;
|
||||
|
||||
documentBuilder = getDocumentBuilder();
|
||||
try {
|
||||
Document document = documentBuilder.parse(new InputSource(req
|
||||
.getInputStream()));
|
||||
|
||||
// Get the root element of the document
|
||||
Element rootElement = document.getDocumentElement();
|
||||
|
||||
lockInfoNode = rootElement;
|
||||
|
||||
if (lockInfoNode != null) {
|
||||
NodeList childList = lockInfoNode.getChildNodes();
|
||||
Node lockScopeNode = null;
|
||||
Node lockTypeNode = null;
|
||||
Node lockOwnerNode = null;
|
||||
|
||||
Node currentNode = null;
|
||||
String nodeName = null;
|
||||
|
||||
for (int i = 0; i < childList.getLength(); i++) {
|
||||
currentNode = childList.item(i);
|
||||
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE
|
||||
|| currentNode.getNodeType() == Node.TEXT_NODE) {
|
||||
|
||||
nodeName = currentNode.getNodeName();
|
||||
|
||||
if (nodeName.endsWith("locktype")) {
|
||||
lockTypeNode = currentNode;
|
||||
}
|
||||
if (nodeName.endsWith("lockscope")) {
|
||||
lockScopeNode = currentNode;
|
||||
}
|
||||
if (nodeName.endsWith("owner")) {
|
||||
lockOwnerNode = currentNode;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (lockScopeNode != null) {
|
||||
String scope = null;
|
||||
childList = lockScopeNode.getChildNodes();
|
||||
for (int i = 0; i < childList.getLength(); i++) {
|
||||
currentNode = childList.item(i);
|
||||
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||
scope = currentNode.getNodeName();
|
||||
|
||||
if (scope.endsWith("exclusive")) {
|
||||
_exclusive = true;
|
||||
} else if (scope.equals("shared")) {
|
||||
_exclusive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scope == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lockTypeNode != null) {
|
||||
childList = lockTypeNode.getChildNodes();
|
||||
for (int i = 0; i < childList.getLength(); i++) {
|
||||
currentNode = childList.item(i);
|
||||
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||
_type = currentNode.getNodeName();
|
||||
|
||||
if (_type.endsWith("write")) {
|
||||
_type = "write";
|
||||
} else if (_type.equals("read")) {
|
||||
_type = "read";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_type == null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lockOwnerNode != null) {
|
||||
childList = lockOwnerNode.getChildNodes();
|
||||
for (int i = 0; i < childList.getLength(); i++) {
|
||||
currentNode = childList.item(i);
|
||||
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE
|
||||
|| currentNode.getNodeType() == Node.TEXT_NODE) {
|
||||
if (currentNode.getFirstChild() != null) {
|
||||
_lockOwner = currentNode.getFirstChild()
|
||||
.getNodeValue();
|
||||
} else {
|
||||
_lockOwner = currentNode.toString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_lockOwner == null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (DOMException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
LOG.error("DOM exception", e);
|
||||
return false;
|
||||
} catch (SAXException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
LOG.error("SAX exception", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ties to read the timeout from request
|
||||
*/
|
||||
private int getTimeout(ITransaction transaction, HttpServletRequest req) {
|
||||
|
||||
int lockDuration = DEFAULT_TIMEOUT;
|
||||
String lockDurationStr = req.getHeader("Timeout");
|
||||
|
||||
if (lockDurationStr == null) {
|
||||
lockDuration = DEFAULT_TIMEOUT;
|
||||
} else {
|
||||
int commaPos = lockDurationStr.indexOf(',');
|
||||
// if multiple timeouts, just use the first one
|
||||
if (commaPos != -1) {
|
||||
lockDurationStr = lockDurationStr.substring(0, commaPos);
|
||||
}
|
||||
if (lockDurationStr.startsWith("Second-")) {
|
||||
lockDuration = new Integer(lockDurationStr.substring(7))
|
||||
.intValue();
|
||||
} else {
|
||||
if (lockDurationStr.equalsIgnoreCase("infinity")) {
|
||||
lockDuration = MAX_TIMEOUT;
|
||||
} else {
|
||||
try {
|
||||
lockDuration = new Integer(lockDurationStr).intValue();
|
||||
} catch (NumberFormatException e) {
|
||||
lockDuration = MAX_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lockDuration <= 0) {
|
||||
lockDuration = DEFAULT_TIMEOUT;
|
||||
}
|
||||
if (lockDuration > MAX_TIMEOUT) {
|
||||
lockDuration = MAX_TIMEOUT;
|
||||
}
|
||||
}
|
||||
return lockDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the response XML with all lock information
|
||||
*/
|
||||
private void generateXMLReport(ITransaction transaction,
|
||||
HttpServletResponse resp, LockedObject lo) throws IOException {
|
||||
|
||||
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||
namespaces.put("DAV:", "D");
|
||||
|
||||
resp.setStatus(WebdavStatus.SC_OK);
|
||||
resp.setContentType("text/xml; charset=UTF-8");
|
||||
|
||||
XMLWriter generatedXML = new XMLWriter(resp.getWriter(), namespaces);
|
||||
generatedXML.writeXMLHeader();
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::activelock", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||
generatedXML.writeProperty("DAV::" + _type);
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||
if (_exclusive) {
|
||||
generatedXML.writeProperty("DAV::exclusive");
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::shared");
|
||||
}
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||
|
||||
int depth = lo.getLockDepth();
|
||||
|
||||
generatedXML.writeElement("DAV::depth", XMLWriter.OPENING);
|
||||
if (depth == INFINITY) {
|
||||
generatedXML.writeText("Infinity");
|
||||
} else {
|
||||
generatedXML.writeText(String.valueOf(depth));
|
||||
}
|
||||
generatedXML.writeElement("DAV::depth", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::owner", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
generatedXML.writeText(_lockOwner);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::owner", XMLWriter.CLOSING);
|
||||
|
||||
long timeout = lo.getTimeoutMillis();
|
||||
generatedXML.writeElement("DAV::timeout", XMLWriter.OPENING);
|
||||
generatedXML.writeText("Second-" + timeout / 1000);
|
||||
generatedXML.writeElement("DAV::timeout", XMLWriter.CLOSING);
|
||||
|
||||
String lockToken = lo.getID();
|
||||
generatedXML.writeElement("DAV::locktoken", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
generatedXML.writeText("opaquelocktoken:" + lockToken);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::locktoken", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::activelock", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
|
||||
resp.addHeader("Lock-Token", "<opaquelocktoken:" + lockToken + ">");
|
||||
|
||||
generatedXML.sendData();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the lock for a Mac OS Finder client
|
||||
*/
|
||||
private void doMacLockRequestWorkaround(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws LockFailedException, IOException {
|
||||
LockedObject lo;
|
||||
int depth = getDepth(req);
|
||||
int lockDuration = getTimeout(transaction, req);
|
||||
if (lockDuration < 0 || lockDuration > MAX_TIMEOUT)
|
||||
lockDuration = DEFAULT_TIMEOUT;
|
||||
|
||||
boolean lockSuccess = false;
|
||||
lockSuccess = _resourceLocks.exclusiveLock(transaction, _path,
|
||||
_lockOwner, depth, lockDuration);
|
||||
|
||||
if (lockSuccess) {
|
||||
// Locks successfully placed - return information about
|
||||
lo = _resourceLocks.getLockedObjectByPath(transaction, _path);
|
||||
if (lo != null) {
|
||||
generateXMLReport(transaction, resp, lo);
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
// Locking was not successful
|
||||
sendLockFailError(transaction, req, resp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an error report to the client
|
||||
*/
|
||||
private void sendLockFailError(ITransaction transaction,
|
||||
HttpServletRequest req, HttpServletResponse resp)
|
||||
throws IOException {
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
errorList.put(_path, WebdavStatus.SC_LOCKED);
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.ITransaction;
|
||||
import cn.tenfell.webos.common.webdav.sf.IWebdavStore;
|
||||
import cn.tenfell.webos.common.webdav.sf.StoredObject;
|
||||
import cn.tenfell.webos.common.webdav.sf.WebdavStatus;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.AccessDeniedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.WebdavException;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.IResourceLocks;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.LockedObject;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletRequest;
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class DoMkcol extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoMkcol.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private IResourceLocks _resourceLocks;
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoMkcol(IWebdavStore store, IResourceLocks resourceLocks,
|
||||
boolean readOnly) {
|
||||
_store = store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (!_readOnly) {
|
||||
String path = getRelativePath(req);
|
||||
String parentPath = getParentPath(getCleanPath(path));
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||
// TODO remove
|
||||
LOG
|
||||
.trace("MkCol on locked resource (parentPath) not executable!"
|
||||
+ "\n Sending SC_FORBIDDEN (403) error response!");
|
||||
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
String tempLockOwner = "doMkcol" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
StoredObject parentSo, so = null;
|
||||
try {
|
||||
parentSo = _store.getStoredObject(transaction, parentPath);
|
||||
if (parentSo == null) {
|
||||
// parent not exists
|
||||
resp.sendError(WebdavStatus.SC_CONFLICT);
|
||||
return;
|
||||
}
|
||||
if (parentPath != null && parentSo.isFolder()) {
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
if (so == null) {
|
||||
_store.createFolder(transaction, path);
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
} else {
|
||||
// object already exists
|
||||
if (so.isNullResource()) {
|
||||
|
||||
LockedObject nullResourceLo = _resourceLocks
|
||||
.getLockedObjectByPath(transaction,
|
||||
path);
|
||||
if (nullResourceLo == null) {
|
||||
resp
|
||||
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
String nullResourceLockToken = nullResourceLo
|
||||
.getID();
|
||||
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||
String lockToken = null;
|
||||
if (lockTokens != null)
|
||||
lockToken = lockTokens[0];
|
||||
else {
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
if (lockToken.equals(nullResourceLockToken)) {
|
||||
so.setNullResource(false);
|
||||
so.setFolder(true);
|
||||
|
||||
String[] nullResourceLockOwners = nullResourceLo
|
||||
.getOwner();
|
||||
String owner = null;
|
||||
if (nullResourceLockOwners != null)
|
||||
owner = nullResourceLockOwners[0];
|
||||
|
||||
if (_resourceLocks.unlock(transaction,
|
||||
lockToken, owner)) {
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
} else {
|
||||
resp
|
||||
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO remove
|
||||
LOG
|
||||
.trace("MkCol on lock-null-resource with wrong lock-token!"
|
||||
+ "\n Sending multistatus error report!");
|
||||
|
||||
errorList.put(path, WebdavStatus.SC_LOCKED);
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
|
||||
} else {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp
|
||||
.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (parentPath != null && parentSo.isResource()) {
|
||||
// TODO remove
|
||||
LOG
|
||||
.trace("MkCol on resource is not executable"
|
||||
+ "\n Sending SC_METHOD_NOT_ALLOWED (405) error response!");
|
||||
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(parentSo);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
path, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.ITransaction;
|
||||
import cn.tenfell.webos.common.webdav.sf.IWebdavStore;
|
||||
import cn.tenfell.webos.common.webdav.sf.WebdavStatus;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.AccessDeniedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.ObjectAlreadyExistsException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.WebdavException;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.ResourceLocks;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletRequest;
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class DoMove extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoMove.class);
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
private DoDelete _doDelete;
|
||||
private DoCopy _doCopy;
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoMove(IWebdavStore _store, ResourceLocks resourceLocks, DoDelete doDelete,
|
||||
DoCopy doCopy, boolean readOnly) {
|
||||
this._store = _store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_doDelete = doDelete;
|
||||
_doCopy = doCopy;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
|
||||
if (!_readOnly) {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
String sourcePath = getRelativePath(req);
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, sourcePath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return;
|
||||
}
|
||||
|
||||
String destinationPath = parseDestinationHeader(req, resp);
|
||||
if (destinationPath == null) {
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks,
|
||||
destinationPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return;
|
||||
}
|
||||
|
||||
String tempLockOwner = "doMove" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
|
||||
if (_resourceLocks.lock(transaction, sourcePath, tempLockOwner,
|
||||
false, 0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||
try {
|
||||
boolean ok = _store.moveObject(transaction, destinationPath, sourcePath);
|
||||
if (!ok) {
|
||||
if (_doCopy.copyResource(transaction, req, resp)) {
|
||||
|
||||
errorList = new Hashtable<String, Integer>();
|
||||
_doDelete.deleteResource(transaction, sourcePath,
|
||||
errorList, req, resp);
|
||||
if (!errorList.isEmpty()) {
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (ObjectAlreadyExistsException e) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
sourcePath, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
errorList.put(req.getHeader("Destination"),
|
||||
WebdavStatus.SC_LOCKED);
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.IMethodExecutor;
|
||||
import cn.tenfell.webos.common.webdav.sf.ITransaction;
|
||||
import cn.tenfell.webos.common.webdav.sf.WebdavStatus;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletRequest;
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
public class DoNotImplemented implements IMethodExecutor {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoNotImplemented.class);
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoNotImplemented(boolean readOnly) {
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException {
|
||||
LOG.trace("-- " + req.getMethod());
|
||||
|
||||
if (_readOnly) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} else
|
||||
resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED);
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.ITransaction;
|
||||
import cn.tenfell.webos.common.webdav.sf.IWebdavStore;
|
||||
import cn.tenfell.webos.common.webdav.sf.StoredObject;
|
||||
import cn.tenfell.webos.common.webdav.sf.WebdavStatus;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.AccessDeniedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.WebdavException;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.ResourceLocks;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletRequest;
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
public class DoOptions extends DeterminableMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoOptions.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
|
||||
public DoOptions(IWebdavStore store, ResourceLocks resLocks) {
|
||||
_store = store;
|
||||
_resourceLocks = resLocks;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
String tempLockOwner = "doOptions" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
String path = getRelativePath(req);
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
StoredObject so = null;
|
||||
try {
|
||||
resp.addHeader("DAV", "1, 2");
|
||||
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
String methodsAllowed = determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.addHeader("MS-Author-Via", "DAV");
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||
tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,609 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.*;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.AccessDeniedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.WebdavException;
|
||||
import cn.tenfell.webos.common.webdav.sf.fromcatalina.XMLHelper;
|
||||
import cn.tenfell.webos.common.webdav.sf.fromcatalina.XMLWriter;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.LockedObject;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.ResourceLocks;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
public class DoPropfind extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoPropfind.class);
|
||||
|
||||
/**
|
||||
* PROPFIND - Specify a property mask.
|
||||
*/
|
||||
private static final int FIND_BY_PROPERTY = 0;
|
||||
|
||||
/**
|
||||
* PROPFIND - Display all properties.
|
||||
*/
|
||||
private static final int FIND_ALL_PROP = 1;
|
||||
|
||||
/**
|
||||
* PROPFIND - Return property names.
|
||||
*/
|
||||
private static final int FIND_PROPERTY_NAMES = 2;
|
||||
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
private IMimeTyper _mimeTyper;
|
||||
|
||||
private int _depth;
|
||||
|
||||
public DoPropfind(IWebdavStore store, ResourceLocks resLocks,
|
||||
IMimeTyper mimeTyper) {
|
||||
_store = store;
|
||||
_resourceLocks = resLocks;
|
||||
_mimeTyper = mimeTyper;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
// Retrieve the resources
|
||||
String path = getCleanPath(getRelativePath(req));
|
||||
String tempLockOwner = "doPropfind" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
_depth = getDepth(req);
|
||||
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false,
|
||||
_depth, TEMP_TIMEOUT, TEMPORARY)) {
|
||||
|
||||
StoredObject so = null;
|
||||
try {
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
if (so == null) {
|
||||
resp.setContentType("text/xml; charset=UTF-8");
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND, req
|
||||
.getRequestURI());
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<String> properties = null;
|
||||
path = getCleanPath(getRelativePath(req));
|
||||
|
||||
int propertyFindType = FIND_ALL_PROP;
|
||||
Node propNode = null;
|
||||
|
||||
if (req.getContentLength() > 0) {
|
||||
DocumentBuilder documentBuilder = getDocumentBuilder();
|
||||
try {
|
||||
Document document = documentBuilder
|
||||
.parse(new InputSource(req.getInputStream()));
|
||||
// Get the root element of the document
|
||||
Element rootElement = document.getDocumentElement();
|
||||
|
||||
propNode = XMLHelper
|
||||
.findSubElement(rootElement, "prop");
|
||||
if (propNode != null) {
|
||||
propertyFindType = FIND_BY_PROPERTY;
|
||||
} else if (XMLHelper.findSubElement(rootElement,
|
||||
"propname") != null) {
|
||||
propertyFindType = FIND_PROPERTY_NAMES;
|
||||
} else if (XMLHelper.findSubElement(rootElement,
|
||||
"allprop") != null) {
|
||||
propertyFindType = FIND_ALL_PROP;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// no content, which means it is a allprop request
|
||||
propertyFindType = FIND_ALL_PROP;
|
||||
}
|
||||
|
||||
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||
namespaces.put("DAV:", "D");
|
||||
|
||||
if (propertyFindType == FIND_BY_PROPERTY) {
|
||||
propertyFindType = 0;
|
||||
properties = XMLHelper.getPropertiesFromXML(propNode);
|
||||
}
|
||||
|
||||
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
|
||||
resp.setContentType("text/xml; charset=UTF-8");
|
||||
|
||||
// Create multistatus object
|
||||
XMLWriter generatedXML = new XMLWriter(resp.getWriter(),
|
||||
namespaces);
|
||||
generatedXML.writeXMLHeader();
|
||||
generatedXML
|
||||
.writeElement("DAV::multistatus", XMLWriter.OPENING);
|
||||
if (_depth == 0) {
|
||||
parseProperties(transaction, req, generatedXML, path,
|
||||
propertyFindType, properties, _mimeTyper
|
||||
.getMimeType(transaction, path));
|
||||
} else {
|
||||
recursiveParseProperties(transaction, path, req,
|
||||
generatedXML, propertyFindType, properties, _depth,
|
||||
_mimeTyper.getMimeType(transaction, path));
|
||||
}
|
||||
generatedXML
|
||||
.writeElement("DAV::multistatus", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.sendData();
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (WebdavException e) {
|
||||
LOG.warn("Sending internal error!", e);
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||
tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
errorList.put(path, WebdavStatus.SC_LOCKED);
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* goes recursive through all folders. used by propfind
|
||||
*
|
||||
* @param currentPath
|
||||
* the current path
|
||||
* @param req
|
||||
* HttpServletRequest
|
||||
* @param generatedXML
|
||||
* @param propertyFindType
|
||||
* @param properties
|
||||
* @param depth
|
||||
* depth of the propfind
|
||||
* @throws IOException
|
||||
* if an error in the underlying store occurs
|
||||
*/
|
||||
private void recursiveParseProperties(ITransaction transaction,
|
||||
String currentPath, HttpServletRequest req, XMLWriter generatedXML,
|
||||
int propertyFindType, Vector<String> properties, int depth,
|
||||
String mimeType) throws WebdavException {
|
||||
|
||||
parseProperties(transaction, req, generatedXML, currentPath,
|
||||
propertyFindType, properties, mimeType);
|
||||
|
||||
if (depth > 0) {
|
||||
// no need to get name if depth is already zero
|
||||
String[] names = _store.getChildrenNames(transaction, currentPath);
|
||||
names = names == null ? new String[] {} : names;
|
||||
String newPath = null;
|
||||
|
||||
for (String name : names) {
|
||||
newPath = currentPath;
|
||||
if (!(newPath.endsWith("/"))) {
|
||||
newPath += "/";
|
||||
}
|
||||
newPath += name;
|
||||
recursiveParseProperties(transaction, newPath, req,
|
||||
generatedXML, propertyFindType, properties, depth - 1,
|
||||
mimeType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Propfind helper method.
|
||||
*
|
||||
* @param req
|
||||
* The servlet request
|
||||
* @param generatedXML
|
||||
* XML response to the Propfind request
|
||||
* @param path
|
||||
* Path of the current resource
|
||||
* @param type
|
||||
* Propfind type
|
||||
* @param propertiesVector
|
||||
* If the propfind type is find properties by name, then this Vector
|
||||
* contains those properties
|
||||
*/
|
||||
private void parseProperties(ITransaction transaction,
|
||||
HttpServletRequest req, XMLWriter generatedXML, String path,
|
||||
int type, Vector<String> propertiesVector, String mimeType)
|
||||
throws WebdavException {
|
||||
|
||||
StoredObject so = _store.getStoredObject(transaction, path);
|
||||
|
||||
boolean isFolder = so.isFolder();
|
||||
final String creationdate = creationDateFormat(so.getCreationDate());
|
||||
final String lastModified = lastModifiedDateFormat(so.getLastModified());
|
||||
String resourceLength = String.valueOf(so.getResourceLength());
|
||||
|
||||
// ResourceInfo resourceInfo = new ResourceInfo(path, resources);
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.OPENING);
|
||||
String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
|
||||
+ WebdavStatus.getStatusText(WebdavStatus.SC_OK));
|
||||
|
||||
// Generating href element
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
|
||||
String href = req.getContextPath();
|
||||
String servletPath = req.getServletPath();
|
||||
if (servletPath != null) {
|
||||
if ((href.endsWith("/")) && (servletPath.startsWith("/")))
|
||||
href += servletPath.substring(1);
|
||||
else
|
||||
href += servletPath;
|
||||
}
|
||||
if ((href.endsWith("/")) && (path.startsWith("/")))
|
||||
href += path.substring(1);
|
||||
else
|
||||
href += path;
|
||||
if ((isFolder) && (!href.endsWith("/")))
|
||||
href += "/";
|
||||
|
||||
generatedXML.writeText(rewriteUrl(href));
|
||||
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
|
||||
String resourceName = path;
|
||||
int lastSlash = path.lastIndexOf('/');
|
||||
if (lastSlash != -1)
|
||||
resourceName = resourceName.substring(lastSlash + 1);
|
||||
|
||||
switch (type) {
|
||||
|
||||
case FIND_ALL_PROP:
|
||||
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeProperty("DAV::creationdate", creationdate);
|
||||
generatedXML.writeElement("DAV::displayname", XMLWriter.OPENING);
|
||||
generatedXML.writeData(resourceName);
|
||||
generatedXML.writeElement("DAV::displayname", XMLWriter.CLOSING);
|
||||
if (!isFolder) {
|
||||
generatedXML
|
||||
.writeProperty("DAV::getlastmodified", lastModified);
|
||||
generatedXML.writeProperty("DAV::getcontentlength",
|
||||
resourceLength);
|
||||
String contentType = mimeType;
|
||||
if (contentType != null) {
|
||||
generatedXML.writeProperty("DAV::getcontenttype",
|
||||
contentType);
|
||||
}
|
||||
generatedXML.writeProperty("DAV::getetag", getETag(so));
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.NO_CONTENT);
|
||||
} else {
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::collection",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.CLOSING);
|
||||
}
|
||||
|
||||
writeSupportedLockElements(transaction, generatedXML, path);
|
||||
|
||||
writeLockDiscoveryElements(transaction, generatedXML, path);
|
||||
|
||||
generatedXML.writeProperty("DAV::source", "");
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText(status);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||
|
||||
break;
|
||||
|
||||
case FIND_PROPERTY_NAMES:
|
||||
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
|
||||
generatedXML
|
||||
.writeElement("DAV::creationdate", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::displayname", XMLWriter.NO_CONTENT);
|
||||
if (!isFolder) {
|
||||
generatedXML.writeElement("DAV::getcontentlanguage",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::getcontentlength",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::getcontenttype",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::getetag", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::getlastmodified",
|
||||
XMLWriter.NO_CONTENT);
|
||||
}
|
||||
generatedXML
|
||||
.writeElement("DAV::resourcetype", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::supportedlock",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::source", XMLWriter.NO_CONTENT);
|
||||
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText(status);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||
|
||||
break;
|
||||
|
||||
case FIND_BY_PROPERTY:
|
||||
|
||||
Vector<String> propertiesNotFound = new Vector<String>();
|
||||
|
||||
// Parse the list of properties
|
||||
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
|
||||
Enumeration<String> properties = propertiesVector.elements();
|
||||
|
||||
while (properties.hasMoreElements()) {
|
||||
|
||||
String property = (String) properties.nextElement();
|
||||
|
||||
if (property.equals("DAV::creationdate")) {
|
||||
generatedXML.writeProperty("DAV::creationdate",
|
||||
creationdate);
|
||||
} else if (property.equals("DAV::displayname")) {
|
||||
generatedXML.writeElement("DAV::displayname",
|
||||
XMLWriter.OPENING);
|
||||
generatedXML.writeData(resourceName);
|
||||
generatedXML.writeElement("DAV::displayname",
|
||||
XMLWriter.CLOSING);
|
||||
} else if (property.equals("DAV::getcontentlanguage")) {
|
||||
if (isFolder) {
|
||||
propertiesNotFound.addElement(property);
|
||||
} else {
|
||||
generatedXML.writeElement("DAV::getcontentlanguage",
|
||||
XMLWriter.NO_CONTENT);
|
||||
}
|
||||
} else if (property.equals("DAV::getcontentlength")) {
|
||||
if (isFolder) {
|
||||
propertiesNotFound.addElement(property);
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::getcontentlength",
|
||||
resourceLength);
|
||||
}
|
||||
} else if (property.equals("DAV::getcontenttype")) {
|
||||
if (isFolder) {
|
||||
propertiesNotFound.addElement(property);
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::getcontenttype",
|
||||
mimeType);
|
||||
}
|
||||
} else if (property.equals("DAV::getetag")) {
|
||||
if (isFolder || so.isNullResource()) {
|
||||
propertiesNotFound.addElement(property);
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::getetag", getETag(so));
|
||||
}
|
||||
} else if (property.equals("DAV::getlastmodified")) {
|
||||
if (isFolder) {
|
||||
propertiesNotFound.addElement(property);
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::getlastmodified",
|
||||
lastModified);
|
||||
}
|
||||
} else if (property.equals("DAV::resourcetype")) {
|
||||
if (isFolder) {
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::collection",
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.CLOSING);
|
||||
} else {
|
||||
generatedXML.writeElement("DAV::resourcetype",
|
||||
XMLWriter.NO_CONTENT);
|
||||
}
|
||||
} else if (property.equals("DAV::source")) {
|
||||
generatedXML.writeProperty("DAV::source", "");
|
||||
} else if (property.equals("DAV::supportedlock")) {
|
||||
|
||||
writeSupportedLockElements(transaction, generatedXML, path);
|
||||
|
||||
} else if (property.equals("DAV::lockdiscovery")) {
|
||||
|
||||
writeLockDiscoveryElements(transaction, generatedXML, path);
|
||||
|
||||
} else {
|
||||
propertiesNotFound.addElement(property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText(status);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||
|
||||
Enumeration<String> propertiesNotFoundList = propertiesNotFound
|
||||
.elements();
|
||||
|
||||
if (propertiesNotFoundList.hasMoreElements()) {
|
||||
|
||||
status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
|
||||
+ " "
|
||||
+ WebdavStatus.getStatusText(WebdavStatus.SC_NOT_FOUND));
|
||||
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
|
||||
while (propertiesNotFoundList.hasMoreElements()) {
|
||||
generatedXML.writeElement((String) propertiesNotFoundList
|
||||
.nextElement(), XMLWriter.NO_CONTENT);
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText(status);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::propstat", XMLWriter.CLOSING);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.CLOSING);
|
||||
|
||||
so = null;
|
||||
}
|
||||
|
||||
private void writeSupportedLockElements(ITransaction transaction,
|
||||
XMLWriter generatedXML, String path) {
|
||||
|
||||
LockedObject lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||
path);
|
||||
|
||||
generatedXML.writeElement("DAV::supportedlock", XMLWriter.OPENING);
|
||||
|
||||
if (lo == null) {
|
||||
// both locks (shared/exclusive) can be granted
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::exclusive", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::write", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::shared", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::write", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING);
|
||||
|
||||
} else {
|
||||
// LockObject exists, checking lock state
|
||||
// if an exclusive lock exists, no further lock is possible
|
||||
if (lo.isShared()) {
|
||||
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::shared", XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::" + lo.getType(),
|
||||
XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockentry", XMLWriter.CLOSING);
|
||||
}
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::supportedlock", XMLWriter.CLOSING);
|
||||
|
||||
lo = null;
|
||||
}
|
||||
|
||||
private void writeLockDiscoveryElements(ITransaction transaction,
|
||||
XMLWriter generatedXML, String path) {
|
||||
|
||||
LockedObject lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||
path);
|
||||
|
||||
if (lo != null && !lo.hasExpired()) {
|
||||
|
||||
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::activelock", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
|
||||
generatedXML.writeProperty("DAV::" + lo.getType());
|
||||
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
|
||||
if (lo.isExclusive()) {
|
||||
generatedXML.writeProperty("DAV::exclusive");
|
||||
} else {
|
||||
generatedXML.writeProperty("DAV::shared");
|
||||
}
|
||||
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::depth", XMLWriter.OPENING);
|
||||
if (_depth == INFINITY) {
|
||||
generatedXML.writeText("Infinity");
|
||||
} else {
|
||||
generatedXML.writeText(String.valueOf(_depth));
|
||||
}
|
||||
generatedXML.writeElement("DAV::depth", XMLWriter.CLOSING);
|
||||
|
||||
String[] owners = lo.getOwner();
|
||||
if (owners != null) {
|
||||
for (int i = 0; i < owners.length; i++) {
|
||||
generatedXML.writeElement("DAV::owner", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
generatedXML.writeText(owners[i]);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::owner", XMLWriter.CLOSING);
|
||||
}
|
||||
} else {
|
||||
generatedXML.writeElement("DAV::owner", XMLWriter.NO_CONTENT);
|
||||
}
|
||||
|
||||
int timeout = (int) (lo.getTimeoutMillis() / 1000);
|
||||
String timeoutStr = new Integer(timeout).toString();
|
||||
generatedXML.writeElement("DAV::timeout", XMLWriter.OPENING);
|
||||
generatedXML.writeText("Second-" + timeoutStr);
|
||||
generatedXML.writeElement("DAV::timeout", XMLWriter.CLOSING);
|
||||
|
||||
String lockToken = lo.getID();
|
||||
|
||||
generatedXML.writeElement("DAV::locktoken", XMLWriter.OPENING);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
generatedXML.writeText("opaquelocktoken:" + lockToken);
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::locktoken", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::activelock", XMLWriter.CLOSING);
|
||||
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.CLOSING);
|
||||
|
||||
} else {
|
||||
generatedXML.writeElement("DAV::lockdiscovery",
|
||||
XMLWriter.NO_CONTENT);
|
||||
}
|
||||
|
||||
lo = null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.*;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.AccessDeniedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.WebdavException;
|
||||
import cn.tenfell.webos.common.webdav.sf.fromcatalina.XMLHelper;
|
||||
import cn.tenfell.webos.common.webdav.sf.fromcatalina.XMLWriter;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.LockedObject;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.ResourceLocks;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
public class DoProppatch extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoProppatch.class);
|
||||
|
||||
private boolean _readOnly;
|
||||
private IWebdavStore _store;
|
||||
private ResourceLocks _resourceLocks;
|
||||
|
||||
public DoProppatch(IWebdavStore store, ResourceLocks resLocks,
|
||||
boolean readOnly) {
|
||||
_readOnly = readOnly;
|
||||
_store = store;
|
||||
_resourceLocks = resLocks;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (_readOnly) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
String path = getRelativePath(req);
|
||||
String parentPath = getParentPath(getCleanPath(path));
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // parent is locked
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, path)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // resource is locked
|
||||
}
|
||||
|
||||
// TODO for now, PROPPATCH just sends a valid response, stating that
|
||||
// everything is fine, but doesn't do anything.
|
||||
|
||||
// Retrieve the resources
|
||||
String tempLockOwner = "doProppatch" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
StoredObject so = null;
|
||||
LockedObject lo = null;
|
||||
try {
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
lo = _resourceLocks.getLockedObjectByPath(transaction,
|
||||
getCleanPath(path));
|
||||
|
||||
if (so == null) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||
return;
|
||||
// we do not to continue since there is no root
|
||||
// resource
|
||||
}
|
||||
|
||||
if (so.isNullResource()) {
|
||||
String methodsAllowed = DeterminableMethod
|
||||
.determineMethodsAllowed(so);
|
||||
resp.addHeader("Allow", methodsAllowed);
|
||||
resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||
boolean lockTokenMatchesIfHeader = (lockTokens != null && lockTokens[0].equals(lo.getID()));
|
||||
if (lo != null && lo.isExclusive() && !lockTokenMatchesIfHeader) {
|
||||
// Object on specified path is LOCKED
|
||||
errorList = new Hashtable<String, Integer>();
|
||||
errorList.put(path, new Integer(WebdavStatus.SC_LOCKED));
|
||||
sendReport(req, resp, errorList);
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> toset = null;
|
||||
List<String> toremove = null;
|
||||
List<String> tochange = new Vector<String>();
|
||||
// contains all properties from
|
||||
// toset and toremove
|
||||
|
||||
path = getCleanPath(getRelativePath(req));
|
||||
|
||||
Node tosetNode = null;
|
||||
Node toremoveNode = null;
|
||||
|
||||
if (req.getContentLength() != 0) {
|
||||
DocumentBuilder documentBuilder = getDocumentBuilder();
|
||||
try {
|
||||
Document document = documentBuilder
|
||||
.parse(new InputSource(req.getInputStream()));
|
||||
// Get the root element of the document
|
||||
Element rootElement = document.getDocumentElement();
|
||||
|
||||
tosetNode = XMLHelper.findSubElement(XMLHelper
|
||||
.findSubElement(rootElement, "set"), "prop");
|
||||
toremoveNode = XMLHelper.findSubElement(XMLHelper
|
||||
.findSubElement(rootElement, "remove"), "prop");
|
||||
} catch (Exception e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// no content: error
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
HashMap<String, String> namespaces = new HashMap<String, String>();
|
||||
namespaces.put("DAV:", "D");
|
||||
|
||||
if (tosetNode != null) {
|
||||
toset = XMLHelper.getPropertiesFromXML(tosetNode);
|
||||
tochange.addAll(toset);
|
||||
}
|
||||
|
||||
if (toremoveNode != null) {
|
||||
toremove = XMLHelper.getPropertiesFromXML(toremoveNode);
|
||||
tochange.addAll(toremove);
|
||||
}
|
||||
|
||||
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
|
||||
resp.setContentType("text/xml; charset=UTF-8");
|
||||
|
||||
// Create multistatus object
|
||||
XMLWriter generatedXML = new XMLWriter(resp.getWriter(),
|
||||
namespaces);
|
||||
generatedXML.writeXMLHeader();
|
||||
generatedXML
|
||||
.writeElement("DAV::multistatus", XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.OPENING);
|
||||
String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK
|
||||
+ " " + WebdavStatus.getStatusText(WebdavStatus.SC_OK));
|
||||
|
||||
// Generating href element
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
|
||||
|
||||
String href = req.getContextPath();
|
||||
if ((href.endsWith("/")) && (path.startsWith("/")))
|
||||
href += path.substring(1);
|
||||
else
|
||||
href += path;
|
||||
if ((so.isFolder()) && (!href.endsWith("/")))
|
||||
href += "/";
|
||||
|
||||
generatedXML.writeText(rewriteUrl(href));
|
||||
|
||||
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
|
||||
|
||||
for (Iterator<String> iter = tochange.iterator(); iter
|
||||
.hasNext();) {
|
||||
String property = (String) iter.next();
|
||||
|
||||
generatedXML.writeElement("DAV::propstat",
|
||||
XMLWriter.OPENING);
|
||||
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
|
||||
generatedXML.writeElement(property, XMLWriter.NO_CONTENT);
|
||||
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.OPENING);
|
||||
generatedXML.writeText(status);
|
||||
generatedXML.writeElement("DAV::status", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.writeElement("DAV::propstat",
|
||||
XMLWriter.CLOSING);
|
||||
}
|
||||
|
||||
generatedXML.writeElement("DAV::response", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML
|
||||
.writeElement("DAV::multistatus", XMLWriter.CLOSING);
|
||||
|
||||
generatedXML.sendData();
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(); // To change body of catch statement use
|
||||
// File | Settings | File Templates.
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||
tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright 1999,2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.ITransaction;
|
||||
import cn.tenfell.webos.common.webdav.sf.IWebdavStore;
|
||||
import cn.tenfell.webos.common.webdav.sf.StoredObject;
|
||||
import cn.tenfell.webos.common.webdav.sf.WebdavStatus;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.AccessDeniedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.WebdavException;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.IResourceLocks;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.LockedObject;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletRequest;
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class DoPut extends AbstractMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoPut.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private IResourceLocks _resourceLocks;
|
||||
private boolean _readOnly;
|
||||
private boolean _lazyFolderCreationOnPut;
|
||||
|
||||
private String _userAgent;
|
||||
|
||||
public DoPut(IWebdavStore store, IResourceLocks resLocks, boolean readOnly,
|
||||
boolean lazyFolderCreationOnPut) {
|
||||
_store = store;
|
||||
_resourceLocks = resLocks;
|
||||
_readOnly = readOnly;
|
||||
_lazyFolderCreationOnPut = lazyFolderCreationOnPut;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (!_readOnly) {
|
||||
String path = getRelativePath(req);
|
||||
String parentPath = getParentPath(path);
|
||||
|
||||
_userAgent = req.getHeader("User-Agent");
|
||||
|
||||
Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, parentPath)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // parent is locked
|
||||
}
|
||||
|
||||
if (!checkLocks(transaction, req, resp, _resourceLocks, path)) {
|
||||
resp.setStatus(WebdavStatus.SC_LOCKED);
|
||||
return; // resource is locked
|
||||
}
|
||||
|
||||
String tempLockOwner = "doPut" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
|
||||
TEMP_TIMEOUT, TEMPORARY)) {
|
||||
StoredObject parentSo, so = null;
|
||||
try {
|
||||
parentSo = _store.getStoredObject(transaction, parentPath);
|
||||
if (parentPath != null && parentSo != null
|
||||
&& parentSo.isResource()) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
|
||||
} else if (parentPath != null && parentSo == null
|
||||
&& _lazyFolderCreationOnPut) {
|
||||
_store.createFolder(transaction, parentPath);
|
||||
|
||||
} else if (parentPath != null && parentSo == null
|
||||
&& !_lazyFolderCreationOnPut) {
|
||||
errorList.put(parentPath, WebdavStatus.SC_NOT_FOUND);
|
||||
sendReport(req, resp, errorList);
|
||||
return;
|
||||
}
|
||||
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
|
||||
if (so == null) {
|
||||
_store.createResource(transaction, path);
|
||||
// resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
} else {
|
||||
// This has already been created, just update the data
|
||||
if (so.isNullResource()) {
|
||||
|
||||
LockedObject nullResourceLo = _resourceLocks
|
||||
.getLockedObjectByPath(transaction, path);
|
||||
if (nullResourceLo == null) {
|
||||
resp
|
||||
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
String nullResourceLockToken = nullResourceLo
|
||||
.getID();
|
||||
String[] lockTokens = getLockIdFromIfHeader(req);
|
||||
String lockToken = null;
|
||||
if (lockTokens != null) {
|
||||
lockToken = lockTokens[0];
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
if (lockToken.equals(nullResourceLockToken)) {
|
||||
so.setNullResource(false);
|
||||
so.setFolder(false);
|
||||
|
||||
String[] nullResourceLockOwners = nullResourceLo
|
||||
.getOwner();
|
||||
String owner = null;
|
||||
if (nullResourceLockOwners != null)
|
||||
owner = nullResourceLockOwners[0];
|
||||
|
||||
if (!_resourceLocks.unlock(transaction,
|
||||
lockToken, owner)) {
|
||||
resp
|
||||
.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
errorList.put(path, WebdavStatus.SC_LOCKED);
|
||||
sendReport(req, resp, errorList);
|
||||
}
|
||||
}
|
||||
}
|
||||
// User-Agent workarounds
|
||||
doUserAgentWorkaround(resp);
|
||||
|
||||
// setting resourceContent
|
||||
long resourceLength = _store
|
||||
.setResourceContent(transaction, path, req
|
||||
.getInputStream(), null, null);
|
||||
|
||||
so = _store.getStoredObject(transaction, path);
|
||||
if (resourceLength > 0)
|
||||
so.setResourceLength(resourceLength);
|
||||
// Now lets report back what was actually saved
|
||||
|
||||
} catch (AccessDeniedException e) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
} catch (WebdavException e) {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction,
|
||||
path, tempLockOwner);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resp
|
||||
*/
|
||||
private void doUserAgentWorkaround(HttpServletResponse resp) {
|
||||
if (_userAgent != null && _userAgent.indexOf("WebDAVFS") != -1
|
||||
&& _userAgent.indexOf("Transmit") == -1) {
|
||||
LOG.trace("DoPut.execute() : do workaround for user agent '"
|
||||
+ _userAgent + "'");
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
} else if (_userAgent != null && _userAgent.indexOf("Transmit") != -1) {
|
||||
// Transmit also uses WEBDAVFS 1.x.x but crashes
|
||||
// with SC_CREATED response
|
||||
LOG.trace("DoPut.execute() : do workaround for user agent '"
|
||||
+ _userAgent + "'");
|
||||
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||
} else {
|
||||
resp.setStatus(WebdavStatus.SC_CREATED);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package cn.tenfell.webos.common.webdav.sf.methods;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.ITransaction;
|
||||
import cn.tenfell.webos.common.webdav.sf.IWebdavStore;
|
||||
import cn.tenfell.webos.common.webdav.sf.StoredObject;
|
||||
import cn.tenfell.webos.common.webdav.sf.WebdavStatus;
|
||||
import cn.tenfell.webos.common.webdav.sf.exceptions.LockFailedException;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.IResourceLocks;
|
||||
import cn.tenfell.webos.common.webdav.sf.locking.LockedObject;
|
||||
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletRequest;
|
||||
import cn.tenfell.webos.common.webdav.sf.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
public class DoUnlock extends DeterminableMethod {
|
||||
|
||||
private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
|
||||
.getLogger(DoUnlock.class);
|
||||
|
||||
private IWebdavStore _store;
|
||||
private IResourceLocks _resourceLocks;
|
||||
private boolean _readOnly;
|
||||
|
||||
public DoUnlock(IWebdavStore store, IResourceLocks resourceLocks,
|
||||
boolean readOnly) {
|
||||
_store = store;
|
||||
_resourceLocks = resourceLocks;
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void execute(ITransaction transaction, HttpServletRequest req,
|
||||
HttpServletResponse resp) throws IOException, LockFailedException {
|
||||
LOG.trace("-- " + this.getClass().getName());
|
||||
|
||||
if (_readOnly) {
|
||||
resp.sendError(WebdavStatus.SC_FORBIDDEN);
|
||||
return;
|
||||
} else {
|
||||
|
||||
String path = getRelativePath(req);
|
||||
String tempLockOwner = "doUnlock" + System.currentTimeMillis()
|
||||
+ req.toString();
|
||||
try {
|
||||
if (_resourceLocks.lock(transaction, path, tempLockOwner,
|
||||
false, 0, TEMP_TIMEOUT, TEMPORARY)) {
|
||||
|
||||
String lockId = getLockIdFromLockTokenHeader(req);
|
||||
LockedObject lo;
|
||||
if (lockId != null
|
||||
&& ((lo = _resourceLocks.getLockedObjectByID(
|
||||
transaction, lockId)) != null)) {
|
||||
|
||||
String[] owners = lo.getOwner();
|
||||
String owner = null;
|
||||
if (lo.isShared()) {
|
||||
// more than one owner is possible
|
||||
if (owners != null) {
|
||||
for (int i = 0; i < owners.length; i++) {
|
||||
// remove owner from LockedObject
|
||||
lo.removeLockedObjectOwner(owners[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// exclusive, only one lock owner
|
||||
if (owners != null)
|
||||
owner = owners[0];
|
||||
else
|
||||
owner = null;
|
||||
}
|
||||
|
||||
if (_resourceLocks.unlock(transaction, lockId, owner)) {
|
||||
StoredObject so = _store.getStoredObject(
|
||||
transaction, path);
|
||||
if (so == null) {
|
||||
resp.sendError(WebdavStatus.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (so.isNullResource()) {
|
||||
_store.removeObject(transaction, path);
|
||||
}
|
||||
|
||||
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
|
||||
} else {
|
||||
LOG.trace("DoUnlock failure at " + lo.getPath());
|
||||
resp.sendError(WebdavStatus.SC_METHOD_FAILURE);
|
||||
}
|
||||
|
||||
} else {
|
||||
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
} catch (LockFailedException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
_resourceLocks.unlockTemporaryLockedObjects(transaction, path,
|
||||
tempLockOwner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,425 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* webdav 服务端
|
||||
*
|
||||
* 简易文档: https://tech.yandex.com/disk/doc/dg/reference/put-docpage/
|
||||
* 文档: http://www.webdav.org/specs/rfc2518.html
|
||||
*/
|
||||
class webdavServer {
|
||||
public $lastError = '';
|
||||
public $method = '';
|
||||
public function __construct($root,$DAV_PRE_PATH) {
|
||||
$this->root = $root;
|
||||
$this->initPath($DAV_PRE_PATH);
|
||||
$this->start();
|
||||
}
|
||||
public function initPath($DAV_PRE_PATH){
|
||||
$GLOBALS['requestFrom'] = 'webdav';
|
||||
$this->method = 'http'.HttpHeader::method();
|
||||
$uri = rtrim($_SERVER['REQUEST_URI'],'/').'/'; //带有后缀的从domain之后部分;
|
||||
if(!$this->pathCheck($uri)){//路径长度限制
|
||||
$this->lastError = LNG('common.lengthLimit');
|
||||
$this->response(array("code"=>404));exit;
|
||||
}
|
||||
$this->urlBase = substr($uri,0,strpos($uri,$DAV_PRE_PATH)+1); //$find之前;
|
||||
$this->urlBase = rtrim($this->urlBase,'/').$DAV_PRE_PATH;
|
||||
$this->uri = $this->pathGet();
|
||||
$this->path = $this->parsePath($this->uri);
|
||||
if(strpos($uri,$DAV_PRE_PATH) === false){
|
||||
$this->lastError = LNG('common.noPermission');
|
||||
$this->response(array("code"=>404));exit;
|
||||
}
|
||||
}
|
||||
public function checkUser(){
|
||||
$user = HttpAuth::get();
|
||||
if($user['user'] == 'admin' && $user['pass'] == '123'){
|
||||
return true;
|
||||
}
|
||||
HttpAuth::error();
|
||||
}
|
||||
private function pathCheck($path){
|
||||
$PATH_LENGTH_MAX = 4096;//路径最长限制;
|
||||
return strlen($path) >= $PATH_LENGTH_MAX ? false:true;
|
||||
}
|
||||
|
||||
public function start(){
|
||||
$this->checkUser();
|
||||
$method = 'http'.HttpHeader::method();
|
||||
if(!method_exists($this,$method)){
|
||||
pr($method.' not exists!;');exit;
|
||||
}
|
||||
$notCheck = array('httpMKCOL','httpPUT');
|
||||
if( !in_array($method,$notCheck) &&
|
||||
!$this->pathExists($this->path,true) ){
|
||||
$result = array('code' => 404);
|
||||
}else{
|
||||
$result = $this->$method();
|
||||
}
|
||||
if(!$result) return;//文件下载;
|
||||
self::response($result);
|
||||
}
|
||||
public function pathGet($dest=false){
|
||||
$path = $dest ? $_SERVER['HTTP_DESTINATION'] : $_SERVER['REQUEST_URI'];
|
||||
$path = KodIO::clear(rawurldecode($path));
|
||||
if(!strstr($path,KodIO::clear($this->urlBase))) return false;
|
||||
return substr($path,strpos($path,$this->urlBase)+ strlen($this->urlBase) );
|
||||
}
|
||||
|
||||
public function pathExists($path,$allowInRecycle=false){
|
||||
return file_exists($path);
|
||||
}
|
||||
public function pathMkdir($path){
|
||||
return mkdir($path,0777,true);
|
||||
}
|
||||
public function pathInfo($path){
|
||||
return path_info($path);
|
||||
}
|
||||
public function pathList($path){
|
||||
return path_list($path);
|
||||
}
|
||||
// range支持;
|
||||
public function pathOut($path){
|
||||
echo file_get_contents($path);
|
||||
}
|
||||
public function pathPut($path,$tempFile=''){
|
||||
if(!$tempFile){
|
||||
return file_put_contents($path,'');
|
||||
}
|
||||
return move_path($tempFile,$path);
|
||||
}
|
||||
public function pathRemove($path){
|
||||
if(is_file($path)){
|
||||
return @unlink($this->path);
|
||||
}else{
|
||||
return del_dir($this->path);
|
||||
}
|
||||
}
|
||||
public function pathMove($path,$dest){
|
||||
return move_path($path,$dest);
|
||||
}
|
||||
public function pathCopy($path,$dest){
|
||||
return copy_dir($path,$dest);
|
||||
}
|
||||
public function parsePath($path){
|
||||
return $path;
|
||||
}
|
||||
public function parseItem($item,$isInfo){
|
||||
$pathAdd = $this->pathGet().'/'.$item['name'];
|
||||
$pathAdd = '/'.str_replace('%2F','/',rawurlencode($pathAdd));
|
||||
if($isInfo){
|
||||
$pathAdd = '/'.str_replace('%2F','/',rawurlencode($this->pathGet()));
|
||||
}
|
||||
if(!trim($item['modifyTime'])){$item['modifyTime'] = time();}
|
||||
if(!trim($item['createTime'])){$item['createTime'] = time();}
|
||||
|
||||
$result = array(
|
||||
'href' => KodIO::clear($this->urlBase.$pathAdd),
|
||||
'modifyTime' => @gmdate("D, d M Y H:i:s",$item['modifyTime']).' GMT',
|
||||
'createTime' => @gmdate("Y-m-d\\TH:i:s\\Z",$item['createTime']),
|
||||
'size' => $item['size'] ? $item['size']:0,
|
||||
);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function parseItemXml($itemFile,$isInfo){
|
||||
$item = $this->parseItem($itemFile,$isInfo);
|
||||
if ($itemFile['type'] == 'folder') {//getetag
|
||||
$xmlAdd = "<D:resourcetype><D:collection/></D:resourcetype>";
|
||||
$xmlAdd.= "<D:getcontenttype>httpd/unix-directory</D:getcontenttype>";
|
||||
$item['href'] = rtrim($item['href'],'/').'/';
|
||||
if(isset($_SERVER['HTTP_DATE']) && isset($_SERVER['HTTP_DEPTH'])){
|
||||
// goodsync同步处理;HTTP_DATE/HTTP_DEPTH; 首次列表展开失败问题处理;
|
||||
$result['modifyTime'] = $_SERVER['HTTP_DATE'];
|
||||
}
|
||||
}else{
|
||||
$ext = $itemFile['ext'] ? $itemFile['ext']:get_path_ext($itemFile['name']);
|
||||
$mime = get_file_mime($ext);
|
||||
$xmlAdd = '<D:resourcetype/>';
|
||||
$xmlAdd.= "<D:getcontenttype>{$mime}</D:getcontenttype>";
|
||||
}
|
||||
|
||||
$infoMore = array();
|
||||
$picker = array(
|
||||
'hasFile','hasFolder','fileInfo','fileInfoMore','oexeContent','parentID','isTruePath',
|
||||
'isReadable','isWriteable','sourceRoot','icon','iconClassName','children',
|
||||
);
|
||||
foreach ($picker as $key){
|
||||
if(array_key_exists($key,$itemFile)){$infoMore[$key] = $itemFile[$key];}
|
||||
}
|
||||
if($itemFile['type'] == 'file'){
|
||||
$param = array('path'=>$itemFile['path']);
|
||||
$infoMore['fileOutLink'] = Action('user.index')->apiSignMake('explorer/index/fileOut',$param);
|
||||
}
|
||||
if($infoMore){
|
||||
$xmlAdd.= "<D:extendFileInfo>".base64_encode(json_encode($infoMore))."</D:extendFileInfo>";
|
||||
}
|
||||
|
||||
return "
|
||||
<D:response>
|
||||
<D:href>{$item['href']}</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:getlastmodified>{$item['modifyTime']}</D:getlastmodified>
|
||||
<D:creationdate>{$item['createTime']}</D:creationdate>
|
||||
<D:getcontentlength>{$item['size']}</D:getcontentlength>
|
||||
{$xmlAdd}
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>";
|
||||
}
|
||||
public function pathListMerge($listData){
|
||||
if(!$listData) return $listData;
|
||||
$keyList = array('fileList','folderList','groupList');
|
||||
$list = array();
|
||||
foreach ($listData as $key=>$typeList){
|
||||
if(!in_array($key,$keyList) || !is_array($typeList)) continue;
|
||||
$list = array_merge($list,$typeList);
|
||||
}
|
||||
//去除名称中的/分隔; 兼容存储挂载
|
||||
foreach ($list as &$item) {
|
||||
$item['name'] = str_replace('/','@',$item['name']);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function httpPROPFIND(){
|
||||
$listFile = $this->pathList($this->path);
|
||||
$list = $this->pathListMerge($listFile);
|
||||
$pathInfo = $listFile['current'];
|
||||
if(!is_array($list) || $pathInfo['exists'] === false ){//不存在;
|
||||
return array("code" => 404,"body" => $this->errorBody('ObjectNotFound','not exist'));
|
||||
}
|
||||
if(isset($listFile['folderList'])){
|
||||
$pathInfo['type'] = 'folder';
|
||||
}
|
||||
//只显示属性;
|
||||
$isInfo = $pathInfo['type'] == 'file' || HttpHeader::get('Depth') == '0';
|
||||
// kodbox webdav挂载获取文件夹属性;
|
||||
if( $pathInfo['type'] == 'folder' &&
|
||||
HttpHeader::get('X_DAV_ACTION') == 'infoChildren'){
|
||||
$pathInfo = IO::infoWithChildren($pathInfo['path']);
|
||||
}
|
||||
if($isInfo){
|
||||
$list = array($pathInfo);
|
||||
}else{
|
||||
$pathInfo['name'] = '';
|
||||
$list = array_merge(array($pathInfo),$list);
|
||||
}
|
||||
$out = '';
|
||||
foreach ($list as $itemFile){
|
||||
$out .= $this->parseItemXml($itemFile,$isInfo);
|
||||
}
|
||||
// write_log([$this->pathGet(),$this->path,$pathInfo],'webdav');
|
||||
$code = 207;//207 => 200;
|
||||
if(strstr($this->uri,'.xbel')){$code = 200;} // 兼容floccus
|
||||
|
||||
// 扩展kod内容;
|
||||
$infoMore = array();
|
||||
$picker = array('groupShow','pageInfo','targetSpace','listTypePhoto','listTypeSet','pageSizeArray');
|
||||
foreach ($picker as $key){
|
||||
if(array_key_exists($key,$listFile)){$infoMore[$key] = $listFile[$key];}
|
||||
}
|
||||
$infoMoreData = $infoMore ? base64_encode(json_encode($infoMore)):'';
|
||||
return array(
|
||||
"code" => $code,
|
||||
"headers" => array('X-extendFileList: '.$infoMoreData),
|
||||
"body" => "<D:multistatus xmlns:D=\"DAV:\">\n{$out}\n</D:multistatus>"
|
||||
);
|
||||
}
|
||||
|
||||
public function httpHEAD() {
|
||||
$info = $this->pathInfo($this->path);
|
||||
if(!$info || $info['type'] == 'folder'){
|
||||
return array(
|
||||
'code' => 200,
|
||||
'headers' => array(
|
||||
'Content-Type: text/html; charset=utf8',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'code'=> 200,
|
||||
'headers' => array(
|
||||
'Vary: Range',
|
||||
'Accept-Ranges: bytes',
|
||||
'Content-length: '.$info['size'],
|
||||
'Content-type: '.get_file_mime($info['ext']),
|
||||
'Last-Modified: '.gmdate("D, d M Y H:i:s ", $info['mtime'])."GMT",
|
||||
'Cache-Control: max-age=86400,must-revalidate',
|
||||
// 'ETag: "'.md5($info['mtime'].$info['path']).'"',
|
||||
)
|
||||
);
|
||||
}
|
||||
public function httpOPTIONS() {
|
||||
return array(
|
||||
'code' => 200,
|
||||
'headers' => array(
|
||||
'DAV: 1, 2, 3, extended-kodbox',
|
||||
'MS-Author-Via: DAV',
|
||||
'Allow: OPTIONS, PROPFIND, PROPPATCH, MKCOL, GET, PUT, DELETE, COPY, MOVE, LOCK, UNLOCK, HEAD',
|
||||
'Accept-Ranges: bytes',
|
||||
'Content-Length: 0',
|
||||
)
|
||||
);
|
||||
}
|
||||
public function httpPROPPATCH(){
|
||||
$out = '
|
||||
<D:response>
|
||||
<D:href>'.$_SERVER['REQUEST_URI'].'</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<m:Win32LastAccessTime xmlns:m="urn:schemas-microsoft-com:" />
|
||||
<m:Win32CreationTime xmlns:m="urn:schemas-microsoft-com:" />
|
||||
<m:Win32LastModifiedTime xmlns:m="urn:schemas-microsoft-com:" />
|
||||
<m:Win32FileAttributes xmlns:m="urn:schemas-microsoft-com:" />
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>';
|
||||
return array(
|
||||
"code" => 207,
|
||||
"body" => "<D:multistatus xmlns:D=\"DAV:\">\n{$out}\n</D:multistatus>"
|
||||
);
|
||||
}
|
||||
|
||||
public function httpGET() {
|
||||
$this->pathOut($this->path);
|
||||
}
|
||||
// 分片支持; X-Expected-Entity-Length
|
||||
public function httpPUT() {
|
||||
$tempFile = $this->uploadFile();
|
||||
if($tempFile){
|
||||
$code = 204;
|
||||
}else{
|
||||
$tempFile = '';
|
||||
$code = 201;
|
||||
}
|
||||
$result = $this->pathPut($this->path,$tempFile);
|
||||
if($result == false){$code = 404;}
|
||||
return array("code"=>$code);
|
||||
}
|
||||
|
||||
// 兼容move_uploaded_file 和 流的方式上传
|
||||
private function uploadFile(){
|
||||
@mk_dir(TEMP_FILES);
|
||||
$dest = TEMP_FILES.'upload_dav_'.rand_string(32);
|
||||
$outFp = @fopen($dest, "wb");
|
||||
$in = @fopen("php://input","rb");
|
||||
if(!$in || !$outFp){
|
||||
@unlink($dest);return false;
|
||||
}
|
||||
while (!feof($in)) {
|
||||
fwrite($outFp, fread($in, 1024*200));
|
||||
}
|
||||
fclose($in);fclose($outFp);
|
||||
if(@filesize($dest) > 0) return $dest;
|
||||
@unlink($dest);return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新建文件夹
|
||||
*/
|
||||
public function httpMKCOL() {
|
||||
if ($this->pathExists($this->path)) {
|
||||
return array('code' => 409);
|
||||
}
|
||||
$res = $this->pathMkdir($this->path);
|
||||
return array('code' => $res?201:403);
|
||||
}
|
||||
|
||||
public function httpMOVE() {
|
||||
$dest = $this->parsePath($this->pathGet(true));
|
||||
if (isset($_SERVER["HTTP_OVERWRITE"])) {
|
||||
$options["overwrite"] = $_SERVER["HTTP_OVERWRITE"] == "T";
|
||||
}
|
||||
$res = $this->pathMove($this->path,$dest);
|
||||
return array('code' => $res?201:404);
|
||||
}
|
||||
public function httpCOPY() {
|
||||
$dest = $this->parsePath($this->pathGet(true));
|
||||
$res = $this->pathCopy($this->path,$dest);
|
||||
return array('code' => $res?201:404);
|
||||
}
|
||||
public function httpDELETE() {
|
||||
$res = $this->pathRemove($this->path);
|
||||
return array('code' => $res?200:503);
|
||||
}
|
||||
public function httpLOCK() {
|
||||
$token = md5($this->path);
|
||||
$lockInfo = '<D:prop xmlns:d="DAV:">
|
||||
<D:lockdiscovery>
|
||||
<D:activelock>
|
||||
<D:locktype><d:write/></D:locktype>
|
||||
<D:lockscope><d:exclusive/></D:lockscope>
|
||||
<D:depth>infinity</D:depth>
|
||||
<D:owner>'.$this->xmlGet('lockinfo/owner/href').'</D:owner>
|
||||
<D:timeout>Infinite</D:timeout>
|
||||
<D:locktoken><D:href>opaquelocktoken:'.$token.'</D:href></D:locktoken>
|
||||
</D:activelock>
|
||||
</D:lockdiscovery>
|
||||
</D:prop>';
|
||||
return array(
|
||||
'code' => 200,
|
||||
'headers' => array(
|
||||
'Lock-Token: '.$token,
|
||||
'Connection: keep-alive',
|
||||
),
|
||||
'body' => $lockInfo,
|
||||
);
|
||||
}
|
||||
public function httpUNLOCK() {
|
||||
return array('code' => 204);
|
||||
}
|
||||
|
||||
public function xmlGet($key){
|
||||
static $xml = false;
|
||||
if(!$xml){
|
||||
$body = file_get_contents('php://input');
|
||||
$xml = new DOMDocument();
|
||||
$xml->loadXML($body);
|
||||
}
|
||||
|
||||
$tag = array_shift(explode('/', $key));
|
||||
$objData = $xml->getElementsByTagNameNS('DAV:', $tag);
|
||||
if($objData) return $objData[0]->nodeValue;
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getLastError(){return $this->lastError;}
|
||||
public function errorBody($title='',$desc=''){
|
||||
if(!$desc){$desc = $this->getLastError();}
|
||||
return
|
||||
'<D:error xmlns:D="DAV:" xmlns:S="http://kodcloud.com">
|
||||
<S:exception>'.htmlentities($title).'</S:exception>
|
||||
<S:message>'.htmlentities($desc).'</S:message>
|
||||
</D:error>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出应答信息
|
||||
* @param array $data [header:array,code:int,body:string]
|
||||
*/
|
||||
public function response($data) {
|
||||
$headers = is_array($data['headers']) ? $data['headers'] :array();
|
||||
$headers[] = HttpHeader::code($data['code']);
|
||||
$headers[] = 'Pragma: no-cache';
|
||||
$headers[] = 'Cache-Control: no-cache';
|
||||
$headers[] = 'X-DAV-BY: kodbox';
|
||||
foreach ($headers as $header) {
|
||||
header($header);
|
||||
}
|
||||
|
||||
if($data['code'] >= 400 && !$data['body']){
|
||||
$data['body'] = $this->errorBody();
|
||||
}
|
||||
// write_log(array($_SERVER['REQUEST_URI'],$header,$GLOBALS['_SERVER']),'webdav');
|
||||
if (is_string($data['body'])) {
|
||||
header('Content-Type: text/xml; charset=UTF-8');
|
||||
echo '<?xml version="1.0" encoding="utf-8"?>'."\n".$data['body'];
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.tenfell.webos.common.annt.*;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.DbUtil;
|
||||
import cn.tenfell.webos.modules.entity.SysDict;
|
||||
import cn.tenfell.webos.modules.entity.SysDictDetail;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@BeanAction(val = "dict")
|
||||
public class DictAction {
|
||||
/**
|
||||
* 获取字典列表
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "list", type = 1)
|
||||
@Auth(val = "system")
|
||||
public R list(Dict param) {
|
||||
int currentPage = param.getInt("current");
|
||||
Integer pageSize = param.getInt("pageSize");
|
||||
CommonBean.PageRes<SysDict> data = DbUtil.pageObject("select *", "from sys_dict", SysDict.class, currentPage, pageSize);
|
||||
if (CollUtil.isNotEmpty(data.getData())) {
|
||||
String codesStr = StrUtil.format("'{}'", CollUtil.join(CollUtil.getFieldValues(data.getData(), "code", String.class), "','"));
|
||||
List<SysDictDetail> list = DbUtil.queryList("select * from sys_dict_detail where code in ( " + codesStr + " )", SysDictDetail.class);
|
||||
if (CollUtil.isNotEmpty(list)) {
|
||||
Map<String, List<SysDictDetail>> map = new HashMap<>();
|
||||
List<List<SysDictDetail>> tmps = CollUtil.groupByField(list, "code");
|
||||
for (List<SysDictDetail> tmp : tmps) {
|
||||
map.put(tmp.get(0).getCode(), tmp);
|
||||
}
|
||||
data.getData().forEach(sysDict -> sysDict.setDetails(map.get(sysDict.getCode())));
|
||||
}
|
||||
}
|
||||
return R.ok(data, "获取成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增编辑字典
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "edit", type = 1)
|
||||
@Auth(val = "system")
|
||||
public R edit(SysDict data) {
|
||||
return DbUtil.commonEdit(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增编辑字典项
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "childEdit", type = 1)
|
||||
@Auth(val = "system")
|
||||
public R childEdit(SysDictDetail data) {
|
||||
return DbUtil.commonEdit(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个字典
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "info", type = 1)
|
||||
@Auth(val = "system")
|
||||
public R info(SysDict data) {
|
||||
SysDict db = DbUtil.queryObject("select * from sys_dict where id = ?", SysDict.class, data.getId());
|
||||
Assert.notNull(db, "当前记录不存在");
|
||||
return R.ok(db, "获取成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个字典项
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "childInfo", type = 1)
|
||||
@Auth(val = "system")
|
||||
public R childInfo(SysDictDetail data) {
|
||||
SysDictDetail db = DbUtil.queryObject("select * from sys_dict_detail where id = ?", SysDictDetail.class, data.getId());
|
||||
Assert.notNull(db, "当前记录不存在");
|
||||
return R.ok(db, "获取成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除字典
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "dels", type = 1)
|
||||
@Auth(val = "system")
|
||||
@Transactional
|
||||
public R dels(List<String> codes) {
|
||||
Assert.notEmpty(codes, "当前数据不存在");
|
||||
String codesStr = StrUtil.format("'{}'", CollUtil.join(codes, "','"));
|
||||
int count = DbUtil.delete("delete from sys_dict where code in ( " + codesStr + " )", SysDict.class);
|
||||
DbUtil.delete("delete from sys_dict_detail where code in ( " + codesStr + " )", SysDictDetail.class);
|
||||
Assert.isTrue(count > 0, "删除失败");
|
||||
return R.ok(null, "删除成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除字典项
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "childDels", type = 1)
|
||||
@Auth(val = "system")
|
||||
public R childDels(List<String> ids) {
|
||||
Assert.notEmpty(ids, "当前数据不存在");
|
||||
String idsStr = StrUtil.format("'{}'", CollUtil.join(ids, "','"));
|
||||
int count = DbUtil.delete("delete from sys_dict_detail where id in ( " + idsStr + " )", SysDictDetail.class);
|
||||
Assert.isTrue(count > 0, "删除失败");
|
||||
return R.ok(null, "删除成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下拉框
|
||||
*/
|
||||
@Action(val = "select", type = 1)
|
||||
@Login(val = false)
|
||||
public R select(SysDict dict) {
|
||||
List<SysDictDetail> list = selectData(dict);
|
||||
return R.ok(list, "获取成功");
|
||||
}
|
||||
|
||||
private List<SysDictDetail> selectData(SysDict dict) {
|
||||
return DbUtil.queryList("select * from sys_dict_detail where code = ?", SysDictDetail.class, dict.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下拉框Map
|
||||
*/
|
||||
@Action(val = "selectMap", type = 1)
|
||||
@Login(val = false)
|
||||
public R selectMap(SysDict dict) {
|
||||
List<SysDictDetail> list = selectData(dict);
|
||||
Map<String, String> data = CollUtil.fieldValueAsMap(list, "val", "name");
|
||||
return R.ok(data, "获取成功");
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,641 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.net.multipart.MultipartFormData;
|
||||
import cn.hutool.core.net.multipart.UploadFile;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.annt.Login;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemInface;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.common.filesystem.LocalFileSystem;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.*;
|
||||
import cn.tenfell.webos.modules.entity.*;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@BeanAction(val = "fileSystem")
|
||||
public class FileSystemAction {
|
||||
//查询文件列表
|
||||
public static CommonBean.Page<CommonBean.PathInfo> fileListPage(Dict param) {
|
||||
String parentPath = param.getStr("parentPath");
|
||||
Integer type = param.getInt("type");
|
||||
if (type == null) {
|
||||
type = 0;
|
||||
}
|
||||
String next = "";
|
||||
if (type != 0) {
|
||||
if (type == 1) {
|
||||
next = param.getStr("next");
|
||||
} else {
|
||||
type = 0;
|
||||
}
|
||||
}
|
||||
String shareCode = param.getStr("shareCode");
|
||||
String sharePwd = param.getStr("sharePwd");
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(parentPath, shareCode, sharePwd);
|
||||
if (StrUtil.isNotBlank(plainPath.getSioNo()) && plainPath.getCipherPath().split("/").length == 1) {
|
||||
//分享的数据的列表
|
||||
ShareFile sf;
|
||||
if (StrUtil.isNotBlank(shareCode)) {
|
||||
sf = DbUtil.queryObject("select * from share_file where code = ?", ShareFile.class, shareCode);
|
||||
} else {
|
||||
sf = DbUtil.queryObject("select * from share_file where no = ?", ShareFile.class, plainPath.getSioNo());
|
||||
}
|
||||
String[] files = sf.getFiles().split(";");
|
||||
String realPath = plainPath.getRealPath();
|
||||
String cipherPath = plainPath.getCipherPath();
|
||||
CommonBean.Page<CommonBean.PathInfo> page = new CommonBean.Page<>();
|
||||
page.setType(0);
|
||||
List<CommonBean.PathInfo> list = new ArrayList<>();
|
||||
for (String file : files) {
|
||||
plainPath.setRealPath(realPath + "/" + file);
|
||||
plainPath.setCipherPath(cipherPath + "/" + file);
|
||||
CommonBean.PathInfo info = FileSystemUtil.ACTION.fileInfo(plainPath);
|
||||
if (info != null) {
|
||||
info.setPath(parentPath + "/" + info.getPath());
|
||||
list.add(info);
|
||||
}
|
||||
}
|
||||
page.setList(list);
|
||||
return page;
|
||||
} else {
|
||||
CommonBean.Page<CommonBean.PathInfo> page = FileSystemUtil.ACTION.listFiles(plainPath, next);
|
||||
if (page == null) {
|
||||
page = new CommonBean.Page<>();
|
||||
page.setList(new ArrayList<>());
|
||||
}
|
||||
List<CommonBean.PathInfo> list = page.getList();
|
||||
for (CommonBean.PathInfo info : list) {
|
||||
String pj = "/";
|
||||
if (StrUtil.equals(parentPath, "/")) {
|
||||
pj = "";
|
||||
}
|
||||
info.setPath(parentPath + pj + info.getPath());
|
||||
}
|
||||
page.setList(list);
|
||||
return page;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件列表
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "fileList", type = 1)
|
||||
@Login(val = false)
|
||||
public R fileList(Dict param) {
|
||||
return R.ok(fileListPage(param), "获取成功");
|
||||
}
|
||||
|
||||
@Action(val = "downUrl", type = 1)
|
||||
@Login(val = false)
|
||||
public R downUrl(Dict param) {
|
||||
String path = param.getStr("path");
|
||||
String shareCode = param.getStr("shareCode");
|
||||
String sharePwd = param.getStr("sharePwd");
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, shareCode, sharePwd);
|
||||
String url = FileSystemUtil.ACTION.downloadUrl(plainPath);
|
||||
if (StrUtil.isNotBlank(url)) {
|
||||
return R.okData(url);
|
||||
}
|
||||
return R.failed("当前功能敬请期待");
|
||||
}
|
||||
|
||||
@Action(val = "fileInfo", type = 1)
|
||||
@Login(val = false)
|
||||
public R fileInfo(Dict param) {
|
||||
String path = param.getStr("path");
|
||||
if (path.startsWith("{trash:")) {
|
||||
return UserRecycleAction.infoByPath(path);
|
||||
}
|
||||
String[] sz = StrUtil.replace(path, "\\", "/").split("/");
|
||||
String parentPath = ArrayUtil.join(ArrayUtil.remove(sz, sz.length - 1), "/");
|
||||
String shareCode = param.getStr("shareCode");
|
||||
String sharePwd = param.getStr("sharePwd");
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, shareCode, sharePwd);
|
||||
CommonBean.PathInfo info;
|
||||
if (sz.length == 1) {
|
||||
info = new CommonBean.PathInfo();
|
||||
info.setPath(path);
|
||||
info.setType(2);
|
||||
if (StrUtil.isNotBlank(plainPath.getSioNo())) {
|
||||
ShareFile sf = FileSystemUtil.getShareFileByNo(plainPath.getSioNo());
|
||||
info.setName(sf.getName());
|
||||
info.setCreatedAt(LocalDateTimeUtil.formatNormal(sf.getShareTime()));
|
||||
} else if (StrUtil.isNotBlank(plainPath.getUioNo())) {
|
||||
IoUserDrive iud = FileSystemUtil.getIoUserDriveByNo(plainPath.getUioNo());
|
||||
info.setName(iud.getName());
|
||||
info.setCreatedAt(LocalDateTimeUtil.formatNormal(iud.getCreatedTime()));
|
||||
info.setUpdatedAt(LocalDateTimeUtil.formatNormal(iud.getUpdatedTime()));
|
||||
} else if (StrUtil.isNotBlank(plainPath.getIoNo())) {
|
||||
IoDrive id = FileSystemUtil.getIoDriveByNo(plainPath.getIoNo());
|
||||
info.setName(id.getName());
|
||||
info.setCreatedAt(LocalDateTimeUtil.formatNormal(id.getCreatedTime()));
|
||||
info.setUpdatedAt(LocalDateTimeUtil.formatNormal(id.getUpdatedTime()));
|
||||
}
|
||||
} else {
|
||||
info = FileSystemUtil.ACTION.fileInfo(plainPath);
|
||||
info.setPath(parentPath + "/" + info.getPath());
|
||||
}
|
||||
return R.okData(info);
|
||||
}
|
||||
|
||||
@Action(val = "pathName", type = 1)
|
||||
@Login(val = false)
|
||||
public R pathName(Dict param) {
|
||||
String path = param.getStr("path");
|
||||
String shareCode = param.getStr("shareCode");
|
||||
String sharePwd = param.getStr("sharePwd");
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, shareCode, sharePwd);
|
||||
String pathName = FileSystemUtil.ACTION.pathName(plainPath);
|
||||
if (StrUtil.isNotBlank(pathName)) {
|
||||
return R.okData(pathName);
|
||||
}
|
||||
return R.failed("当前功能敬请期待");
|
||||
}
|
||||
|
||||
@Action(val = "remove", type = 1)
|
||||
public R remove(Dict param) {
|
||||
String flag = copyMoveDel(param, "remove");
|
||||
if (StrUtil.equals(flag, "1")) {
|
||||
return R.ok(null, "删除成功");
|
||||
}
|
||||
return R.failed("删除失败");
|
||||
}
|
||||
|
||||
private String copyMoveDel(Dict param, String type) {
|
||||
String sourceParent = param.getStr("sourceParent");
|
||||
List<String> sourceChildren = param.getBean("sourceChildren");
|
||||
List<Integer> sourceTypes = param.getBean("sourceTypes");
|
||||
String target = param.getStr("target");
|
||||
FileSystemInface.PlainPath sourceParentPath = FileSystemUtil.cipherPath2PlainPathByLogin(sourceParent, "", "");
|
||||
if (StrUtil.equals(type, "copy")) {
|
||||
FileSystemInface.PlainPath targetPath = FileSystemUtil.cipherPath2PlainPathByLogin(target, "", "");
|
||||
return FileSystemUtil.ACTION.copy(sourceParentPath, sourceChildren, sourceTypes, targetPath);
|
||||
} else if (StrUtil.equals(type, "move")) {
|
||||
FileSystemInface.PlainPath targetPath = FileSystemUtil.cipherPath2PlainPathByLogin(target, "", "");
|
||||
return FileSystemUtil.ACTION.move(sourceParentPath, sourceChildren, sourceTypes, targetPath);
|
||||
} else if (StrUtil.equals(type, "remove")) {
|
||||
List<CommonBean.PathInfo> infos = null;
|
||||
Map<String, String> removeMap = null;
|
||||
String realPath = sourceParentPath.getRealPath();
|
||||
String cipherPath = sourceParentPath.getCipherPath();
|
||||
if (!StrUtil.equals(sourceParentPath.getDriveType(), FileSystemUtil.SERVER_DRIVE_TYPE)) {
|
||||
infos = new ArrayList<>();
|
||||
if (StrUtil.equals(sourceParentPath.getDriveType(), FileSystemUtil.LOCAL_DRIVE_TYPE)) {
|
||||
removeMap = new HashMap<>();
|
||||
}
|
||||
for (String child : sourceChildren) {
|
||||
sourceParentPath.setRealPath(realPath + "/" + child);
|
||||
sourceParentPath.setCipherPath(cipherPath + "/" + child);
|
||||
infos.add(FileSystemUtil.ACTION.fileInfo(sourceParentPath));
|
||||
if (StrUtil.equals(sourceParentPath.getDriveType(), FileSystemUtil.LOCAL_DRIVE_TYPE)) {
|
||||
String cycleId = IdUtil.fastSimpleUUID();
|
||||
String cycle = ProjectUtil.rootPath + "/recycle/" + cycleId + ".zip";
|
||||
OutputStream fos = FileUtil.getOutputStream(cycle);
|
||||
List<File> list = new ArrayList<>();
|
||||
File obj = new File(sourceParentPath.getRealPath());
|
||||
if (obj.exists()) {
|
||||
if (obj.isDirectory()) {
|
||||
List<File> all = FileUtil.loopFiles(obj);
|
||||
if (all != null) {
|
||||
list.addAll(all);
|
||||
}
|
||||
} else if (obj.isFile()) {
|
||||
list.add(obj);
|
||||
}
|
||||
}
|
||||
String[] paths = new String[list.size()];
|
||||
InputStream[] ins = new InputStream[list.size()];
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
File file = list.get(i);
|
||||
ins[i] = FileUtil.getInputStream(file);
|
||||
paths[i] = StrUtil.replace(StrUtil.replace(file.getAbsolutePath(), "\\", "/"), realPath + "/", "");
|
||||
}
|
||||
ZipUtil.zip(fos, paths, ins);
|
||||
removeMap.put(child, cycleId);
|
||||
}
|
||||
}
|
||||
sourceParentPath.setRealPath(realPath);
|
||||
sourceParentPath.setCipherPath(cipherPath);
|
||||
}
|
||||
String removeStr = FileSystemUtil.ACTION.remove(sourceParentPath, sourceChildren, sourceTypes);
|
||||
sourceParentPath.setRealPath(realPath);
|
||||
sourceParentPath.setCipherPath(cipherPath);
|
||||
if (StrUtil.equals(removeStr, "1") && infos != null) {
|
||||
for (CommonBean.PathInfo info : infos) {
|
||||
IoUserRecycle iur = new IoUserRecycle();
|
||||
iur.setUserId(LoginAuthUtil.getUser().getId());
|
||||
iur.setSize(info.getSize());
|
||||
iur.setType(info.getType());
|
||||
iur.setName(info.getName());
|
||||
iur.setDeletedTime(LocalDateTime.now());
|
||||
iur.setRemovePath(sourceParentPath.getCipherPath() + "/" + info.getPath());
|
||||
if (StrUtil.equals(sourceParentPath.getDriveType(), FileSystemUtil.LOCAL_DRIVE_TYPE)) {
|
||||
iur.setId(removeMap.get(info.getPath()));
|
||||
} else {
|
||||
iur.setId(IdUtil.fastSimpleUUID());
|
||||
}
|
||||
DbUtil.insertObject(iur);
|
||||
}
|
||||
} else if (StrUtil.equals(removeStr, "0") && removeMap != null) {
|
||||
removeMap.forEach((s, path) -> FileUtil.del(path));
|
||||
}
|
||||
return removeStr;
|
||||
} else {
|
||||
Assert.isTrue(false, "此操作不存在");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Action(val = "copy", type = 1)
|
||||
public R copy(Dict param) {
|
||||
String resData = copyMoveDel(param, "copy");
|
||||
return R.ok(resData, "请根据结果判断");
|
||||
}
|
||||
|
||||
@Action(val = "move", type = 1)
|
||||
public R move(Dict param) {
|
||||
String resData = copyMoveDel(param, "move");
|
||||
return R.ok(resData, "请根据结果判断");
|
||||
}
|
||||
|
||||
@Action(val = "rename", type = 1)
|
||||
public R rename(Dict param) {
|
||||
String path = param.getStr("path");
|
||||
String name = param.getStr("name");
|
||||
Integer type = param.getInt("type");
|
||||
FileSystemInface.PlainPath file = FileSystemUtil.cipherPath2PlainPathByLogin(path, "", "");
|
||||
boolean flag = FileSystemUtil.ACTION.rename(file, name, type);
|
||||
if (flag) {
|
||||
return R.ok();
|
||||
}
|
||||
return R.failed();
|
||||
}
|
||||
|
||||
@Action(val = "serverJd", type = 1)
|
||||
public R serverJd(Dict param) {
|
||||
String taskId = param.getStr("taskId");
|
||||
CommonBean.CopyMoveFile cmf = CacheUtil.getValue("copymove:task:" + taskId);
|
||||
if (cmf == null) {
|
||||
return R.failed();
|
||||
}
|
||||
return R.okData(cmf);
|
||||
}
|
||||
|
||||
@Action(val = "serverConfirm", type = 1)
|
||||
public R serverConfirm(Dict param) {
|
||||
String taskId = param.getStr("taskId");
|
||||
FileSystemUtil.serverConfirm(taskId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Action(val = "serverStop", type = 1)
|
||||
public R serverStop(Dict param) {
|
||||
String taskId = param.getStr("taskId");
|
||||
FileSystemUtil.serverStop(taskId);
|
||||
return R.ok(null, "将在下一个文件进行终止");
|
||||
}
|
||||
|
||||
@Action(val = "createDir", type = 1)
|
||||
public R createDir(Dict param) {
|
||||
String path = param.getStr("path");
|
||||
String name = param.getStr("name");
|
||||
FileSystemInface.PlainPath file = FileSystemUtil.cipherPath2PlainPathByLogin(path, "", "");
|
||||
String fileId = FileSystemUtil.ACTION.createDir(file, name);
|
||||
if (StrUtil.isNotBlank(fileId)) {
|
||||
return R.okData(fileId);
|
||||
}
|
||||
return R.failed();
|
||||
}
|
||||
|
||||
@Action(val = "getDriveType", type = 1)
|
||||
@Login(val = false)
|
||||
public R getDriveType(Dict param) {
|
||||
String path = param.getStr("path");
|
||||
String shareCode = param.getStr("shareCode");
|
||||
String sharePwd = param.getStr("sharePwd");
|
||||
FileSystemInface.PlainPath file = FileSystemUtil.cipherPath2PlainPathByLogin(path, shareCode, sharePwd);
|
||||
return R.ok(file.getDriveType(), "获取驱动类型成功");
|
||||
}
|
||||
|
||||
@Action(val = "uploadPre", type = 1)
|
||||
public R uploadPre(Dict param) {
|
||||
String path = param.getStr("path");
|
||||
Assert.isFalse(path.startsWith("{sio:"), "此目录不支持上传");
|
||||
FileSystemInface.PlainPath parentPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, "", "");
|
||||
String expand = param.getStr("expand");
|
||||
String name = param.getStr("name");
|
||||
String data = FileSystemUtil.ACTION.uploadPre(parentPath, name, expand);
|
||||
return R.ok(data, "获取上传数据成功");
|
||||
}
|
||||
|
||||
@Action(val = "uploadUrl", type = 1)
|
||||
public R uploadUrl(Dict param) {
|
||||
String path = param.getStr("path");
|
||||
Assert.isFalse(path.startsWith("{sio:"), "此目录不支持上传");
|
||||
String name = param.getStr("name");
|
||||
FileSystemInface.PlainPath parentPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, "", "");
|
||||
String expand = param.getStr("expand");
|
||||
String data = FileSystemUtil.ACTION.uploadUrl(parentPath, name, expand);
|
||||
return R.ok(data, "上传验证完成");
|
||||
}
|
||||
|
||||
@Action(val = "uploadAfter", type = 1)
|
||||
public R uploadAfter(Dict param) {
|
||||
String path = param.getStr("path");
|
||||
Assert.isFalse(path.startsWith("{sio:"), "此目录不支持上传");
|
||||
String name = param.getStr("name");
|
||||
FileSystemInface.PlainPath parentPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, "", "");
|
||||
String expand = param.getStr("expand");
|
||||
String data = FileSystemUtil.ACTION.uploadAfter(parentPath, name, expand);
|
||||
return R.ok(data, "上传验证完成");
|
||||
}
|
||||
|
||||
@Action(val = "pathEncrypt", type = 1)
|
||||
@Login(val = false)
|
||||
public R pathEncrypt(Dict dict) {
|
||||
String path = dict.getStr("path");
|
||||
JSONObject map = JSONUtil.createObj().set("path", path);
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
if (user != null) {
|
||||
map.set("hash", user.getId());
|
||||
}
|
||||
String str = SecureUtil.rsa(ProjectUtil.startConfig.getStr("zlPrivate"), ProjectUtil.startConfig.getStr("zlPublic")).encryptBase64(map.toString(), CharsetUtil.CHARSET_UTF_8, KeyType.PublicKey);
|
||||
return R.okData(str);
|
||||
}
|
||||
|
||||
private InputStream getContentByType(Dict dict, int type) {
|
||||
//type 1下载内容(使用重定向) 2.ajax获取内容(支持的使用重定向,不支持的使用中转)
|
||||
String pathCipher = dict.getStr("path");
|
||||
String jsonStr = SecureUtil.rsa(ProjectUtil.startConfig.getStr("zlPrivate"), ProjectUtil.startConfig.getStr("zlPublic")).decryptStr(pathCipher, KeyType.PrivateKey);
|
||||
JSONObject map = JSONUtil.parseObj(jsonStr);
|
||||
String path = map.getStr("path");
|
||||
String userId = map.getStr("hash");
|
||||
String share = dict.getStr("share");
|
||||
String pwd = dict.getStr("pwd");
|
||||
String lastname = dict.getStr("lastname");
|
||||
if (StrUtil.isBlank(path)) {
|
||||
return null;
|
||||
}
|
||||
SysUser user = null;
|
||||
if (StrUtil.isNotBlank(userId)) {
|
||||
user = DbUtil.queryObject("select * from sys_user where id = ?", SysUser.class, userId);
|
||||
}
|
||||
if (user == null) {
|
||||
user = LoginAuthUtil.getUser();
|
||||
}
|
||||
FileSystemInface.PlainPath plainPath;
|
||||
if (StrUtil.equals(lastname, "1")) {
|
||||
String[] sz = StrUtil.replace(path, "\\", "/").split("/");
|
||||
String name = sz[sz.length - 1];
|
||||
String parentPath = ArrayUtil.join(ArrayUtil.remove(sz, sz.length - 1), "/");
|
||||
FileSystemInface.PlainPath parent = FileSystemUtil.cipherPath2PlainPathByUser(parentPath, share, pwd, user);
|
||||
List<CommonBean.PathInfo> list = FileSystemUtil.ACTION.searchFile(parent, name);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return null;
|
||||
}
|
||||
plainPath = FileSystemUtil.cipherPath2PlainPathByUser(parentPath + "/" + list.get(0).getPath(), share, pwd, user);
|
||||
} else {
|
||||
plainPath = FileSystemUtil.cipherPath2PlainPathByUser(path, share, pwd, user);
|
||||
}
|
||||
if (type == 2) {
|
||||
//需要中转才能获取内容的在这里处理
|
||||
}
|
||||
//重定向获取
|
||||
String url = FileSystemUtil.ACTION.downloadUrl(plainPath);
|
||||
Context ctx = ProjectUtil.getContext().getCtx();
|
||||
ctx.headerSet("Location", url);
|
||||
ctx.headerSet("Access-Control-Allow-Origin", "*");
|
||||
ctx.headerSet("Access-Control-Allow-Credentials", "true");
|
||||
ctx.headerSet("Access-Control-Allow-Methods", "*");
|
||||
ctx.headerSet("Access-Control-Allow-Headers", "*");
|
||||
ctx.headerSet("Access-Control-Expose-Headers", "*");
|
||||
ctx.headerSet("Access-Control-Max-Age", "86400");
|
||||
ctx.status(302);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Action(val = "content", type = 2)
|
||||
@Login(val = false)
|
||||
public InputStream content(Dict dict) {
|
||||
return getContentByType(dict, 2);
|
||||
}
|
||||
|
||||
|
||||
@Action(val = "url", type = 2)
|
||||
@Login(val = false)
|
||||
public InputStream url(Dict dict) {
|
||||
return getContentByType(dict, 1);
|
||||
}
|
||||
|
||||
@Action(val = "availableMainName", type = 1)
|
||||
public R availableMainName(Dict dict) {
|
||||
String path = dict.getStr("path");
|
||||
String mainName = dict.getStr("mainName");
|
||||
String ext = dict.getStr("ext");
|
||||
FileSystemInface.PlainPath parentPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, "", "");
|
||||
//返回新名称,不存在直接返回mainName,存在返回可能的mainName新名称
|
||||
String newMainName = FileSystemUtil.ACTION.availableMainName(parentPath, mainName, ext);
|
||||
return R.okData(newMainName);
|
||||
}
|
||||
|
||||
@Action(val = "unzip", type = 1)
|
||||
public R unzip(Dict dict) {
|
||||
String path = dict.getStr("path");
|
||||
FileSystemInface.PlainPath file = FileSystemUtil.cipherPath2PlainPathByLogin(path, "", "");
|
||||
//返回新名称,不存在直接返回mainName,存在返回可能的mainName新名称
|
||||
String status = FileSystemUtil.ACTION.unzip(file);
|
||||
if (StrUtil.equals(status, "1")) {
|
||||
return R.ok(null, "解压成功");
|
||||
}
|
||||
return R.failed("解压失败");
|
||||
}
|
||||
|
||||
|
||||
@Action(val = "zip", type = 1)
|
||||
public R zip(JSONObject dict) {
|
||||
List<String> paths = dict.getBeanList("paths", String.class);
|
||||
List<FileSystemInface.PlainPath> files = new ArrayList<>();
|
||||
for (String path : paths) {
|
||||
FileSystemInface.PlainPath file = FileSystemUtil.cipherPath2PlainPathByLogin(path, "", "");
|
||||
files.add(file);
|
||||
}
|
||||
FileSystemInface.PlainPath parent = FileSystemUtil.cipherPath2PlainPathByLogin(dict.getStr("parentPath"), "", "");
|
||||
//返回新名称,不存在直接返回mainName,存在返回可能的mainName新名称
|
||||
String flag = FileSystemUtil.ACTION.zip(files, parent);
|
||||
if (StrUtil.equals("1", flag)) {
|
||||
return R.ok("1", "压缩成功");
|
||||
} else if (StrUtil.equals("2", flag)) {
|
||||
return R.ok("2", "此盘暂不支持压缩");
|
||||
}
|
||||
return R.failed("压缩失败");
|
||||
}
|
||||
|
||||
|
||||
@Action(val = "uploadSmallFile", type = 0)
|
||||
public R uploadSmallFile(Context ctx) throws Exception {
|
||||
MultipartFormData multiForm = ProjectContext.getMultipart(ctx);
|
||||
Map<String, String[]> param = multiForm.getParamMap();
|
||||
String parentPath = param.get("parentPath")[0];
|
||||
Assert.isFalse(parentPath.startsWith("{sio:"), "此目录不支持上传");
|
||||
UploadFile file = multiForm.getFile("file");
|
||||
InputStream in;
|
||||
if (file.size() == 0) {
|
||||
in = new ByteArrayInputStream(new byte[0]);
|
||||
} else {
|
||||
in = file.getFileInputStream();
|
||||
}
|
||||
String name = param.get("name")[0];
|
||||
FileSystemInface.PlainPath parent = FileSystemUtil.cipherPath2PlainPathByLogin(parentPath, "", "");
|
||||
String fileId = FileSystemUtil.ACTION.uploadByServer(parent, name, in, null, null);
|
||||
Assert.notBlank(fileId, "保存失败");
|
||||
return R.ok(fileId, "保存成功");
|
||||
}
|
||||
|
||||
//以下方法主要针对本地文件
|
||||
@Action(val = "localFileUpload", type = 0)
|
||||
@Login(val = false)
|
||||
public R localFileUpload(Context ctx) {
|
||||
String upload_id = ctx.header("upload-id");
|
||||
String index = ctx.header("upload-index");
|
||||
String fp_hash = ctx.header("fp-hash");
|
||||
try {
|
||||
String filePath = ProjectUtil.rootPath + "/tmpUpload/" + upload_id + "/" + index + ".data";
|
||||
FileUtil.writeFromStream(ctx.bodyAsStream(), filePath);
|
||||
String nowHash = SecureUtil.md5(new File(filePath));
|
||||
if (fp_hash.equals(nowHash)) {
|
||||
return R.ok("1", "上传成功");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
return R.ok("2", "分片上传失败");
|
||||
}
|
||||
|
||||
@Action(val = "localFileViewer", type = 0)
|
||||
@Login(val = false)
|
||||
public InputStream localFileViewer(Context ctx) {
|
||||
String ext = ctx.param("ext");
|
||||
String path = ctx.param("path");
|
||||
String tbPath = CommonUtil.getTbPath(ext, path);
|
||||
if (StrUtil.isBlank(tbPath)) {
|
||||
return null;
|
||||
}
|
||||
if (CommonUtil.isImage(ext)) {
|
||||
ctx.headerSet("content-type", "image/jpeg");
|
||||
} else {
|
||||
ctx.headerSet("content-type", "application/octet-stream");
|
||||
}
|
||||
return FileUtil.getInputStream(tbPath);
|
||||
}
|
||||
|
||||
@Action(val = "localFileDown", type = 0)
|
||||
@Login(val = false)
|
||||
public InputStream localFileDown(Context ctx) {
|
||||
String tempId = ctx.param("tempId");
|
||||
Dict data = CacheUtil.getValue("file_down:" + tempId);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
String name = data.getStr("name");
|
||||
String realPath;
|
||||
if (StrUtil.equals(data.getStr("type"), FileSystemUtil.SERVER_DRIVE_TYPE)) {
|
||||
realPath = data.getStr("realPath");
|
||||
} else if (StrUtil.equals(data.getStr("type"), FileSystemUtil.LOCAL_DRIVE_TYPE)) {
|
||||
realPath = LocalFileSystem.getRealFile(data.getStr("realFilePath"), data.getStr("realPath"));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
File file = new File(realPath);
|
||||
long size = file.length();
|
||||
if (size == 0) {
|
||||
return null;
|
||||
}
|
||||
//type 1试探请求 2分片下载 3完整下载
|
||||
int type = 3;
|
||||
if (StrUtil.equals(ctx.method(), "HEAD")) {
|
||||
type = 1;
|
||||
}
|
||||
long start = 0;
|
||||
long end = 0;
|
||||
if (type == 3) {
|
||||
String range = ctx.header("Range");
|
||||
if (StrUtil.isNotBlank(range)) {
|
||||
type = 1;
|
||||
String[] sz = range.split("=")[1].split(",")[0].split("-");
|
||||
if (sz.length > 0) {
|
||||
start = Convert.toLong(sz[0].trim());
|
||||
if (sz.length > 1) {
|
||||
end = Convert.toLong(sz[1].trim());
|
||||
} else {
|
||||
end = size - 1;
|
||||
}
|
||||
type = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (CommonUtil.isImage(FileUtil.extName(name))) {
|
||||
ctx.headerSet("Content-Type", "image/jpeg");
|
||||
} else {
|
||||
ctx.headerSet("Content-Type", "application/octet-stream");
|
||||
}
|
||||
ctx.headerSet("Accept-Ranges", "bytes");
|
||||
ctx.headerSet("Content-Disposition", "attachment; filename=\"" + URLUtil.encodeAll(name) + "\"");
|
||||
if (type == 1 || type == 3) {
|
||||
start = 0;
|
||||
end = size - 1;
|
||||
}
|
||||
ctx.headerSet("Content-Length", Convert.toStr(end - start + 1));
|
||||
if (type == 1) {
|
||||
return null;
|
||||
}
|
||||
InputStream in = FileUtil.getInputStream(file);
|
||||
long length = end - start + 1;
|
||||
InputStream resIn;
|
||||
if (ProjectUtil.startConfig.getBool("rangeFilter")) {
|
||||
ctx.headerSet("Content-Range", "bytes " + start + "-" + end + "/" + size);
|
||||
resIn = new ShardingInputStream(in, start, length);
|
||||
} else {
|
||||
resIn = in;
|
||||
}
|
||||
if (type == 2) {
|
||||
ctx.status(206);
|
||||
} else if (type == 3) {
|
||||
ctx.status(200);
|
||||
}
|
||||
return resIn;
|
||||
}
|
||||
|
||||
@Action(val = "commonDriveReq", type = 0)
|
||||
public R commonDriveReq(Context req) {
|
||||
String path = URLUtil.decode(req.header("drive-path"));
|
||||
Assert.notBlank(path, "路径不可为空");
|
||||
FileSystemInface.PlainPath drivePath = FileSystemUtil.cipherPath2PlainPathByLogin(path, "", "");
|
||||
return FileSystemUtil.ACTION.commonReq(drivePath, req);
|
||||
}
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.lang.func.VoidFunc1;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.Db;
|
||||
import cn.hutool.db.ds.simple.SimpleDataSource;
|
||||
import cn.hutool.db.transaction.TransactionLevel;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.annt.Login;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.DbUtil;
|
||||
import cn.tenfell.webos.common.util.ProjectUtil;
|
||||
import cn.tenfell.webos.modules.entity.IoDrive;
|
||||
import cn.tenfell.webos.modules.entity.IoUserDrive;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.List;
|
||||
|
||||
@BeanAction(val = "install")
|
||||
public class InstallAction {
|
||||
@Action(val = "save", type = 1)
|
||||
@Login(val = false)
|
||||
public R save(Dict dict) {
|
||||
Assert.isFalse(ProjectUtil.hasInstall || FileUtil.exist(ProjectUtil.projectConfigPath),
|
||||
"当前系统已经安装,请勿重复安装");
|
||||
List<Integer> list = CollUtil.newArrayList(1, 2);
|
||||
for (Integer i : list) {
|
||||
dict.set("type", i);
|
||||
try {
|
||||
R r = check(dict);
|
||||
if (Convert.toInt(r.get("code")) != 0) {
|
||||
return r;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return R.failed();
|
||||
}
|
||||
}
|
||||
JSONObject data = dict.getBean("data");
|
||||
//先写入配置
|
||||
FileUtil.writeUtf8String(data.toString(), ProjectUtil.projectConfigPath);
|
||||
//初始化配置
|
||||
R f = ProjectUtil.install();
|
||||
if (Convert.toInt(f.get("code"),-1) != 0) {
|
||||
return f;
|
||||
}
|
||||
//写入用户数据
|
||||
JSONObject user = dict.getBean("user");
|
||||
try {
|
||||
DbUtil.get().tx(TransactionLevel.READ_COMMITTED, (VoidFunc1<Db>) db -> {
|
||||
DbUtil.set(db);
|
||||
SysUser userDb = UserAction.createMain(user.getStr("username"), user.getStr("password"), 1);
|
||||
IoDrive param = new IoDrive();
|
||||
param.setName("默认存储");
|
||||
param.setDriveType(FileSystemUtil.LOCAL_DRIVE_TYPE);
|
||||
param.setMaxSize(0);
|
||||
param.setUserDriveName("默认存储");
|
||||
String path = ProjectUtil.rootPath + "/" + IdUtil.fastSimpleUUID();
|
||||
param.setPath(path);
|
||||
param.setSecondTransmission(1);
|
||||
param.setRealFilePath(ProjectUtil.rootPath);
|
||||
IoDrive ioDb = IoDriveAction.saveIoDrive(param, userDb.getId());
|
||||
IoUserDrive param2 = new IoUserDrive();
|
||||
param2.setUserId(userDb.getId());
|
||||
param2.setDriveId(ioDb.getId());
|
||||
param2.setMaxSize(0);
|
||||
param2.setName("默认存储");
|
||||
param2.setValid(1);
|
||||
IoUserDriveAction.saveIoUserDrive(param2, ioDb, userDb);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
ProjectUtil.uninstall();
|
||||
return R.error(e);
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Action(val = "check", type = 1)
|
||||
@Login(val = false)
|
||||
public R check(Dict dict) throws Exception {
|
||||
Assert.isFalse(ProjectUtil.hasInstall || FileUtil.exist(ProjectUtil.projectConfigPath),
|
||||
"当前系统已经安装,请勿重复安装");
|
||||
JSONObject data = dict.getBean("data");
|
||||
int type = dict.getInt("type");
|
||||
if (type == 1) {
|
||||
//检查数据库
|
||||
DataSource dataSource = null;
|
||||
String sqlType = data.getStr("sqlType");
|
||||
if (StrUtil.equals(sqlType, "sqlite")) {
|
||||
JSONObject sqlite = data.getJSONObject("sqlite");
|
||||
String path = ProjectUtil.rootPath + sqlite.getStr("path");
|
||||
FileUtil.mkParentDirs(path);
|
||||
dataSource = new SimpleDataSource(StrUtil.format("jdbc:sqlite:{}", path), null, null, null);
|
||||
} else if (StrUtil.equals(sqlType, "mysql")) {
|
||||
JSONObject mysql = data.getJSONObject("mysql");
|
||||
String host = mysql.getStr("host");
|
||||
Integer port = mysql.getInt("port");
|
||||
String database = mysql.getStr("database");
|
||||
String user = mysql.getStr("user");
|
||||
String password = mysql.getStr("password");
|
||||
dataSource = new SimpleDataSource(StrUtil.format("jdbc:mysql://{}:{}/{}?useSSL=false&serverTimezone=GMT%2B8", host, port, database), user, password, "com.mysql.jdbc.Driver");
|
||||
}
|
||||
if (dataSource == null) {
|
||||
return R.failed();
|
||||
}
|
||||
if (!dataSource.getConnection().isClosed()) {
|
||||
return R.ok();
|
||||
}
|
||||
} else if (type == 2) {
|
||||
//检查缓存
|
||||
String cacheType = data.getStr("cacheType");
|
||||
if (StrUtil.equals(cacheType, "file")) {
|
||||
JSONObject file = data.getJSONObject("file");
|
||||
String dir = StrUtil.format("{}{}", ProjectUtil.rootPath, file.getStr("path"));
|
||||
FileUtil.mkdir(dir);
|
||||
if (FileUtil.exist(dir)) {
|
||||
return R.ok();
|
||||
} else {
|
||||
return R.failed("当前目录不存在");
|
||||
}
|
||||
} else if (StrUtil.equals(cacheType, "redis")) {
|
||||
JSONObject redis = data.getJSONObject("redis");
|
||||
String host = redis.getStr("host");
|
||||
Integer port = redis.getInt("port");
|
||||
Integer database = redis.getInt("database");
|
||||
String password = redis.getStr("password");
|
||||
Jedis jedis = new Jedis(host, port);
|
||||
jedis.auth(password);
|
||||
jedis.select(database);
|
||||
if (jedis.isConnected()) {
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
} else if (type == 4) {
|
||||
//检查接口
|
||||
return R.okData(Dict.create().set("rootPath", ProjectUtil.rootPath));
|
||||
} else {
|
||||
return R.failed();
|
||||
}
|
||||
return R.failed();
|
||||
}
|
||||
}
|
@ -0,0 +1,282 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.Auth;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.annt.Transactional;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemInface;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.DbUtil;
|
||||
import cn.tenfell.webos.common.util.LoginAuthUtil;
|
||||
import cn.tenfell.webos.common.util.TokenDataUtil;
|
||||
import cn.tenfell.webos.common.util.ValidaUtil;
|
||||
import cn.tenfell.webos.modules.entity.IoDrive;
|
||||
import cn.tenfell.webos.modules.entity.IoTokenData;
|
||||
import cn.tenfell.webos.modules.entity.IoUserDrive;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@BeanAction(val = "ioDrive")
|
||||
public class IoDriveAction {
|
||||
public static CommonBean.PageRes<IoDrive> mainUserDrive(Dict param){
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
Assert.isTrue(user.getUserType()==1,"权限不足");
|
||||
int currentPage = param.getInt("current");
|
||||
Integer pageSize = param.getInt("pageSize");
|
||||
String where = " where 1=1";
|
||||
List<Object> params = new ArrayList<>();
|
||||
if (!LoginAuthUtil.isSystem()) {
|
||||
where += " and parent_user_id = ?";
|
||||
params.add(user.getId());
|
||||
}
|
||||
CommonBean.PageRes<IoDrive> data = DbUtil.pageObject("select *", "from io_drive" + where, IoDrive.class, currentPage, pageSize, params.toArray());
|
||||
for (IoDrive id : data.getData()) {
|
||||
id.setName("(" + UserAction.getUserName(id.getParentUserId()) + ")" + id.getName());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* 获取硬盘列表
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "list", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R list(Dict param) {
|
||||
return R.ok(mainUserDrive(param), "获取成功");
|
||||
}
|
||||
|
||||
@Action(val = "info", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R info(IoDrive data) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
String where = " where id = ?";
|
||||
List<Object> params = new ArrayList<>();
|
||||
params.add(data.getId());
|
||||
if (!LoginAuthUtil.isSystem()) {
|
||||
where += " and parent_user_id = ?";
|
||||
params.add(user.getId());
|
||||
}
|
||||
IoDrive db = DbUtil.queryObject("select * from io_drive" + where, IoDrive.class, params.toArray());
|
||||
Assert.notNull(db, "当前记录不存在");
|
||||
return R.ok(db, "获取成功");
|
||||
}
|
||||
|
||||
public static IoDrive saveIoDrive(IoDrive param, String userId) {
|
||||
IoDrive data = new IoDrive();
|
||||
data.setParentUserId(userId);
|
||||
data.setCreatedTime(LocalDateTime.now());
|
||||
data.setName(param.getName());
|
||||
data.setDriveType(param.getDriveType());
|
||||
data.setMaxSize(param.getMaxSize());
|
||||
data.setUserDriveName(param.getUserDriveName());
|
||||
data.setPath(StrUtil.replace(StrUtil.replace(param.getPath(), "\\", "/"), "//", "/"));
|
||||
data.setUseSize(0L);
|
||||
data.setSecondTransmission(param.getSecondTransmission());
|
||||
if (StrUtil.equals(data.getDriveType(), FileSystemUtil.LOCAL_DRIVE_TYPE) && data.getSecondTransmission() == 1) {
|
||||
Assert.notBlank(param.getRealFilePath(), "开启秒传但是缺少真实文件目录");
|
||||
}
|
||||
if (StrUtil.isNotBlank(param.getRealFilePath())) {
|
||||
String tmp = StrUtil.replace(StrUtil.replace(param.getRealFilePath(), "\\", "/"), "//", "/");
|
||||
if (tmp.endsWith("/")) {
|
||||
tmp = tmp.substring(0, tmp.length() - 1);
|
||||
}
|
||||
data.setRealFilePath(tmp);
|
||||
}
|
||||
data.setAvailSize(data.getMaxSize());
|
||||
data.setUpdatedTime(data.getCreatedTime());
|
||||
IoTokenData itd = TokenDataUtil.getTokenDataByIdOrToken(data.getDriveType(), param.getTokenId());
|
||||
data.setTokenId(itd.getId());
|
||||
Long no = DbUtil.queryLong("select max(`no`) as `max_no` from `io_drive`", IoDrive.class);
|
||||
no++;
|
||||
data.setNo(no.intValue());
|
||||
data.setId(IdUtil.fastSimpleUUID());
|
||||
Assert.isTrue(DbUtil.insertObject(data), "硬盘创建失败");
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加硬盘
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "save", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R save(IoDrive param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
ValidaUtil.init(param)
|
||||
.notBlank("name", "名称")
|
||||
.notBlank("driveType", "类型")
|
||||
.notNull("maxSize", "容量")
|
||||
.notBlank("tokenId", "配置", StrUtil.equals(param.getDriveType(), FileSystemUtil.LOCAL_DRIVE_TYPE))
|
||||
.notBlank("path", "路径");
|
||||
if (StrUtil.equals(param.getDriveType(), FileSystemUtil.LOCAL_DRIVE_TYPE)) {
|
||||
Assert.isTrue(LoginAuthUtil.isSystem(), "非管理员无法添加本地磁盘");
|
||||
}
|
||||
saveIoDrive(param, user.getId());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改硬盘
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "update", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R update(IoDrive param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
String where = " where id = ?";
|
||||
List<Object> params = new ArrayList<>();
|
||||
params.add(param.getId());
|
||||
if (!LoginAuthUtil.isSystem()) {
|
||||
where += " and parent_user_id = ?";
|
||||
params.add(user.getId());
|
||||
}
|
||||
IoDrive db = DbUtil.queryObject("select * from io_drive" + where, IoDrive.class, params.toArray());
|
||||
Assert.notNull(db, "当前记录不存在");
|
||||
if (StrUtil.isNotBlank(param.getName())) {
|
||||
db.setName(param.getName());
|
||||
}
|
||||
if (param.getMaxSize() > 0) {
|
||||
db.setMaxSize(param.getMaxSize());
|
||||
db.setAvailSize(db.getMaxSize() - db.getUseSize());
|
||||
if (db.getAvailSize() < 0) {
|
||||
db.setAvailSize(0);
|
||||
}
|
||||
}
|
||||
if (StrUtil.isNotBlank(param.getTokenId())) {
|
||||
IoTokenData itd = TokenDataUtil.getTokenDataByIdOrToken(db.getDriveType(), param.getTokenId());
|
||||
db.setTokenId(itd.getId());
|
||||
}
|
||||
if (StrUtil.isNotBlank(param.getUserDriveName())) {
|
||||
db.setUserDriveName(param.getUserDriveName());
|
||||
}
|
||||
db.setUpdatedTime(LocalDateTime.now());
|
||||
return DbUtil.commonEdit(db);
|
||||
}
|
||||
|
||||
public static void delIoDriveByIds(List<IoDrive> list){
|
||||
if(CollUtil.isEmpty(list)){
|
||||
return;
|
||||
}
|
||||
List<String> ids = CollUtil.getFieldValues(list, "id", String.class);
|
||||
String idsStr = StrUtil.format("'{}'", CollUtil.join(ids, "','"));
|
||||
DbUtil.delete("delete from io_drive where id in ( " + idsStr + " )", IoDrive.class);
|
||||
List<IoUserDrive> iuds = DbUtil.queryList("select * from io_user_drive where drive_id in ( " + idsStr + " )", IoUserDrive.class);
|
||||
IoUserDriveAction.delIoUserDriveByIds(iuds);
|
||||
}
|
||||
|
||||
@Action(val = "dels", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
@Transactional
|
||||
public R dels(List<String> ids) {
|
||||
Assert.notEmpty(ids, "当前数据不存在");
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
String idsStr = StrUtil.format("'{}'", CollUtil.join(ids, "','"));
|
||||
String where = "";
|
||||
List<Object> params = new ArrayList<>();
|
||||
if (!LoginAuthUtil.isSystem()) {
|
||||
where += " and parent_user_id = ?";
|
||||
params.add(user.getId());
|
||||
}
|
||||
List<IoDrive> list = DbUtil.queryList("select * from io_drive where id in ( " + idsStr + " )" + where, IoDrive.class, params.toArray());
|
||||
delIoDriveByIds(list);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取下拉框
|
||||
*/
|
||||
@Action(val = "select")
|
||||
@Auth(val = "main_auth")
|
||||
public R select() {
|
||||
List<IoDrive> list = selectData();
|
||||
return R.ok(list, "获取成功");
|
||||
}
|
||||
|
||||
private List<IoDrive> selectData() {
|
||||
SysUser loginUser = LoginAuthUtil.getUser();
|
||||
if (LoginAuthUtil.isSystem()) {
|
||||
return DbUtil.queryList("select * from io_drive", IoDrive.class);
|
||||
} else {
|
||||
return DbUtil.queryList("select * from io_drive where parent_user_id = ?", IoDrive.class, loginUser.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下拉框Map
|
||||
*/
|
||||
@Action(val = "selectMap")
|
||||
@Auth(val = "main_auth")
|
||||
public R selectMap() {
|
||||
List<IoDrive> list = selectData();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (IoDrive id : list) {
|
||||
map.put(id.getId(), id.getName() + "(" + id.getPath() + ")");
|
||||
}
|
||||
return R.ok(map, "获取成功");
|
||||
}
|
||||
|
||||
@Action(val = "getTokenId", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R getTokenId(Dict param) {
|
||||
String driveType = param.getStr("driveType");
|
||||
String tokenId = param.getStr("tokenId");
|
||||
IoTokenData itd = TokenDataUtil.getTokenDataByIdOrToken(driveType, tokenId);
|
||||
Assert.notNull(itd, "当前token配置有误,请重新输入");
|
||||
Assert.notBlank(itd.getId(), "当前token配置有误,请重新输入");
|
||||
return R.ok(itd.getId(), "获取成功");
|
||||
}
|
||||
|
||||
@Action(val = "getFolderByParentPath", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R getFolderByParentPath(Dict param) {
|
||||
String driveType = param.getStr("driveType");
|
||||
if (StrUtil.equals(driveType, FileSystemUtil.LOCAL_DRIVE_TYPE)) {
|
||||
driveType = FileSystemUtil.SERVER_DRIVE_TYPE;
|
||||
Assert.isTrue(LoginAuthUtil.isSystem(), "非管理员无法访问本地磁盘");
|
||||
}
|
||||
String tokenId = param.getStr("tokenId");
|
||||
String parentPath = param.getStr("parentPath");
|
||||
if (StrUtil.isBlank(parentPath)) {
|
||||
parentPath = FileSystemUtil.ACTION.getRootId(driveType);
|
||||
}
|
||||
FileSystemInface.PlainPath parent = new FileSystemInface.PlainPath();
|
||||
parent.setRealPath(parentPath);
|
||||
parent.setTokenId(tokenId);
|
||||
parent.setDriveType(driveType);
|
||||
List<Dict> data = new ArrayList<>();
|
||||
String next = "";
|
||||
while (true) {
|
||||
CommonBean.Page<CommonBean.PathInfo> page = FileSystemUtil.ACTION.listFiles(parent, next);
|
||||
List<CommonBean.PathInfo> list = page.getList();
|
||||
if (CollUtil.isNotEmpty(list)) {
|
||||
for (CommonBean.PathInfo pathInfo : list) {
|
||||
if (pathInfo.getType() != 2) {
|
||||
continue;
|
||||
}
|
||||
data.add(Dict.create()
|
||||
.set("name", pathInfo.getName())
|
||||
.set("path", parentPath + "/" + pathInfo.getPath())
|
||||
);
|
||||
}
|
||||
}
|
||||
if (StrUtil.isBlank(page.getNext())) {
|
||||
break;
|
||||
}
|
||||
next = page.getNext();
|
||||
}
|
||||
return R.okData(Dict.create().set("data", data).set("parent", parentPath));
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.DbUtil;
|
||||
import cn.tenfell.webos.common.util.LoginAuthUtil;
|
||||
import cn.tenfell.webos.modules.entity.IoFileAss;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@BeanAction(val = "ioFileAss")
|
||||
public class IoFileAssAction {
|
||||
@Action(val = "list", type = 1)
|
||||
public R list(Dict param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
List<IoFileAss> list = DbUtil.queryList("select * from io_file_ass where user_id = ?", IoFileAss.class, user.getId());
|
||||
return R.ok(list, "获取成功");
|
||||
}
|
||||
}
|
@ -0,0 +1,327 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.Auth;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.annt.Transactional;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemInface;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.DbUtil;
|
||||
import cn.tenfell.webos.common.util.LoginAuthUtil;
|
||||
import cn.tenfell.webos.common.util.ValidaUtil;
|
||||
import cn.tenfell.webos.modules.entity.IoDrive;
|
||||
import cn.tenfell.webos.modules.entity.IoUserDrive;
|
||||
import cn.tenfell.webos.modules.entity.IoUserStar;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@BeanAction(val = "ioUserDrive")
|
||||
public class IoUserDriveAction {
|
||||
@Action(val = "starList", type = 1)
|
||||
public R starList(Dict param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
List<IoUserStar> list = DbUtil.queryList("select * from io_user_star where user_id = ?", IoUserStar.class, user.getId());
|
||||
return R.okData(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取特殊目录
|
||||
*/
|
||||
@Action(val = "specialPath", type = 1)
|
||||
public R specialPath(Dict param) {
|
||||
String type = param.getStr("type");
|
||||
Assert.notBlank(type, "参数错误");
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
IoUserStar ius = DbUtil.queryObject("select * from io_user_star where user_id = ? and type = ?", IoUserStar.class, user.getId(), type);
|
||||
Assert.notNull(ius, "当前用户暂无此目录");
|
||||
return R.okData(ius.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取特殊目录文件
|
||||
*/
|
||||
@Action(val = "specialFiles", type = 1)
|
||||
public R specialFiles(Dict param) {
|
||||
String type = param.getStr("type");
|
||||
Assert.notBlank(type, "参数错误");
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
IoUserStar ius = DbUtil.queryObject("select * from io_user_star where user_id = ? and type = ?", IoUserStar.class, user.getId(), type);
|
||||
Assert.notNull(ius, "当前用户暂无此目录");
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(ius.getPath(), "", "");
|
||||
String next = "";
|
||||
List<CommonBean.Page<CommonBean.PathInfo>> pages = new ArrayList<>();
|
||||
while (true) {
|
||||
CommonBean.Page<CommonBean.PathInfo> page = FileSystemUtil.ACTION.listFiles(plainPath, next);
|
||||
pages.add(page);
|
||||
if (page.getType() != 0 && StrUtil.isNotBlank(page.getNext())) {
|
||||
next = page.getNext();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
List<CommonBean.PathInfo> list = new ArrayList<>();
|
||||
for (CommonBean.Page<CommonBean.PathInfo> page : pages) {
|
||||
if (page == null) {
|
||||
continue;
|
||||
}
|
||||
List<CommonBean.PathInfo> tmps = page.getList();
|
||||
if (tmps == null || tmps.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
list.addAll(tmps);
|
||||
}
|
||||
for (CommonBean.PathInfo info : list) {
|
||||
info.setPath(ius.getPath() + "/" + info.getPath());
|
||||
}
|
||||
return R.ok(Dict.create().set("list", list).set("parentPath", ius.getPath()), "获取成功");
|
||||
}
|
||||
public static CommonBean.PageRes<IoUserDrive> userDriveList(Dict param){
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
String where = " where 1=1";
|
||||
List<Object> params = new ArrayList<>();
|
||||
String selectData = "select *";
|
||||
if (!LoginAuthUtil.isSystem()) {
|
||||
boolean isMain = LoginAuthUtil.isMain();
|
||||
if (isMain) {
|
||||
//主用户
|
||||
selectData = "select *";
|
||||
where += " and parent_user_id = ?";
|
||||
params.add(user.getId());
|
||||
} else {
|
||||
//子用户
|
||||
selectData = "select id,name,use_size,max_size,avail_size,no,is_system";
|
||||
where += " and valid = 1 and user_id = ?";
|
||||
params.add(user.getId());
|
||||
}
|
||||
}
|
||||
if (StrUtil.isNotBlank(param.getStr("userId"))) {
|
||||
where += " and user_id = ?";
|
||||
params.add(param.getStr("userId"));
|
||||
}
|
||||
int currentPage = param.getInt("current");
|
||||
Integer pageSize = param.getInt("pageSize");
|
||||
CommonBean.PageRes<IoUserDrive> data = DbUtil.pageObject(selectData, "from io_user_drive" + where, IoUserDrive.class, currentPage, pageSize, params.toArray());
|
||||
if (LoginAuthUtil.isMain()) {
|
||||
boolean isAdmin = LoginAuthUtil.isSystem();
|
||||
for (IoUserDrive iud : data.getData()) {
|
||||
String name = iud.getName();
|
||||
if (isAdmin) {
|
||||
name = "(" + UserAction.getUserName(iud.getParentUserId()) + "-" + UserAction.getUserName(iud.getUserId()) + ")" + name;
|
||||
} else {
|
||||
name = "(" + UserAction.getUserName(iud.getUserId()) + ")" + name;
|
||||
}
|
||||
iud.setName(name);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取盘列表
|
||||
*/
|
||||
@Action(val = "list", type = 1)
|
||||
public R list(Dict param) {
|
||||
return R.ok(userDriveList(param), "获取成功");
|
||||
}
|
||||
|
||||
@Action(val = "info", type = 1)
|
||||
public R info(IoUserDrive data) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
String where = " where id = ?";
|
||||
List<Object> params = new ArrayList<>();
|
||||
params.add(data.getId());
|
||||
if (LoginAuthUtil.isMain()) {
|
||||
//主用户
|
||||
where += " and parent_user_id = ?";
|
||||
params.add(user.getId());
|
||||
} else {
|
||||
//子用户
|
||||
where += " and valid = 1 and user_id = ?";
|
||||
params.add(user.getId());
|
||||
}
|
||||
IoUserDrive db = DbUtil.queryObject("select * from io_user_drive" + where, IoUserDrive.class, params.toArray());
|
||||
Assert.notNull(db, "当前记录不存在");
|
||||
return R.ok(db, "获取成功");
|
||||
}
|
||||
|
||||
public static void saveIoUserDrive(IoUserDrive param, IoDrive id, SysUser user) {
|
||||
String currentPath = "{io:" + id.getNo() + "}";
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPath(currentPath);
|
||||
String fileId = FileSystemUtil.ACTION.createDir(plainPath, IdUtil.fastSimpleUUID());
|
||||
Assert.notBlank(fileId, "目录创建失败");
|
||||
IoUserDrive data = new IoUserDrive();
|
||||
SysUser parentUser = DbUtil.queryObject("select * from sys_user where id = ?", SysUser.class, id.getParentUserId());
|
||||
Assert.notNull(parentUser, "父级用户不可为空");
|
||||
Assert.isTrue(StrUtil.equals(parentUser.getParentUserNo(), user.getParentUserNo()), "此盘不可分配给此用户");
|
||||
data.setParentUserId(parentUser.getId());
|
||||
data.setUserId(user.getId());
|
||||
data.setDriveId(param.getDriveId());
|
||||
if (StrUtil.isNotBlank(param.getName())) {
|
||||
data.setName(param.getName());
|
||||
} else {
|
||||
data.setName(id.getUserDriveName());
|
||||
}
|
||||
data.setPath(currentPath + "/" + fileId);
|
||||
data.setUseSize(0L);
|
||||
data.setMaxSize(param.getMaxSize());
|
||||
data.setAvailSize(data.getMaxSize());
|
||||
data.setCreatedTime(LocalDateTime.now());
|
||||
data.setUpdatedTime(data.getCreatedTime());
|
||||
id.setUseSize(id.getUseSize() + data.getMaxSize());
|
||||
long ava = id.getMaxSize() - id.getUseSize();
|
||||
Assert.isTrue(ava >= 0, "当前硬盘可分配容量不足");
|
||||
id.setAvailSize(ava);
|
||||
boolean flag = DbUtil.updateObject(id, Entity.create().set("id", id.getId())) > 0;
|
||||
Assert.isTrue(flag, "更新硬盘出错");
|
||||
long no = DbUtil.queryLong("select max(`no`) as `max_no` from `io_user_drive`", IoUserDrive.class);
|
||||
no++;
|
||||
data.setNo((int) no);
|
||||
data.setValid(param.getValid());
|
||||
data.setId(IdUtil.fastSimpleUUID());
|
||||
DbUtil.insertObject(data);
|
||||
int isSystem = checkAndCreateUserStar(user.getId(), "{uio:" + no + "}", data.getId());
|
||||
data.setIsSystem(isSystem);
|
||||
DbUtil.upsertObject(data, "id");
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户磁盘
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "save", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
@Transactional
|
||||
public R save(IoUserDrive param) {
|
||||
//userId driveId maxSize name valid
|
||||
ValidaUtil.init(param)
|
||||
.notBlank("userId", "用户选择")
|
||||
.notBlank("driveId", "磁盘选择")
|
||||
.greater("maxSize", 0, "最大大小")
|
||||
.notBlank("name", "磁盘名称")
|
||||
.notBlank("valid", "启用状态");
|
||||
SysUser loginUser = LoginAuthUtil.getUser();
|
||||
IoDrive id = DbUtil.queryObject("select * from io_drive where id = ?", IoDrive.class, param.getDriveId());
|
||||
Assert.notNull(id, "此硬盘不存在");
|
||||
String sql = "select * from sys_user where id = ?";
|
||||
List<Object> params = new ArrayList<>();
|
||||
params.add(param.getUserId());
|
||||
if (!LoginAuthUtil.isSystem()) {
|
||||
//非管理员主用户,只能操作自己和自己用户下的
|
||||
sql += " and parent_user_no = ?";
|
||||
params.add(loginUser.getParentUserNo());
|
||||
Assert.isTrue(StrUtil.equals(id.getParentUserId(), loginUser.getId()), "此硬盘不存在");
|
||||
}
|
||||
SysUser user = DbUtil.queryObject(sql, SysUser.class, params.toArray());
|
||||
Assert.notNull(user, "此用户不存在");
|
||||
saveIoUserDrive(param, id, user);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
private static int checkAndCreateUserStar(String userId, String parentPath, String iudId) {
|
||||
List<Dict> list = CollUtil.newArrayList(
|
||||
Dict.create().set("name", "桌面").set("type", "desktop"),
|
||||
Dict.create().set("name", "下载").set("type", "downloads"),
|
||||
Dict.create().set("name", "文档").set("type", "documents"),
|
||||
Dict.create().set("name", "视频").set("type", "videos"),
|
||||
Dict.create().set("name", "图片").set("type", "pictures"),
|
||||
Dict.create().set("name", "音乐").set("type", "music")
|
||||
);
|
||||
int isSystem = 2;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
Dict item = list.get(i);
|
||||
long count = DbUtil.queryLong("select count(0) from io_user_star where user_id = ? and type = ?", IoUserStar.class, userId, item.getStr("type"));
|
||||
if (count == 0) {
|
||||
IoUserStar ius = new IoUserStar();
|
||||
ius.setIudId(iudId);
|
||||
ius.setId(IdUtil.fastSimpleUUID());
|
||||
ius.setUserId(userId);
|
||||
ius.setName(item.getStr("name"));
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPath(parentPath);
|
||||
String fileId = FileSystemUtil.ACTION.createDir(plainPath, item.getStr("name"));
|
||||
Assert.notBlank(fileId, item.getStr("name") + "目录创建失败");
|
||||
ius.setPath(parentPath + "/" + fileId);
|
||||
ius.setType(item.getStr("type"));
|
||||
boolean flag = DbUtil.insertObject(ius);
|
||||
Assert.isTrue(flag, item.getStr("name") + "目录保存失败");
|
||||
if (StrUtil.equals(item.getStr("type"), "desktop")) {
|
||||
isSystem = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改硬盘
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "update", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
@Transactional
|
||||
public R update(IoUserDrive param) {
|
||||
//id,userId,maxSize name valid
|
||||
SysUser loginUser = LoginAuthUtil.getUser();
|
||||
IoUserDrive dbData = DbUtil.queryObject("select * from io_user_drive where id = ? and parent_user_id = ?", IoUserDrive.class, param.getId(), loginUser.getId());
|
||||
Assert.notNull(dbData, "此网盘不存在");
|
||||
if (StrUtil.isNotBlank(param.getUserId()) && !StrUtil.equals(param.getUserId(), dbData.getUserId())) {
|
||||
SysUser user = DbUtil.queryObject("select * from sys_user where id = ? and parent_user_no = ?", SysUser.class, param.getUserId(), loginUser.getParentUserNo());
|
||||
Assert.notNull(user, "此用户不存在");
|
||||
dbData.setUserId(user.getId());
|
||||
}
|
||||
if (param.getMaxSize() > 0 && param.getMaxSize() != dbData.getMaxSize()) {
|
||||
IoDrive id = DbUtil.queryObject("select * from io_drive where id = ? and parent_user_id = ?", IoDrive.class, dbData.getDriveId(), loginUser.getId());
|
||||
Assert.notNull(id, "此硬盘不存在");
|
||||
id.setUseSize(id.getUseSize() - dbData.getMaxSize() + param.getMaxSize());
|
||||
long ava = id.getMaxSize() - id.getUseSize();
|
||||
Assert.isTrue(ava >= 0, "当前硬盘可分配容量不足");
|
||||
id.setAvailSize(ava);
|
||||
boolean flag = DbUtil.updateObject(id, Entity.create().set("id", id.getId())) > 0;
|
||||
Assert.isTrue(flag, "更新硬盘出错");
|
||||
dbData.setMaxSize(param.getMaxSize());
|
||||
}
|
||||
if (StrUtil.isNotBlank(param.getName()) && !StrUtil.equals(param.getName(), dbData.getName())) {
|
||||
dbData.setName(param.getName());
|
||||
}
|
||||
dbData.setValid(param.getValid());
|
||||
dbData.setUpdatedTime(LocalDateTime.now());
|
||||
return DbUtil.commonEdit(dbData);
|
||||
}
|
||||
public static void delIoUserDriveByIds(List<IoUserDrive> list){
|
||||
if(CollUtil.isEmpty(list)){
|
||||
return;
|
||||
}
|
||||
List<String> iudIds = CollUtil.getFieldValues(list, "id", String.class);
|
||||
String iudIdsStr = StrUtil.format("'{}'", CollUtil.join(iudIds, "','"));
|
||||
DbUtil.delete("delete from io_user_drive where id in ( " + iudIdsStr + " )", IoUserDrive.class);
|
||||
DbUtil.delete("delete from io_user_star where iud_id in ( " + iudIdsStr + " )", IoUserStar.class);
|
||||
}
|
||||
|
||||
@Action(val = "dels", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
@Transactional
|
||||
public R dels(List<String> ids) {
|
||||
Assert.notEmpty(ids, "当前数据不存在");
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
String idsStr = StrUtil.format("'{}'", CollUtil.join(ids, "','"));
|
||||
String where = "";
|
||||
List<Object> params = new ArrayList<>();
|
||||
if (!LoginAuthUtil.isSystem()) {
|
||||
where += " and parent_user_id = ?";
|
||||
params.add(user.getId());
|
||||
}
|
||||
List<IoUserDrive> list = DbUtil.queryList("select * from io_user_drive where id in ( " + idsStr + " )" + where, IoUserDrive.class, params.toArray());
|
||||
delIoUserDriveByIds(list);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
@ -0,0 +1,494 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.Method;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.annt.Login;
|
||||
import cn.tenfell.webos.common.annt.Transactional;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemInface;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.DbUtil;
|
||||
import cn.tenfell.webos.common.util.LoginAuthUtil;
|
||||
import cn.tenfell.webos.common.util.ProjectUtil;
|
||||
import cn.tenfell.webos.modules.entity.SoftUserData;
|
||||
import cn.tenfell.webos.modules.entity.SoftUserOffice;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@BeanAction(val = "office")
|
||||
public class OfficeAction {
|
||||
private static String CACHE_BIND_SQL = "select * from soft_user_office where id = ?";
|
||||
private static String CACHE_COOKIE_APPCODE = "tenfellOfficeCookie";
|
||||
private static String CACHE_COOKIE_SQL = "select * from soft_user_data where user_id = ? and app_code = '" + CACHE_COOKIE_APPCODE + "'";
|
||||
|
||||
@Action(val = "save", type = 1)
|
||||
@Login(val = false)
|
||||
public R save(Dict param) {
|
||||
SoftUserOfficeData suod = getSoftUserOfficeByPath(param);
|
||||
SoftUserOffice suo = suod.getSoftUserOffice();
|
||||
if (suo == null) {
|
||||
Assert.isTrue(false, "该绑定不存在或已过期,保存失败");
|
||||
}
|
||||
SysUser user = DbUtil.queryObject(LoginAuthUtil.userCacheSql, SysUser.class, suo.getUserId());
|
||||
Assert.notNull(user, "文件用户异常");
|
||||
SoftUserData sud = DbUtil.queryObject(CACHE_COOKIE_SQL, SoftUserData.class, suo.getUserId());
|
||||
Assert.notNull(sud, "当前用户cookie已过期,请重新扫码");
|
||||
String cookie = sud.getData();
|
||||
Assert.notBlank(cookie, "当前用户cookie已过期,请重新扫码");
|
||||
String resStr = HttpUtil.createGet("https://drive.kdocs.cn/api/v5/groups/" + suo.getGroupId() + "/files/" + suo.getJinShanId() + "/download?isblocks=false&support_checksums=md5,sha1,sha224,sha256,sha384,sha512").cookie(cookie).execute().body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
String url = res.getStr("url");
|
||||
Assert.notBlank(url, res.getStr("msg"));
|
||||
InputStream in = getInputStream(url);
|
||||
FileSystemInface.PlainPath plainPath = FileSystemUtil.cipherPath2PlainPathByUser(suo.getParentPath(), "", "", user);
|
||||
String newFile = FileSystemUtil.ACTION.uploadByServer(plainPath, suo.getName(), in, null, null);
|
||||
Assert.notBlank(newFile, "文件上传失败");
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
private InputStream getInputStream(String url) {
|
||||
HttpRequest req = HttpUtil.createGet(url);
|
||||
req.header("Referer", "https://www.kdocs.cn/", true);
|
||||
return req.executeAsync().bodyStream();
|
||||
}
|
||||
|
||||
private SoftUserOfficeData getSoftUserOfficeByPath(Dict param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
String path = param.getStr("path");
|
||||
boolean hasFileAuth = false;
|
||||
FileSystemInface.PlainPath plainPath = null;
|
||||
if (user != null) {
|
||||
try {
|
||||
plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, "", "");
|
||||
hasFileAuth = true;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
if (plainPath == null) {
|
||||
try {
|
||||
String shareCode = param.getStr("shareCode");
|
||||
String sharePwd = param.getStr("sharePwd");
|
||||
plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, shareCode, sharePwd);
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
if (plainPath == null) {
|
||||
Assert.isTrue(false, "权限不足");
|
||||
}
|
||||
CommonBean.PathInfo fileInfo = FileSystemUtil.ACTION.fileInfo(plainPath);
|
||||
String name = fileInfo.getName();
|
||||
//返回 1.文件链接 2.分享链接 3.扫码+阿里云 4.阿里云
|
||||
String id = SecureUtil.md5(FileUtil.getParent("/" + plainPath.getRealPath(), 1) + "/" + name);
|
||||
SoftUserOffice suo = DbUtil.queryObject(CACHE_BIND_SQL, SoftUserOffice.class, id);
|
||||
return SoftUserOfficeData.init(suo, hasFileAuth, id, path, plainPath, fileInfo);
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class SoftUserOfficeData {
|
||||
private SoftUserOffice softUserOffice;
|
||||
private Boolean hasFileAuth;
|
||||
private String suoId;
|
||||
private String path;
|
||||
private FileSystemInface.PlainPath plainPath;
|
||||
private CommonBean.PathInfo fileInfo;
|
||||
|
||||
public static SoftUserOfficeData init(SoftUserOffice suo, boolean hasFileAuth, String suoId, String path, FileSystemInface.PlainPath plainPath, CommonBean.PathInfo fileInfo) {
|
||||
SoftUserOfficeData suod = new SoftUserOfficeData();
|
||||
suod.setSoftUserOffice(suo);
|
||||
suod.setHasFileAuth(hasFileAuth);
|
||||
suod.setSuoId(suoId);
|
||||
suod.setPath(path);
|
||||
suod.setPlainPath(plainPath);
|
||||
suod.setFileInfo(fileInfo);
|
||||
return suod;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访问链接
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "url", type = 1)
|
||||
@Login(val = false)
|
||||
public R url(Dict param) {
|
||||
SoftUserOfficeData suod = getSoftUserOfficeByPath(param);
|
||||
SoftUserOffice suo = suod.getSoftUserOffice();
|
||||
String cookieUserId = null;
|
||||
if (suo != null) {
|
||||
//数据库绑定
|
||||
cookieUserId = suo.getUserId();
|
||||
} else {
|
||||
//数据库未绑定
|
||||
if (suod.getHasFileAuth()) {
|
||||
cookieUserId = LoginAuthUtil.getUser().getId();
|
||||
}
|
||||
}
|
||||
String cookie = null;
|
||||
if (StrUtil.isNotBlank(cookieUserId)) {
|
||||
SoftUserData sud = DbUtil.queryObject(CACHE_COOKIE_SQL, SoftUserData.class, cookieUserId);
|
||||
if (sud != null) {
|
||||
String resStr = HttpUtil.createGet("https://account.wps.cn/p/signin/login_twice_verify/status").cookie(sud.getData()).execute().body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
if (StrUtil.equals(res.getStr("result"), "ok")) {
|
||||
cookie = sud.getData();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (StrUtil.isBlank(cookie)) {
|
||||
// 数据库绑定,cookie无效/数据库未绑定,cookie无效
|
||||
if (suod.getHasFileAuth()) {
|
||||
//主人
|
||||
return R.okData(CommonBean.OfficeUrlData.init(3));
|
||||
} else {
|
||||
//非主人
|
||||
return R.okData(CommonBean.OfficeUrlData.init(4));
|
||||
}
|
||||
}
|
||||
//cookie是有效的
|
||||
JSONObject jsFileInfo = null;
|
||||
if (suo != null) {
|
||||
String resStr = HttpUtil.createGet("https://drive.kdocs.cn/api/v5/links/batch/query?file_ids=" + suo.getJinShanId() + "&with_sharer=true").cookie(cookie).execute().body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
JSONArray list = res.getJSONArray("link_list");
|
||||
if (list.size() > 0) {
|
||||
//file_id,group_id,link_id,link_url
|
||||
jsFileInfo = list.getJSONObject(0);
|
||||
}
|
||||
}
|
||||
if (jsFileInfo == null) {
|
||||
//文档不存在
|
||||
if (!suod.getHasFileAuth()) {
|
||||
//非主人 数据库未绑定,cookie无效 / 数据库绑定,cookie有效,金山文档不存在
|
||||
return R.okData(CommonBean.OfficeUrlData.init(4));
|
||||
}
|
||||
boolean isCreate = false;
|
||||
if (suo == null) {
|
||||
//绑定数据
|
||||
isCreate = true;
|
||||
suo = new SoftUserOffice();
|
||||
suo.setId(suod.getSuoId());
|
||||
}
|
||||
suo.setExpireTime(LocalDateTime.now().plusDays(30));
|
||||
suo.setPath(suod.getPath());
|
||||
suo.setUserId(cookieUserId);
|
||||
String[] sz = suod.getPath().split("/");
|
||||
String parent = ArrayUtil.join(ArrayUtil.remove(sz, sz.length - 1), "/");
|
||||
suo.setParentPath(parent);
|
||||
suo.setName(suod.getFileInfo().getName());
|
||||
jsFileInfo = uplaodFileToKdocs(suod.getPlainPath(), suod.getFileInfo(), cookie);
|
||||
suo.setGroupId(jsFileInfo.getStr("group_id"));
|
||||
Assert.notNull(jsFileInfo, "当前账号金山文档上传失败");
|
||||
Assert.notBlank(jsFileInfo.getStr("file_id"), "当前账号金山文档上传失败");
|
||||
suo.setJinShanId(jsFileInfo.getStr("file_id"));
|
||||
if (isCreate) {
|
||||
DbUtil.insertObject(suo);
|
||||
} else {
|
||||
DbUtil.upsertObject(suo, "id");
|
||||
}
|
||||
}
|
||||
//文档已存在,获取分享地址
|
||||
String resStr = HttpUtil.createGet("https://drive.kdocs.cn/api/v5/links/" + jsFileInfo.getStr("file_id") + "?with_clink=true&with_corp_file_flag=true").cookie(cookie).execute().body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
JSONObject clink = res.getJSONObject("clink");
|
||||
//1.任何人可编辑 2任何人可评论 3任何人可查看 4关闭 5.未知模式
|
||||
int coordinationVal = 0;
|
||||
if (clink == null || StrUtil.isBlank(clink.getStr("link_url"))) {
|
||||
if (!suod.getHasFileAuth()) {
|
||||
// 数据库绑定,cookie有效,金山文档存在,无分享链接
|
||||
return R.okData(CommonBean.OfficeUrlData.init(4));
|
||||
}
|
||||
coordinationVal = 4;
|
||||
} else {
|
||||
if (StrUtil.equals(clink.getStr("ranges"), "anyone") && StrUtil.equals(clink.getStr("status"), "open")) {
|
||||
if (StrUtil.equals(clink.getStr("link_permission"), "write")) {
|
||||
//编辑模式
|
||||
coordinationVal = 1;
|
||||
} else if (StrUtil.equals(clink.getStr("link_permission"), "read")) {
|
||||
List<String> ext_perm_list = clink.getBeanList("ext_perm_list", String.class);
|
||||
if (ext_perm_list != null && ext_perm_list.contains("comment")) {
|
||||
coordinationVal = 2;
|
||||
} else {
|
||||
coordinationVal = 3;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
coordinationVal = 5;
|
||||
}
|
||||
}
|
||||
if (suod.getHasFileAuth()) {
|
||||
//主人返回文件链接
|
||||
return R.okData(CommonBean.OfficeUrlData.init(jsFileInfo.getStr("link_url"),
|
||||
suo.getExpireTime(), 1,
|
||||
coordinationVal));
|
||||
} else {
|
||||
//非主人返回分享链接
|
||||
return R.okData(CommonBean.OfficeUrlData.init(clink.getStr("link_url"),
|
||||
suo.getExpireTime(), 2,
|
||||
coordinationVal
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private String getCsrfByCookie(String cookie) {
|
||||
String csrf = null;
|
||||
String[] sz = cookie.split(";");
|
||||
for (String oneCookie : sz) {
|
||||
oneCookie = oneCookie.trim();
|
||||
if (StrUtil.isBlank(oneCookie)) {
|
||||
break;
|
||||
}
|
||||
String[] sz2 = oneCookie.split("=", 2);
|
||||
if (StrUtil.equals(sz2[0].trim(), "csrf")) {
|
||||
csrf = sz2[1].trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.notBlank(csrf, "cookie中不存在csrf");
|
||||
return csrf;
|
||||
}
|
||||
|
||||
private JSONObject uplaodFileToKdocs(FileSystemInface.PlainPath plainPath, CommonBean.PathInfo fileInfo, String cookie) {
|
||||
//file_id,group_id,link_id,link_url
|
||||
String group_id = getGroupByCookie(cookie);
|
||||
String csrf = getCsrfByCookie(cookie);
|
||||
File file = FileUtil.writeFromStream(FileSystemUtil.ACTION.getInputStream(plainPath, 0, 0), ProjectUtil.rootPath + "/tmpUpload/" + IdUtil.fastSimpleUUID() + ".data");
|
||||
JSONObject param = JSONUtil
|
||||
.createObj()
|
||||
.set("client_stores", "ks3,ks3sh")
|
||||
.set("csrfmiddlewaretoken", csrf)
|
||||
.set("groupid", group_id)
|
||||
.set("name", fileInfo.getName())
|
||||
.set("parent_path", new ArrayList<>())
|
||||
.set("parentid", 0)
|
||||
.set("req_by_internal", false)
|
||||
.set("size", fileInfo.getSize())
|
||||
.set("startswithfilename", fileInfo.getName())
|
||||
.set("successactionstatus", 201);
|
||||
String resStr = HttpUtil.createPost("https://drive.kdocs.cn/api/v5/files/upload/create")
|
||||
.cookie(cookie)
|
||||
.body(param.toString())
|
||||
.execute().body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
Assert.isTrue(StrUtil.equals("ok", res.getStr("result")), StrUtil.isNotBlank(res.getStr("msg")) ? res.getStr("msg") : "新建文件失败");
|
||||
JSONObject uploadinfo = res.getJSONObject("uploadinfo");
|
||||
String putFileUrl = uploadinfo.getStr("url");
|
||||
JSONObject params = uploadinfo.getJSONObject("params");
|
||||
String store = uploadinfo.getStr("store");
|
||||
HttpResponse resp = HttpUtil.createPost(putFileUrl).cookie(cookie).form(params).form("file", file).execute();
|
||||
String etag = StrUtil.replace(resp.header("ETag"), "\"", "").trim();
|
||||
String newfilename = resp.header("newfilename");
|
||||
param = JSONUtil.createObj()
|
||||
.set("csrfmiddlewaretoken", csrf)
|
||||
.set("etag", etag)
|
||||
.set("groupid", group_id)
|
||||
.set("isUpNewVer", false)
|
||||
.set("key", "temp_" + IdUtil.fastSimpleUUID())
|
||||
.set("must_create", true)
|
||||
.set("name", fileInfo.getName())
|
||||
.set("parent_path", new ArrayList<>())
|
||||
.set("parentid", 0)
|
||||
.set("sha1", newfilename)
|
||||
.set("size", fileInfo.getSize())
|
||||
.set("store", store);
|
||||
resStr = HttpUtil.createPost("https://drive.kdocs.cn/api/v5/files/file")
|
||||
.header("referer", "https://www.kdocs.cn/")
|
||||
.cookie(cookie).body(param.toString()).execute().body();
|
||||
res = JSONUtil.parseObj(resStr);
|
||||
String file_id = res.getStr("id");
|
||||
Assert.notBlank(file_id, "文件上传失败");
|
||||
return JSONUtil.createObj()
|
||||
.set("file_id", file_id)
|
||||
.set("group_id", res.getStr("groupid"))
|
||||
.set("link_id", res.getStr("link_id"))
|
||||
.set("link_url", res.getStr("link_url"));
|
||||
}
|
||||
|
||||
private String getGroupByCookie(String cookie) {
|
||||
String resStr = HttpUtil.createGet("https://t.kdocs.cn/kdteam/api/v1/teams?offset=0&count=50").cookie(cookie).execute().body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
if (res.getInt("code") == 0) {
|
||||
JSONObject data = res.getJSONObject("data");
|
||||
JSONArray teams = data.getJSONArray("teams");
|
||||
for (int i = 0; i < teams.size(); i++) {
|
||||
JSONObject one = teams.getJSONObject(i);
|
||||
if (StrUtil.equals(one.getStr("name"), "Webos Office")) {
|
||||
return one.getStr("id");
|
||||
}
|
||||
}
|
||||
}
|
||||
String csrf = getCsrfByCookie(cookie);
|
||||
JSONObject param = JSONUtil.createObj().set("corp_id", 0).set("csrfmiddlewaretoken", csrf).set("name", "Webos Office").set("version", 1);
|
||||
resStr = HttpUtil.createPost("https://t.kdocs.cn/kdteam/api/v1/teams").cookie(cookie).body(param.toString()).execute().body();
|
||||
res = JSONUtil.parseObj(resStr);
|
||||
Assert.isTrue(res.getInt("code", -1) == 0, "创建团队失败");
|
||||
JSONObject data = res.getJSONObject("data");
|
||||
Assert.notNull(data, "创建团队失败");
|
||||
Assert.notBlank(data.getStr("id"), "创建团队失败");
|
||||
return data.getStr("id");
|
||||
}
|
||||
|
||||
@Action(val = "saveCookie", type = 1)
|
||||
public R saveCookie(Dict param) {
|
||||
//https://www.kdocs.cn/group/1975076871?from=docs
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
String cookie = param.getStr("cookie");
|
||||
String url = "https://www.kdocs.cn/?from=docs&show=all";
|
||||
HttpResponse resp = HttpUtil.createGet(url).cookie(cookie).execute();
|
||||
String wpsua = "";
|
||||
List<String> list = resp.headers().get("Set-Cookie");
|
||||
for (String str : list) {
|
||||
if (str.indexOf("wpsua") != -1) {
|
||||
String[] sz = str.split(";");
|
||||
for (String str1 : sz) {
|
||||
str1 = str1.trim();
|
||||
if (str1.indexOf("wpsua=") != -1) {
|
||||
wpsua = str1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.notBlank(wpsua, "当前cookie存在问题");
|
||||
cookie += ";" + wpsua;
|
||||
SoftUserData sud = DbUtil.queryObject(CACHE_COOKIE_SQL, SoftUserData.class, user.getId());
|
||||
if (sud == null) {
|
||||
sud = new SoftUserData();
|
||||
sud.setUserId(user.getId());
|
||||
sud.setAppCode(CACHE_COOKIE_APPCODE);
|
||||
}
|
||||
sud.setData(cookie);
|
||||
DbUtil.commonEdit(sud);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Action(val = "logOut", type = 1)
|
||||
@Transactional
|
||||
public R logOut(Dict param) {
|
||||
SoftUserOfficeData suod = getSoftUserOfficeByPath(param);
|
||||
SoftUserOffice suo = suod.getSoftUserOffice();
|
||||
Assert.notNull(suo, "未正确读取文件数据");
|
||||
SoftUserData sud = DbUtil.queryObject(CACHE_COOKIE_SQL, SoftUserData.class, suo.getUserId());
|
||||
Assert.notNull(sud, "未正确读取cookie数据");
|
||||
String cookie = sud.getData();
|
||||
String csrf = getCsrfByCookie(cookie);
|
||||
JSONObject data = JSONUtil
|
||||
.createObj()
|
||||
.set("csrfmiddlewaretoken", csrf);
|
||||
String resStr = HttpUtil.createRequest(Method.DELETE, "https://drive.kdocs.cn/api/v3/groups/" + suo.getGroupId() + "/files/" + suo.getJinShanId()).cookie(cookie).body(data.toString()).execute().body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
Assert.isTrue(StrUtil.equals(res.getStr("result"), "ok"), "删除文件失败");
|
||||
DbUtil.delete("delete from soft_user_office where id = ?", SoftUserOffice.class, suo.getId());
|
||||
DbUtil.delete("delete from soft_user_data where user_id = ? and app_code = '" + CACHE_COOKIE_APPCODE + "'", SoftUserData.class, suo.getUserId());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Action(val = "renewal", type = 1)
|
||||
public R renewal(Dict param) {
|
||||
SoftUserOfficeData suod = getSoftUserOfficeByPath(param);
|
||||
SoftUserOffice suo = suod.getSoftUserOffice();
|
||||
Assert.notNull(suo, "未正确读取文件数据");
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusDays(30);
|
||||
suo.setExpireTime(expireTime);
|
||||
DbUtil.upsertObject(suo, "id");
|
||||
return R.ok(expireTime, "续期成功");
|
||||
}
|
||||
|
||||
@Action(val = "coordination", type = 1)
|
||||
@Transactional
|
||||
public R coordination(Dict param) {
|
||||
//1.任何人可编辑 2任何人可评论 3任何人可查看 4关闭
|
||||
Integer value = param.getInt("value");
|
||||
SoftUserOfficeData suod = getSoftUserOfficeByPath(param);
|
||||
SoftUserOffice suo = suod.getSoftUserOffice();
|
||||
Assert.notNull(suo, "未正确读取文件数据");
|
||||
SoftUserData sud = DbUtil.queryObject(CACHE_COOKIE_SQL, SoftUserData.class, suo.getUserId());
|
||||
Assert.notNull(sud, "未正确读取cookie数据");
|
||||
String cookie = sud.getData();
|
||||
String csrf = getCsrfByCookie(cookie);
|
||||
if (value == 4) {
|
||||
String resStr = HttpUtil.createRequest(Method.DELETE, "https://drive.kdocs.cn/api/v5/groups/" + suo.getGroupId() + "/files/" + suo.getJinShanId() + "/share")
|
||||
.cookie(cookie)
|
||||
.body(JSONUtil.createObj().set("csrfmiddlewaretoken", csrf).toString())
|
||||
.execute().body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
Assert.isTrue(StrUtil.equals("ok", res.getStr("result")), StrUtil.isNotBlank(res.getStr("msg")) ? res.getStr("msg") : "关闭协同功能失败");
|
||||
return R.ok();
|
||||
}
|
||||
String resStr = HttpUtil.createGet("https://drive.kdocs.cn/api/v5/links/" + suo.getJinShanId() + "?with_clink=true&with_corp_file_flag=true").cookie(cookie).execute().body();
|
||||
JSONObject res = JSONUtil.parseObj(resStr);
|
||||
JSONObject clink = res.getJSONObject("clink");
|
||||
String url;
|
||||
Method method;
|
||||
JSONObject body = JSONUtil.createObj()
|
||||
.set("range", "anyone")
|
||||
.set("csrfmiddlewaretoken", csrf)
|
||||
.set("clink", true);
|
||||
if (value == 1) {
|
||||
body.set("permission", "write");
|
||||
} else if (value == 2) {
|
||||
body.set("permission", "read").set("ext_perm_list", CollUtil.newArrayList("comment"));
|
||||
} else if (value == 3) {
|
||||
body.set("permission", "read");
|
||||
} else {
|
||||
Assert.isTrue(false, "参数错误");
|
||||
}
|
||||
boolean has = false;
|
||||
if (clink != null && StrUtil.isNotBlank(clink.getStr("sid"))) {
|
||||
if (!StrUtil.equals(clink.getStr("ranges"), "anyone") || !StrUtil.equals(clink.getStr("status"), "open")) {
|
||||
HttpUtil.createPost("https://drive.kdocs.cn/api/v5/links")
|
||||
.cookie(cookie)
|
||||
.body(JSONUtil.createObj()
|
||||
.set("clink", true)
|
||||
.set("csrfmiddlewaretoken", csrf)
|
||||
.set("fileid", suo.getJinShanId())
|
||||
.set("range", "anyone")
|
||||
.toString()).execute();
|
||||
}
|
||||
//存在
|
||||
url = "https://drive.kdocs.cn/api/v3/links/" + clink.getStr("sid");
|
||||
method = Method.PUT;
|
||||
body.set("sid", clink.getStr("sid"))
|
||||
.set("status", "open");
|
||||
has = true;
|
||||
} else {
|
||||
//不存在
|
||||
url = "https://drive.kdocs.cn/api/v5/links";
|
||||
method = Method.POST;
|
||||
body.set("fileid", suo.getJinShanId());
|
||||
}
|
||||
resStr = HttpUtil.createRequest(method, url).cookie(cookie).body(body.toString()).execute().body();
|
||||
res = JSONUtil.parseObj(resStr);
|
||||
if (has) {
|
||||
Assert.isTrue(res.getLong("id") > 0, "开启协同功能失败");
|
||||
} else {
|
||||
Assert.isTrue(StrUtil.equals("ok", res.getStr("result")), StrUtil.isNotBlank(res.getStr("msg")) ? res.getStr("msg") : "开启协同功能失败");
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.codec.Base64Decoder;
|
||||
import cn.hutool.core.net.multipart.MultipartFormData;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.Method;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.annt.Login;
|
||||
import cn.tenfell.webos.common.util.ProjectContext;
|
||||
import org.noear.solon.core.NvMap;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@BeanAction(val = "proxy")
|
||||
public class ProxyAction {
|
||||
@Action(val = "http")
|
||||
@Login(val = false)
|
||||
public InputStream http(Context ctx) {
|
||||
String url = ctx.param("url");
|
||||
if (StrUtil.isBlank(url)) {
|
||||
return null;
|
||||
}
|
||||
url = Base64Decoder.decodeStr(url);
|
||||
boolean cdx = StrUtil.isNotBlank(ctx.param("cdx"));
|
||||
//创建请求
|
||||
HttpRequest httpReq = HttpUtil.createRequest(Method.valueOf(ctx.method()), url);
|
||||
if(cdx){
|
||||
httpReq.setMaxRedirectCount(20);
|
||||
}
|
||||
//整合headers
|
||||
String header = ctx.param("header");
|
||||
if (StrUtil.isNotBlank(header)) {
|
||||
Map<String, String> headerMapping = getHeaderMapping(Base64Decoder.decodeStr(header));
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
Map<String,String> tmp = ctx.headerMap();
|
||||
tmp.forEach((key, val) -> {
|
||||
String newKey = headerMapping.get(key.toLowerCase());
|
||||
if (StrUtil.isBlank(newKey)) {
|
||||
return;
|
||||
}
|
||||
headers.put(newKey, val);
|
||||
});
|
||||
httpReq.headerMap(headers, true);
|
||||
}
|
||||
String expHeader = ctx.param("expHeader");
|
||||
if (StrUtil.isNotBlank(header)) {
|
||||
Map<String, String> headerMapping = getHeaderMapping(Base64Decoder.decodeStr(expHeader));
|
||||
headerMapping.forEach((key, val) -> httpReq.header(key, val));
|
||||
}
|
||||
|
||||
if (!StrUtil.equals(ctx.method(), "GET")) {
|
||||
//整合提交的数据
|
||||
if (StrUtil.containsAny(ctx.contentType(),"json","JSON")) {
|
||||
//body提交
|
||||
try{
|
||||
String body = ctx.body();
|
||||
httpReq.body(body);
|
||||
}catch (Exception e){
|
||||
|
||||
}
|
||||
} else {
|
||||
//表单提交
|
||||
NvMap nvMap = ctx.paramMap();
|
||||
Map<String, Object> httpParam = new HashMap<>();
|
||||
nvMap.forEach((key, val) -> {
|
||||
if (StrUtil.equals(key, "module") ||
|
||||
StrUtil.equals(key, "action") ||
|
||||
StrUtil.equals(key, "url") ||
|
||||
StrUtil.equals(key, "expHeader") ||
|
||||
StrUtil.equals(key, "resHeader") ||
|
||||
StrUtil.equals(key, "header") ||
|
||||
StrUtil.equals(key, "cdx")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
httpParam.put(key, val);
|
||||
});
|
||||
httpReq.form(httpParam);
|
||||
}
|
||||
}
|
||||
//发起请求并返回
|
||||
HttpResponse resp = httpReq.executeAsync();
|
||||
//整合headers
|
||||
String resHeader = ctx.param("resHeader");
|
||||
if (StrUtil.isNotBlank(resHeader)) {
|
||||
Map<String, String> headerMapping = getHeaderMapping(Base64Decoder.decodeStr(resHeader));
|
||||
resp.headers().forEach((s, strings) -> {
|
||||
if (StrUtil.isBlank(s)) {
|
||||
return;
|
||||
}
|
||||
String newKey = headerMapping.get(s.toLowerCase());
|
||||
if (StrUtil.isBlank(newKey)) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < strings.size(); i++) {
|
||||
ctx.headerAdd(newKey, strings.get(i));
|
||||
}
|
||||
});
|
||||
}
|
||||
return resp.bodyStream();
|
||||
}
|
||||
|
||||
private Map<String, String> getHeaderMapping(String str) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
if (StrUtil.isBlank(str)) {
|
||||
return map;
|
||||
}
|
||||
String[] fhStrs = str.split(";");
|
||||
for (String fhStr : fhStrs) {
|
||||
if (StrUtil.isBlank(fhStr)) {
|
||||
continue;
|
||||
}
|
||||
String[] mhStrs = fhStr.split(":");
|
||||
String key = mhStrs[0];
|
||||
if (StrUtil.isBlank(key)) {
|
||||
continue;
|
||||
}
|
||||
String val = mhStrs.length > 1 ? mhStrs[1] : mhStrs[0];
|
||||
map.put(key.toLowerCase(), val.toUpperCase());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.annt.Login;
|
||||
import cn.tenfell.webos.common.annt.Transactional;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemInface;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.DbUtil;
|
||||
import cn.tenfell.webos.common.util.LoginAuthUtil;
|
||||
import cn.tenfell.webos.modules.entity.ShareFile;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@BeanAction(val = "shareFile")
|
||||
public class ShareFileAction {
|
||||
|
||||
/**
|
||||
* 获取随机code
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "getCode", type = 1)
|
||||
public R getCode(Dict param) {
|
||||
while (true) {
|
||||
String code = RandomUtil.randomString(RandomUtil.BASE_CHAR + RandomUtil.BASE_CHAR.toUpperCase() + RandomUtil.BASE_NUMBER, 8);
|
||||
long count = DbUtil.queryLong("select count(0) from share_file where code = ?", ShareFile.class, code);
|
||||
if (count == 0) {
|
||||
return R.okData(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Action(val = "save", type = 1)
|
||||
@Transactional
|
||||
public R save(ShareFile sf) {
|
||||
Assert.notBlank(sf.getName(), "分享标题不可为空");
|
||||
if (StrUtil.isNotBlank(sf.getId())) {
|
||||
ShareFile db = DbUtil.queryObject("select * from share_file where id = ? and user_id = ?", ShareFile.class, sf.getId(), LoginAuthUtil.getUser().getId());
|
||||
Assert.notNull(db, "此分享数据不存在");
|
||||
db.setPassword(sf.getPassword());
|
||||
db.setExpireTime(sf.getExpireTime());
|
||||
db.setName(sf.getName());
|
||||
DbUtil.updateObject(db, Entity.create().set("id", sf.getId()));
|
||||
} else {
|
||||
Assert.notBlank(sf.getCode(), "编码不可为空");
|
||||
Assert.notBlank(sf.getFiles(), "文件不可为空");
|
||||
Assert.notBlank(sf.getPath(), "当前文件不允许分享");
|
||||
//验证用户是否有此路径的权限
|
||||
FileSystemUtil.cipherPath2PlainPathByLogin(sf.getPath(), "", "");
|
||||
sf.setId(IdUtil.fastSimpleUUID());
|
||||
long count = DbUtil.queryLong("select count(0) from share_file where code = ?", ShareFile.class, sf.getCode());
|
||||
Assert.isTrue(count == 0, "当前编号已存在");
|
||||
sf.setUserId(LoginAuthUtil.getUser().getId());
|
||||
sf.setViewNum(0);
|
||||
sf.setDownNum(0);
|
||||
sf.setShareTime(LocalDateTime.now());
|
||||
Long no = DbUtil.queryLong("select max(`no`) as `max_no` from `share_file`", ShareFile.class);
|
||||
no++;
|
||||
sf.setNo(no.intValue());
|
||||
Assert.isTrue(DbUtil.insertObject(sf), "分享失败");
|
||||
}
|
||||
return R.ok(null, "分享成功");
|
||||
}
|
||||
|
||||
@Action(val = "hasShare", type = 1)
|
||||
@Login(val = false)
|
||||
public R hasShare(Dict dict) {
|
||||
String code = dict.getStr("code");
|
||||
Assert.notBlank(code, "编码不存在");
|
||||
long count = DbUtil.queryLong("select count(0) from share_file where code = ?", ShareFile.class, code);
|
||||
if (count > 0) {
|
||||
return R.ok();
|
||||
}
|
||||
return R.failed();
|
||||
}
|
||||
|
||||
@Action(val = "shareData", type = 1)
|
||||
@Login(val = false)
|
||||
public R shareData(Dict dict) {
|
||||
String code = dict.getStr("code");
|
||||
Assert.notBlank(code, "编码不存在");
|
||||
String password = dict.getStr("password");
|
||||
ShareFile shareData = DbUtil.queryObject("select * from share_file where code = ?", ShareFile.class, code);
|
||||
if (shareData == null) {
|
||||
return R.failed("当前分享不存在");
|
||||
}
|
||||
if (shareData.getExpireTime() != null && shareData.getExpireTime().isAfter(LocalDate.of(2000, 1, 1))) {
|
||||
if (LocalDate.now().isAfter(shareData.getExpireTime())) {
|
||||
return R.failed("当前分享已过期");
|
||||
}
|
||||
}
|
||||
if (StrUtil.isNotBlank(shareData.getPassword())) {
|
||||
if (!StrUtil.equals(password, shareData.getPassword())) {
|
||||
return R.okData(Dict.create().set("type", -1));
|
||||
}
|
||||
}
|
||||
return R.okData(Dict.create().set("type", 1).set("data", shareData));
|
||||
}
|
||||
|
||||
@Action(val = "list", type = 1)
|
||||
public R list(Dict param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
int currentPage = param.getInt("current");
|
||||
Integer pageSize = param.getInt("pageSize");
|
||||
String where = " where 1=1";
|
||||
List<Object> params = new ArrayList<>();
|
||||
where += " and user_id = ?";
|
||||
params.add(user.getId());
|
||||
CommonBean.PageRes<ShareFile> data = DbUtil.pageObject("select *", "from share_file" + where, ShareFile.class, currentPage, pageSize, params.toArray());
|
||||
data.getData().forEach(shareFile -> {
|
||||
String[] sz = shareFile.getFiles().split(";");
|
||||
CommonBean.PathInfo info = null;
|
||||
String parent = "{sio:" + shareFile.getNo() + "}";
|
||||
try{
|
||||
boolean dirMode = true;
|
||||
if (sz.length == 1) {
|
||||
FileSystemInface.PlainPath path = FileSystemUtil.cipherPath2PlainPathByLogin(parent + "/" + shareFile.getFiles(), "", "");
|
||||
info = FileSystemUtil.ACTION.fileInfo(path);
|
||||
if (info != null) {
|
||||
dirMode = false;
|
||||
info.setPath(parent + "/" + info.getPath());
|
||||
}
|
||||
}
|
||||
if (dirMode) {
|
||||
info = new CommonBean.PathInfo();
|
||||
info.setName(shareFile.getName());
|
||||
info.setPath(parent);
|
||||
info.setCreatedAt(shareFile.getShareTime().toString());
|
||||
info.setType(2);
|
||||
}
|
||||
}catch (Exception e){
|
||||
info = new CommonBean.PathInfo();
|
||||
info.setName("此文件可能丢失,建议删除");
|
||||
info.setPath(parent);
|
||||
info.setCreatedAt(shareFile.getShareTime().toString());
|
||||
info.setType(2);
|
||||
}
|
||||
shareFile.setFiles(JSONUtil.parse(info).toString());
|
||||
});
|
||||
return R.ok(data, "获取成功");
|
||||
}
|
||||
|
||||
@Action(val = "dels", type = 1)
|
||||
public R dels(List<String> ids) {
|
||||
Assert.notEmpty(ids, "当前数据不存在");
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
String idsStr = StrUtil.format("'{}'", CollUtil.join(ids, "','"));
|
||||
String where = "";
|
||||
List<Object> params = new ArrayList<>();
|
||||
where += " and user_id = ?";
|
||||
params.add(user.getId());
|
||||
DbUtil.delete("delete from share_file where id in ( " + idsStr + " )" + where, ShareFile.class, params.toArray());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Action(val = "info", type = 1)
|
||||
public R info(Dict param) {
|
||||
String id = param.getStr("id");
|
||||
return R.okData(DbUtil.queryObject("select * from share_file where id = ? and user_id = ?", ShareFile.class, id, LoginAuthUtil.getUser().getId()));
|
||||
}
|
||||
|
||||
@Action(val = "findOne", type = 1)
|
||||
public R findOne(Dict param) {
|
||||
String path = param.getStr("path");
|
||||
String files = param.getStr("files");
|
||||
ShareFile shareFile = DbUtil.queryObject("select * from share_file where user_id = ? and path = ? and files = ?",
|
||||
ShareFile.class, LoginAuthUtil.getUser().getId(),
|
||||
path, files);
|
||||
if (shareFile != null) {
|
||||
return R.okData(shareFile.getId());
|
||||
}
|
||||
return R.failed();
|
||||
}
|
||||
}
|
@ -0,0 +1,305 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.net.multipart.UploadFile;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.annt.Login;
|
||||
import cn.tenfell.webos.common.annt.Transactional;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.DbUtil;
|
||||
import cn.tenfell.webos.common.util.LoginAuthUtil;
|
||||
import cn.tenfell.webos.common.util.ProjectContext;
|
||||
import cn.tenfell.webos.common.util.ProjectUtil;
|
||||
import cn.tenfell.webos.modules.entity.IoFileAss;
|
||||
import cn.tenfell.webos.modules.entity.SoftUser;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@BeanAction(val = "softUser")
|
||||
public class SoftUserAction {
|
||||
|
||||
/**
|
||||
* 已安装列表
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "hasList", type = 1)
|
||||
public R hasList(Dict param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
List<SoftUser> list = DbUtil.queryList("select * from soft_user where user_id = ?", SoftUser.class, user.getId());
|
||||
return R.okData(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加轻应用
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "addIframe", type = 1)
|
||||
public R addIframe(Dict param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
SoftUser su = new SoftUser();
|
||||
su.setImgPath(param.getStr("icon"));
|
||||
su.setName(param.getStr("name"));
|
||||
su.setCode(SecureUtil.md5(param.getStr("url")));
|
||||
su.setDescr(param.getStr("name"));
|
||||
su.setScreenShots("[]");
|
||||
su.setVersion("1.0.0");
|
||||
su.setAuthor("手动添加");
|
||||
su.setEffect(param.getStr("name"));
|
||||
su.setType(1);
|
||||
su.setIframeUrl(param.getStr("url"));
|
||||
su.setUserId(user.getId());
|
||||
su.setIsStore(0);
|
||||
R r = DbUtil.commonEdit(su);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加软件
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "addSoft", type = 0)
|
||||
@Transactional
|
||||
public R addSoft(Context ctx) {
|
||||
String dir = ProjectUtil.webPath + "/apps/" + IdUtil.fastSimpleUUID();
|
||||
try {
|
||||
String zipPath = dir + "/1.zip";
|
||||
UploadFile uFile = ProjectContext.getMultipart(ctx).getFile("file");
|
||||
FileUtil.writeFromStream(uFile.getFileInputStream(), zipPath);
|
||||
ZipUtil.unzip(zipPath, dir);
|
||||
FileUtil.del(zipPath);
|
||||
String indexJsonPath = dir + "/index.json";
|
||||
Assert.isTrue(FileUtil.exist(indexJsonPath), "程序缺少index.json文件");
|
||||
JSONObject appRes = JSONUtil.parseObj(FileUtil.readString(indexJsonPath, CharsetUtil.CHARSET_UTF_8));
|
||||
String code = appRes.getStr("code");
|
||||
Assert.notBlank(code, "程序编码不存在");
|
||||
Assert.isFalse(FileUtil.exist(ProjectUtil.webPath + "/apps/" + code), "此程序已存在");
|
||||
SoftUser su = appRes.toBean(SoftUser.class);
|
||||
su.setType(0);
|
||||
su.setUserId(LoginAuthUtil.getUser().getId());
|
||||
su.setIsStore(0);
|
||||
R r = DbUtil.commonEdit(su);
|
||||
createFileAss(appRes, su.getId(), su.getUserId(), appRes.getStr("code"));
|
||||
FileUtil.rename(new File(dir), code, true);
|
||||
return r;
|
||||
} catch (Exception e) {
|
||||
FileUtil.del(dir);
|
||||
return R.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地软件库(包含从商城安装的)
|
||||
*/
|
||||
@Action(val = "list", type = 1)
|
||||
public R list(Dict param) {
|
||||
List<CommonBean.SoftStore> localAppList = new ArrayList<>();
|
||||
String appPath = ProjectUtil.webPath + "/apps";
|
||||
File[] codeFiles = new File(appPath).listFiles();
|
||||
if (codeFiles == null || codeFiles.length == 0) {
|
||||
return R.okData(localAppList);
|
||||
}
|
||||
for (File codeFile : codeFiles) {
|
||||
if (!codeFile.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
String jsonPath = appPath + "/" + codeFile.getName() + "/index.json";
|
||||
if (!FileUtil.exist(jsonPath)) {
|
||||
continue;
|
||||
}
|
||||
JSONObject appRes = JSONUtil.parseObj(FileUtil.readString(jsonPath, CharsetUtil.CHARSET_UTF_8));
|
||||
String effect = appRes.getStr("effect");
|
||||
if (StrUtil.isNotBlank(effect)) {
|
||||
String tag = "script";
|
||||
appRes.set("effect", effect.replaceAll("(?i)<" + tag, "<no" + tag).replaceAll("(?i)</" + tag + ">", "</no" + tag + ">"));
|
||||
}
|
||||
if (!StrUtil.equals(appRes.getStr("code"), codeFile.getName())) {
|
||||
continue;
|
||||
}
|
||||
appRes.set("type", 0);
|
||||
localAppList.add(appRes.toBean(CommonBean.SoftStore.class));
|
||||
}
|
||||
return R.okData(localAppList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 先卸载软件
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "uninstall", type = 1)
|
||||
@Transactional
|
||||
public R uninstall(SoftUser data) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
Assert.notBlank(data.getCode(), "参数不足");
|
||||
if (StrUtil.isBlank(data.getId())) {
|
||||
SoftUser db = DbUtil.queryObject("select * from soft_user where code = ? and user_id = ?", SoftUser.class, data.getCode(), user.getId());
|
||||
if (db == null) {
|
||||
return R.ok();
|
||||
}
|
||||
data.setId(db.getId());
|
||||
}
|
||||
DbUtil.delete("delete from io_file_ass where user_id = ? and soft_user_id = ?", IoFileAss.class, user.getId(), data.getId());
|
||||
DbUtil.delete("delete from soft_user where id = ? and user_id = ?", SoftUser.class, data.getId(), user.getId());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装软件
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "install", type = 1)
|
||||
@Transactional
|
||||
public R install(CommonBean.SoftStore param) {
|
||||
Assert.notBlank(param.getCode(), "编码不可为空,软件安装失败");
|
||||
if (param.getIsLocal() == null || param.getIsLocal() != 1) {
|
||||
//安装远程软件
|
||||
if (param.getType() == 0) {
|
||||
//插件
|
||||
String dir = ProjectUtil.webPath + "/apps/" + param.getCode();
|
||||
String zipPath = dir + "/1.zip";
|
||||
HttpUtil.downloadFile(param.getDownloadUrl(), zipPath);
|
||||
ZipUtil.unzip(zipPath, dir);
|
||||
FileUtil.del(zipPath);
|
||||
}
|
||||
}
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
SoftUser db = DbUtil.queryObject("select * from soft_user where code = ? and user_id = ? ", SoftUser.class, param.getCode(), user.getId());
|
||||
if (db != null) {
|
||||
uninstall(db);
|
||||
}
|
||||
SoftUser su = new SoftUser();
|
||||
su.setImgPath(param.getImgPath());
|
||||
su.setName(param.getName());
|
||||
su.setCode(param.getCode());
|
||||
su.setDescr(param.getDescr());
|
||||
su.setScreenShots(param.getScreenShots());
|
||||
su.setVersion(param.getVersion());
|
||||
su.setAuthor(param.getAuthor());
|
||||
su.setEffect(param.getEffect());
|
||||
su.setType(param.getType());
|
||||
su.setIframeUrl(param.getIframeUrl());
|
||||
su.setUserId(user.getId());
|
||||
su.setDownloadUrl(param.getDownloadUrl());
|
||||
if (param.getIsLocal() == null || param.getIsLocal() != 1) {
|
||||
//远程
|
||||
su.setStoreId(param.getId());
|
||||
su.setIsStore(1);
|
||||
} else {
|
||||
//本地
|
||||
su.setIsStore(0);
|
||||
}
|
||||
R r = DbUtil.commonEdit(su);
|
||||
if (param.getType() == 0) {
|
||||
//插件
|
||||
String dir = ProjectUtil.webPath + "/apps/" + param.getCode();
|
||||
String indexJsonPath = dir + "/index.json";
|
||||
Assert.isTrue(FileUtil.exist(indexJsonPath), "插件缺少index.json文件");
|
||||
JSONObject appRes = JSONUtil.parseObj(FileUtil.readString(indexJsonPath, CharsetUtil.CHARSET_UTF_8));
|
||||
createFileAss(appRes, su.getId(), user.getId(), param.getCode());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private void createFileAss(JSONObject appRes, String suId, String userId, String code) {
|
||||
createFileAssByType(appRes, suId, userId, code, "rightMenuAddAll", "addall");
|
||||
createFileAssByType(appRes, suId, userId, code, "rightMenuOpenWith", "openwith");
|
||||
createFileAssByType(appRes, suId, userId, code, "rightMenuNew", "new");
|
||||
}
|
||||
|
||||
private void createFileAssByType(JSONObject appRes, String suId, String userId, String code,
|
||||
String menuType, String dbType) {
|
||||
JSONArray rightMenuNew = appRes.getJSONArray(menuType);
|
||||
if (rightMenuNew != null && rightMenuNew.size() > 0) {
|
||||
for (int i = 0; i < rightMenuNew.size(); i++) {
|
||||
JSONObject newData = rightMenuNew.getJSONObject(i);
|
||||
String ext = newData.getStr("ext");
|
||||
if (StrUtil.isBlank(ext)) {
|
||||
continue;
|
||||
}
|
||||
IoFileAss ifa = new IoFileAss();
|
||||
ifa.setId(IdUtil.fastSimpleUUID());
|
||||
ifa.setSoftUserId(suId);
|
||||
ifa.setUserId(userId);
|
||||
ifa.setExt(ext);
|
||||
ifa.setIconUrl(newData.getStr("icon"));
|
||||
ifa.setAction(dbType);
|
||||
ifa.setActionName(newData.getStr("name"));
|
||||
ifa.setExpAction(newData.getStr("expAction"));
|
||||
ifa.setSortNum(0);
|
||||
ifa.setUrl("apps/" + code + "/index.html");
|
||||
ifa.setAppName(appRes.getStr("name"));
|
||||
Assert.isTrue(DbUtil.insertObject(ifa), "插入文件关联成功");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 升级检查
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "checkUpdate", type = 1)
|
||||
public R checkUpdate(Dict param) {
|
||||
String code = param.getStr("code");
|
||||
String version = param.getStr("version");
|
||||
if (StrUtil.isBlank(code) || StrUtil.isBlank(version)) {
|
||||
return R.failed("无需升级");
|
||||
}
|
||||
String appPath = ProjectUtil.webPath + "/apps";
|
||||
String jsonPath = appPath + "/" + code + "/index.json";
|
||||
if (!FileUtil.exist(jsonPath)) {
|
||||
return R.failed("无需升级");
|
||||
}
|
||||
JSONObject res = JSONUtil.parseObj(FileUtil.readUtf8String(jsonPath));
|
||||
if (!StrUtil.equals(res.getStr("version"), version)) {
|
||||
return R.ok();
|
||||
}
|
||||
return R.failed("无需升级");
|
||||
}
|
||||
|
||||
@Action(val = "update", type = 1)
|
||||
@Transactional
|
||||
public R update(CommonBean.SoftStore param) {
|
||||
Assert.notBlank(param.getCode(), "编码不可为空,软件安装失败");
|
||||
if (param.getIsLocal() == null || param.getIsLocal() == 1) {
|
||||
return R.failed("非远程软件暂停升级");
|
||||
}
|
||||
return install(param);
|
||||
}
|
||||
|
||||
@Action(val = "hasInstall", type = 1)
|
||||
@Login(val = false)
|
||||
public R hasInstall(Dict param) {
|
||||
Assert.notBlank(param.getStr("code"), "编码不可为空");
|
||||
String appPath = ProjectUtil.webPath + "/apps";
|
||||
String jsonPath = appPath + "/" + param.getStr("code") + "/index.json";
|
||||
if (FileUtil.exist(jsonPath)) {
|
||||
return R.ok();
|
||||
}
|
||||
return R.failed("暂未安装");
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.DbUtil;
|
||||
import cn.tenfell.webos.common.util.LoginAuthUtil;
|
||||
import cn.tenfell.webos.modules.entity.SoftUserData;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
|
||||
@BeanAction(val = "softUserData")
|
||||
public class SoftUserDataAction {
|
||||
/**
|
||||
* 保存数据
|
||||
*/
|
||||
@Action(val = "save", type = 1)
|
||||
public R save(SoftUserData param) {
|
||||
Assert.notBlank(param.getAppCode(), "软件编码不可为空");
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
SoftUserData dbData = DbUtil.queryObject("select * from soft_user_data where user_id = ? and app_code = ?", SoftUserData.class, user.getId(), param.getAppCode());
|
||||
if (dbData == null) {
|
||||
dbData = new SoftUserData();
|
||||
dbData.setUserId(user.getId());
|
||||
dbData.setAppCode(param.getAppCode());
|
||||
dbData.setData(param.getData());
|
||||
dbData.setId(IdUtil.fastSimpleUUID());
|
||||
Assert.isTrue(DbUtil.insertObject(dbData), "保存失败");
|
||||
} else {
|
||||
dbData.setData(param.getData());
|
||||
Assert.isTrue(DbUtil.updateObject(dbData, Entity.create().set("id", dbData.getId())) > 0, "保存失败");
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Action(val = "get", type = 1)
|
||||
public R get(SoftUserData param) {
|
||||
Assert.notBlank(param.getAppCode(), "软件编码不可为空");
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
SoftUserData dbData = DbUtil.queryObject("select * from soft_user_data where user_id = ? and app_code = ?", SoftUserData.class, user.getId(), param.getAppCode());
|
||||
if (dbData == null) {
|
||||
return R.failed();
|
||||
}
|
||||
return R.okData(dbData.getData());
|
||||
}
|
||||
}
|
@ -0,0 +1,449 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.tenfell.webos.common.annt.*;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.DbUtil;
|
||||
import cn.tenfell.webos.common.util.LoginAuthUtil;
|
||||
import cn.tenfell.webos.common.util.ProjectUtil;
|
||||
import cn.tenfell.webos.modules.entity.IoDrive;
|
||||
import cn.tenfell.webos.modules.entity.IoUserDrive;
|
||||
import cn.tenfell.webos.modules.entity.SysConfig;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@BeanAction(val = "user")
|
||||
public class UserAction {
|
||||
public static String getUserName(String userId) {
|
||||
return DbUtil.queryString("select username from sys_user where id = ?", SysUser.class, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建子用户
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "createChild", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R createChild(SysUser param) {
|
||||
Assert.notBlank(param.getUsername(), "用户名不可为空");
|
||||
if (LoginAuthUtil.isSystem()) {
|
||||
Assert.notBlank(param.getParentUserNo(), "主用户编号不可为空");
|
||||
} else {
|
||||
SysUser loginUser = LoginAuthUtil.getUser();
|
||||
param.setParentUserNo(loginUser.getParentUserNo());
|
||||
}
|
||||
long count = DbUtil.queryLong("select count(0) from sys_user where username = ? and parent_user_no = ?", SysUser.class, param.getUsername(), param.getParentUserNo());
|
||||
Assert.isTrue(count < 1, "当前用户已存在");
|
||||
param.setPassword(ProjectUtil.startConfig.getStr("defaultPwd"));
|
||||
createChildUser(param);
|
||||
return R.ok(null, "创建子用户成功");
|
||||
}
|
||||
|
||||
private CommonBean.AccessToken createChildUser(SysUser user) {
|
||||
user.setId(IdUtil.fastSimpleUUID());
|
||||
user.setPassword(LoginAuthUtil.encodePassword(user.getPassword()));
|
||||
user.setCreatedTime(LocalDateTime.now());
|
||||
user.setUpdatedTime(LocalDateTime.now());
|
||||
user.setUserType(2);
|
||||
user.setIsAdmin(2);
|
||||
Assert.isTrue(DbUtil.insertObject(user), "子用户创建失败");
|
||||
CommonBean.AccessToken token = LoginAuthUtil.createToken(user.getId(), user.getPassword(),null, user.getUserType());
|
||||
return token;
|
||||
}
|
||||
|
||||
public static synchronized SysUser createMain(String username, String password, Integer isAdmin) {
|
||||
Assert.notBlank(username, "用户名不可为空");
|
||||
long count = DbUtil.queryLong("select count(0) from sys_user where username = ? and user_type = 1", SysUser.class, username);
|
||||
Assert.isTrue(count < 1, "当前用户已存在");
|
||||
Long maxNo = DbUtil.queryLong("select max(parent_user_no) from sys_user where user_type = 1", SysUser.class);
|
||||
if (maxNo == null || maxNo == 0) {
|
||||
maxNo = 10000L;
|
||||
}
|
||||
Long parentUserNo = maxNo + 1L;
|
||||
SysUser user = new SysUser();
|
||||
user.setId(IdUtil.fastSimpleUUID());
|
||||
user.setUsername(username);
|
||||
user.setPassword(LoginAuthUtil.encodePassword(password));
|
||||
user.setParentUserNo(Convert.toStr(parentUserNo));
|
||||
user.setCreatedTime(LocalDateTime.now());
|
||||
user.setUpdatedTime(LocalDateTime.now());
|
||||
user.setUserType(1);
|
||||
user.setValid(1);
|
||||
user.setIsAdmin(isAdmin);
|
||||
Assert.isTrue(DbUtil.insertObject(user), "创建主用户失败");
|
||||
SysConfig config = new SysConfig();
|
||||
config.setId(IdUtil.fastSimpleUUID());
|
||||
config.setParentUserId(user.getId());
|
||||
config.setOpenReg(0);
|
||||
Assert.isTrue(DbUtil.insertObject(config), "主用户配置写入失败");
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建主用户
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "createMain", type = 1)
|
||||
@Transactional
|
||||
@Auth(val = "system")
|
||||
public R createMain(SysUser param) {
|
||||
createMain(param.getUsername(), ProjectUtil.startConfig.getStr("defaultPwd"), 2);
|
||||
return R.ok(null, "主用户创建成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 子用户注册
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "reg", type = 1)
|
||||
@Login(val = false)
|
||||
public R reg(SysUser param) {
|
||||
Assert.notBlank(param.getUsername(), "用户名不可为空");
|
||||
Assert.notBlank(param.getPassword(), "密码不可为空");
|
||||
Assert.notBlank(param.getParentUserNo(), "主用户编号不可为空");
|
||||
SysUser parentUser = DbUtil.queryObject("select * from sys_user where user_type = 1 and parent_user_no = ? and valid = 1", SysUser.class, param.getParentUserNo());
|
||||
Assert.notNull(parentUser, "当前主用户不存在");
|
||||
SysConfig config = DbUtil.queryObject("select * from sys_config where parent_user_id = ?", SysConfig.class, parentUser.getId());
|
||||
Assert.notNull(config, "当前主用户配置异常");
|
||||
Assert.isTrue(config.getOpenReg() != null && config.getOpenReg() == 1, "当前主用户未开放注册");
|
||||
long count = DbUtil.queryLong("select count(0) from sys_user where username = ? and parent_user_no = ?", SysUser.class, param.getUsername(), param.getParentUserNo());
|
||||
Assert.isTrue(count < 1, "当前用户已被注册");
|
||||
param.setValid(1);
|
||||
CommonBean.AccessToken token = createChildUser(param);
|
||||
return R.ok(token, "注册成功");
|
||||
}
|
||||
|
||||
public static CommonBean.AccessToken userLogin(SysUser user){
|
||||
Assert.notBlank(user.getUsername(), "用户名不可为空");
|
||||
Assert.notBlank(user.getPassword(), "密码不可为空");
|
||||
Assert.isTrue(CollUtil.newArrayList(1, 2).contains(user.getUserType()), "用户类型不正确");
|
||||
SysUser dbUser;
|
||||
if (user.getUserType() == 2) {
|
||||
//子用户登录
|
||||
Assert.notBlank(user.getParentUserNo(), "主用户编号不可为空");
|
||||
dbUser = DbUtil.queryObject("select * from sys_user where user_type = 2 and username = ? and parent_user_no = ?", SysUser.class, user.getUsername(), user.getParentUserNo());
|
||||
} else {
|
||||
//主用户登录
|
||||
dbUser = DbUtil.queryObject("select * from sys_user where user_type = 1 and username = ?", SysUser.class, user.getUsername());
|
||||
}
|
||||
Assert.notNull(dbUser, "用户名或密码错误");
|
||||
Assert.isTrue(LoginAuthUtil.checkPassword(user.getPassword(), dbUser.getPassword()), "用户名或密码错误");
|
||||
Assert.isTrue(dbUser.getValid() == 1, "当前用户已禁用");
|
||||
CommonBean.AccessToken token = LoginAuthUtil.createToken(dbUser.getId(), dbUser.getPassword(),null, dbUser.getUserType());
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "login", type = 1)
|
||||
@Login(val = false)
|
||||
public R login(SysUser user) {
|
||||
return R.ok(userLogin(user), "登录成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "loginByLock", type = 1)
|
||||
@Login(val = false)
|
||||
public R loginByLock(Dict param) {
|
||||
String userId = param.getStr("userId");
|
||||
String password = param.getStr("password");
|
||||
Assert.notBlank(userId, "用户Id不可为空");
|
||||
Assert.notBlank(password, "密码不可为空");
|
||||
SysUser dbUser = DbUtil.queryObject("select * from sys_user where id = ?", SysUser.class, userId);
|
||||
Assert.notNull(dbUser, "此用户不存在");
|
||||
if (StrUtil.isNotBlank(dbUser.getSpPassword())) {
|
||||
Assert.isTrue(LoginAuthUtil.checkPassword(password, dbUser.getPassword()) || LoginAuthUtil.checkPassword(password, dbUser.getSpPassword()), "密码错误");
|
||||
} else {
|
||||
Assert.isTrue(LoginAuthUtil.checkPassword(password, dbUser.getPassword()), "密码错误");
|
||||
}
|
||||
Assert.isTrue(dbUser.getValid() == 1, "当前用户已禁用");
|
||||
CommonBean.AccessToken token = LoginAuthUtil.createToken(dbUser.getId(), dbUser.getPassword(),null, dbUser.getUserType());
|
||||
return R.ok(token, "登录成功");
|
||||
}
|
||||
|
||||
@Action(val = "lock", type = 1)
|
||||
public R lock(Dict param) {
|
||||
LoginAuthUtil.toLock();
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Action(val = "checkLock", type = 1)
|
||||
public R checkLock(Dict param) {
|
||||
return LoginAuthUtil.checkLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录信息
|
||||
*
|
||||
* @param ctx
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "info")
|
||||
public R info(Context ctx) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
user.setPassword("");
|
||||
user.setSpPassword("");
|
||||
return R.ok(user, "获取成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新token
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "refreshToken", type = 1)
|
||||
@Login(val = false)
|
||||
public R refreshToken(Dict param) {
|
||||
CommonBean.AccessToken token = LoginAuthUtil.refreshToken(param.getStr("refreshToken"));
|
||||
Assert.notNull(token, "刷新token失败");
|
||||
return R.ok(token, "刷新token成功");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "infoById", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R infoById(SysUser data) {
|
||||
SysUser db = DbUtil.queryObject("select * from sys_user where id = ?", SysUser.class, data.getId());
|
||||
Assert.notNull(db, "当前记录不存在");
|
||||
if (!LoginAuthUtil.isSystem()) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
Assert.isTrue(StrUtil.equals(db.getParentUserNo(), user.getParentUserNo()), "当前记录不存在");
|
||||
}
|
||||
db.setPassword("");
|
||||
db.setSpPassword("");
|
||||
return R.ok(db, "获取成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "list", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R list(Dict param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
String keyword = param.getStr("keyword");
|
||||
int currentPage = param.getInt("current");
|
||||
Integer pageSize = param.getInt("pageSize");
|
||||
String where = " where 1=1";
|
||||
List<Object> sqlParams = new ArrayList<>();
|
||||
if (!LoginAuthUtil.isSystem()) {
|
||||
where += " and parent_user_no = ?";
|
||||
sqlParams.add(user.getParentUserNo());
|
||||
}
|
||||
if (StrUtil.isNotBlank(keyword)) {
|
||||
where += " and username like ?";
|
||||
sqlParams.add("%" + keyword + "%");
|
||||
}
|
||||
CommonBean.PageRes<SysUser> data = DbUtil.pageObject("select *", "from sys_user" + where, SysUser.class, currentPage, pageSize, sqlParams.toArray());
|
||||
data.getData().forEach(item -> item.setPassword(""));
|
||||
return R.ok(data, "获取成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户信息
|
||||
* 主用户专享
|
||||
*/
|
||||
@Action(val = "update", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R update(SysUser param) {
|
||||
//用户名,图像,昵称,有效
|
||||
Assert.notBlank(param.getId(), "参数不足");
|
||||
SysUser user = DbUtil.queryObject("select * from sys_user where id = ?", SysUser.class, param.getId());
|
||||
Assert.notNull(user, "此用户不存在");
|
||||
boolean isSystem = LoginAuthUtil.isSystem();
|
||||
SysUser loginUser = LoginAuthUtil.getUser();
|
||||
if (!isSystem) {
|
||||
Assert.isTrue(StrUtil.equals(loginUser.getParentUserNo(), user.getParentUserNo()), "权限不足");
|
||||
}
|
||||
if (StrUtil.isNotBlank(param.getUsername()) && !StrUtil.equals(user.getUsername(), param.getUsername())) {
|
||||
if (!isSystem) {
|
||||
Assert.isTrue(!StrUtil.equals(loginUser.getId(), user.getId()), "请联系管理员修改用户名");
|
||||
}
|
||||
if (user.getUserType() == 1) {
|
||||
//主用户
|
||||
long count = DbUtil.queryLong("select count(0) from sys_user where user_type = 1 and username = ? and id != ?", SysUser.class, param.getUsername(), user.getId());
|
||||
Assert.isTrue(count < 1, "当前用户名被占用");
|
||||
} else {
|
||||
//子用户
|
||||
long count = DbUtil.queryLong("select count(0) from sys_user where user_type = 2 and username = ? and parent_user_no = ? and id != ?", SysUser.class, param.getUsername(), user.getParentUserNo(), user.getId());
|
||||
Assert.isTrue(count < 1, "当前用户名被占用");
|
||||
}
|
||||
user.setUsername(param.getUsername());
|
||||
}
|
||||
user.setImgPath(param.getImgPath());
|
||||
user.setNickName(param.getNickName());
|
||||
user.setValid(param.getValid());
|
||||
boolean flag = DbUtil.updateObject(user, Entity.create().set("id", param.getId())) > 0;
|
||||
Assert.isTrue(flag, "操作失败");
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改当前登录用户密码
|
||||
*/
|
||||
@Action(val = "updatePassword", type = 1)
|
||||
public R updatePassword(Dict param) {
|
||||
String oldPwd = param.getStr("oldPassword");
|
||||
Assert.notBlank(oldPwd, "旧密码不可为空");
|
||||
String password = param.getStr("password");
|
||||
Assert.notBlank(oldPwd, "新密码不可为空");
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
SysUser db = DbUtil.queryObject("select * from sys_user where id = ?", SysUser.class, user.getId());
|
||||
Assert.isTrue(LoginAuthUtil.checkPassword(oldPwd, db.getPassword()), "旧密码不正确");
|
||||
db.setPassword(LoginAuthUtil.encodePassword(password));
|
||||
db.setUpdatedTime(LocalDateTime.now());
|
||||
boolean flag = DbUtil.updateObject(db, Entity.create().set("id", db.getId())) > 0;
|
||||
Assert.isTrue(flag, "修改失败");
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置用户密码
|
||||
*/
|
||||
@Action(val = "resetPassword", type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R resetPassword(SysUser param) {
|
||||
Assert.notBlank(param.getId(), "参数不足");
|
||||
SysUser db = DbUtil.queryObject("select * from sys_user where id = ?", SysUser.class, param.getId());
|
||||
Assert.notNull(db, "此用户不存在");
|
||||
if (!LoginAuthUtil.isSystem()) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
Assert.isTrue(StrUtil.equals(user.getParentUserNo(), db.getParentUserNo()), "权限不足");
|
||||
}
|
||||
db.setPassword(LoginAuthUtil.encodePassword(ProjectUtil.startConfig.getStr("defaultPwd")));
|
||||
db.setUpdatedTime(LocalDateTime.now());
|
||||
boolean flag = DbUtil.updateObject(db, Entity.create().set("id", db.getId())) > 0;
|
||||
Assert.isTrue(flag, "修改失败");
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
@Action(val = "updateInfo", type = 1)
|
||||
public R updateInfo(SysUser param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
if (StrUtil.isNotBlank(param.getUsername())) {
|
||||
if (user.getUserType() == 1) {
|
||||
//主用户
|
||||
long count = DbUtil.queryLong("select count(0) from sys_user where user_type = 1 and username = ? and id != ?", SysUser.class, param.getUsername(), user.getId());
|
||||
Assert.isTrue(count < 1, "当前用户名被占用");
|
||||
} else {
|
||||
//子用户
|
||||
long count = DbUtil.queryLong("select count(0) from sys_user where user_type = 2 and username = ? and parent_user_no = ? and id != ?", SysUser.class, param.getUsername(), user.getParentUserNo(), user.getId());
|
||||
Assert.isTrue(count < 1, "当前用户名被占用");
|
||||
}
|
||||
user.setUsername(param.getUsername());
|
||||
}
|
||||
user.setNickName(param.getNickName());
|
||||
user.setImgPath(param.getImgPath());
|
||||
if (StrUtil.isNotBlank(param.getSpPassword())) {
|
||||
user.setSpPassword(LoginAuthUtil.encodePassword(param.getSpPassword()));
|
||||
}
|
||||
boolean flag = DbUtil.updateObject(user, Entity.create().set("id", user.getId())) > 0;
|
||||
Assert.isTrue(flag, "修改失败");
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取下拉框
|
||||
*/
|
||||
@Action(val = "select")
|
||||
@Auth(val = "main_auth")
|
||||
public R select() {
|
||||
List<SysUser> list = selectData();
|
||||
return R.ok(list, "获取成功");
|
||||
}
|
||||
|
||||
private List<SysUser> selectData() {
|
||||
SysUser loginUser = LoginAuthUtil.getUser();
|
||||
return DbUtil.queryList("select * from sys_user where parent_user_no = ?", SysUser.class, loginUser.getParentUserNo());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下拉框Map
|
||||
*/
|
||||
@Action(val = "selectMap")
|
||||
@Auth(val = "main_auth")
|
||||
public R selectMap() {
|
||||
List<SysUser> list = selectData();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (SysUser user : list) {
|
||||
map.put(user.getId(), user.getUsername() + "(" + user.getNickName() + ")");
|
||||
}
|
||||
return R.ok(map, "获取成功");
|
||||
}
|
||||
|
||||
@Action(val = "del",type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R del(SysUser data) {
|
||||
SysUser db = DbUtil.queryObject("select * from sys_user where id = ?",SysUser.class,data.getId());
|
||||
if(db == null){
|
||||
return R.ok();
|
||||
}
|
||||
SysUser loginUser = LoginAuthUtil.getUser();
|
||||
if(!LoginAuthUtil.isSystem()){
|
||||
Assert.isTrue(StrUtil.equals(loginUser.getParentUserNo(),db.getParentUserNo()),"权限不足");
|
||||
}
|
||||
Assert.isTrue(!StrUtil.equals(loginUser.getId(),db.getId()),"自己不能删除自己");
|
||||
if(db.getUserType() == 1){
|
||||
//主用户,删除IoDrive
|
||||
List<IoDrive> list = DbUtil.queryList("select * from io_drive where parent_user_id = ?", IoDrive.class, db.getId());
|
||||
IoDriveAction.delIoDriveByIds(list);
|
||||
}else if(db.getUserType() == 2){
|
||||
//子用户,删除IoUserDrive
|
||||
List<IoUserDrive> list = DbUtil.queryList("select * from io_user_drive where user_id = ?", IoUserDrive.class, db.getId());
|
||||
IoUserDriveAction.delIoUserDriveByIds(list);
|
||||
}else{
|
||||
Assert.isTrue(false,"此用户类型不存在");
|
||||
}
|
||||
DbUtil.delete("delete from sys_user where id = ?", SysUser.class,db.getId());
|
||||
return R.ok(null,"删除成功");
|
||||
}
|
||||
|
||||
@Action(val = "defaultPwd",type = 1)
|
||||
@Auth(val = "main_auth")
|
||||
public R defaultPwd(Dict dict) {
|
||||
return R.okData(ProjectUtil.startConfig.getStr("defaultPwd"));
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.annt.Transactional;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemInface;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.DbUtil;
|
||||
import cn.tenfell.webos.common.util.LoginAuthUtil;
|
||||
import cn.tenfell.webos.common.util.ProjectUtil;
|
||||
import cn.tenfell.webos.modules.entity.IoUserRecycle;
|
||||
import cn.tenfell.webos.modules.entity.SysUser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@BeanAction(val = "userRecycle")
|
||||
public class UserRecycleAction {
|
||||
public static R infoByPath(String path) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
if (user == null) {
|
||||
return R.failed("请登录后重试");
|
||||
}
|
||||
String id = path2id(path);
|
||||
IoUserRecycle iur = DbUtil.queryObject("select * from io_user_recycle where id = ? and user_id = ?", IoUserRecycle.class, id, user.getId());
|
||||
Assert.notNull(iur, "权限不足");
|
||||
CommonBean.PathInfo info = new CommonBean.PathInfo();
|
||||
info.setPath(iur.getRemovePath());
|
||||
info.setType(iur.getType());
|
||||
info.setName(iur.getName());
|
||||
info.setSize(iur.getSize());
|
||||
info.setCreatedAt(LocalDateTimeUtil.formatNormal(iur.getDeletedTime()));
|
||||
if (info.getType() == 1) {
|
||||
info.setExt(FileUtil.extName(iur.getName()));
|
||||
}
|
||||
return R.okData(info);
|
||||
}
|
||||
|
||||
private static String path2id(String path) {
|
||||
String[] sz = path.split(":");
|
||||
sz = sz[1].split("}");
|
||||
return sz[0];
|
||||
}
|
||||
|
||||
public static String id2realpath(String cycleId) {
|
||||
return ProjectUtil.rootPath + "/recycle/" + cycleId + ".zip";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取回收站列表
|
||||
*/
|
||||
@Action(val = "list", type = 1)
|
||||
public R list(Dict param) {
|
||||
int currentPage = param.getInt("current");
|
||||
Integer pageSize = param.getInt("pageSize");
|
||||
String where = " where 1=1";
|
||||
List<Object> params = new ArrayList<>();
|
||||
where += " and user_id = ?";
|
||||
params.add(LoginAuthUtil.getUser().getId());
|
||||
CommonBean.PageRes<IoUserRecycle> data = DbUtil.pageObject("select *", "from io_user_recycle" + where, IoUserRecycle.class, currentPage, pageSize, params.toArray());
|
||||
return R.ok(data, "获取成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取回收站列表
|
||||
*/
|
||||
@Action(val = "restoreByPaths", type = 1)
|
||||
public R restoreByPaths(List<String> paths) {
|
||||
boolean hasError = false;
|
||||
boolean hasSuccess = false;
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
for (int i = 0; i < paths.size(); i++) {
|
||||
String id = path2id(paths.get(i));
|
||||
try {
|
||||
IoUserRecycle iur = DbUtil.queryObject("select * from io_user_recycle where id = ? and user_id = ?", IoUserRecycle.class, id, user.getId());
|
||||
Assert.notNull(iur, "权限不足");
|
||||
FileSystemInface.PlainPath path = FileSystemUtil.cipherPath2PlainPathByLogin(iur.getRemovePath(), "", "");
|
||||
boolean flag = FileSystemUtil.ACTION.restore(path, iur);
|
||||
if (flag) {
|
||||
hasSuccess = true;
|
||||
DbUtil.delete("delete from io_user_recycle where id = ?", IoUserRecycle.class, id);
|
||||
} else {
|
||||
hasError = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
hasError = true;
|
||||
ProjectUtil.showConsoleErr(e);
|
||||
}
|
||||
}
|
||||
if (hasSuccess && hasError) {
|
||||
return R.ok(null, "部分恢复成功");
|
||||
} else if (hasSuccess) {
|
||||
return R.ok(null, "恢复成功");
|
||||
} else {
|
||||
return R.failed("恢复失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 彻底删除
|
||||
*/
|
||||
@Action(val = "clearByPaths", type = 1)
|
||||
@Transactional
|
||||
public R clearByPaths(List<String> paths) {
|
||||
List<String> ids = new ArrayList<>();
|
||||
for (String path : paths) {
|
||||
String id = path2id(path);
|
||||
ids.add(id);
|
||||
}
|
||||
delByIds(ids);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
private void delByIds(List<String> ids) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
String idsStr = "'" + CollUtil.join(ids, "','") + "'";
|
||||
int count = DbUtil.delete("delete from io_user_recycle where id in ( " + idsStr + " ) and user_id = ?", IoUserRecycle.class, user.getId());
|
||||
if (count == ids.size()) {
|
||||
for (String id : ids) {
|
||||
String zipPath = id2realpath(id);
|
||||
if (FileUtil.exist(zipPath)) {
|
||||
FileUtil.del(zipPath);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Assert.isTrue(false, "彻底删除失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Action(val = "clear", type = 1)
|
||||
@Transactional
|
||||
public R clear(Dict param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
List<IoUserRecycle> list = DbUtil.queryList("select id from io_user_recycle where user_id = ?", IoUserRecycle.class, user.getId());
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return R.ok();
|
||||
}
|
||||
List<String> ids = CollUtil.getFieldValues(list, "id", String.class);
|
||||
delByIds(ids);
|
||||
return R.ok(null, "清空回收站成功");
|
||||
}
|
||||
|
||||
@Action(val = "count", type = 1)
|
||||
public R count(Dict param) {
|
||||
SysUser user = LoginAuthUtil.getUser();
|
||||
long count = DbUtil.queryLong("select count(0) from io_user_recycle where user_id = ?", IoUserRecycle.class, user.getId());
|
||||
return R.ok(count, "获取数量成功");
|
||||
}
|
||||
}
|
137
api/src/main/java/cn/tenfell/webos/modules/action/WpsAction.java
Normal file
137
api/src/main/java/cn/tenfell/webos/modules/action/WpsAction.java
Normal file
@ -0,0 +1,137 @@
|
||||
package cn.tenfell.webos.modules.action;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.tenfell.webos.common.annt.Action;
|
||||
import cn.tenfell.webos.common.annt.BeanAction;
|
||||
import cn.tenfell.webos.common.annt.Login;
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import cn.tenfell.webos.common.filesystem.AliyunDriveFileSystem;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemInface;
|
||||
import cn.tenfell.webos.common.filesystem.FileSystemUtil;
|
||||
import cn.tenfell.webos.common.server.R;
|
||||
import cn.tenfell.webos.common.util.CacheUtil;
|
||||
import cn.tenfell.webos.common.util.ValidaUtil;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@BeanAction(val = "wps")
|
||||
public class WpsAction {
|
||||
private final static WpsDriveSystem wpsDriveSystem;
|
||||
|
||||
static {
|
||||
wpsDriveSystem = new WpsDriveSystem();
|
||||
}
|
||||
|
||||
private static JSONObject getCommonToken() {
|
||||
return CacheUtil.getCacheData("wps:common_token", () -> JSONUtil.parseObj(HttpUtil.get("https://plugins.webos.tenfell.cn/aliyundrive_third_token/index.php?type=5")), 300);
|
||||
}
|
||||
|
||||
private static class WpsDriveSystem extends AliyunDriveFileSystem {
|
||||
@Override
|
||||
public JSONObject getToken(String tokenId) {
|
||||
return getCommonToken();
|
||||
}
|
||||
}
|
||||
|
||||
@Action(val = "save", type = 1)
|
||||
public R save(Dict param) throws Exception {
|
||||
String path = param.getStr("path");
|
||||
String fileId = param.getStr("fileId");
|
||||
String name = param.getStr("name");
|
||||
ValidaUtil.init(param)
|
||||
.notBlank("name", "名称")
|
||||
.notBlank("path", "路径")
|
||||
.notBlank("fileId", "文件id");
|
||||
FileSystemInface.PlainPath tmp = new FileSystemInface.PlainPath();
|
||||
tmp.setDriveType(FileSystemUtil.ALI_PAN_DRIVE_TYPE);
|
||||
tmp.setRealPath(fileId);
|
||||
String[] sz = StrUtil.replace(path, "\\", "/").split("/");
|
||||
String parentPath = ArrayUtil.join(ArrayUtil.remove(sz, sz.length - 1), "/");
|
||||
FileSystemInface.PlainPath plainParentPath = FileSystemUtil.cipherPath2PlainPathByLogin(parentPath, "", "");
|
||||
String newFile = FileSystemUtil.ACTION.uploadByServer(plainParentPath, name, wpsDriveSystem.getInputStream(tmp, 0, 0), null, null);
|
||||
Assert.notBlank(newFile, "文件上传失败");
|
||||
return R.ok(null, "保存成功");
|
||||
}
|
||||
|
||||
@Action(val = "url", type = 1)
|
||||
@Login(val = false)
|
||||
public R url(Dict param) {
|
||||
String path = param.getStr("path");
|
||||
boolean edit = StrUtil.equals(param.getStr("edit"), "edit");
|
||||
boolean hasFileAuth = false;
|
||||
FileSystemInface.PlainPath plainPath = null;
|
||||
try {
|
||||
plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, "", "");
|
||||
hasFileAuth = true;
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
if (edit && plainPath != null) {
|
||||
try {
|
||||
CommonBean.WpsUrlData data = FileSystemUtil.ACTION.getWpsUrl(plainPath, true);
|
||||
Assert.notNull(data, "当前网盘还未支持");
|
||||
data.setHasFileAuth(true);
|
||||
if (data.getType() == 1) {
|
||||
return R.okData(data);
|
||||
}
|
||||
if (data.getType() == 2) {
|
||||
pushFile2AliAndGetUrl(plainPath, data, true);
|
||||
}
|
||||
if (data.getType() == 0) {
|
||||
return R.failed("文件获取失败,建议刷新页面重试");
|
||||
}
|
||||
return R.okData(data);
|
||||
} catch (Exception e) {
|
||||
//无权限
|
||||
}
|
||||
}
|
||||
String shareCode = param.getStr("shareCode");
|
||||
String sharePwd = param.getStr("sharePwd");
|
||||
plainPath = FileSystemUtil.cipherPath2PlainPathByLogin(path, shareCode, sharePwd);
|
||||
CommonBean.WpsUrlData data = FileSystemUtil.ACTION.getWpsUrl(plainPath, false);
|
||||
data.setHasFileAuth(hasFileAuth);
|
||||
Assert.notNull(data, "当前网盘还未支持");
|
||||
if (data.getType() == 1) {
|
||||
return R.okData(data);
|
||||
}
|
||||
if (data.getType() == 2) {
|
||||
pushFile2AliAndGetUrl(plainPath, data, false);
|
||||
}
|
||||
if (data.getType() == 0) {
|
||||
return R.failed("文件获取失败,建议刷新页面重试");
|
||||
}
|
||||
return R.okData(data);
|
||||
}
|
||||
|
||||
private void pushFile2AliAndGetUrl(FileSystemInface.PlainPath plainPath, CommonBean.WpsUrlData data, boolean edit) {
|
||||
CommonBean.PathInfo info = FileSystemUtil.ACTION.fileInfo(plainPath);
|
||||
InputStream in = FileSystemUtil.ACTION.getInputStream(plainPath, 0, 0);
|
||||
FileSystemInface.PlainPath tmp = new FileSystemInface.PlainPath();
|
||||
tmp.setDriveType(FileSystemUtil.ALI_PAN_DRIVE_TYPE);
|
||||
String ali_path = getCommonToken().getStr("list_id");
|
||||
tmp.setRealPath(ali_path);
|
||||
String name = IdUtil.fastSimpleUUID() + "." + info.getExt();
|
||||
String fileId = wpsDriveSystem.uploadByServer(tmp, name, in, null, null);
|
||||
if (StrUtil.isBlank(fileId)) {
|
||||
data.setType(0);
|
||||
return;
|
||||
}
|
||||
tmp.setRealPath(ali_path + "/" + fileId);
|
||||
CommonBean.WpsUrlData res = wpsDriveSystem.getWpsUrl(tmp, edit);
|
||||
if (res.getType() == 0) {
|
||||
data.setType(0);
|
||||
return;
|
||||
}
|
||||
data.setUrl(res.getUrl());
|
||||
data.setToken(res.getToken());
|
||||
data.setEdit(res.isEdit());
|
||||
data.setFileId(fileId);
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package cn.tenfell.webos.modules.entity;
|
||||
|
||||
import cn.tenfell.webos.common.bean.CommonBean.BaseEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* io_drive
|
||||
*/
|
||||
@Data
|
||||
public class IoDrive extends BaseEntity {
|
||||
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 硬盘名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 物理路径,此路径之后的位置为用户可操作的位置,例如:
|
||||
* 本地文件 /test/aaa
|
||||
* 本地文件 D:\iodrive_test
|
||||
* 阿里云盘 62c803562d0bd15e70cd409b97e9b4195526f180
|
||||
* 123云盘 1657351
|
||||
*/
|
||||
private String path;
|
||||
/**
|
||||
* 已用大小MB,等价于所有user_drive的max_size和
|
||||
*/
|
||||
private long useSize;
|
||||
/**
|
||||
* 最大大小MB,0表示无限制
|
||||
*/
|
||||
private long maxSize;
|
||||
/**
|
||||
* 可用大小MB,0表示无限制
|
||||
*/
|
||||
private long availSize;
|
||||
/**
|
||||
* token表数据
|
||||
*/
|
||||
private String tokenId;
|
||||
/**
|
||||
* 给用户分配网盘时候的默认盘名
|
||||
*/
|
||||
private String userDriveName;
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
private LocalDateTime createdTime;
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
private LocalDateTime updatedTime;
|
||||
/**
|
||||
* 硬盘类型
|
||||
*/
|
||||
private String driveType;
|
||||
/**
|
||||
* 主用户id
|
||||
*/
|
||||
private String parentUserId;
|
||||
/**
|
||||
* 磁盘编号 从1开始
|
||||
* 文件会以{io:1}开头
|
||||
*/
|
||||
private Integer no;
|
||||
/**
|
||||
* 是否支持秒传1支持2不支持
|
||||
*/
|
||||
private Integer secondTransmission;
|
||||
/**
|
||||
* 秒传下有效,秒传文件真实目录
|
||||
*/
|
||||
private String realFilePath;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package cn.tenfell.webos.modules.entity;
|
||||
|
||||
import cn.tenfell.webos.common.bean.CommonBean.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* io_file_ass
|
||||
*/
|
||||
@Data
|
||||
public class IoFileAss extends BaseEntity {
|
||||
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 用户软件id
|
||||
*/
|
||||
private String softUserId;
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private String userId;
|
||||
/**
|
||||
* 拓展名
|
||||
*/
|
||||
private String ext;
|
||||
/**
|
||||
* 行为addall,openwith,new
|
||||
*/
|
||||
private String action;
|
||||
/**
|
||||
* 操作名称
|
||||
*/
|
||||
private String actionName;
|
||||
/**
|
||||
* 注册的按钮A-Z大写
|
||||
*/
|
||||
private String bindKey;
|
||||
/**
|
||||
* 排序越大越靠前
|
||||
*/
|
||||
private long sortNum;
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 图标地址
|
||||
*/
|
||||
private String iconUrl;
|
||||
/**
|
||||
* 拓展动作
|
||||
*/
|
||||
private String expAction;
|
||||
/**
|
||||
* 软件名称
|
||||
*/
|
||||
private String appName;
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package cn.tenfell.webos.modules.entity;
|
||||
|
||||
import cn.tenfell.webos.common.bean.CommonBean.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* io_offline_download
|
||||
*/
|
||||
@Data
|
||||
public class IoOfflineDownload extends BaseEntity {
|
||||
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 主用户id
|
||||
*/
|
||||
private String parentUserId;
|
||||
/**
|
||||
* 下载地址
|
||||
*/
|
||||
private String downUrl;
|
||||
/**
|
||||
* 0已创建1下载中2下载完成3下载失败
|
||||
*/
|
||||
private long status;
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private String userId;
|
||||
/**
|
||||
* 当前文件的位置相对于user_drive的位置,如{io:1}/abc
|
||||
*/
|
||||
private String parentPath;
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package cn.tenfell.webos.modules.entity;
|
||||
|
||||
import cn.tenfell.webos.common.bean.CommonBean;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* io_token_data
|
||||
* 硬盘token数据
|
||||
*/
|
||||
@Data
|
||||
public class IoTokenData extends CommonBean.BaseEntity {
|
||||
/**
|
||||
* 主键id
|
||||
* 固定算法 md5(driveType + 网址 + 用户id)
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 硬盘类型
|
||||
*/
|
||||
private String driveType;
|
||||
/**
|
||||
* token数据
|
||||
*/
|
||||
private String tokenData;
|
||||
/**
|
||||
* 需要在这之前提前20分钟刷新token
|
||||
*/
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
/**
|
||||
* 与token无关的拓展数据
|
||||
* 方便拓展备用
|
||||
*/
|
||||
private String expData;
|
||||
/**
|
||||
* 错误次数
|
||||
*/
|
||||
private Integer errCount;
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package cn.tenfell.webos.modules.entity;
|
||||
|
||||
import cn.tenfell.webos.common.bean.CommonBean.BaseEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* io_user_drive
|
||||
*/
|
||||
@Data
|
||||
public class IoUserDrive extends BaseEntity {
|
||||
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 主用户id
|
||||
*/
|
||||
private String parentUserId;
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private String userId;
|
||||
/**
|
||||
* 硬盘id
|
||||
*/
|
||||
private String driveId;
|
||||
/**
|
||||
* 磁盘名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 相对于drive的位置,此位置之后为该用户可操作的位置,例如
|
||||
* {io:1}/abc
|
||||
* 如果此时文件路径为{uio:1}/ccf.txt
|
||||
* {io:1}的path为/test
|
||||
* 则等价于实际的文件在/test/abc/ccf.txt
|
||||
*/
|
||||
private String path;
|
||||
/**
|
||||
* 已用大小MB
|
||||
*/
|
||||
private long useSize;
|
||||
/**
|
||||
* 最大大小MB,不能为0
|
||||
*/
|
||||
private long maxSize;
|
||||
/**
|
||||
* 可用大小MB,不可为0
|
||||
*/
|
||||
private long availSize;
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
private LocalDateTime createdTime;
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
private LocalDateTime updatedTime;
|
||||
|
||||
/**
|
||||
* 磁盘编号 从1开始
|
||||
* 文件会以{uio:1}开头
|
||||
*/
|
||||
private Integer no;
|
||||
|
||||
/**
|
||||
* 1有效 2无效
|
||||
*/
|
||||
private Integer valid;
|
||||
/**
|
||||
* 是否系统盘 1是 2否
|
||||
*/
|
||||
private Integer isSystem;
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package cn.tenfell.webos.modules.entity;
|
||||
|
||||
import cn.tenfell.webos.common.bean.CommonBean.BaseEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* io_user_recycle
|
||||
*/
|
||||
@Data
|
||||
public class IoUserRecycle extends BaseEntity {
|
||||
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
private String userId;
|
||||
/**
|
||||
* 大小
|
||||
*/
|
||||
private long size;
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private int type;
|
||||
/**
|
||||
* 文件名/目录名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 删除时间
|
||||
*/
|
||||
private LocalDateTime deletedTime;
|
||||
/**
|
||||
* 原删除路径
|
||||
* 第三方盘目录:{uio:1}/dirId/dirId
|
||||
* 第三方盘文件:{uio:1}/dirId/fileId
|
||||
* 本地盘目录:{uio:1}/一级目录/二级目录
|
||||
* 本地盘文件:{uio:1}/一级目录/张三.mp3
|
||||
*/
|
||||
private String removePath;
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user