react-native-kookit 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ANDROID_SMB_LIBRARY_COMPARISON.md +170 -0
- package/SMB_CLIENT_STATIC_METHODS.md +175 -0
- package/SMB_HYBRID_IMPLEMENTATION.md +160 -0
- package/android/build.gradle +3 -1
- package/android/src/main/java/expo/modules/kookit/JcifsShareEnumerator.kt +75 -0
- package/android/src/main/java/expo/modules/kookit/SmbClient.kt +41 -6
- package/build/SmbClient.d.ts +40 -0
- package/build/SmbClient.d.ts.map +1 -1
- package/build/SmbClient.js +91 -0
- package/build/SmbClient.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Android SMB库对比分析与推荐
|
|
2
|
+
|
|
3
|
+
## 当前使用的库:SMBJ
|
|
4
|
+
|
|
5
|
+
### 优点
|
|
6
|
+
|
|
7
|
+
- ✅ 轻量级,专注SMB2/3协议
|
|
8
|
+
- ✅ 现代化设计,良好的性能
|
|
9
|
+
- ✅ 活跃维护,最新版本0.13.0 (2023)
|
|
10
|
+
- ✅ 支持SMB2/3的高级功能
|
|
11
|
+
|
|
12
|
+
### 缺点
|
|
13
|
+
|
|
14
|
+
- ❌ **不支持share枚举** - 这是我们当前的问题
|
|
15
|
+
- ❌ 没有NetShareEnum API
|
|
16
|
+
- ❌ 需要手动测试常见share名称
|
|
17
|
+
|
|
18
|
+
## 备选方案1:JCIFS (CodeLibs版本)
|
|
19
|
+
|
|
20
|
+
### 基本信息
|
|
21
|
+
|
|
22
|
+
```gradle
|
|
23
|
+
implementation 'org.codelibs:jcifs:2.1.39'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 优点
|
|
27
|
+
|
|
28
|
+
- ✅ **完整的SMB协议支持** (SMB1/2/3)
|
|
29
|
+
- ✅ **支持网络浏览** - 可能包括share枚举
|
|
30
|
+
- ✅ 成熟的库,有长期历史
|
|
31
|
+
- ✅ 支持Java 17+
|
|
32
|
+
- ✅ 现代化API设计
|
|
33
|
+
|
|
34
|
+
### 缺点
|
|
35
|
+
|
|
36
|
+
- ❌ 体积较大
|
|
37
|
+
- ❌ 需要额外学习新API
|
|
38
|
+
- ❌ 依赖Bouncy Castle加密库
|
|
39
|
+
|
|
40
|
+
### Share枚举支持
|
|
41
|
+
|
|
42
|
+
基于文档分析,JCIFS应该支持类似这样的用法:
|
|
43
|
+
|
|
44
|
+
```java
|
|
45
|
+
// 可能的API (需要验证)
|
|
46
|
+
SmbFile server = new SmbFile("smb://192.168.1.100/", context);
|
|
47
|
+
SmbFile[] shares = server.listFiles(); // 列出所有shares
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 备选方案2:JCIFS-NG (AgNO3版本)
|
|
51
|
+
|
|
52
|
+
### 基本信息
|
|
53
|
+
|
|
54
|
+
```gradle
|
|
55
|
+
implementation 'eu.agno3.jcifs:jcifs-ng:2.1.9'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 优点
|
|
59
|
+
|
|
60
|
+
- ✅ **支持SMB2支持** (2.02协议级别)
|
|
61
|
+
- ✅ 基于原始JCIFS但经过清理和改进
|
|
62
|
+
- ✅ 移除了全局状态,线程安全
|
|
63
|
+
|
|
64
|
+
### 缺点
|
|
65
|
+
|
|
66
|
+
- ❌ **明确弃用了server browsing功能** (2.1版本)
|
|
67
|
+
- ❌ 不再支持share枚举
|
|
68
|
+
- ❌ API与原始JCIFS有差异
|
|
69
|
+
|
|
70
|
+
## 备选方案3:SMB4J
|
|
71
|
+
|
|
72
|
+
### 基本信息
|
|
73
|
+
|
|
74
|
+
```gradle
|
|
75
|
+
implementation 'com.github.smb4j:smb4j:1.0.0'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 优点
|
|
79
|
+
|
|
80
|
+
- ✅ 现代化设计
|
|
81
|
+
- ✅ 支持SMB2/3
|
|
82
|
+
|
|
83
|
+
### 缺点
|
|
84
|
+
|
|
85
|
+
- ❌ 相对较新,生态不够成熟
|
|
86
|
+
- ❌ 文档不够完整
|
|
87
|
+
|
|
88
|
+
## 推荐方案:尝试JCIFS (CodeLibs)
|
|
89
|
+
|
|
90
|
+
### 理由
|
|
91
|
+
|
|
92
|
+
1. **完整功能支持** - 作为最全面的SMB库,很可能支持share枚举
|
|
93
|
+
2. **现代维护** - CodeLibs版本是维护最积极的JCIFS分支
|
|
94
|
+
3. **向后兼容** - 支持所有SMB版本,兼容性最好
|
|
95
|
+
|
|
96
|
+
### 实施建议
|
|
97
|
+
|
|
98
|
+
#### 第一步:添加依赖测试
|
|
99
|
+
|
|
100
|
+
```gradle
|
|
101
|
+
dependencies {
|
|
102
|
+
// 当前SMBJ
|
|
103
|
+
implementation 'com.hierynomus:smbj:0.13.0'
|
|
104
|
+
|
|
105
|
+
// 添加JCIFS测试
|
|
106
|
+
implementation 'org.codelibs:jcifs:2.1.39'
|
|
107
|
+
implementation 'org.slf4j:slf4j-nop:2.0.13'
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### 第二步:创建JCIFS实现的listShares
|
|
112
|
+
|
|
113
|
+
```kotlin
|
|
114
|
+
import org.codelibs.jcifs.smb.CIFSContext
|
|
115
|
+
import org.codelibs.jcifs.smb.context.SingletonContext
|
|
116
|
+
import org.codelibs.jcifs.smb.impl.SmbFile
|
|
117
|
+
import org.codelibs.jcifs.smb.impl.NtlmPasswordAuthenticator
|
|
118
|
+
|
|
119
|
+
class JcifsShareEnumerator {
|
|
120
|
+
fun listShares(host: String, username: String, password: String, domain: String? = null): List<String> {
|
|
121
|
+
val context = SingletonContext.getInstance()
|
|
122
|
+
val auth = NtlmPasswordAuthenticator(domain, username, password)
|
|
123
|
+
val authContext = context.withCredentials(auth)
|
|
124
|
+
|
|
125
|
+
return try {
|
|
126
|
+
val serverUrl = "smb://$host/"
|
|
127
|
+
val server = SmbFile(serverUrl, authContext)
|
|
128
|
+
|
|
129
|
+
server.listFiles()?.map { share ->
|
|
130
|
+
share.name.removeSuffix("/")
|
|
131
|
+
} ?: emptyList()
|
|
132
|
+
} catch (e: Exception) {
|
|
133
|
+
emptyList()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### 第三步:集成到现有API
|
|
140
|
+
|
|
141
|
+
```kotlin
|
|
142
|
+
// 在SmbClient.kt中添加
|
|
143
|
+
@Synchronized
|
|
144
|
+
fun listSharesJcifs(): List<String> {
|
|
145
|
+
// 使用JCIFS实现
|
|
146
|
+
return JcifsShareEnumerator().listShares(host, username, password, domain)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@Synchronized
|
|
150
|
+
fun listShares(): List<String> {
|
|
151
|
+
// 首先尝试JCIFS方法
|
|
152
|
+
val jcifsShares = listSharesJcifs()
|
|
153
|
+
if (jcifsShares.isNotEmpty()) {
|
|
154
|
+
return jcifsShares
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 如果失败,回退到当前的测试方法
|
|
158
|
+
return listSharesViaTesting()
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## 总结
|
|
163
|
+
|
|
164
|
+
**推荐采用JCIFS (CodeLibs版本)** 作为share枚举的补充方案:
|
|
165
|
+
|
|
166
|
+
1. **混合策略** - 保留SMBJ作为主要SMB客户端,仅使用JCIFS进行share枚举
|
|
167
|
+
2. **向后兼容** - 如果JCIFS枚举失败,回退到当前的测试方法
|
|
168
|
+
3. **最小改动** - 只需要添加一个枚举方法,不需要重写整个SMB客户端
|
|
169
|
+
|
|
170
|
+
这样既能解决share枚举问题,又能保持现有代码的稳定性。
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# SMB Client 静态方法使用示例
|
|
2
|
+
|
|
3
|
+
SmbClient新增了多个静态方法来管理多个客户端实例:
|
|
4
|
+
|
|
5
|
+
## 新增的静态方法
|
|
6
|
+
|
|
7
|
+
### 1. `SmbClient.disposeClient(clientId: string)`
|
|
8
|
+
|
|
9
|
+
销毁指定客户端ID的客户端实例
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// 销毁特定的客户端
|
|
13
|
+
const disposed = await SmbClient.disposeClient("my_client_id");
|
|
14
|
+
console.log("Client disposed:", disposed); // true/false
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### 2. `SmbClient.getClientStatus(clientId: string)`
|
|
18
|
+
|
|
19
|
+
获取指定客户端ID的状态信息
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// 获取客户端状态
|
|
23
|
+
const status = SmbClient.getClientStatus("my_client_id");
|
|
24
|
+
console.log("Client status:", status);
|
|
25
|
+
// 返回: {
|
|
26
|
+
// exists: true,
|
|
27
|
+
// isConnected: true,
|
|
28
|
+
// currentShare: 'C$',
|
|
29
|
+
// isDisposed: false
|
|
30
|
+
// }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 3. `SmbClient.getAllClientsStatus()`
|
|
34
|
+
|
|
35
|
+
获取所有活跃客户端的状态
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// 获取所有客户端状态
|
|
39
|
+
const allStatus = SmbClient.getAllClientsStatus();
|
|
40
|
+
allStatus.forEach((status, clientId) => {
|
|
41
|
+
console.log(`Client ${clientId}:`, status);
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 4. `SmbClient.getInstance(clientId: string)`
|
|
46
|
+
|
|
47
|
+
获取指定客户端ID的实例对象
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// 获取现有的客户端实例
|
|
51
|
+
const existingClient = SmbClient.getInstance("my_client_id");
|
|
52
|
+
if (existingClient) {
|
|
53
|
+
await existingClient.listFiles("/");
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 5. `SmbClient.disposeAllClients()`
|
|
58
|
+
|
|
59
|
+
销毁所有客户端实例
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// 清理所有客户端
|
|
63
|
+
const disposedCount = await SmbClient.disposeAllClients();
|
|
64
|
+
console.log(`Disposed ${disposedCount} clients`);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 使用示例
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import SmbClient from "react-native-kookit";
|
|
71
|
+
|
|
72
|
+
export class SmbManager {
|
|
73
|
+
private clients: Map<string, SmbClient> = new Map();
|
|
74
|
+
|
|
75
|
+
// 创建命名客户端
|
|
76
|
+
async createNamedClient(
|
|
77
|
+
name: string,
|
|
78
|
+
config: SmbConnectionConfig
|
|
79
|
+
): Promise<SmbClient> {
|
|
80
|
+
const client = await SmbClient.create(name);
|
|
81
|
+
await client.connect(config);
|
|
82
|
+
this.clients.set(name, client);
|
|
83
|
+
return client;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 检查客户端是否存在且连接
|
|
87
|
+
isClientReady(name: string): boolean {
|
|
88
|
+
const status = SmbClient.getClientStatus(name);
|
|
89
|
+
return status.exists && status.isConnected && !status.isDisposed;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 安全获取客户端
|
|
93
|
+
getClient(name: string): SmbClient | null {
|
|
94
|
+
if (this.isClientReady(name)) {
|
|
95
|
+
return SmbClient.getInstance(name);
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 销毁指定客户端
|
|
101
|
+
async removeClient(name: string): Promise<boolean> {
|
|
102
|
+
this.clients.delete(name);
|
|
103
|
+
return await SmbClient.disposeClient(name);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 获取所有客户端状态报告
|
|
107
|
+
getStatusReport(): Record<string, any> {
|
|
108
|
+
const report: Record<string, any> = {};
|
|
109
|
+
const allStatus = SmbClient.getAllClientsStatus();
|
|
110
|
+
|
|
111
|
+
allStatus.forEach((status, clientId) => {
|
|
112
|
+
report[clientId] = {
|
|
113
|
+
...status,
|
|
114
|
+
managed: this.clients.has(clientId),
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return report;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 清理所有资源
|
|
122
|
+
async cleanup(): Promise<void> {
|
|
123
|
+
this.clients.clear();
|
|
124
|
+
await SmbClient.disposeAllClients();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 使用示例
|
|
129
|
+
const smbManager = new SmbManager();
|
|
130
|
+
|
|
131
|
+
// 创建多个客户端
|
|
132
|
+
const client1 = await smbManager.createNamedClient("server1", {
|
|
133
|
+
host: "192.168.1.100",
|
|
134
|
+
username: "user1",
|
|
135
|
+
password: "pass1",
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const client2 = await smbManager.createNamedClient("server2", {
|
|
139
|
+
host: "192.168.1.101",
|
|
140
|
+
username: "user2",
|
|
141
|
+
password: "pass2",
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// 检查状态
|
|
145
|
+
console.log("Status report:", smbManager.getStatusReport());
|
|
146
|
+
|
|
147
|
+
// 使用特定客户端
|
|
148
|
+
const client = smbManager.getClient("server1");
|
|
149
|
+
if (client) {
|
|
150
|
+
const files = await client.listFiles("/");
|
|
151
|
+
console.log("Files:", files);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 清理资源
|
|
155
|
+
await smbManager.cleanup();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## 类型定义
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
export interface SmbClientStatus {
|
|
162
|
+
exists: boolean;
|
|
163
|
+
isConnected: boolean;
|
|
164
|
+
currentShare: string | null;
|
|
165
|
+
isDisposed: boolean;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface SmbClientInfo {
|
|
169
|
+
isConnected: boolean;
|
|
170
|
+
currentShare: string | null;
|
|
171
|
+
isDisposed: boolean;
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
这些静态方法使得SmbClient的多实例管理变得更加简单和安全。
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# SMB 混合实现方案
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
为了解决SMB share枚举的问题,我们实现了一个混合方案:
|
|
6
|
+
|
|
7
|
+
- 主要SMB客户端使用SMBJ(可靠性高)
|
|
8
|
+
- Share枚举使用JCIFS(功能更完整)
|
|
9
|
+
|
|
10
|
+
## 实现细节
|
|
11
|
+
|
|
12
|
+
### 1. 依赖库
|
|
13
|
+
|
|
14
|
+
- **SMBJ**: 主要SMB客户端操作(连接、文件操作等)
|
|
15
|
+
- **JCIFS**: Share枚举和发现(org.codelibs:jcifs:2.1.39)
|
|
16
|
+
|
|
17
|
+
### 2. 架构设计
|
|
18
|
+
|
|
19
|
+
#### SmbClient.kt 主类
|
|
20
|
+
|
|
21
|
+
- 保留SMBJ作为主要SMB客户端
|
|
22
|
+
- 添加JcifsShareEnumerator辅助类
|
|
23
|
+
- 存储连接凭据以供JCIFS使用
|
|
24
|
+
|
|
25
|
+
#### JcifsShareEnumerator.kt 辅助类
|
|
26
|
+
|
|
27
|
+
- 专门用于Share枚举
|
|
28
|
+
- 使用JCIFS进行完整的Share发现
|
|
29
|
+
- 提供连接测试功能
|
|
30
|
+
|
|
31
|
+
### 3. listShares() 方法流程
|
|
32
|
+
|
|
33
|
+
```kotlin
|
|
34
|
+
fun listShares(): List<String> {
|
|
35
|
+
// 1. 尝试使用JCIFS进行完整Share枚举
|
|
36
|
+
try {
|
|
37
|
+
val shares = jcifsEnumerator.listShares(host, username, password, domain)
|
|
38
|
+
if (shares.isNotEmpty()) {
|
|
39
|
+
return shares
|
|
40
|
+
}
|
|
41
|
+
} catch (e: Exception) {
|
|
42
|
+
// 继续到fallback方法
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 2. Fallback: 使用SMBJ测试常见Share名称
|
|
46
|
+
try {
|
|
47
|
+
// 测试常见Windows和用户定义的Share
|
|
48
|
+
val commonShares = listOf("C$", "D$", "shared", "public", ...)
|
|
49
|
+
// 返回可访问的Share
|
|
50
|
+
} catch (e: Exception) {
|
|
51
|
+
// 最终fallback: 返回常见Share名称
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 4. 优势
|
|
57
|
+
|
|
58
|
+
1. **可靠性**: SMBJ作为主要客户端,稳定性高
|
|
59
|
+
2. **完整性**: JCIFS提供完整的Share枚举
|
|
60
|
+
3. **容错性**: 多层fallback机制
|
|
61
|
+
4. **性能**: JCIFS仅用于Share枚举,不影响主要操作
|
|
62
|
+
|
|
63
|
+
### 5. Share参数说明
|
|
64
|
+
|
|
65
|
+
#### 对于列出SMB根目录(Share根目录):
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// 连接到具体Share
|
|
69
|
+
await smbClient.connect({
|
|
70
|
+
host: "192.168.1.100",
|
|
71
|
+
username: "user",
|
|
72
|
+
password: "pass",
|
|
73
|
+
share: "C$", // 指定具体的Share
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// 列出Share根目录
|
|
77
|
+
const files = await smbClient.listFiles(""); // 空字符串表示Share根目录
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### 对于列出所有可用Share:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// 只需要认证,不指定share
|
|
84
|
+
await smbClient.connect({
|
|
85
|
+
host: "192.168.1.100",
|
|
86
|
+
username: "user",
|
|
87
|
+
password: "pass",
|
|
88
|
+
// 不指定share参数
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// 获取所有可用Share
|
|
92
|
+
const shares = await smbClient.listShares();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 6. 常见Share类型
|
|
96
|
+
|
|
97
|
+
#### Windows系统Share:
|
|
98
|
+
|
|
99
|
+
- `C$`, `D$`, `E$`: 驱动器共享(需要管理员权限)
|
|
100
|
+
- `ADMIN$`: 管理共享
|
|
101
|
+
- `IPC$`: 进程间通信共享
|
|
102
|
+
|
|
103
|
+
#### 用户定义Share:
|
|
104
|
+
|
|
105
|
+
- `shared`, `public`, `data`: 常见的用户共享文件夹
|
|
106
|
+
- `home`, `Users`: 用户目录共享
|
|
107
|
+
- `documents`, `media`: 媒体和文档共享
|
|
108
|
+
|
|
109
|
+
## 使用示例
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const smbClient = new SmbClient();
|
|
113
|
+
|
|
114
|
+
// 1. 连接到服务器
|
|
115
|
+
await smbClient.connect({
|
|
116
|
+
host: "192.168.1.100",
|
|
117
|
+
username: "myuser",
|
|
118
|
+
password: "mypass",
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// 2. 获取所有可用Share
|
|
122
|
+
const shares = await smbClient.listShares();
|
|
123
|
+
console.log("Available shares:", shares);
|
|
124
|
+
// 输出: ['C$', 'shared', 'public', 'documents']
|
|
125
|
+
|
|
126
|
+
// 3. 连接到具体Share
|
|
127
|
+
await smbClient.openShare("shared");
|
|
128
|
+
|
|
129
|
+
// 4. 列出Share根目录
|
|
130
|
+
const files = await smbClient.listFiles("");
|
|
131
|
+
|
|
132
|
+
// 5. 列出子目录
|
|
133
|
+
const subFiles = await smbClient.listFiles("subfolder");
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## 故障排除
|
|
137
|
+
|
|
138
|
+
### 如果JCIFS枚举失败:
|
|
139
|
+
|
|
140
|
+
- 自动fallback到SMBJ测试方法
|
|
141
|
+
- 返回测试可访问的常见Share
|
|
142
|
+
|
|
143
|
+
### 如果所有方法都失败:
|
|
144
|
+
|
|
145
|
+
- 返回最常见的Share名称列表
|
|
146
|
+
- 用户可以手动尝试连接
|
|
147
|
+
|
|
148
|
+
## 技术细节
|
|
149
|
+
|
|
150
|
+
### JCIFS配置:
|
|
151
|
+
|
|
152
|
+
- 使用NtlmPasswordAuthenticator进行认证
|
|
153
|
+
- 通过SmbFile访问服务器根目录
|
|
154
|
+
- 枚举所有可用的Share
|
|
155
|
+
|
|
156
|
+
### SMBJ fallback:
|
|
157
|
+
|
|
158
|
+
- 测试常见Share名称的可访问性
|
|
159
|
+
- 短暂连接后立即关闭以测试可用性
|
|
160
|
+
- 收集所有可访问的Share名称
|
package/android/build.gradle
CHANGED
|
@@ -43,8 +43,10 @@ android {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
dependencies {
|
|
46
|
-
// SMB client library
|
|
46
|
+
// SMB client library (primary)
|
|
47
47
|
implementation 'com.hierynomus:smbj:0.13.0'
|
|
48
|
+
// JCIFS library for share enumeration
|
|
49
|
+
implementation 'org.codelibs:jcifs:2.1.39'
|
|
48
50
|
// Suppress SLF4J binding warnings
|
|
49
51
|
implementation 'org.slf4j:slf4j-nop:2.0.13'
|
|
50
52
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
package expo.modules.kookit
|
|
2
|
+
|
|
3
|
+
import jcifs.CIFSContext
|
|
4
|
+
import jcifs.context.SingletonContext
|
|
5
|
+
import jcifs.smb.SmbFile
|
|
6
|
+
import jcifs.smb.NtlmPasswordAuthenticator
|
|
7
|
+
import java.util.*
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* JCIFS-based SMB share enumerator
|
|
11
|
+
* Used as a supplement to SMBJ for share enumeration functionality
|
|
12
|
+
*/
|
|
13
|
+
class JcifsShareEnumerator {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* List all available shares on an SMB server using JCIFS
|
|
17
|
+
* @param host SMB server hostname or IP
|
|
18
|
+
* @param username Username for authentication
|
|
19
|
+
* @param password Password for authentication
|
|
20
|
+
* @param domain Domain name (optional)
|
|
21
|
+
* @return List of share names, empty if enumeration fails
|
|
22
|
+
*/
|
|
23
|
+
fun listShares(host: String, username: String, password: String, domain: String? = null): List<String> {
|
|
24
|
+
return try {
|
|
25
|
+
// Create JCIFS context with authentication
|
|
26
|
+
val context = SingletonContext.getInstance()
|
|
27
|
+
val auth = NtlmPasswordAuthenticator(domain, username, password)
|
|
28
|
+
val authContext = context.withCredentials(auth)
|
|
29
|
+
|
|
30
|
+
// Connect to server root to enumerate shares
|
|
31
|
+
val serverUrl = "smb://$host/"
|
|
32
|
+
val server = SmbFile(serverUrl, authContext)
|
|
33
|
+
|
|
34
|
+
// List all shares
|
|
35
|
+
val shares = server.listFiles()
|
|
36
|
+
shares?.mapNotNull { shareFile ->
|
|
37
|
+
val shareName = shareFile.name.removeSuffix("/")
|
|
38
|
+
// Filter out hidden/administrative shares if needed
|
|
39
|
+
if (shareName.isNotBlank() && !shareName.startsWith(".")) {
|
|
40
|
+
shareName
|
|
41
|
+
} else null
|
|
42
|
+
} ?: emptyList()
|
|
43
|
+
|
|
44
|
+
} catch (e: Exception) {
|
|
45
|
+
// If JCIFS enumeration fails, return empty list
|
|
46
|
+
// The caller can fall back to other methods
|
|
47
|
+
emptyList()
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Test if JCIFS can connect to the server
|
|
53
|
+
* @param host SMB server hostname or IP
|
|
54
|
+
* @param username Username for authentication
|
|
55
|
+
* @param password Password for authentication
|
|
56
|
+
* @param domain Domain name (optional)
|
|
57
|
+
* @return true if connection successful, false otherwise
|
|
58
|
+
*/
|
|
59
|
+
fun testConnection(host: String, username: String, password: String, domain: String? = null): Boolean {
|
|
60
|
+
return try {
|
|
61
|
+
val context = SingletonContext.getInstance()
|
|
62
|
+
val auth = NtlmPasswordAuthenticator(domain, username, password)
|
|
63
|
+
val authContext = context.withCredentials(auth)
|
|
64
|
+
|
|
65
|
+
val serverUrl = "smb://$host/"
|
|
66
|
+
val server = SmbFile(serverUrl, authContext)
|
|
67
|
+
|
|
68
|
+
// Test if we can access the server
|
|
69
|
+
server.exists()
|
|
70
|
+
true
|
|
71
|
+
} catch (e: Exception) {
|
|
72
|
+
false
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -41,11 +41,23 @@ class SmbClient {
|
|
|
41
41
|
private var session: Session? = null
|
|
42
42
|
private var share: DiskShare? = null
|
|
43
43
|
private var connected = false
|
|
44
|
+
private val jcifsEnumerator = JcifsShareEnumerator()
|
|
45
|
+
private var lastHost: String? = null
|
|
46
|
+
private var lastUsername: String? = null
|
|
47
|
+
private var lastPassword: String? = null
|
|
48
|
+
private var lastDomain: String? = null
|
|
44
49
|
|
|
45
50
|
@Synchronized
|
|
46
51
|
@Throws(IOException::class)
|
|
47
52
|
fun connect(config: SmbConnectionConfig) {
|
|
48
53
|
disconnect()
|
|
54
|
+
|
|
55
|
+
// Store connection parameters for share enumeration
|
|
56
|
+
lastHost = config.host
|
|
57
|
+
lastUsername = config.username
|
|
58
|
+
lastPassword = config.password
|
|
59
|
+
lastDomain = config.domain
|
|
60
|
+
|
|
49
61
|
val conn = smb.connect(config.host)
|
|
50
62
|
connection = conn
|
|
51
63
|
val auth = AuthenticationContext(
|
|
@@ -70,9 +82,26 @@ class SmbClient {
|
|
|
70
82
|
@Synchronized
|
|
71
83
|
fun listShares(): List<String> {
|
|
72
84
|
val s = session ?: throw IOException("Not authenticated")
|
|
85
|
+
|
|
86
|
+
// First try JCIFS for more complete share enumeration
|
|
87
|
+
if (lastHost != null && lastUsername != null && lastPassword != null) {
|
|
88
|
+
try {
|
|
89
|
+
val shares = jcifsEnumerator.listShares(
|
|
90
|
+
lastHost!!,
|
|
91
|
+
lastUsername!!,
|
|
92
|
+
lastPassword!!,
|
|
93
|
+
lastDomain ?: ""
|
|
94
|
+
)
|
|
95
|
+
if (shares.isNotEmpty()) {
|
|
96
|
+
return shares
|
|
97
|
+
}
|
|
98
|
+
} catch (e: Exception) {
|
|
99
|
+
// JCIFS failed, fall back to SMBJ testing approach
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
73
103
|
return try {
|
|
74
|
-
// SMBJ
|
|
75
|
-
// We implement a practical approach by testing common share names
|
|
104
|
+
// Fallback: SMBJ testing approach
|
|
76
105
|
val availableShares = mutableListOf<String>()
|
|
77
106
|
|
|
78
107
|
// Common Windows shares
|
|
@@ -95,12 +124,12 @@ class SmbClient {
|
|
|
95
124
|
|
|
96
125
|
// If no shares found, suggest some common ones
|
|
97
126
|
if (availableShares.isEmpty()) {
|
|
98
|
-
|
|
127
|
+
listOf("C$", "shared", "public")
|
|
128
|
+
} else {
|
|
129
|
+
availableShares
|
|
99
130
|
}
|
|
100
|
-
|
|
101
|
-
availableShares
|
|
102
131
|
} catch (e: Exception) {
|
|
103
|
-
//
|
|
132
|
+
// Final fallback to common share names if all enumeration methods fail
|
|
104
133
|
listOf("C$", "shared", "public", "data")
|
|
105
134
|
}
|
|
106
135
|
}
|
|
@@ -114,6 +143,12 @@ class SmbClient {
|
|
|
114
143
|
session = null
|
|
115
144
|
connection = null
|
|
116
145
|
connected = false
|
|
146
|
+
|
|
147
|
+
// Clear stored credentials
|
|
148
|
+
lastHost = null
|
|
149
|
+
lastUsername = null
|
|
150
|
+
lastPassword = null
|
|
151
|
+
lastDomain = null
|
|
117
152
|
}
|
|
118
153
|
|
|
119
154
|
@Synchronized
|
package/build/SmbClient.d.ts
CHANGED
|
@@ -8,6 +8,17 @@ export interface SmbClientEventHandlers {
|
|
|
8
8
|
onComplete?: () => void;
|
|
9
9
|
onError?: (error: Error) => void;
|
|
10
10
|
}
|
|
11
|
+
export interface SmbClientStatus {
|
|
12
|
+
exists: boolean;
|
|
13
|
+
isConnected: boolean;
|
|
14
|
+
currentShare: string | null;
|
|
15
|
+
isDisposed: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface SmbClientInfo {
|
|
18
|
+
isConnected: boolean;
|
|
19
|
+
currentShare: string | null;
|
|
20
|
+
isDisposed: boolean;
|
|
21
|
+
}
|
|
11
22
|
export declare class SmbClient {
|
|
12
23
|
private clientId;
|
|
13
24
|
private isDisposed;
|
|
@@ -15,6 +26,7 @@ export declare class SmbClient {
|
|
|
15
26
|
private eventSubscriptions;
|
|
16
27
|
private _isConnected;
|
|
17
28
|
private _currentShare;
|
|
29
|
+
private static instances;
|
|
18
30
|
constructor(clientId?: string);
|
|
19
31
|
private setupEventListeners;
|
|
20
32
|
/**
|
|
@@ -121,6 +133,34 @@ export declare class SmbClient {
|
|
|
121
133
|
* @returns New SMB client instance
|
|
122
134
|
*/
|
|
123
135
|
static create(clientId?: string): Promise<SmbClient>;
|
|
136
|
+
/**
|
|
137
|
+
* Dispose a specific SMB client by client ID
|
|
138
|
+
* @param clientId Client ID to dispose
|
|
139
|
+
* @returns true if client was found and disposed, false otherwise
|
|
140
|
+
*/
|
|
141
|
+
static disposeClient(clientId: string): Promise<boolean>;
|
|
142
|
+
/**
|
|
143
|
+
* Get the status of a specific SMB client by client ID
|
|
144
|
+
* @param clientId Client ID to check
|
|
145
|
+
* @returns Client status or null if not found
|
|
146
|
+
*/
|
|
147
|
+
static getClientStatus(clientId: string): SmbClientStatus;
|
|
148
|
+
/**
|
|
149
|
+
* Get all active client IDs and their status
|
|
150
|
+
* @returns Map of client IDs to their status
|
|
151
|
+
*/
|
|
152
|
+
static getAllClientsStatus(): Map<string, SmbClientInfo>;
|
|
153
|
+
/**
|
|
154
|
+
* Get a specific SMB client instance by client ID
|
|
155
|
+
* @param clientId Client ID to retrieve
|
|
156
|
+
* @returns SMB client instance or null if not found
|
|
157
|
+
*/
|
|
158
|
+
static getInstance(clientId: string): SmbClient | null;
|
|
159
|
+
/**
|
|
160
|
+
* Dispose all SMB clients
|
|
161
|
+
* @returns Number of clients disposed
|
|
162
|
+
*/
|
|
163
|
+
static disposeAllClients(): Promise<number>;
|
|
124
164
|
}
|
|
125
165
|
export default SmbClient;
|
|
126
166
|
//# sourceMappingURL=SmbClient.d.ts.map
|
package/build/SmbClient.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmbClient.d.ts","sourceRoot":"","sources":["../src/SmbClient.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,WAAW,EAIZ,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,KAAK,IAAI,CAAC;IACX,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,kBAAkB,CAAqC;IAC/D,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,aAAa,CAAuB;
|
|
1
|
+
{"version":3,"file":"SmbClient.d.ts","sourceRoot":"","sources":["../src/SmbClient.ts"],"names":[],"mappings":"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"}
|
package/build/SmbClient.js
CHANGED
|
@@ -6,10 +6,14 @@ export class SmbClient {
|
|
|
6
6
|
eventSubscriptions = [];
|
|
7
7
|
_isConnected = false;
|
|
8
8
|
_currentShare = null;
|
|
9
|
+
// 静态实例管理
|
|
10
|
+
static instances = new Map();
|
|
9
11
|
constructor(clientId) {
|
|
10
12
|
this.clientId =
|
|
11
13
|
clientId ||
|
|
12
14
|
`smb_client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
15
|
+
// 将实例添加到静态管理器
|
|
16
|
+
SmbClient.instances.set(this.clientId, this);
|
|
13
17
|
this.setupEventListeners();
|
|
14
18
|
}
|
|
15
19
|
setupEventListeners() {
|
|
@@ -225,6 +229,8 @@ export class SmbClient {
|
|
|
225
229
|
this.isDisposed = true;
|
|
226
230
|
this._isConnected = false;
|
|
227
231
|
this._currentShare = null;
|
|
232
|
+
// 从静态实例管理器中移除
|
|
233
|
+
SmbClient.instances.delete(this.clientId);
|
|
228
234
|
}
|
|
229
235
|
/**
|
|
230
236
|
* List all SMB clients
|
|
@@ -242,6 +248,91 @@ export class SmbClient {
|
|
|
242
248
|
await client.initialize();
|
|
243
249
|
return client;
|
|
244
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Dispose a specific SMB client by client ID
|
|
253
|
+
* @param clientId Client ID to dispose
|
|
254
|
+
* @returns true if client was found and disposed, false otherwise
|
|
255
|
+
*/
|
|
256
|
+
static async disposeClient(clientId) {
|
|
257
|
+
const client = SmbClient.instances.get(clientId);
|
|
258
|
+
if (client) {
|
|
259
|
+
await client.dispose();
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
// Try to dispose on native side even if not in our instances map
|
|
263
|
+
try {
|
|
264
|
+
await ReactNativeKookitModule.disposeSmbClient(clientId);
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get the status of a specific SMB client by client ID
|
|
273
|
+
* @param clientId Client ID to check
|
|
274
|
+
* @returns Client status or null if not found
|
|
275
|
+
*/
|
|
276
|
+
static getClientStatus(clientId) {
|
|
277
|
+
const client = SmbClient.instances.get(clientId);
|
|
278
|
+
if (client) {
|
|
279
|
+
return {
|
|
280
|
+
exists: true,
|
|
281
|
+
isConnected: client._isConnected,
|
|
282
|
+
currentShare: client._currentShare,
|
|
283
|
+
isDisposed: client.isDisposed,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
exists: false,
|
|
288
|
+
isConnected: false,
|
|
289
|
+
currentShare: null,
|
|
290
|
+
isDisposed: true,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Get all active client IDs and their status
|
|
295
|
+
* @returns Map of client IDs to their status
|
|
296
|
+
*/
|
|
297
|
+
static getAllClientsStatus() {
|
|
298
|
+
const statusMap = new Map();
|
|
299
|
+
SmbClient.instances.forEach((client, clientId) => {
|
|
300
|
+
statusMap.set(clientId, {
|
|
301
|
+
isConnected: client._isConnected,
|
|
302
|
+
currentShare: client._currentShare,
|
|
303
|
+
isDisposed: client.isDisposed,
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
return statusMap;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Get a specific SMB client instance by client ID
|
|
310
|
+
* @param clientId Client ID to retrieve
|
|
311
|
+
* @returns SMB client instance or null if not found
|
|
312
|
+
*/
|
|
313
|
+
static getInstance(clientId) {
|
|
314
|
+
return SmbClient.instances.get(clientId) || null;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Dispose all SMB clients
|
|
318
|
+
* @returns Number of clients disposed
|
|
319
|
+
*/
|
|
320
|
+
static async disposeAllClients() {
|
|
321
|
+
const clientIds = Array.from(SmbClient.instances.keys());
|
|
322
|
+
let disposedCount = 0;
|
|
323
|
+
for (const clientId of clientIds) {
|
|
324
|
+
try {
|
|
325
|
+
await SmbClient.disposeClient(clientId);
|
|
326
|
+
disposedCount++;
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// Continue with next client even if disposal fails
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// Clear the instances map
|
|
333
|
+
SmbClient.instances.clear();
|
|
334
|
+
return disposedCount;
|
|
335
|
+
}
|
|
245
336
|
}
|
|
246
337
|
export default SmbClient;
|
|
247
338
|
//# sourceMappingURL=SmbClient.js.map
|
package/build/SmbClient.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmbClient.js","sourceRoot":"","sources":["../src/SmbClient.ts"],"names":[],"mappings":"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","sourcesContent":["import ReactNativeKookitModule from \"./ReactNativeKookitModule\";\nimport type {\n SmbConnectionConfig,\n SmbFileInfo,\n SmbProgressEvent,\n SmbCompleteEvent,\n SmbErrorEvent,\n} from \"./ReactNativeKookit.types\";\n\nexport interface SmbClientEventHandlers {\n onProgress?: (progress: {\n transferred: number;\n total: number;\n percentage: number;\n }) => void;\n onComplete?: () => void;\n onError?: (error: Error) => void;\n}\n\nexport class SmbClient {\n private clientId: string;\n private isDisposed: boolean = false;\n private eventHandlers: SmbClientEventHandlers = {};\n private eventSubscriptions: Array<{ remove: () => void }> = [];\n private _isConnected: boolean = false;\n private _currentShare: string | null = null;\n\n constructor(clientId?: string) {\n this.clientId =\n clientId ||\n `smb_client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n this.setupEventListeners();\n }\n\n private setupEventListeners() {\n const progressSub = ReactNativeKookitModule.addListener(\n \"onSmbProgress\",\n (event: SmbProgressEvent) => {\n if (event.clientId === this.clientId && this.eventHandlers.onProgress) {\n this.eventHandlers.onProgress({\n transferred: event.transferred,\n total: event.total,\n percentage: event.percentage,\n });\n }\n }\n );\n\n const completeSub = ReactNativeKookitModule.addListener(\n \"onSmbComplete\",\n (event: SmbCompleteEvent) => {\n if (event.clientId === this.clientId && this.eventHandlers.onComplete) {\n this.eventHandlers.onComplete();\n }\n }\n );\n\n const errorSub = ReactNativeKookitModule.addListener(\n \"onSmbError\",\n (event: SmbErrorEvent) => {\n if (event.clientId === this.clientId && this.eventHandlers.onError) {\n this.eventHandlers.onError(new Error(event.error));\n }\n }\n );\n\n this.eventSubscriptions = [progressSub, completeSub, errorSub];\n }\n\n /**\n * Set event handlers for SMB operations\n */\n setEventHandlers(handlers: SmbClientEventHandlers): void {\n this.eventHandlers = { ...handlers };\n }\n\n /**\n * Initialize the SMB client\n */\n private async initialize(): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n await ReactNativeKookitModule.createSmbClient(this.clientId);\n }\n\n /**\n * Connect to SMB server\n * @param config Connection configuration\n */\n async connect(config: SmbConnectionConfig): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n await ReactNativeKookitModule.smbClientConnect(this.clientId, config);\n this._isConnected = true;\n this._currentShare = config.share || null;\n }\n\n /**\n * Connect to a specific share\n * @param shareName Name of the share to connect to\n */\n async connectShare(shareName: string): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n\n await ReactNativeKookitModule.smbClientConnectShare(\n this.clientId,\n shareName\n );\n this._currentShare = shareName;\n }\n\n /**\n * List all available shares on the SMB server\n * @returns Array of share names\n */\n async listShares(): Promise<string[]> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n\n return ReactNativeKookitModule.smbClientListShares(this.clientId);\n }\n\n /**\n * Disconnect from SMB server\n */\n async disconnect(): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n await ReactNativeKookitModule.smbClientDisconnect(this.clientId);\n this._isConnected = false;\n this._currentShare = null;\n }\n\n /**\n * List files and directories in the specified path\n * @param path Path to list (empty string for share root)\n * @returns Array of file information\n */\n async list(path?: string): Promise<SmbFileInfo[]> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n if (!this._currentShare) throw new Error(\"No share connected\");\n\n return ReactNativeKookitModule.smbClientList(this.clientId, path);\n }\n\n /**\n * Download a file from SMB server\n * @param remotePath Path to the remote file\n * @param localPath Local path to save the file\n */\n async download(remotePath: string, localPath: string): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n if (!this._currentShare) throw new Error(\"No share connected\");\n\n await ReactNativeKookitModule.smbClientDownload(\n this.clientId,\n remotePath,\n localPath\n );\n }\n\n /**\n * Upload a file to SMB server\n * @param localPath Local path of the file to upload\n * @param remotePath Remote path to save the file\n */\n async upload(localPath: string, remotePath: string): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n if (!this._currentShare) throw new Error(\"No share connected\");\n\n await ReactNativeKookitModule.smbClientUpload(\n this.clientId,\n localPath,\n remotePath\n );\n }\n\n /**\n * Delete a file or directory from SMB server\n * @param remotePath Path to the file or directory to delete\n * @param isDirectory Whether the path is a directory\n */\n async delete(\n remotePath: string,\n isDirectory: boolean = false\n ): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n if (!this._currentShare) throw new Error(\"No share connected\");\n\n await ReactNativeKookitModule.smbClientDelete(\n this.clientId,\n remotePath,\n isDirectory\n );\n }\n\n /**\n * Create a directory on SMB server\n * @param remotePath Path of the directory to create\n */\n async createDirectory(remotePath: string): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n if (!this._currentShare) throw new Error(\"No share connected\");\n\n await ReactNativeKookitModule.smbClientCreateDirectory(\n this.clientId,\n remotePath\n );\n }\n\n /**\n * Get the current connection status\n */\n async getStatus(): Promise<{\n clientId: string;\n exists: boolean;\n connected: boolean;\n currentShare: string | null;\n }> {\n const status = await ReactNativeKookitModule.getSmbClientStatus(\n this.clientId\n );\n return {\n clientId: this.clientId,\n currentShare: this._currentShare,\n ...status,\n };\n }\n\n /**\n * Get the client ID\n */\n getClientId(): string {\n return this.clientId;\n }\n\n /**\n * Get the current share name\n */\n getCurrentShare(): string | null {\n return this._currentShare;\n }\n\n /**\n * Check if connected to SMB server\n */\n async isConnected(): Promise<boolean> {\n if (this.isDisposed) return false;\n try {\n const status = await this.getStatus();\n return status.exists && status.connected;\n } catch {\n return false;\n }\n }\n\n /**\n * Check if connected to SMB server (synchronous)\n */\n isConnectedSync(): boolean {\n return this._isConnected;\n }\n\n /**\n * Check if connected to a share\n */\n hasShareConnected(): boolean {\n return this._isConnected && this._currentShare !== null;\n }\n\n /**\n * Dispose the SMB client and clean up resources\n */\n async dispose(): Promise<void> {\n if (this.isDisposed) return;\n try {\n await ReactNativeKookitModule.disposeSmbClient(this.clientId);\n } catch {}\n this.eventSubscriptions.forEach((s) => s.remove());\n this.eventSubscriptions = [];\n this.isDisposed = true;\n this._isConnected = false;\n this._currentShare = null;\n }\n\n /**\n * List all SMB clients\n */\n static async listClients(): Promise<{\n clients: Record<string, { connected: boolean }>;\n count: number;\n }> {\n return ReactNativeKookitModule.listSmbClients();\n }\n\n /**\n * Create a new SMB client instance\n * @param clientId Optional client ID\n * @returns New SMB client instance\n */\n static async create(clientId?: string): Promise<SmbClient> {\n const client = new SmbClient(clientId);\n await client.initialize();\n return client;\n }\n}\n\nexport default SmbClient;\n"]}
|
|
1
|
+
{"version":3,"file":"SmbClient.js","sourceRoot":"","sources":["../src/SmbClient.ts"],"names":[],"mappings":"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","sourcesContent":["import ReactNativeKookitModule from \"./ReactNativeKookitModule\";\nimport type {\n SmbConnectionConfig,\n SmbFileInfo,\n SmbProgressEvent,\n SmbCompleteEvent,\n SmbErrorEvent,\n} from \"./ReactNativeKookit.types\";\n\nexport interface SmbClientEventHandlers {\n onProgress?: (progress: {\n transferred: number;\n total: number;\n percentage: number;\n }) => void;\n onComplete?: () => void;\n onError?: (error: Error) => void;\n}\n\nexport interface SmbClientStatus {\n exists: boolean;\n isConnected: boolean;\n currentShare: string | null;\n isDisposed: boolean;\n}\n\nexport interface SmbClientInfo {\n isConnected: boolean;\n currentShare: string | null;\n isDisposed: boolean;\n}\n\nexport class SmbClient {\n private clientId: string;\n private isDisposed: boolean = false;\n private eventHandlers: SmbClientEventHandlers = {};\n private eventSubscriptions: Array<{ remove: () => void }> = [];\n private _isConnected: boolean = false;\n private _currentShare: string | null = null;\n\n // 静态实例管理\n private static instances: Map<string, SmbClient> = new Map();\n\n constructor(clientId?: string) {\n this.clientId =\n clientId ||\n `smb_client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n\n // 将实例添加到静态管理器\n SmbClient.instances.set(this.clientId, this);\n\n this.setupEventListeners();\n }\n\n private setupEventListeners() {\n const progressSub = ReactNativeKookitModule.addListener(\n \"onSmbProgress\",\n (event: SmbProgressEvent) => {\n if (event.clientId === this.clientId && this.eventHandlers.onProgress) {\n this.eventHandlers.onProgress({\n transferred: event.transferred,\n total: event.total,\n percentage: event.percentage,\n });\n }\n }\n );\n\n const completeSub = ReactNativeKookitModule.addListener(\n \"onSmbComplete\",\n (event: SmbCompleteEvent) => {\n if (event.clientId === this.clientId && this.eventHandlers.onComplete) {\n this.eventHandlers.onComplete();\n }\n }\n );\n\n const errorSub = ReactNativeKookitModule.addListener(\n \"onSmbError\",\n (event: SmbErrorEvent) => {\n if (event.clientId === this.clientId && this.eventHandlers.onError) {\n this.eventHandlers.onError(new Error(event.error));\n }\n }\n );\n\n this.eventSubscriptions = [progressSub, completeSub, errorSub];\n }\n\n /**\n * Set event handlers for SMB operations\n */\n setEventHandlers(handlers: SmbClientEventHandlers): void {\n this.eventHandlers = { ...handlers };\n }\n\n /**\n * Initialize the SMB client\n */\n private async initialize(): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n await ReactNativeKookitModule.createSmbClient(this.clientId);\n }\n\n /**\n * Connect to SMB server\n * @param config Connection configuration\n */\n async connect(config: SmbConnectionConfig): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n await ReactNativeKookitModule.smbClientConnect(this.clientId, config);\n this._isConnected = true;\n this._currentShare = config.share || null;\n }\n\n /**\n * Connect to a specific share\n * @param shareName Name of the share to connect to\n */\n async connectShare(shareName: string): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n\n await ReactNativeKookitModule.smbClientConnectShare(\n this.clientId,\n shareName\n );\n this._currentShare = shareName;\n }\n\n /**\n * List all available shares on the SMB server\n * @returns Array of share names\n */\n async listShares(): Promise<string[]> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n\n return ReactNativeKookitModule.smbClientListShares(this.clientId);\n }\n\n /**\n * Disconnect from SMB server\n */\n async disconnect(): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n await ReactNativeKookitModule.smbClientDisconnect(this.clientId);\n this._isConnected = false;\n this._currentShare = null;\n }\n\n /**\n * List files and directories in the specified path\n * @param path Path to list (empty string for share root)\n * @returns Array of file information\n */\n async list(path?: string): Promise<SmbFileInfo[]> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n if (!this._currentShare) throw new Error(\"No share connected\");\n\n return ReactNativeKookitModule.smbClientList(this.clientId, path);\n }\n\n /**\n * Download a file from SMB server\n * @param remotePath Path to the remote file\n * @param localPath Local path to save the file\n */\n async download(remotePath: string, localPath: string): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n if (!this._currentShare) throw new Error(\"No share connected\");\n\n await ReactNativeKookitModule.smbClientDownload(\n this.clientId,\n remotePath,\n localPath\n );\n }\n\n /**\n * Upload a file to SMB server\n * @param localPath Local path of the file to upload\n * @param remotePath Remote path to save the file\n */\n async upload(localPath: string, remotePath: string): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n if (!this._currentShare) throw new Error(\"No share connected\");\n\n await ReactNativeKookitModule.smbClientUpload(\n this.clientId,\n localPath,\n remotePath\n );\n }\n\n /**\n * Delete a file or directory from SMB server\n * @param remotePath Path to the file or directory to delete\n * @param isDirectory Whether the path is a directory\n */\n async delete(\n remotePath: string,\n isDirectory: boolean = false\n ): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n if (!this._currentShare) throw new Error(\"No share connected\");\n\n await ReactNativeKookitModule.smbClientDelete(\n this.clientId,\n remotePath,\n isDirectory\n );\n }\n\n /**\n * Create a directory on SMB server\n * @param remotePath Path of the directory to create\n */\n async createDirectory(remotePath: string): Promise<void> {\n if (this.isDisposed) throw new Error(\"SMB client has been disposed\");\n if (!this._isConnected) throw new Error(\"Not connected to SMB server\");\n if (!this._currentShare) throw new Error(\"No share connected\");\n\n await ReactNativeKookitModule.smbClientCreateDirectory(\n this.clientId,\n remotePath\n );\n }\n\n /**\n * Get the current connection status\n */\n async getStatus(): Promise<{\n clientId: string;\n exists: boolean;\n connected: boolean;\n currentShare: string | null;\n }> {\n const status = await ReactNativeKookitModule.getSmbClientStatus(\n this.clientId\n );\n return {\n clientId: this.clientId,\n currentShare: this._currentShare,\n ...status,\n };\n }\n\n /**\n * Get the client ID\n */\n getClientId(): string {\n return this.clientId;\n }\n\n /**\n * Get the current share name\n */\n getCurrentShare(): string | null {\n return this._currentShare;\n }\n\n /**\n * Check if connected to SMB server\n */\n async isConnected(): Promise<boolean> {\n if (this.isDisposed) return false;\n try {\n const status = await this.getStatus();\n return status.exists && status.connected;\n } catch {\n return false;\n }\n }\n\n /**\n * Check if connected to SMB server (synchronous)\n */\n isConnectedSync(): boolean {\n return this._isConnected;\n }\n\n /**\n * Check if connected to a share\n */\n hasShareConnected(): boolean {\n return this._isConnected && this._currentShare !== null;\n }\n\n /**\n * Dispose the SMB client and clean up resources\n */\n async dispose(): Promise<void> {\n if (this.isDisposed) return;\n try {\n await ReactNativeKookitModule.disposeSmbClient(this.clientId);\n } catch {}\n this.eventSubscriptions.forEach((s) => s.remove());\n this.eventSubscriptions = [];\n this.isDisposed = true;\n this._isConnected = false;\n this._currentShare = null;\n\n // 从静态实例管理器中移除\n SmbClient.instances.delete(this.clientId);\n }\n\n /**\n * List all SMB clients\n */\n static async listClients(): Promise<{\n clients: Record<string, { connected: boolean }>;\n count: number;\n }> {\n return ReactNativeKookitModule.listSmbClients();\n }\n\n /**\n * Create a new SMB client instance\n * @param clientId Optional client ID\n * @returns New SMB client instance\n */\n static async create(clientId?: string): Promise<SmbClient> {\n const client = new SmbClient(clientId);\n await client.initialize();\n return client;\n }\n\n /**\n * Dispose a specific SMB client by client ID\n * @param clientId Client ID to dispose\n * @returns true if client was found and disposed, false otherwise\n */\n static async disposeClient(clientId: string): Promise<boolean> {\n const client = SmbClient.instances.get(clientId);\n if (client) {\n await client.dispose();\n return true;\n }\n\n // Try to dispose on native side even if not in our instances map\n try {\n await ReactNativeKookitModule.disposeSmbClient(clientId);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get the status of a specific SMB client by client ID\n * @param clientId Client ID to check\n * @returns Client status or null if not found\n */\n static getClientStatus(clientId: string): SmbClientStatus {\n const client = SmbClient.instances.get(clientId);\n if (client) {\n return {\n exists: true,\n isConnected: client._isConnected,\n currentShare: client._currentShare,\n isDisposed: client.isDisposed,\n };\n }\n return {\n exists: false,\n isConnected: false,\n currentShare: null,\n isDisposed: true,\n };\n }\n\n /**\n * Get all active client IDs and their status\n * @returns Map of client IDs to their status\n */\n static getAllClientsStatus(): Map<string, SmbClientInfo> {\n const statusMap = new Map();\n SmbClient.instances.forEach((client, clientId) => {\n statusMap.set(clientId, {\n isConnected: client._isConnected,\n currentShare: client._currentShare,\n isDisposed: client.isDisposed,\n });\n });\n return statusMap;\n }\n\n /**\n * Get a specific SMB client instance by client ID\n * @param clientId Client ID to retrieve\n * @returns SMB client instance or null if not found\n */\n static getInstance(clientId: string): SmbClient | null {\n return SmbClient.instances.get(clientId) || null;\n }\n\n /**\n * Dispose all SMB clients\n * @returns Number of clients disposed\n */\n static async disposeAllClients(): Promise<number> {\n const clientIds = Array.from(SmbClient.instances.keys());\n let disposedCount = 0;\n\n for (const clientId of clientIds) {\n try {\n await SmbClient.disposeClient(clientId);\n disposedCount++;\n } catch {\n // Continue with next client even if disposal fails\n }\n }\n\n // Clear the instances map\n SmbClient.instances.clear();\n\n return disposedCount;\n }\n}\n\nexport default SmbClient;\n"]}
|
package/package.json
CHANGED