开源首发

This commit is contained in:
afan 2024-02-28 17:21:32 +08:00
parent 7dc6657cbb
commit 075b3f12c7
911 changed files with 65397 additions and 189 deletions

27
.gitignore vendored
View File

@ -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/**

View File

@ -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%
- [√] 独立的应用商店(可下载应用插件、后期可注册开发者发布自己制作的插件)
- [√] 文件永久链接复制和直接文件下载
- [√] 全盘文件分享(可控制是否允许下载)
- [√] 用户权限管控
> 更多功能就不意义介绍了,请到官网体验
## 问答

View File

@ -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` )
[![](imgs/dl.png)]()
*登录*
[![](imgs/zm.png)]()
*桌面*
[![](imgs/cd.png)]()
*开始*
[![](imgs/sz.png)]()
*设置*
[![](imgs/gj.png)]()
*小工具*
[![](imgs/cc.png)]()
*文件管理器*
[![](imgs/cs.png)]()
*上传*
[![](imgs/xj.png)]()
*新建*
[![](imgs/gx.png)]()
*共享*
[![](imgs/wdbj.png)]()
*文档编辑*
[![](imgs/tpbj.png)]()
*图片编辑*
[![](imgs/yy.png)]()
*在线音视频*
[![](imgs/yl.png)]()
*休闲娱乐*
[![](imgs/cj.png)]()
*应用商店*
[![](imgs/wp.png)]()
*网盘模式*
## 在线体验
<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
View 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>

View 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);
}
}
}

View 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;
}

View 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();
}

View File

@ -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();
}

View 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;
}

View File

@ -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;
}

View 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);
}
}
}

View File

@ -0,0 +1,7 @@
package cn.tenfell.webos.common.bean;
public interface StartInface {
static String start(){
return "19920117";
}
}

View 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

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 + "&params=" + 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;
}
}

View File

@ -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;
}
}

View File

@ -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()));
}
}

View 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;
}
}

View 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";
}

View 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;
}

View 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);
}

View 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();
}
}
}

View 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);
}
}

View 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;
}
}
}

View 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));
}
}

View 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) {
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View 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() {
}
}

View File

@ -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("此流不支持重置");
}
}

View 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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}

View 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";
}
}

View 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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -0,0 +1,12 @@
package cn.tenfell.webos.common.webdav.sf;
import java.security.Principal;
public interface ITransaction {
Principal getPrincipal();
HttpServletRequest getRequest();
HttpServletResponse getResponse();
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
};

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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'];
}
}
}

View File

@ -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, "获取成功");
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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));
}
}

View File

@ -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, "获取成功");
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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("暂未安装");
}
}

View File

@ -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());
}
}

View File

@ -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"));
}
}

View File

@ -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, "获取数量成功");
}
}

View 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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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