Browse Source

Merge remote-tracking branch 'refs/remotes/origin/feature/smb'

chendeben 9 months ago
parent
commit
4826d7fe37
60 changed files with 10803 additions and 721 deletions
  1. 1 1
      entry/build-profile.json5
  2. 4 11
      entry/oh-package-lock.json5
  3. 0 1
      entry/oh-package.json5
  4. 0 1
      entry/oh_modules/libentry.so/Index.d.ts
  5. 0 6
      entry/oh_modules/libentry.so/oh-package.json5
  6. 13 1
      entry/src/main/cpp/CMakeLists.txt
  7. 15 0
      entry/src/main/cpp/include/Makefile.am
  8. 139 0
      entry/src/main/cpp/include/amiga_os/config.h
  9. 139 0
      entry/src/main/cpp/include/apple/config.h
  10. 65 0
      entry/src/main/cpp/include/asprintf.h
  11. 139 0
      entry/src/main/cpp/include/esp/config.h
  12. 668 0
      entry/src/main/cpp/include/libsmb2-private.h
  13. 21 0
      entry/src/main/cpp/include/module.modulemap
  14. 143 0
      entry/src/main/cpp/include/picow/FreeRTOSConfig.h
  15. 2 0
      entry/src/main/cpp/include/picow/README.md
  16. 139 0
      entry/src/main/cpp/include/picow/config.h
  17. 27 0
      entry/src/main/cpp/include/picow/lwipopts.h
  18. 90 0
      entry/src/main/cpp/include/picow/lwipopts_examples_common.h
  19. 377 0
      entry/src/main/cpp/include/portable-endian.h
  20. 139 0
      entry/src/main/cpp/include/ps3/config.h
  21. 63 0
      entry/src/main/cpp/include/slist.h
  22. 172 0
      entry/src/main/cpp/include/smb2/libsmb2-dcerpc-lsa.h
  23. 180 0
      entry/src/main/cpp/include/smb2/libsmb2-dcerpc-srvsvc.h
  24. 155 0
      entry/src/main/cpp/include/smb2/libsmb2-dcerpc.h
  25. 497 0
      entry/src/main/cpp/include/smb2/libsmb2-raw.h
  26. 1406 0
      entry/src/main/cpp/include/smb2/libsmb2.h
  27. 549 0
      entry/src/main/cpp/include/smb2/smb2-errors.h
  28. 83 0
      entry/src/main/cpp/include/smb2/smb2-ioctl.h
  29. 1251 0
      entry/src/main/cpp/include/smb2/smb2.h
  30. 139 0
      entry/src/main/cpp/include/xbox 360/config.h
  31. 139 0
      entry/src/main/cpp/include/xbox/config.h
  32. BIN
      entry/src/main/cpp/libs/arm64-v8a/libsmb2.so
  33. BIN
      entry/src/main/cpp/libs/arm64-v8a/libsmb2.so.1
  34. 690 22
      entry/src/main/cpp/napi_init.cpp
  35. 23 1
      entry/src/main/cpp/types/libentry/Index.d.ts
  36. 2 0
      entry/src/main/ets/common/constants/CommonConstants.ets
  37. 1 1
      entry/src/main/ets/common/enums/RemoteDriveManagerStates.ets
  38. 5 0
      entry/src/main/ets/common/enums/RemoteDriveType.ets
  39. 2 1
      entry/src/main/ets/common/enums/SongType.ets
  40. 331 0
      entry/src/main/ets/common/network/NavidromeApi.ets
  41. 122 0
      entry/src/main/ets/common/network/RemoteSongCache.ets
  42. 146 0
      entry/src/main/ets/common/network/SmbBridge.ets
  43. 142 0
      entry/src/main/ets/common/network/SmbFileCache.ets
  44. 151 0
      entry/src/main/ets/common/network/WebDavFileCache.ets
  45. 4 4
      entry/src/main/ets/common/util/DataBaseUtil.ets
  46. 13 9
      entry/src/main/ets/common/util/MediaTable.ets
  47. 33 0
      entry/src/main/ets/common/util/RemoteDriveLabel.ets
  48. 450 170
      entry/src/main/ets/common/util/RemoteDriveManager.ets
  49. 707 0
      entry/src/main/ets/dialog/RemoteDriveAccountDialog.ets
  50. 0 351
      entry/src/main/ets/dialog/WebDavAccountDialog.ets
  51. 3 3
      entry/src/main/ets/entryability/EntryAbility.ets
  52. 62 58
      entry/src/main/ets/pages/NewIndex.ets
  53. 56 1
      entry/src/main/ets/pages/SettingPage.ets
  54. 563 0
      entry/src/main/ets/pages/SmbTestPage.ets
  55. 22 14
      entry/src/main/ets/pages/WebDavMainPage.ets
  56. 446 56
      entry/src/main/ets/view/LocalMusic.ets
  57. 4 4
      entry/src/main/ets/viewmodel/Song.ets
  58. 6 2
      entry/src/main/ets/viewmodel/WebDavAccount.ets
  59. 61 1
      entry/src/main/ets/workers/Worker.ets
  60. 3 2
      entry/src/main/resources/base/profile/main_pages.json

+ 1 - 1
entry/build-profile.json5

@@ -20,4 +20,4 @@
       "name": "ohosTest",
       "name": "ohosTest",
     }
     }
   ]
   ]
-}
+}

+ 4 - 11
entry/oh-package-lock.json5

@@ -8,18 +8,17 @@
   "specifiers": {
   "specifiers": {
     "@ohos/ijkplayer@../ijkplayer": "@ohos/ijkplayer@../ijkplayer",
     "@ohos/ijkplayer@../ijkplayer": "@ohos/ijkplayer@../ijkplayer",
     "@tencent/wechat_open_sdk@1.0.0": "@tencent/wechat_open_sdk@1.0.0",
     "@tencent/wechat_open_sdk@1.0.0": "@tencent/wechat_open_sdk@1.0.0",
-    "@types/libijkplayer_audio_napi.so@../ijkplayer/src/main/cpp/types/ijkplayer_audio_napi": "@types/libijkplayer_audio_napi.so@../ijkplayer/src/main/cpp/types/ijkplayer_audio_napi",
-    "libentry.so@src/main/cpp/types/libentry": "libentry.so@src/main/cpp/types/libentry"
+    "@types/libijkplayer_audio_napi.so@../ijkplayer/src/main/cpp/types/ijkplayer_audio_napi": "@types/libijkplayer_audio_napi.so@../ijkplayer/src/main/cpp/types/ijkplayer_audio_napi"
   },
   },
   "packages": {
   "packages": {
     "@ohos/ijkplayer@../ijkplayer": {
     "@ohos/ijkplayer@../ijkplayer": {
       "name": "@ohos/ijkplayer",
       "name": "@ohos/ijkplayer",
       "version": "2.0.5",
       "version": "2.0.5",
-      "resolved": "../ijkplayer",
+      "resolved": "",
       "registryType": "local"
       "registryType": "local"
     },
     },
     "@tencent/wechat_open_sdk@1.0.0": {
     "@tencent/wechat_open_sdk@1.0.0": {
-      "name": "@tencent/wechat_open_sdk",
+      "name": "",
       "version": "1.0.0",
       "version": "1.0.0",
       "integrity": "sha512-rB1bWPTeMEir10+RguSlURf2ji/m7Xs0exh+EdRUEYhwKqnxMGylaASjX/A1nqZM1KBmx5+MVwt9DayTReP3mA==",
       "integrity": "sha512-rB1bWPTeMEir10+RguSlURf2ji/m7Xs0exh+EdRUEYhwKqnxMGylaASjX/A1nqZM1KBmx5+MVwt9DayTReP3mA==",
       "resolved": "https://repo.harmonyos.com/ohpm/@tencent/wechat_open_sdk/-/wechat_open_sdk-1.0.0.har",
       "resolved": "https://repo.harmonyos.com/ohpm/@tencent/wechat_open_sdk/-/wechat_open_sdk-1.0.0.har",
@@ -28,13 +27,7 @@
     "@types/libijkplayer_audio_napi.so@../ijkplayer/src/main/cpp/types/ijkplayer_audio_napi": {
     "@types/libijkplayer_audio_napi.so@../ijkplayer/src/main/cpp/types/ijkplayer_audio_napi": {
       "name": "@types/libijkplayer_audio_napi.so",
       "name": "@types/libijkplayer_audio_napi.so",
       "version": "2.0.3-rc.3",
       "version": "2.0.3-rc.3",
-      "resolved": "../ijkplayer/src/main/cpp/types/ijkplayer_audio_napi",
-      "registryType": "local"
-    },
-    "libentry.so@src/main/cpp/types/libentry": {
-      "name": "libentry.so",
-      "version": "1.0.0",
-      "resolved": "src/main/cpp/types/libentry",
+      "resolved": "",
       "registryType": "local"
       "registryType": "local"
     }
     }
   }
   }

+ 0 - 1
entry/oh-package.json5

@@ -7,7 +7,6 @@
   "license": "LGPLv2.1 or later",
   "license": "LGPLv2.1 or later",
   "dependencies": {
   "dependencies": {
     "@ohos/ijkplayer": "file:../ijkplayer",//注释掉这个代码,用ohpm install @ohos/ijkplayer去装2.0.6版本
     "@ohos/ijkplayer": "file:../ijkplayer",//注释掉这个代码,用ohpm install @ohos/ijkplayer去装2.0.6版本
-    "libentry.so": "file:./src/main/cpp/types/libentry",
 //    "@csj/openadsdk": "file:libs/openadsdk_6.7.0.har",
 //    "@csj/openadsdk": "file:libs/openadsdk_6.7.0.har",
     "@tencent/wechat_open_sdk": "1.0.0"
     "@tencent/wechat_open_sdk": "1.0.0"
   }
   }

+ 0 - 1
entry/oh_modules/libentry.so/Index.d.ts

@@ -1 +0,0 @@
-export const add: (a: number, b: number) => number;

+ 0 - 6
entry/oh_modules/libentry.so/oh-package.json5

@@ -1,6 +0,0 @@
-{
-  "name": "libentry.so",
-  "types": "./Index.d.ts",
-  "version": "1.0.0",
-  "description": "Please describe the basic information."
-}

+ 13 - 1
entry/src/main/cpp/CMakeLists.txt

@@ -2,6 +2,9 @@
 cmake_minimum_required(VERSION 3.5.0)
 cmake_minimum_required(VERSION 3.5.0)
 project(native7)
 project(native7)
 
 
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
 
 
 if(DEFINED PACKAGE_FIND_FILE)
 if(DEFINED PACKAGE_FIND_FILE)
@@ -17,5 +20,14 @@ include_directories(${NATIVERENDER_ROOT_PATH}
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../ijkplayer/src/main/cpp/ijksdl
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../ijkplayer/src/main/cpp/ijksdl
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../ijkplayer/src/main/cpp/ijkplayer)
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../../../ijkplayer/src/main/cpp/ijkplayer)
 
 
+set(SMB2_LIB_NAME libsmb2.so.1)
+set(SMB2_LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/libs/${OHOS_ARCH}/${SMB2_LIB_NAME})
+if(EXISTS ${SMB2_LIB_PATH})
+    add_library(smb2 SHARED IMPORTED)
+    set_target_properties(smb2 PROPERTIES IMPORTED_LOCATION ${SMB2_LIB_PATH})
+else()
+    message(FATAL_ERROR "${SMB2_LIB_NAME} not found for architecture ${OHOS_ARCH} at ${SMB2_LIB_PATH}")
+endif()
+
 add_library(entry SHARED napi_init.cpp)
 add_library(entry SHARED napi_init.cpp)
-target_link_libraries(entry PUBLIC libace_napi.z.so)
+target_link_libraries(entry PUBLIC libace_napi.z.so hilog_ndk.z smb2)

+ 15 - 0
entry/src/main/cpp/include/Makefile.am

@@ -0,0 +1,15 @@
+smb2dir = $(includedir)/smb2
+dist_smb2_HEADERS = \
+	smb2/libsmb2.h \
+	smb2/libsmb2-dcerpc.h \
+	smb2/libsmb2-dcerpc-lsa.h \
+	smb2/libsmb2-dcerpc-srvsvc.h \
+	smb2/libsmb2-raw.h \
+	smb2/smb2.h \
+	smb2/smb2-errors.h
+
+dist_noinst_HEADERS = \
+	asprintf.h \
+	libsmb2-private.h \
+	portable-endian.h \
+	slist.h

+ 139 - 0
entry/src/main/cpp/include/amiga_os/config.h

@@ -0,0 +1,139 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Whether or not TCP sockets should be allowed to linger after closure */
+#define CONFIGURE_OPTION_TCP_LINGER 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <gssapi/gssapi.h> header file. */
+/* #undef HAVE_GSSAPI_GSSAPI_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Whether we use gssapi_krb5 or not */
+/* #undef HAVE_LIBKRB5 */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Whether we have linger */
+/* #undef HAVE_LINGER */
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#define HAVE_NETDB_H 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+#define HAVE_NETINET_TCP_H 1
+
+/* Define to 1 if you have the <poll.h> header file. */
+/* #undef HAVE_POLL_H */
+
+/* Whether sockaddr struct has sa_len */
+#define HAVE_SOCKADDR_LEN 1
+
+/* Whether we have sockaddr_Storage */
+/* #undef HAVE_SOCKADDR_STORAGE */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/errno.h> header file. */
+#define HAVE_SYS_ERRNO_H 1
+
+/* Define to 1 if you have the <sys/fcntl.h> header file. */
+#define HAVE_SYS_FCNTL_H 1
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+/* #undef HAVE_SYS_POLL_H */
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define to 1 if you have the <sys/unistd.h> header file. */
+#define HAVE_SYS_UNISTD_H 1
+
+/* Define to 1 if you have the <sys/_iovec.h> header file. */
+/* #undef HAVE_SYS__IOVEC_H */
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "libsmb2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "ronniesahlberg@gmail.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libsmb2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libsmb2 4.0.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libsmb2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "4.0.0"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "4.0.0"

+ 139 - 0
entry/src/main/cpp/include/apple/config.h

@@ -0,0 +1,139 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Whether or not TCP sockets should be allowed to linger after closure */
+#define CONFIGURE_OPTION_TCP_LINGER 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <gssapi/gssapi.h> header file. */
+#define HAVE_GSSAPI_GSSAPI_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Whether we use gssapi_krb5 or not */
+/* #undef HAVE_LIBKRB5 */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Whether we have linger */
+#define HAVE_LINGER 1
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#define HAVE_NETDB_H 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+#define HAVE_NETINET_TCP_H 1
+
+/* Define to 1 if you have the <poll.h> header file. */
+#define HAVE_POLL_H 1
+
+/* Whether sockaddr struct has sa_len */
+#define HAVE_SOCKADDR_LEN 1
+
+/* Whether we have sockaddr_Storage */
+#define HAVE_SOCKADDR_STORAGE 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/errno.h> header file. */
+#define HAVE_SYS_ERRNO_H 1
+
+/* Define to 1 if you have the <sys/fcntl.h> header file. */
+#define HAVE_SYS_FCNTL_H 1
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#define HAVE_SYS_POLL_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define to 1 if you have the <sys/unistd.h> header file. */
+#define HAVE_SYS_UNISTD_H 1
+
+/* Define to 1 if you have the <sys/_iovec.h> header file. */
+/* #undef HAVE_SYS__IOVEC_H */
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "libsmb2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "ronniesahlberg@gmail.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libsmb2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libsmb2 4.0.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libsmb2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "4.0.0"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "4.0.0"

+ 65 - 0
entry/src/main/cpp/include/asprintf.h

@@ -0,0 +1,65 @@
+
+#ifndef _ASPRINTF_H_
+#define _ASPRINTF_H_
+
+#if !defined(__AROS__) && !defined(__ps2sdk_iop__)
+#include <malloc.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#ifdef _XBOX
+#define inline __inline
+#endif
+
+#ifndef _XBOX
+#ifndef _vscprintf
+/* For some reason, MSVC fails to honour this #ifndef. */
+/* Hence function renamed to _vscprintf_so(). */
+static inline int _vscprintf_so(const char * format, va_list pargs) {
+  int retval;
+  va_list argcopy;
+  va_copy(argcopy, pargs);
+  retval = vsnprintf(NULL, 0, format, argcopy);
+  va_end(argcopy);
+  return retval;
+}
+#endif /* _vscprintf */
+#endif
+
+#ifndef vasprintf
+static inline int vasprintf(char **strp, const char *fmt, va_list ap) {
+#ifdef _XBOX
+  int len = _vscprintf(fmt, ap);
+#else
+  int len = _vscprintf_so(fmt, ap);
+#endif
+  char *str;
+  int r;
+  if (len == -1) return -1;
+  str = malloc((size_t)len + 1);
+  if (!str) return -1;
+#ifdef _XBOX
+  r = _vsnprintf(str, len + 1, fmt, ap); /* "secure" version of vsprintf */
+#else
+  r = vsnprintf(str, len + 1, fmt, ap); /* "secure" version of vsprintf */
+#endif
+  if (r == -1) return free(str), -1;
+  *strp = str;
+  return r;
+}
+#endif /* vasprintf */
+
+#ifndef asprintf
+static inline int asprintf(char *strp[], const char *fmt, ...) {
+  int r;
+  va_list ap;
+  va_start(ap, fmt);
+  r = vasprintf(strp, fmt, ap);
+  va_end(ap);
+  return r;
+}
+#endif /* asprintf */
+
+#endif /* ! _ASPRINTF_H_ */

+ 139 - 0
entry/src/main/cpp/include/esp/config.h

@@ -0,0 +1,139 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Whether or not TCP sockets should be allowed to linger after closure */
+#define CONFIGURE_OPTION_TCP_LINGER 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <gssapi/gssapi.h> header file. */
+/* #undef HAVE_GSSAPI_GSSAPI_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Whether we use gssapi_krb5 or not */
+/* #undef HAVE_LIBKRB5 */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Whether we have linger */
+#define HAVE_LINGER 1
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#define HAVE_NETDB_H 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+/* #undef HAVE_NETINET_TCP_H */
+
+/* Define to 1 if you have the <poll.h> header file. */
+/* #undef HAVE_POLL_H */
+
+/* Whether sockaddr struct has sa_len */
+/* #undef HAVE_SOCKADDR_LEN */
+
+/* Whether we have sockaddr_Storage */
+#define HAVE_SOCKADDR_STORAGE 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/errno.h> header file. */
+/* #undef HAVE_SYS_ERRNO_H */
+
+/* Define to 1 if you have the <sys/fcntl.h> header file. */
+/* #undef HAVE_SYS_FCNTL_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#define HAVE_SYS_POLL_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+/* #undef HAVE_SYS_TIME_H */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define to 1 if you have the <sys/unistd.h> header file. */
+/* #undef HAVE_SYS_UNISTD_H */
+
+/* Define to 1 if you have the <sys/_iovec.h> header file. */
+/* #undef HAVE_SYS__IOVEC_H */
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "libsmb2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "ronniesahlberg@gmail.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libsmb2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libsmb2 4.0.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libsmb2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "4.0.0"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "4.0.0"

+ 668 - 0
entry/src/main/cpp/include/libsmb2-private.h

@@ -0,0 +1,668 @@
+/* -*-  mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil;  -*- */
+/*
+   Copyright (C) 2016 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBSMB2_PRIVATE_H_
+#define _LIBSMB2_PRIVATE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_LIBKRB5
+#include <krb5/krb5.h>
+
+#if __APPLE__
+#import <GSS/GSS.h>
+#else
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+#endif /* __APPLE__ */
+#endif /* HAVE_LIBKRB5 */
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+#ifndef discard_const
+#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
+#endif
+
+#define MAX_ERROR_SIZE 256
+
+#define PAD_TO_32BIT(len) ((len + 0x03) & 0xfffffffc)
+#define PAD_TO_64BIT(len) ((len + 0x07) & 0xfffffff8)
+
+#define SMB2_SPL_SIZE 4
+#define SMB2_HEADER_SIZE 64
+
+#define SMB2_SIGNATURE_SIZE 16
+#define SMB2_KEY_SIZE 16
+
+#define SMB2_MAX_VECTORS 256
+
+struct smb2_io_vectors {
+        size_t num_done;
+        size_t total_size;
+        int niov;
+        struct smb2_iovec iov[SMB2_MAX_VECTORS];
+};
+
+struct smb2_async {
+        uint64_t async_id;
+};
+
+struct smb2_sync {
+        uint32_t process_id;
+        uint32_t tree_id;
+};
+
+struct smb2_header {
+        uint8_t protocol_id[4];
+        uint16_t struct_size;
+        uint16_t credit_charge;
+        uint32_t status;
+        uint16_t command;
+        uint16_t credit_request_response;
+        uint32_t flags;
+        uint32_t next_command;
+        uint64_t message_id;
+        union {
+                struct smb2_async async;
+                struct smb2_sync sync;
+        };
+        uint64_t session_id;
+        uint8_t signature[16];
+};
+
+/* States that we transition when we read data back from the server for
+ * normal SMB2/3 :
+ * 1: SMB2_RECV_SPL        SPL
+ * 2: SMB2_RECV_HEADER     SMB2 Header
+ * 3: SMB2_RECV_FIXED      The fixed part of the payload.
+ * 4: SMB2_RECV_VARIABLE   Optional variable part of the payload.
+ * 5: SMB2_RECV_PAD        Optional padding
+ *
+ * 2-5 will be repeated for compound commands.
+ * 4-5 are optional and may or may not be present depending on the
+ *     type of command.
+ *
+ * States for SMB3 encryption:
+ * 1: SMB2_RECV_SPL        SPL
+ * 2: SMB2_RECV_HEADER     SMB3 Transform Header
+ * 3: SMB2_RECV_TRFM       encrypted payload
+ *
+ * States for cancelled PDUs
+ * This is used when we receive a reply for a PDU not in our waitlist.
+ * This can for example happen if we have cancelled a pdu when it was
+ * in-flight.
+ * 1: SMB2_RECV_SPL        SPL
+ * 2: SMB2_RECV_HEADER     SMB3 Transform Header
+ * 3: SMB2_RECV_UNKNOWN    data for a PDU we are not waiting for
+ */
+enum smb2_recv_state {
+        SMB2_RECV_SPL = 0,
+        SMB2_RECV_HEADER,
+        SMB2_RECV_FIXED,
+        SMB2_RECV_VARIABLE,
+        SMB2_RECV_PAD,
+        SMB2_RECV_TRFM,
+        SMB2_RECV_UNKNOWN,
+};
+
+/* current tree id stack, note: index 0 in the stack is not used
+*/
+#define SMB2_MAX_TREE_NESTING 32
+#define smb2_tree_id(smb2) (((smb2)->tree_id_cur >= 0)?smb2->tree_id[(smb2)->tree_id_cur]:0xdeadbeef)
+
+#define MAX_CREDITS 1024
+#define SMB2_SALT_SIZE 32
+
+struct sync_cb_data {
+	int is_finished;
+	int status;
+	void *ptr;
+};
+
+struct smb2_context {
+
+        t_socket fd;
+
+        struct smb2_server *owning_server;
+
+        t_socket *connecting_fds;
+        size_t connecting_fds_count;
+        struct addrinfo *addrinfos;
+        const struct addrinfo *next_addrinfo;
+
+        int timeout;
+
+        enum smb2_sec sec;
+
+        uint16_t security_mode;
+        uint32_t capabilities;
+        int use_cached_creds;
+
+        enum smb2_negotiate_version version;
+
+        const char *server;
+        const char *share;
+        const char *user;
+
+        /* Only used with --without-libkrb5 */
+        const char *password;
+        const char *domain;
+        const char *workstation;
+        char client_challenge[8];
+
+        void *opaque;
+
+        smb2_error_cb error_cb;
+        smb2_command_cb connect_cb;
+        void *connect_data;
+        struct sync_cb_data connect_cb_data;
+
+        int credits;
+
+        char client_guid[16];
+
+        uint32_t tree_id[SMB2_MAX_TREE_NESTING];
+        int  tree_id_top;
+        int  tree_id_cur;
+        uint64_t message_id;
+        uint64_t session_id;
+        uint64_t async_id;
+        uint8_t *session_key;
+        uint8_t session_key_size;
+
+        uint8_t seal:1;
+        uint8_t sign:1;
+        uint8_t signing_key[SMB2_KEY_SIZE];
+        uint8_t serverin_key[SMB2_KEY_SIZE];
+        uint8_t serverout_key[SMB2_KEY_SIZE];
+        uint8_t salt[SMB2_SALT_SIZE];
+        uint16_t cypher;
+        uint8_t preauthhash[SMB2_PREAUTH_HASH_SIZE];
+
+
+#ifdef HAVE_LIBKRB5
+        /* for delegation of client creds to proxy-client */
+        gss_cred_id_t cred_handle;
+#endif
+        /*
+         * For handling received smb3 encrypted blobs
+         */
+        unsigned char *enc;
+        size_t enc_len;
+        int enc_pos;
+
+        /*
+         * For sending PDUs
+         */
+        struct smb2_pdu *outqueue;
+        struct smb2_pdu *waitqueue;
+
+        /*
+         * For receiving PDUs
+         */
+        struct smb2_io_vectors in;
+        enum smb2_recv_state recv_state;
+        /* SPL for the (compound) command we are currently reading */
+        uint32_t spl;
+        /* buffer to avoid having to malloc the header */
+        uint8_t header[SMB2_HEADER_SIZE];
+        struct smb2_header hdr;
+        /* Offset into smb2->in where the payload for the current PDU starts */
+        size_t payload_offset;
+
+        /* Pointer to the current PDU that we are receiving the reply for.
+         * Only valid once the full smb2 header has been received.
+         */
+        struct smb2_pdu *pdu;
+
+        /* pointer to the pdu to read AFTER the current one is completed
+         * (if this context is a server)
+         */
+        struct smb2_pdu *next_pdu;
+
+        /* flag indicated command packers/unpackers can pass "extra"
+         * content without trying to decode or encode it.  this is
+         * useful for proxies and applies only to the commands with
+         * complex data: query-info, query-directory, ioctl, and
+         * create (contexts).  the command fixed part is always
+         * de/en-coded regardless of this setting
+         */
+        int passthrough;
+
+        /* for oplock/lease breaks, inform the app */
+        smb2_oplock_or_lease_break_cb oplock_or_lease_break_cb;
+
+        /* oplock state, needed to discriminate between notification or response */
+        int oplock_break_count;
+
+        /* last file_id in a create-reply, for "related requests" */
+        smb2_file_id last_file_id;
+
+        /* Server capabilities */
+        uint8_t supports_multi_credit;
+
+        uint32_t max_transact_size;
+        uint32_t max_read_size;
+        uint32_t max_write_size;
+        uint16_t dialect;
+
+        char error_string[MAX_ERROR_SIZE];
+        int nterror;
+
+        /* callbacks for the eventsystem */
+        int events;
+        smb2_change_fd_cb change_fd;
+        smb2_change_events_cb change_events;
+
+        /* dcerpc settings */
+        uint8_t ndr;
+        int endianness;
+
+        /* to maintain lists of contexts for server used */
+        struct smb2_context *next;
+};
+
+/*
+ * Callback for freeing a payload.
+ */
+typedef void (*smb2_free_payload)(struct smb2_context *smb2, void *payload);
+
+
+#define SMB2_MAX_PDU_SIZE 16*1024*1024
+
+struct smb2_pdu {
+        struct smb2_pdu *next;
+        struct smb2_header header;
+
+        struct smb2_pdu *next_compound;
+        uint64_t prev_compound_mid;
+
+        int caller_frees_pdu;
+        smb2_command_cb cb;
+        void *cb_data;
+        void (*free_cb)(void *);
+
+        /* buffer to avoid having to malloc the headers */
+        uint8_t hdr[SMB2_HEADER_SIZE];
+
+        /* pointer to the unmarshalled payload in a reply */
+        void *payload;
+
+        /* callback that frees the any additional memory allocated in the payload.
+         * Or null if no additional memory needs to be freed.
+         */
+        smb2_free_payload free_payload;
+
+        /* For sending/receiving
+         * out contains at least two vectors:
+         * [0]  64 bytes for the smb header
+         * [1+] command and and extra parameters
+         *
+         * in contains at least one vector:
+         * [0+] command and and extra parameters
+         */
+        struct smb2_io_vectors out;
+        struct smb2_io_vectors in;
+
+        /* Data we need to retain between request/reply for QUERY INFO */
+        uint8_t info_type;
+        uint8_t file_info_class;
+
+        /* For encrypted PDUs */
+        uint8_t seal:1;
+        uint32_t crypt_len;
+        unsigned char *crypt;
+        time_t timeout;
+};
+
+struct smb2_dirent_internal {
+        struct smb2_dirent_internal *next;
+        struct smb2dirent dirent;
+};
+
+struct smb2dir {
+        smb2_command_cb cb;
+        void (*free_cb_data)(void *);
+        void *cb_data;
+        smb2_file_id file_id;
+
+        struct smb2_dirent_internal *entries;
+        struct smb2_dirent_internal *current_entry;
+        int index;
+};
+
+
+#define smb2_is_server(ctx) ((ctx)->owning_server != NULL)
+
+void smb2_set_nterror(struct smb2_context *smb2, int nterror,
+                    const char *error_string, ...);
+
+void smb2_close_connecting_fds(struct smb2_context *smb2);
+
+void *smb2_alloc_init(struct smb2_context *smb2, size_t size);
+void *smb2_alloc_data(struct smb2_context *smb2, void *memctx, size_t size);
+
+struct smb2_iovec *smb2_add_iovector(struct smb2_context *smb2,
+                                     struct smb2_io_vectors *v,
+                                     uint8_t *buf, size_t len,
+                                     void (*free)(void *));
+
+int smb2_pad_to_64bit(struct smb2_context *smb2, struct smb2_io_vectors *v);
+
+int smb2_connect_tree_id(struct smb2_context *smb2, uint32_t tree_id);
+int smb2_disconnect_tree_id(struct smb2_context *smb2, uint32_t tree_id);
+
+struct smb2_pdu *smb2_allocate_pdu(struct smb2_context *smb2,
+                                   enum smb2_command command,
+                                   smb2_command_cb cb, void *cb_data);
+int smb2_process_payload_fixed(struct smb2_context *smb2,
+                               struct smb2_pdu *pdu);
+int smb2_process_payload_variable(struct smb2_context *smb2,
+                                  struct smb2_pdu *pdu);
+int smb2_get_fixed_size(struct smb2_context *smb2, struct smb2_pdu *pdu);
+
+struct smb2_pdu *smb2_find_pdu(struct smb2_context *smb2, uint64_t message_id);
+void smb2_free_iovector(struct smb2_context *smb2, struct smb2_io_vectors *v);
+
+void smb2_oplock_break_notify(struct smb2_context *smb2, int status, void *command_data, void *cb_data);
+
+int smb2_decode_header(struct smb2_context *smb2, struct smb2_iovec *iov,
+                       struct smb2_header *hdr);
+int smb2_calc_signature(struct smb2_context *smb2, uint8_t *signature,
+                        struct smb2_iovec *iov, size_t niov);
+
+int smb2_set_uint8(struct smb2_iovec *iov, int offset, uint8_t value);
+int smb2_set_uint16(struct smb2_iovec *iov, int offset, uint16_t value);
+int smb2_set_uint32(struct smb2_iovec *iov, int offset, uint32_t value);
+int smb2_set_uint64(struct smb2_iovec *iov, int offset, uint64_t value);
+
+int smb2_get_uint8(struct smb2_iovec *iov, int offset, uint8_t *value);
+int smb2_get_uint16(struct smb2_iovec *iov, int offset, uint16_t *value);
+int smb2_get_uint32(struct smb2_iovec *iov, int offset, uint32_t *value);
+int smb2_get_uint64(struct smb2_iovec *iov, int offset, uint64_t *value);
+
+int smb2_process_error_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_error_variable(struct smb2_context *smb2,
+                                struct smb2_pdu *pdu);
+int smb2_process_negotiate_fixed(struct smb2_context *smb2,
+                                 struct smb2_pdu *pdu);
+int smb2_process_negotiate_variable(struct smb2_context *smb2,
+                                    struct smb2_pdu *pdu);
+int smb2_process_negotiate_request_fixed(struct smb2_context *smb2,
+                                     struct smb2_pdu *pdu);
+int smb2_process_negotiate_request_variable(struct smb2_context *smb2,
+                                     struct smb2_pdu *pdu);
+int smb2_process_session_setup_fixed(struct smb2_context *smb2,
+                                     struct smb2_pdu *pdu);
+int smb2_process_session_setup_variable(struct smb2_context *smb2,
+                                        struct smb2_pdu *pdu);
+int smb2_process_session_setup_request_fixed(struct smb2_context *smb2,
+                                        struct smb2_pdu *pdu);
+int smb2_process_session_setup_request_variable(struct smb2_context *smb2,
+                                        struct smb2_pdu *pdu);
+int smb2_process_tree_connect_fixed(struct smb2_context *smb2,
+                                        struct smb2_pdu *pdu);
+int smb2_process_tree_connect_request_fixed(struct smb2_context *smb2,
+                                        struct smb2_pdu *pdu);
+int smb2_process_tree_connect_request_variable(struct smb2_context *smb2,
+                                        struct smb2_pdu *pdu);
+int smb2_process_create_fixed(struct smb2_context *smb2,
+                              struct smb2_pdu *pdu);
+int smb2_process_create_variable(struct smb2_context *smb2,
+                                 struct smb2_pdu *pdu);
+int smb2_process_create_request_fixed(struct smb2_context *smb2,
+                              struct smb2_pdu *pdu);
+int smb2_process_create_request_variable(struct smb2_context *smb2,
+                                 struct smb2_pdu *pdu);
+int smb2_process_query_directory_fixed(struct smb2_context *smb2,
+                                       struct smb2_pdu *pdu);
+int smb2_process_query_directory_variable(struct smb2_context *smb2,
+                                          struct smb2_pdu *pdu);
+int smb2_process_query_directory_request_fixed(struct smb2_context *smb2,
+                                       struct smb2_pdu *pdu);
+int smb2_process_query_directory_request_variable(struct smb2_context *smb2,
+                                          struct smb2_pdu *pdu);
+int smb2_process_change_notify_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_change_notify_variable(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_change_notify_request_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_query_info_fixed(struct smb2_context *smb2,
+                                  struct smb2_pdu *pdu);
+int smb2_process_query_info_variable(struct smb2_context *smb2,
+                                     struct smb2_pdu *pdu);
+int smb2_process_query_info_request_fixed(struct smb2_context *smb2,
+                                  struct smb2_pdu *pdu);
+int smb2_process_query_info_request_variable(struct smb2_context *smb2,
+                                     struct smb2_pdu *pdu);
+int smb2_process_close_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_close_request_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_set_info_fixed(struct smb2_context *smb2,
+                                struct smb2_pdu *pdu);
+int smb2_process_set_info_request_fixed(struct smb2_context *smb2,
+                                struct smb2_pdu *pdu);
+int smb2_process_set_info_request_variable(struct smb2_context *smb2,
+                                struct smb2_pdu *pdu);
+int smb2_process_tree_disconnect_fixed(struct smb2_context *smb2,
+                                       struct smb2_pdu *pdu);
+int smb2_process_tree_disconnect_request_fixed(struct smb2_context *smb2,
+                                       struct smb2_pdu *pdu);
+int smb2_process_logoff_fixed(struct smb2_context *smb2,
+                              struct smb2_pdu *pdu);
+int smb2_process_logoff_request_fixed(struct smb2_context *smb2,
+                              struct smb2_pdu *pdu);
+int smb2_process_lock_fixed(struct smb2_context *smb2,
+                            struct smb2_pdu *pdu);
+int smb2_process_lock_request_fixed(struct smb2_context *smb2,
+                            struct smb2_pdu *pdu);
+int smb2_process_lock_request_variable(struct smb2_context *smb2,
+                            struct smb2_pdu *pdu);
+int smb2_process_oplock_break_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_oplock_break_variable(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_oplock_break_request_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_oplock_break_request_variable(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_echo_fixed(struct smb2_context *smb2,
+                            struct smb2_pdu *pdu);
+int smb2_process_echo_request_fixed(struct smb2_context *smb2,
+                            struct smb2_pdu *pdu);
+int smb2_process_flush_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_flush_request_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_read_fixed(struct smb2_context *smb2,
+                            struct smb2_pdu *pdu);
+int smb2_process_read_variable(struct smb2_context *smb2,
+                            struct smb2_pdu *pdu);
+int smb2_process_read_request_fixed(struct smb2_context *smb2,
+                            struct smb2_pdu *pdu);
+int smb2_process_read_request_variable(struct smb2_context *smb2,
+                            struct smb2_pdu *pdu);
+int smb2_process_write_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_write_request_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_write_request_variable(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_ioctl_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_ioctl_variable(struct smb2_context *smb2,
+                                struct smb2_pdu *pdu);
+int smb2_process_ioctl_request_fixed(struct smb2_context *smb2,
+                             struct smb2_pdu *pdu);
+int smb2_process_ioctl_request_variable(struct smb2_context *smb2,
+                                struct smb2_pdu *pdu);
+
+int smb2_decode_file_basic_info(struct smb2_context *smb2,
+                                void *memctx,
+                                struct smb2_file_basic_info *fs,
+                                struct smb2_iovec *vec);
+int smb2_encode_file_basic_info(struct smb2_context *smb2,
+                                struct smb2_file_basic_info *fs,
+                                struct smb2_iovec *vec);
+
+int smb2_decode_file_standard_info(struct smb2_context *smb2,
+                                   void *memctx,
+                                   struct smb2_file_standard_info *fs,
+                                   struct smb2_iovec *vec);
+
+int smb2_encode_file_standard_info(struct smb2_context *smb2,
+                                   struct smb2_file_standard_info *fs,
+                                   struct smb2_iovec *vec);
+
+int smb2_decode_file_stream_info(struct smb2_context *smb2,
+                                   void *memctx,
+                                   struct smb2_file_stream_info *fs,
+                                   struct smb2_iovec *vec);
+
+int smb2_encode_file_stream_info(struct smb2_context *smb2,
+                                   struct smb2_file_stream_info *fs,
+                                   struct smb2_iovec *vec);
+
+int smb2_decode_file_position_info(struct smb2_context *smb2,
+                                   void *memctx,
+                                   struct smb2_file_position_info *fs,
+                                   struct smb2_iovec *vec);
+
+int smb2_encode_file_position_info(struct smb2_context *smb2,
+                                   struct smb2_file_position_info *fs,
+                                   struct smb2_iovec *vec);
+
+int smb2_decode_file_all_info(struct smb2_context *smb2,
+                              void *memctx,
+                              struct smb2_file_all_info *fs,
+                              struct smb2_iovec *vec);
+
+int smb2_encode_file_all_info(struct smb2_context *smb2,
+                              struct smb2_file_all_info *fs,
+                              struct smb2_iovec *vec);
+
+int smb2_decode_file_network_open_info(struct smb2_context *smb2,
+                                       void *memctx,
+                                       struct smb2_file_network_open_info *fs,
+                                       struct smb2_iovec *vec);
+
+int smb2_encode_file_network_open_info(struct smb2_context *smb2,
+                                       struct smb2_file_network_open_info *fs,
+                                       struct smb2_iovec *vec);
+
+int smb2_decode_file_normalized_name_info(struct smb2_context *smb2,
+                                          void *memctx,
+                                          struct smb2_file_name_info *fs,
+                                          struct smb2_iovec *vec);
+int smb2_encode_file_normalized_name_info(struct smb2_context *smb2,
+                                          struct smb2_file_name_info *fs,
+                                          struct smb2_iovec *vec);
+int smb2_decode_security_descriptor(struct smb2_context *smb2,
+                                    void *memctx,
+                                    struct smb2_security_descriptor *sd,
+                                    struct smb2_iovec *vec);
+
+int smb2_decode_file_fs_volume_info(struct smb2_context *smb2,
+                                    void *memctx,
+                                    struct smb2_file_fs_volume_info *fs,
+                                    struct smb2_iovec *vec);
+int smb2_encode_file_fs_volume_info(struct smb2_context *smb2,
+                                    struct smb2_file_fs_volume_info *fs,
+                                    struct smb2_iovec *vec);
+int smb2_decode_file_fs_size_info(struct smb2_context *smb2,
+                                  void *memctx,
+                                  struct smb2_file_fs_size_info *fs,
+                                  struct smb2_iovec *vec);
+int smb2_encode_file_fs_size_info(struct smb2_context *smb2,
+                                  struct smb2_file_fs_size_info *fs,
+                                  struct smb2_iovec *vec);
+int smb2_decode_file_fs_device_info(struct smb2_context *smb2,
+                                    void *memctx,
+                                    struct smb2_file_fs_device_info *fs,
+                                    struct smb2_iovec *vec);
+int smb2_encode_file_fs_device_info(struct smb2_context *smb2,
+                                    struct smb2_file_fs_device_info *fs,
+                                    struct smb2_iovec *vec);
+int smb2_decode_file_fs_attribute_info(struct smb2_context *smb2,
+                                  void *memctx,
+                                  struct smb2_file_fs_attribute_info *fs,
+                                  struct smb2_iovec *vec);
+int smb2_encode_file_fs_attribute_info(struct smb2_context *smb2,
+                                  struct smb2_file_fs_attribute_info *fs,
+                                  struct smb2_iovec *vec);
+int smb2_decode_file_fs_control_info(struct smb2_context *smb2,
+                                     void *memctx,
+                                     struct smb2_file_fs_control_info *fs,
+                                     struct smb2_iovec *vec);
+int smb2_encode_file_fs_control_info(struct smb2_context *smb2,
+                                     struct smb2_file_fs_control_info *fs,
+                                     struct smb2_iovec *vec);
+int smb2_decode_file_fs_full_size_info(struct smb2_context *smb2,
+                                       void *memctx,
+                                       struct smb2_file_fs_full_size_info *fs,
+                                       struct smb2_iovec *vec);
+int smb2_encode_file_fs_full_size_info(struct smb2_context *smb2,
+                                       struct smb2_file_fs_full_size_info *fs,
+                                       struct smb2_iovec *vec);
+int smb2_decode_file_fs_object_id_info(struct smb2_context *smb2,
+                                     void *memctx,
+                                     struct smb2_file_fs_object_id_info *fs,
+                                     struct smb2_iovec *vec);
+int smb2_encode_file_fs_object_id_info(struct smb2_context *smb2,
+                                     struct smb2_file_fs_object_id_info *fs,
+                                     struct smb2_iovec *vec);
+int smb2_decode_file_fs_sector_size_info(struct smb2_context *smb2,
+                                     void *memctx,
+                                     struct smb2_file_fs_sector_size_info *fs,
+                                     struct smb2_iovec *vec);
+int smb2_encode_file_fs_sector_size_info(struct smb2_context *smb2,
+                                     struct smb2_file_fs_sector_size_info *fs,
+                                     struct smb2_iovec *vec);
+int smb2_decode_reparse_data_buffer(struct smb2_context *smb2,
+                                    void *memctx,
+                                    struct smb2_reparse_data_buffer *rp,
+                                    struct smb2_iovec *vec);
+
+int smb2_read_from_buf(struct smb2_context *smb2);
+void smb2_change_events(struct smb2_context *smb2, t_socket fd, int events);
+void smb2_timeout_pdus(struct smb2_context *smb2);
+
+struct dcerpc_context;
+int dcerpc_set_uint8(struct dcerpc_context *ctx, struct smb2_iovec *iov,
+                     int *offset, uint8_t value);
+
+struct dcerpc_pdu;
+int dcerpc_pdu_direction(struct dcerpc_pdu *pdu);
+
+int dcerpc_align_3264(struct dcerpc_context *ctx, int offset);
+
+struct connect_data;                                           /* defined in libsmb2.c */
+void free_c_data(struct smb2_context*, struct connect_data*);  /* defined in libsmb2.c */
+
+int smb2_write_to_socket(struct smb2_context *smb2);
+        
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_LIBSMB2_PRIVATE_H_ */

+ 21 - 0
entry/src/main/cpp/include/module.modulemap

@@ -0,0 +1,21 @@
+module SMB2 [system] [extern_c] {
+    module LibSMB2 {
+        header "apple/config.h"
+        header "smb2/smb2-errors.h"
+        header "smb2/smb2.h"
+        header "smb2/libsmb2.h"
+		header "smb2/libsmb2-dcerpc.h"
+		header "smb2/libsmb2-dcerpc-lsa.h"
+        header "smb2/libsmb2-dcerpc-srvsvc.h"
+    }
+    
+    module Internal {
+        header "libsmb2-private.h"
+    }
+    
+    explicit module Raw {
+        header "smb2/libsmb2-raw.h"
+    }
+    
+    export SMB2
+}

+ 143 - 0
entry/src/main/cpp/include/picow/FreeRTOSConfig.h

@@ -0,0 +1,143 @@
+/*
+ * FreeRTOS V202111.00
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html
+ *----------------------------------------------------------*/
+
+/* Scheduler Related */
+#define configUSE_PREEMPTION                    1
+#define configUSE_TICKLESS_IDLE                 0
+#define configUSE_IDLE_HOOK                     0
+#define configUSE_TICK_HOOK                     0
+#define configTICK_RATE_HZ                      ( ( TickType_t ) 1000 )
+#define configMAX_PRIORITIES                    32
+#define configMINIMAL_STACK_SIZE                ( configSTACK_DEPTH_TYPE ) 2048
+#define configUSE_16_BIT_TICKS                  0
+
+#define configIDLE_SHOULD_YIELD                 1
+
+/* Synchronization Related */
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_APPLICATION_TASK_TAG          0
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               8
+#define configUSE_QUEUE_SETS                    1
+#define configUSE_TIME_SLICING                  1
+#define configUSE_NEWLIB_REENTRANT              0
+// todo need this for lwip FreeRTOS sys_arch to compile
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
+
+/* System */
+#define configSTACK_DEPTH_TYPE                  uint32_t
+#define configMESSAGE_BUFFER_LENGTH_TYPE        size_t
+
+/* Memory allocation related definitions. */
+#define configSUPPORT_STATIC_ALLOCATION         0
+#define configSUPPORT_DYNAMIC_ALLOCATION        1
+#define configTOTAL_HEAP_SIZE                   (128*1024)
+#define configAPPLICATION_ALLOCATED_HEAP        0
+
+/* Hook function related definitions. */
+#define configCHECK_FOR_STACK_OVERFLOW          0
+#define configUSE_MALLOC_FAILED_HOOK            0
+#define configUSE_DAEMON_TASK_STARTUP_HOOK      0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS           0
+#define configUSE_TRACE_FACILITY                1
+#define configUSE_STATS_FORMATTING_FUNCTIONS    0
+
+/* Co-routine related definitions. */
+#define configUSE_CO_ROUTINES                   0
+#define configMAX_CO_ROUTINE_PRIORITIES         1
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                        1
+#define configTIMER_TASK_PRIORITY               ( configMAX_PRIORITIES - 1 )
+#define configTIMER_QUEUE_LENGTH                10
+#define configTIMER_TASK_STACK_DEPTH            1024
+
+/* Interrupt nesting behaviour configuration. */
+/*
+#define configKERNEL_INTERRUPT_PRIORITY         [dependent of processor]
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY    [dependent on processor and application]
+#define configMAX_API_CALL_INTERRUPT_PRIORITY   [dependent on processor and application]
+*/
+
+#if FREE_RTOS_KERNEL_SMP // set by the RP2040 SMP port of FreeRTOS
+/* SMP port only */
+#define configNUM_CORES                         2
+#define configTICK_CORE                         0
+#define configRUN_MULTIPLE_PRIORITIES           1
+#define configUSE_CORE_AFFINITY                 1
+#endif
+
+/* RP2040 specific */
+#define configSUPPORT_PICO_SYNC_INTEROP         1
+#define configSUPPORT_PICO_TIME_INTEROP         1
+
+#include <assert.h>
+/* Define to trap errors during development. */
+#define configASSERT(x)                         assert(x)
+
+/* Set the following definitions to 1 to include the API function, or zero
+to exclude the API function. */
+#define INCLUDE_vTaskPrioritySet                1
+#define INCLUDE_uxTaskPriorityGet               1
+#define INCLUDE_vTaskDelete                     1
+#define INCLUDE_vTaskSuspend                    1
+#define INCLUDE_vTaskDelayUntil                 1
+#define INCLUDE_vTaskDelay                      1
+#define INCLUDE_xTaskGetSchedulerState          1
+#define INCLUDE_xTaskGetCurrentTaskHandle       1
+#define INCLUDE_uxTaskGetStackHighWaterMark     1
+#define INCLUDE_xTaskGetIdleTaskHandle          1
+#define INCLUDE_eTaskGetState                   1
+#define INCLUDE_xTimerPendFunctionCall          1
+#define INCLUDE_xTaskAbortDelay                 1
+#define INCLUDE_xTaskGetHandle                  1
+#define INCLUDE_xTaskResumeFromISR              1
+#define INCLUDE_xQueueGetMutexHolder            1
+
+/* A header file that defines trace macro can be included here. */
+
+#endif /* FREERTOS_CONFIG_H */
+

+ 2 - 0
entry/src/main/cpp/include/picow/README.md

@@ -0,0 +1,2 @@
+# Pico W config files
+These files should be seen as samples and you can start with these or use your own.  They are here so that the examples/picow sample will compile and build.  These have not been tuned in any way, other than to let libsmb2 compile with the Pico W, FreeRTOS and lwip.

+ 139 - 0
entry/src/main/cpp/include/picow/config.h

@@ -0,0 +1,139 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Whether or not TCP sockets should be allowed to linger after closure */
+#define CONFIGURE_OPTION_TCP_LINGER 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+/* #undef HAVE_ARPA_INET_H */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+/* #undef HAVE_ERRNO_H */
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <gssapi/gssapi.h> header file. */
+/* #undef HAVE_GSSAPI_GSSAPI_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+/* #undef HAVE_INTTYPES_H */
+
+/* Whether we use gssapi_krb5 or not */
+/* #undef HAVE_LIBKRB5 */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Whether we have linger */
+/* #undef HAVE_LINGER */
+
+/* Define to 1 if you have the <netdb.h> header file. */
+/* #undef HAVE_NETDB_H */
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+/* #undef HAVE_NETINET_IN_H */
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+/* #undef HAVE_NETINET_TCP_H */
+
+/* Define to 1 if you have the <poll.h> header file. */
+/* #undef HAVE_POLL_H */
+
+/* Whether sockaddr struct has sa_len */
+/* #undef HAVE_SOCKADDR_LEN */
+
+/* Whether we have sockaddr_Storage */
+#define HAVE_SOCKADDR_STORAGE 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+/* #undef HAVE_STRINGS_H */
+
+/* Define to 1 if you have the <string.h> header file. */
+/* #undef HAVE_STRING_H */
+
+/* Define to 1 if you have the <sys/errno.h> header file. */
+/* #undef HAVE_SYS_ERRNO_H */
+
+/* Define to 1 if you have the <sys/fcntl.h> header file. */
+/* #undef HAVE_SYS_FCNTL_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+/* #undef HAVE_SYS_IOCTL_H */
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+/* #undef HAVE_SYS_POLL_H */
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+/* #undef HAVE_SYS_SOCKET_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+/* #undef HAVE_SYS_TIME_H */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+/* #undef HAVE_SYS_UIO_H */
+
+/* Define to 1 if you have the <sys/unistd.h> header file. */
+/* #undef HAVE_SYS_UNISTD_H */
+
+/* Define to 1 if you have the <sys/_iovec.h> header file. */
+/* #undef HAVE_SYS__IOVEC_H */
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "libsmb2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "ronniesahlberg@gmail.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libsmb2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libsmb2 4.0.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libsmb2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "4.0.0"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "4.0.0"

+ 27 - 0
entry/src/main/cpp/include/picow/lwipopts.h

@@ -0,0 +1,27 @@
+#ifndef _LWIPOPTS_H
+#define _LWIPOPTS_H
+
+// Generally you would define your own explicit list of lwIP options
+// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html)
+//
+// This example uses a common include to avoid repetition
+#include "lwipopts_examples_common.h"
+
+#define LWIP_SO_RCVBUF 1
+#define LWIP_TIMEVAL_PRIVATE 0
+#if !NO_SYS
+#define TCPIP_THREAD_STACKSIZE 1024
+#define DEFAULT_THREAD_STACKSIZE 1024
+#define DEFAULT_RAW_RECVMBOX_SIZE 8
+#define TCPIP_MBOX_SIZE 8
+//#define LWIP_TIMEVAL_PRIVATE 0
+
+#define DEFAULT_UDP_RECVMBOX_SIZE TCPIP_MBOX_SIZE
+#define DEFAULT_TCP_RECVMBOX_SIZE TCPIP_MBOX_SIZE
+#define DEFAULT_ACCEPTMBOX_SIZE TCPIP_MBOX_SIZE
+
+// not necessary, can be done either way
+#define LWIP_TCPIP_CORE_LOCKING_INPUT 1
+#endif
+
+#endif

+ 90 - 0
entry/src/main/cpp/include/picow/lwipopts_examples_common.h

@@ -0,0 +1,90 @@
+#ifndef _LWIPOPTS_EXAMPLE_COMMONH_H
+#define _LWIPOPTS_EXAMPLE_COMMONH_H
+
+
+// Common settings used in most of the pico_w examples
+// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
+
+// allow override in some examples
+#ifndef NO_SYS
+#define NO_SYS                      1
+#endif
+// allow override in some examples
+#ifndef LWIP_SOCKET
+#define LWIP_SOCKET                 1
+#endif
+#if PICO_CYW43_ARCH_POLL
+#define MEM_LIBC_MALLOC             1
+#else
+// MEM_LIBC_MALLOC is incompatible with non polling versions
+#define MEM_LIBC_MALLOC             0
+#endif
+#define MEM_ALIGNMENT               4
+#define MEM_SIZE                    4000
+#define MEMP_NUM_TCP_SEG            32
+#define MEMP_NUM_ARP_QUEUE          10
+#define PBUF_POOL_SIZE              24
+#define LWIP_ARP                    1
+#define LWIP_ETHERNET               1
+#define LWIP_ICMP                   1
+#define LWIP_RAW                    1
+#define TCP_WND                     (8 * TCP_MSS)
+#define TCP_MSS                     1460
+#define TCP_SND_BUF                 (8 * TCP_MSS)
+#define TCP_SND_QUEUELEN            ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
+#define LWIP_NETIF_STATUS_CALLBACK  1
+#define LWIP_NETIF_LINK_CALLBACK    1
+#define LWIP_NETIF_HOSTNAME         1
+#define LWIP_NETCONN                0
+#define MEM_STATS                   0
+#define SYS_STATS                   0
+#define MEMP_STATS                  0
+#define LINK_STATS                  0
+// #define ETH_PAD_SIZE                2
+#define LWIP_CHKSUM_ALGORITHM       3
+#define LWIP_DHCP                   1
+#define LWIP_IPV4                   1
+#define LWIP_TCP                    1
+#define LWIP_UDP                    1
+#define LWIP_DNS                    1
+#define LWIP_TCP_KEEPALIVE          1
+#define LWIP_NETIF_TX_SINGLE_PBUF   1
+#define DHCP_DOES_ARP_CHECK         0
+#define LWIP_DHCP_DOES_ACD_CHECK    0
+
+#ifndef NDEBUG
+#define LWIP_DEBUG                  1
+#define LWIP_STATS                  1
+#define LWIP_STATS_DISPLAY          1
+#endif
+
+#define ETHARP_DEBUG                LWIP_DBG_OFF
+#define NETIF_DEBUG                 LWIP_DBG_OFF
+#define PBUF_DEBUG                  LWIP_DBG_OFF
+#define API_LIB_DEBUG               LWIP_DBG_OFF
+#define API_MSG_DEBUG               LWIP_DBG_OFF
+#define SOCKETS_DEBUG               LWIP_DBG_OFF
+#define ICMP_DEBUG                  LWIP_DBG_OFF
+#define INET_DEBUG                  LWIP_DBG_OFF
+#define IP_DEBUG                    LWIP_DBG_OFF
+#define IP_REASS_DEBUG              LWIP_DBG_OFF
+#define RAW_DEBUG                   LWIP_DBG_OFF
+#define MEM_DEBUG                   LWIP_DBG_OFF
+#define MEMP_DEBUG                  LWIP_DBG_OFF
+#define SYS_DEBUG                   LWIP_DBG_OFF
+#define TCP_DEBUG                   LWIP_DBG_OFF
+#define TCP_INPUT_DEBUG             LWIP_DBG_OFF
+#define TCP_OUTPUT_DEBUG            LWIP_DBG_OFF
+#define TCP_RTO_DEBUG               LWIP_DBG_OFF
+#define TCP_CWND_DEBUG              LWIP_DBG_OFF
+#define TCP_WND_DEBUG               LWIP_DBG_OFF
+#define TCP_FR_DEBUG                LWIP_DBG_OFF
+#define TCP_QLEN_DEBUG              LWIP_DBG_OFF
+#define TCP_RST_DEBUG               LWIP_DBG_OFF
+#define UDP_DEBUG                   LWIP_DBG_OFF
+#define TCPIP_DEBUG                 LWIP_DBG_OFF
+#define PPP_DEBUG                   LWIP_DBG_OFF
+#define SLIP_DEBUG                  LWIP_DBG_OFF
+#define DHCP_DEBUG                  LWIP_DBG_OFF
+
+#endif /* __LWIPOPTS_H__ */

+ 377 - 0
entry/src/main/cpp/include/portable-endian.h

@@ -0,0 +1,377 @@
+// "License": Public Domain
+// I, Mathias Panzenbck, place this file hereby into the public domain. Use it at your own risk for whatever you like.
+// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to
+// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it
+// an example on how to get the endian conversion functions on different platforms.
+
+#ifndef PORTABLE_ENDIAN_H__
+#define PORTABLE_ENDIAN_H__
+
+#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) && !defined(_XBOX)
+#define __WINDOWS__
+#endif
+
+#if defined(__PS2__) || defined(PICO_PLATFORM)
+
+#ifndef _LITTLE_ENDIAN
+#define _LITTLE_ENDIAN LITTLE_ENDIAN
+#endif
+#if defined(_EE) || defined(PICO_PLATFORM)
+#include <machine/endian.h>
+#ifdef PICO_PLATFORM
+#include "lwip/def.h"
+#endif
+#endif
+
+#define be16toh(x) PP_NTOHS(x)
+#define htobe16(x) PP_HTONS(x)
+#define htole16(x) (x)
+#define le16toh(x) (x)
+
+#define be32toh(x) PP_NTOHL(x)
+#define htobe32(x) PP_HTONL(x)
+#define htole32(x) (x)
+#define le32toh(x) (x)
+
+#define htobe64(x) be64toh(x)
+#define htole64(x) (x)
+#define le64toh(x) (x)
+
+#elif defined(__DREAMCAST__)
+
+#include <machine/endian.h>
+
+#define be16toh(x) __builtin_bswap16(x)
+#define htobe16(x) __builtin_bswap16(x)
+#define htole16(x) (x)
+#define le16toh(x) (x)
+
+#define be32toh(x) __builtin_bswap32(x)
+#define htobe32(x) __builtin_bswap32(x)
+#define htole32(x) (x)
+#define le32toh(x) (x)
+
+#define be64toh(x) __builtin_bswap64(x)
+#define htobe64(x) __builtin_bswap64(x)
+#define htole64(x) (x)
+#define le64toh(x) (x)
+
+#elif defined(__linux__) || defined(__CYGWIN__) || defined(ESP_PLATFORM) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__GNU__)
+
+#if defined(__linux__) || defined(__CYGWIN__) || defined(PS4_PLATFORM) || defined(ESP_PLATFORM) || defined(__GNU__)
+#include <endian.h>
+/* Include byteswap.h on linux since it might not be automatically included in some cases (e.g. alpine / musl) */
+#if defined(__linux__) || defined(__GLIBC__)
+#include <byteswap.h>
+#endif
+#else
+#include <sys/endian.h>
+#endif
+
+/* These 4 #defines may be needed with older esp-idf environments */
+#ifndef _LITTLE_ENDIAN
+#define _LITTLE_ENDIAN LITTLE_ENDIAN
+#endif
+
+#ifndef __bswap16
+#define __bswap16 __bswap_16
+#endif
+
+#ifndef __bswap32
+#define __bswap32 __bswap_32
+#endif
+
+#ifndef __bswap64
+#define __bswap64 __bswap_64
+#endif
+
+#ifndef be16toh
+#define be16toh(x) betoh16(x)
+#endif
+
+#ifndef le16toh
+#define le16toh(x) letoh16(x)
+#endif
+
+#ifndef be32toh
+#define be32toh(x) betoh32(x)
+#endif
+
+#ifndef le32toh
+#define le32toh(x) letoh32(x)
+#endif
+
+#ifndef be64toh
+#define be64toh(x) betoh64(x)
+#endif
+
+#ifndef le64toh
+#define le64toh(x) letoh64(x)
+#endif
+
+#elif defined(__APPLE__)
+
+#include <libkern/OSByteOrder.h>
+
+#define htobe16(x) OSSwapHostToBigInt16(x)
+#define htole16(x) OSSwapHostToLittleInt16(x)
+#define be16toh(x) OSSwapBigToHostInt16(x)
+#define le16toh(x) OSSwapLittleToHostInt16(x)
+
+#define htobe32(x) OSSwapHostToBigInt32(x)
+#define htole32(x) OSSwapHostToLittleInt32(x)
+#define be32toh(x) OSSwapBigToHostInt32(x)
+#define le32toh(x) OSSwapLittleToHostInt32(x)
+
+#define htobe64(x) OSSwapHostToBigInt64(x)
+#define htole64(x) OSSwapHostToLittleInt64(x)
+#define be64toh(x) OSSwapBigToHostInt64(x)
+#define le64toh(x) OSSwapLittleToHostInt64(x)
+
+#define __BYTE_ORDER BYTE_ORDER
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __PDP_ENDIAN PDP_ENDIAN
+
+#elif defined(PS3_PPU_PLATFORM) || defined(__WIIU__) || defined(__WII__) || defined(__GC__)
+
+#define htobe16(x) (x)
+#define htole16(x) __builtin_bswap16(x)
+#define be16toh(x) (x)
+#define le16toh(x) __builtin_bswap16(x)
+
+#define htobe32(x) (x)
+#define htole32(x) __builtin_bswap32(x)
+#define be32toh(x) (x)
+#define le32toh(x) __builtin_bswap32(x)
+
+#define htobe64(x) (x)
+#define htole64(x) __builtin_bswap64(x)
+#define be64toh(x) (x)
+#define le64toh(x) __builtin_bswap64(x)
+
+#elif defined(__SWITCH__) || defined(__N3DS__) || defined(__NDS__)
+
+#include <machine/endian.h>
+#define htobe16(x) __bswap16(x)
+#define htole16(x) (x)
+#define be16toh(x) __bswap16(x)
+#define le16toh(x) (x)
+
+#define htobe32(x) __bswap32(x)
+#define htole32(x) (x)
+#define be32toh(x) __bswap32(x)
+#define le32toh(x) (x)
+
+#define htobe64(x) __bswap64(x)
+#define htole64(x) (x)
+#define be64toh(x) __bswap64(x)
+#define le64toh(x) (x)
+
+#elif defined(__WINDOWS__) || defined(_XBOX)
+
+#ifdef _XBOX
+#include <xtl.h>
+#else
+#include <windows.h>
+#endif
+
+#if defined(_MSC_VER)
+#include <stdlib.h>
+
+#define htobe16(x) _byteswap_ushort(x)
+#define htole16(x) (x)
+#define be16toh(x) _byteswap_ushort(x)
+#define le16toh(x) (x)
+
+#define htobe32(x) _byteswap_ulong(x)
+#define htole32(x) (x)
+#define be32toh(x) _byteswap_ulong(x)
+#define le32toh(x) (x)
+
+#define htobe64(x) _byteswap_uint64(x)
+#define htole64(x) (x)
+#define be64toh(x) _byteswap_uint64(x)
+#define le64toh(x) (x)
+
+#ifndef __BYTE_ORDER
+#define __BYTE_ORDER BYTE_ORDER
+#endif
+
+#ifndef __BIG_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#endif
+
+#ifndef __LITTLE_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#endif
+
+#ifndef __PDP_ENDIAN
+#define __PDP_ENDIAN PDP_ENDIAN
+#endif
+
+#elif defined(__GNUC__) || defined(__clang__)
+
+#define htobe16(x) __builtin_bswap16(x)
+#define htole16(x) (x)
+#define be16toh(x) __builtin_bswap16(x)
+#define le16toh(x) (x)
+
+#define htobe32(x) __builtin_bswap32(x)
+#define htole32(x) (x)
+#define be32toh(x) __builtin_bswap32(x)
+#define le32toh(x) (x)
+
+#define htobe64(x) __builtin_bswap64(x)
+#define htole64(x) (x)
+#define be64toh(x) __builtin_bswap64(x)
+#define le64toh(x) (x)
+
+#else
+#error platform not supported
+#endif
+
+#elif defined(__amigaos4__) || defined(__AMIGA__)
+
+#if defined(__NEWLIB__)
+#include <machine/endian.h>
+
+#ifndef __bswap16
+#define __bswap16(x) __builtin_bswap16(x)
+#endif
+
+#ifndef __bswap32
+#define __bswap32(x) __builtin_bswap32(x)
+#endif
+
+#ifndef __bswap64
+#define __bswap64(x) __builtin_bswap64(x)
+#endif
+
+#define htobe16(x) (x)
+#define htole16(x) __bswap16(x)
+#define be16toh(x) (x)
+#define le16toh(x) __bswap16(x)
+
+#define htobe32(x) (x)
+#define htole32(x) __bswap32(x)
+#define be32toh(x) (x)
+#define le32toh(x) __bswap32(x)
+
+#define htobe64(x) (x)
+#define htole64(x) __bswap64(x)
+#define be64toh(x) (x)
+#define le64toh(x) __bswap64(x)
+
+#elif defined(__GNUC__)
+
+#define htobe16(x) (x)
+#define htole16(x) __builtin_bswap16(x)
+#define be16toh(x) (x)
+#define le16toh(x) __builtin_bswap16(x)
+
+#define htobe32(x) (x)
+#define htole32(x) __builtin_bswap32(x)
+#define be32toh(x) (x)
+#define le32toh(x) __builtin_bswap32(x)
+
+#define htobe64(x) (x)
+#define htole64(x) __builtin_bswap64(x)
+#define be64toh(x) (x)
+#define le64toh(x) __builtin_bswap64(x)
+
+#else
+#error platform not supported
+#endif
+
+#elif defined(__AROS__)
+
+#include <endian.h>
+
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 8))
+
+#define __bswap16(x) __builtin_bswap16(x)
+#define __bswap32(x) __builtin_bswap32(x)
+#define __bswap64(x) __builtin_bswap64(x)
+
+#else
+
+#define __bswap16(x) ((((uint16_t)(x) & 0xFF00) >> 8) | \
+                         (((uint16_t)(x) & 0x00FF) << 8))
+#define __bswap32(x) ((((uint32_t)(x) & 0xFF000000) >> 24) | \
+                         (((uint32_t)(x) & 0x00FF0000) >>  8) | \
+                         (((uint32_t)(x) & 0x0000FF00) <<  8) | \
+                         (((uint32_t)(x) & 0x000000FF) << 24))
+#define __bswap64(x) ((((uint64_t)(x) & 0xFF00000000000000) >> 56) | \
+                         (((uint64_t)(x) & 0x00FF000000000000) >> 40) | \
+                         (((uint64_t)(x) & 0x0000FF0000000000) >> 24) | \
+                         (((uint64_t)(x) & 0x000000FF00000000) >>  8) | \
+                         (((uint64_t)(x) & 0x00000000FF000000) <<  8) | \
+                         (((uint64_t)(x) & 0x0000000000FF0000) << 24) | \
+                         (((uint64_t)(x) & 0x000000000000FF00) << 40) | \
+                         (((uint64_t)(x) & 0x00000000000000FF) << 56))
+
+#endif
+
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+
+#define htobe16(x) __bswap16(x)
+#define htole16(x) (x)
+#define be16toh(x) __bswap16(x)
+#define le16toh(x) (x)
+
+#define htobe32(x) __bswap32(x)
+#define htole32(x) (x)
+#define be32toh(x) __bswap32(x)
+#define le32toh(x) (x)
+
+#define htobe64(x) __bswap64(x)
+#define htole64(x) (x)
+#define be64toh(x) __bswap64(x)
+#define le64toh(x) (x)
+
+#else
+
+#define htobe16(x) (x)
+#define htole16(x) __bswap16(x)
+#define be16toh(x) (x)
+#define le16toh(x) __bswap16(x)
+
+#define htobe32(x) (x)
+#define htole32(x) __bswap32(x)
+#define be32toh(x) (x)
+#define le32toh(x) __bswap32(x)
+
+#define htobe64(x) (x)
+#define htole64(x) __bswap64(x)
+#define be64toh(x) (x)
+#define le64toh(x) __bswap64(x)
+
+#endif
+
+#elif defined(__GNUC__) || defined(__clang__)
+
+#ifdef __vita__
+#include <machine/endian.h>
+#endif
+
+#define htobe16(x) __builtin_bswap16(x)
+#define htole16(x) (x)
+#define be16toh(x) __builtin_bswap16(x)
+#define le16toh(x) (x)
+
+#define htobe32(x) __builtin_bswap32(x)
+#define htole32(x) (x)
+#define be32toh(x) __builtin_bswap32(x)
+#define le32toh(x) (x)
+
+#define htobe64(x) __builtin_bswap64(x)
+#define htole64(x) (x)
+#define be64toh(x) __builtin_bswap64(x)
+#define le64toh(x) (x)
+
+#else
+#error platform not supported
+#endif
+
+#endif

+ 139 - 0
entry/src/main/cpp/include/ps3/config.h

@@ -0,0 +1,139 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Whether or not TCP sockets should be allowed to linger after closure */
+#define CONFIGURE_OPTION_TCP_LINGER 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+/* #undef HAVE_ARPA_INET_H */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+/* #undef HAVE_DLFCN_H */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <gssapi/gssapi.h> header file. */
+/* #undef HAVE_GSSAPI_GSSAPI_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+/* #undef HAVE_INTTYPES_H */
+
+/* Whether we use gssapi_krb5 or not */
+/* #undef HAVE_LIBKRB5 */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Whether we have linger */
+#define HAVE_LINGER 1
+
+/* Define to 1 if you have the <netdb.h> header file. */
+/* #undef HAVE_NETDB_H */
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+/* #undef HAVE_NETINET_TCP_H */
+
+/* Define to 1 if you have the <poll.h> header file. */
+/* #undef HAVE_POLL_H */
+
+/* Whether sockaddr struct has sa_len */
+/* #undef HAVE_SOCKADDR_LEN */
+
+/* Whether we have sockaddr_Storage */
+/* #undef HAVE_SOCKADDR_STORAGE */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+/* #undef HAVE_STRINGS_H */
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/errno.h> header file. */
+/* #undef HAVE_SYS_ERRNO_H */
+
+/* Define to 1 if you have the <sys/fcntl.h> header file. */
+/* #undef HAVE_SYS_FCNTL_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+/* #undef HAVE_SYS_IOCTL_H */
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+/* #undef HAVE_SYS_POLL_H */
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+/* #undef HAVE_SYS_SOCKET_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+/* #undef HAVE_SYS_STAT_H */
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+/* #undef HAVE_SYS_TIME_H */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+/* #undef HAVE_SYS_UIO_H */
+
+/* Define to 1 if you have the <sys/unistd.h> header file. */
+/* #undef HAVE_SYS_UNISTD_H */
+
+/* Define to 1 if you have the <sys/_iovec.h> header file. */
+/* #undef HAVE_SYS__IOVEC_H */
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "libsmb2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "ronniesahlberg@gmail.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libsmb2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libsmb2 4.0.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libsmb2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "4.0.0"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "4.0.0"

+ 63 - 0
entry/src/main/cpp/include/slist.h

@@ -0,0 +1,63 @@
+/*
+   Copyright (C) 2016 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __smb2_slist_h__
+#define __smb2_slist_h__
+
+#define SMB2_LIST_ADD(list, item) \
+	do {							\
+		(item)->next = (*list);				\
+		(*list) = (item);				\
+	} while (0);
+
+#define SMB2_LIST_ADD_END(list, item)	\
+	if ((*list) == NULL) {	 				\
+	   SMB2_LIST_ADD((list), (item));			\
+	} else {						\
+	   void *head = (*list);				\
+	   while ((*list)->next)				\
+	     (*list) = (*list)->next;				\
+	   (*list)->next = (item);				\
+	   (item)->next = NULL;					\
+	   (*list) = head;					\
+	}
+
+#define SMB2_LIST_REMOVE(list, item) \
+	if ((*list) == (item)) { 				\
+	   (*list) = (item)->next;				\
+	} else if (*list) {					\
+	   void *head = (*list);				\
+	   while ((*list)->next && (*list)->next != (item))     \
+	     (*list) = (*list)->next;				\
+	   if ((*list)->next != NULL) {		    	    	\
+	      (*list)->next = (*list)->next->next;		\
+	   }  		      					\
+	   (*list) = head;					\
+	}
+
+#define SMB2_LIST_LENGTH(list, length) \
+	do { \
+	    (length) = 0; \
+		void *head = (*list); \
+		while ((*list)) { \
+			(*list) = (*list)->next; \
+			(length)++; \
+		} \
+		(*list) = head; \
+	} while (0);
+
+#endif /* __smb2_slist_h__ */

+ 172 - 0
entry/src/main/cpp/include/smb2/libsmb2-dcerpc-lsa.h

@@ -0,0 +1,172 @@
+/* -*-  mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil;  -*- */
+/*
+   Copyright (C) 2020 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBSMB2_DCERPC_LSA_H_
+#define _LIBSMB2_DCERPC_LSA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LSA_CLOSE          0x00
+#define LSA_OPENPOLICY2    0x2c
+#define LSA_LOOKUPSIDS2    0x39
+
+/* Access Mask. LSA specific flags. */
+#define POLICY_VIEW_LOCAL_INFORMATION    0x00000001
+#define POLICY_VIEW_AUDIT_INFORMATION    0x00000002
+#define POLICY_GET_PRIVATE_INFORMATION   0x00000004
+#define POLICY_TRUST_ADMIN               0x00000008
+#define POLICY_CREATE_ACCOUNT            0x00000010
+#define POLICY_CREATE_SECRET             0x00000020
+#define POLICY_CREATE_PRIVILEGE          0x00000040
+#define POLICY_SET_DEFAULT_QUOTA_LIMITS  0x00000080
+#define POLICY_SET_AUDIT_REQUIREMENTS    0x00000100
+#define POLICY_AUDIT_LOG_ADMIN           0x00000200
+#define POLICY_SERVER_ADMIN              0x00000400
+#define POLICY_LOOKUP_NAMES              0x00000800
+#define POLICY_NOTIFICATION              0x00001000
+
+extern unsigned char NT_SID_AUTHORITY[6];
+
+typedef struct RPC_SID {
+        uint8_t Revision;
+        uint8_t SubAuthorityCount;
+        uint8_t IdentifierAuthority[6];
+        uint32_t *SubAuthority;
+} RPC_SID, *PRPC_SID;
+
+typedef struct _LSAPR_TRANSLATED_NAME_EX {
+        uint32_t Use;
+        char *Name;
+        uint32_t DomainIndex;
+        uint32_t Flags;
+} LSAPR_TRANSLATED_NAME_EX, *PLSAPR_TRANSLATED_NAME_EX;
+
+typedef struct _LSAPR_TRANSLATED_NAMES_EX {
+        uint32_t Entries;
+        LSAPR_TRANSLATED_NAME_EX  *Names;
+} LSAPR_TRANSLATED_NAMES_EX, *PLSAPR_TRANSLATED_NAMES_EX;
+
+typedef struct _SID_ENUM_BUFFER {
+        uint32_t Entries;
+        PRPC_SID *SidInfo;
+} LSAPR_SID_ENUM_BUFFER, *PLSAPR_SID_ENUM_BUFFER;
+
+typedef enum _LSAP_LOOKUP_LEVEL {
+        LsapLookupWksta = 1,
+        LsapLookupPDC,
+        LsapLookupTDL,
+        LsapLookupGC,
+        LsapLookupXForestReferral,
+        LsapLookupXForestResolve,
+        LsapLookupRODCReferralToFullDC
+} LSAP_LOOKUP_LEVEL, *PLSAP_LOOKUP_LEVEL;
+
+typedef struct _LSAPR_TRUST_INFORMATION {
+        char *Name;
+        RPC_SID Sid;
+} LSAPR_TRUST_INFORMATION, *PLSAPR_TRUST_INFORMATION;
+
+typedef struct _LSAPR_REFERENCED_DOMAIN_LIST {
+        uint32_t Entries;
+        LSAPR_TRUST_INFORMATION *Domains;
+        uint32_t MaxEntries; /* must be ignored */
+} LSAPR_REFERENCED_DOMAIN_LIST, *PLSAPR_REFERENCED_DOMAIN_LIST;
+
+/* For OPENPOLICY2: RootDirectory MUST be zero. Everything else is ignored. */
+typedef struct _LSAPR_OBJECT_ATTRIBUTES {
+        uint32_t Length;
+        unsigned char *RootDirectory;
+        void *ObjectName;
+        uint32_t Attributes;
+        void *SecurityDescriptor;
+        void *SecurityQualityOfService;
+} LSAPR_OBJECT_ATTRIBUTES, *PLSAPR_OBJECT_ATTRIBUTES;
+
+struct lsa_close_req {
+        struct ndr_context_handle PolicyHandle;
+};
+
+struct lsa_close_rep {
+        uint32_t status;
+
+        struct ndr_context_handle PolicyHandle;
+};
+
+struct lsa_openpolicy2_req {
+        char *SystemName;
+        LSAPR_OBJECT_ATTRIBUTES ObjectAttributes;
+        uint32_t DesiredAccess;
+};
+
+struct lsa_openpolicy2_rep {
+        uint32_t status;
+
+        struct ndr_context_handle PolicyHandle;
+};
+
+struct lsa_lookupsids2_req {
+        struct ndr_context_handle PolicyHandle;
+        LSAPR_SID_ENUM_BUFFER SidEnumBuffer;
+        LSAPR_TRANSLATED_NAMES_EX TranslatedNames;
+        LSAP_LOOKUP_LEVEL LookupLevel;
+};
+
+struct lsa_lookupsids2_rep {
+        uint32_t status;
+
+        LSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains;
+        LSAPR_TRANSLATED_NAMES_EX TranslatedNames;
+        uint32_t MappedCount;
+};
+
+int lsa_Close_rep_coder(struct dcerpc_context *dce,
+                        struct dcerpc_pdu *pdu,
+                        struct smb2_iovec *iov, int *offset,
+                        void *ptr);
+int lsa_Close_req_coder(struct dcerpc_context *dce,
+                        struct dcerpc_pdu *pdu,
+                        struct smb2_iovec *iov, int *offset,
+                        void *ptr);
+int lsa_LookupSids2_rep_coder(struct dcerpc_context *dce,
+                              struct dcerpc_pdu *pdu,
+                              struct smb2_iovec *iov, int *offset,
+                              void *ptr);
+int lsa_LookupSids2_req_coder(struct dcerpc_context *dce,
+                              struct dcerpc_pdu *pdu,
+                              struct smb2_iovec *iov, int *offset,
+                              void *ptr);
+int lsa_OpenPolicy2_rep_coder(struct dcerpc_context *dce,
+                              struct dcerpc_pdu *pdu,
+                              struct smb2_iovec *iov, int *offset,
+                              void *ptr);
+int lsa_OpenPolicy2_req_coder(struct dcerpc_context *dce,
+                              struct dcerpc_pdu *pdu,
+                              struct smb2_iovec *iov, int *offset,
+                              void *ptr);
+int lsa_RPC_SID_coder(struct dcerpc_context *dce,
+                      struct dcerpc_pdu *pdu,
+                      struct smb2_iovec *iov, int *offset,
+                      void *ptr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_LIBSMB2_DCERPC_LSA_H_ */

+ 180 - 0
entry/src/main/cpp/include/smb2/libsmb2-dcerpc-srvsvc.h

@@ -0,0 +1,180 @@
+/* -*-  mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil;  -*- */
+/*
+   Copyright (C) 2018 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBSMB2_DCERPC_SRVSVC_H_
+#define _LIBSMB2_DCERPC_SRVSVC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <smb2/libsmb2-dcerpc.h>
+
+#define SRVSVC_NETRSHAREENUM      0x0f
+#define SRVSVC_NETRSHAREGETINFO   0x10
+
+struct dcerpc_context;
+struct dcerpc_pdu;
+
+
+/* Low 2 bits desctibe the type */
+#define SHARE_TYPE_DISKTREE  0
+#define SHARE_TYPE_PRINTQ    1
+#define SHARE_TYPE_DEVICE    2
+#define SHARE_TYPE_IPC       3
+
+#define SHARE_TYPE_TEMPORARY 0x40000000
+#define SHARE_TYPE_HIDDEN    0x80000000
+
+enum SHARE_INFO_enum {
+        SHARE_INFO_0 = 0,
+        SHARE_INFO_1 = 1,
+};
+
+struct srvsvc_SHARE_INFO_0 {
+        struct dcerpc_utf16 netname;
+};
+int srvsvc_SHARE_INFO_0_coder(struct dcerpc_context *ctx,
+                              struct dcerpc_pdu *pdu,
+                              struct smb2_iovec *iov, int *offset,
+                              void *ptr);
+
+struct srvsvc_SHARE_INFO_0_carray {
+        uint32_t max_count;
+        struct srvsvc_SHARE_INFO_0 *share_info_0;
+};
+
+struct srvsvc_SHARE_INFO_0_CONTAINER {
+        uint32_t EntriesRead;
+        struct srvsvc_SHARE_INFO_0_carray *Buffer;
+};
+
+struct srvsvc_SHARE_INFO_1 {
+        struct dcerpc_utf16 netname;
+        uint32_t type;
+        struct dcerpc_utf16 remark;
+};
+int srvsvc_SHARE_INFO_1_coder(struct dcerpc_context *ctx,
+                              struct dcerpc_pdu *pdu,
+                              struct smb2_iovec *iov, int *offset,
+                              void *ptr);
+
+struct srvsvc_SHARE_INFO_1_carray {
+        uint32_t max_count;
+        struct srvsvc_SHARE_INFO_1 *share_info_1;
+};
+
+struct srvsvc_SHARE_INFO_1_CONTAINER {
+        uint32_t EntriesRead;
+        struct srvsvc_SHARE_INFO_1_carray *Buffer;
+};
+        
+int srvsvc_SHARE_INFO_1_CONTAINER_coder(struct dcerpc_context *dce,
+                                        struct dcerpc_pdu *pdu,
+                                        struct smb2_iovec *iov, int *offset,
+                                        void *ptr);
+
+struct srvsvc_SHARE_ENUM_UNION {
+        uint32_t Level;
+        union {
+                struct srvsvc_SHARE_INFO_0_CONTAINER Level0;
+                struct srvsvc_SHARE_INFO_1_CONTAINER Level1;
+        };
+};
+
+struct srvsvc_SHARE_ENUM_STRUCT {
+        uint32_t Level;
+        struct srvsvc_SHARE_ENUM_UNION ShareInfo;
+};
+
+struct srvsvc_NetrShareEnum_req {
+        struct dcerpc_utf16 ServerName;
+        struct srvsvc_SHARE_ENUM_STRUCT ses;
+        uint32_t PreferedMaximumLength;
+        uint32_t ResumeHandle;
+};
+
+struct srvsvc_NetrShareEnum_rep {
+        uint32_t status;
+
+        struct srvsvc_SHARE_ENUM_STRUCT ses;
+        uint32_t total_entries;
+        uint32_t resume_handle;
+};
+
+struct srvsvc_SHARE_INFO {
+        uint32_t level;
+        union {
+                struct srvsvc_SHARE_INFO_1 ShareInfo1;
+        };
+};
+
+struct srvsvc_NetrShareGetInfo_req {
+        struct dcerpc_utf16 ServerName;
+        struct dcerpc_utf16 NetName;
+        uint32_t Level;
+};
+
+struct srvsvc_NetrShareGetInfo_rep {
+        uint32_t status;
+
+        struct srvsvc_SHARE_INFO InfoStruct;
+};
+
+struct srvsvc_rep {
+        uint32_t status;
+};
+
+/*
+ * Async share_enum()
+ * This function only works when connected to the IPC$ share.
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success. Command_data is struct srvsvc_NetrShareEnum_rep *
+ *          This pointer must be freed using smb2_free_data().
+ * -errno : An error occurred.
+ */
+int smb2_share_enum_async(struct smb2_context *smb2, enum SHARE_INFO_enum level,
+                          smb2_command_cb cb, void *cb_data);
+
+int srvsvc_NetrShareEnum_rep_coder(struct dcerpc_context *dce,
+                                   struct dcerpc_pdu *pdu,
+                                   struct smb2_iovec *iov, int *offset,
+                                   void *ptr);
+int srvsvc_NetrShareEnum_req_coder(struct dcerpc_context *ctx,
+                                   struct dcerpc_pdu *pdu,
+                                   struct smb2_iovec *iov, int *offset,
+                                   void *ptr);
+int srvsvc_NetrShareGetInfo_rep_coder(struct dcerpc_context *dce,
+                                      struct dcerpc_pdu *pdu,
+                                      struct smb2_iovec *iov, int *offset,
+                                      void *ptr);
+int srvsvc_NetrShareGetInfo_req_coder(struct dcerpc_context *ctx,
+                                      struct dcerpc_pdu *pdu,
+                                      struct smb2_iovec *iov, int *offset,
+                                      void *ptr);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_LIBSMB2_DCERPC_SRVSVC_H_ */

+ 155 - 0
entry/src/main/cpp/include/smb2/libsmb2-dcerpc.h

@@ -0,0 +1,155 @@
+/* -*-  mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil;  -*- */
+/*
+   Copyright (C) 2018 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBSMB2_DCERPC_H_
+#define _LIBSMB2_DCERPC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Data representation */
+/* Integer */
+#define DCERPC_DR_BIG_ENDIAN                    0x00
+#define DCERPC_DR_LITTLE_ENDIAN                 0x10
+/* Character */
+#define DCERPC_DR_ASCII                         0x00
+#define DCERPC_DR_EBCDIC                        0x01
+
+struct dcerpc_context;
+struct dcerpc_pdu;
+
+/* Encoder/Decoder for a DCERPC object */
+typedef int (*dcerpc_coder)(struct dcerpc_context *dce, struct dcerpc_pdu *pdu,
+                            struct smb2_iovec *iov, int *offset,
+                            void *ptr);
+
+enum ptr_type {
+        PTR_REF    = 0,
+        PTR_UNIQUE = 1,
+        PTR_FULL   = 2
+};
+
+typedef struct dcerpc_uuid {
+        uint32_t v1;
+        uint16_t v2;
+        uint16_t v3;
+        uint8_t v4[8];
+} dcerpc_uuid_t;
+
+typedef struct p_syntax_id {
+        dcerpc_uuid_t uuid;
+        uint16_t vers;
+        uint16_t vers_minor;
+} p_syntax_id_t;
+
+struct ndr_transfer_syntax {
+        dcerpc_uuid_t uuid;
+        uint16_t vers;
+};
+
+struct ndr_context_handle {
+        uint32_t context_handle_attributes;
+        dcerpc_uuid_t context_handle_uuid;
+};
+
+struct dcerpc_utf16 {
+        uint32_t max_count;       /* internal use only */
+        uint32_t offset;          /* internal use only */
+        uint32_t actual_count;    /* internal use only */
+
+        struct smb2_utf16 *utf16; /* internal use only */
+        
+        const char *utf8;
+};
+
+struct dcerpc_carray {
+        uint32_t max_count;
+        uint8_t *data;
+};
+        
+extern p_syntax_id_t lsa_interface;
+extern p_syntax_id_t srvsvc_interface;
+        
+typedef void (*dcerpc_cb)(struct dcerpc_context *dce, int status,
+                          void *command_data, void *cb_data);
+
+struct dcerpc_context *dcerpc_create_context(struct smb2_context *smb2);
+void dcerpc_free_data(struct dcerpc_context *dce, void *data);
+const char *dcerpc_get_error(struct dcerpc_context *dce);
+int dcerpc_connect_context_async(struct dcerpc_context *dce,
+                                 const char *path, p_syntax_id_t *syntax,
+                                 dcerpc_cb cb, void *cb_data);
+void dcerpc_destroy_context(struct dcerpc_context *dce);
+
+struct smb2_context *dcerpc_get_smb2_context(struct dcerpc_context *dce);
+void *dcerpc_get_pdu_payload(struct dcerpc_pdu *pdu);
+
+int dcerpc_open_async(struct dcerpc_context *dce, dcerpc_cb cb, void *cb_data);
+int dcerpc_call_async(struct dcerpc_context *dce,
+                      int opnum,
+                      dcerpc_coder req_coder, void *req,
+                      dcerpc_coder rep_coder, int decode_size,
+                      dcerpc_cb cb, void *cb_data);
+
+int dcerpc_do_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu,
+                    struct smb2_iovec *iov,
+                    int *offset, void *ptr,
+                    dcerpc_coder coder);
+int dcerpc_ptr_coder(struct dcerpc_context *dce, struct dcerpc_pdu *pdu,
+                     struct smb2_iovec *iov, int *offset, void *ptr,
+                     enum ptr_type type, dcerpc_coder coder);
+int dcerpc_carray_coder(struct dcerpc_context *ctx,
+                        struct dcerpc_pdu *pdu,
+                        struct smb2_iovec *iov, int *offset,
+                        void *ptr, int elem_size, dcerpc_coder coder);
+int dcerpc_uint8_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu,
+                    struct smb2_iovec *iov, int *offset, void *ptr);
+int dcerpc_uint16_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu,
+                     struct smb2_iovec *iov, int *offset, void *ptr);
+int dcerpc_uint32_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu,
+                     struct smb2_iovec *iov, int *offset, void *ptr);
+int dcerpc_uint3264_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu,
+                       struct smb2_iovec *iov, int *offset, void *ptr);
+int dcerpc_conformance_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu,
+                       struct smb2_iovec *iov, int *offset, void *ptr);
+int dcerpc_utf16_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu,
+                      struct smb2_iovec *iov, int *offset, void *ptr);
+int dcerpc_utf16z_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu,
+                        struct smb2_iovec *iov, int *offset, void *ptr);
+int dcerpc_context_handle_coder(struct dcerpc_context *dce,
+                                struct dcerpc_pdu *pdu,
+                                struct smb2_iovec *iov, int *offset,
+                                void *ptr);
+int dcerpc_uuid_coder(struct dcerpc_context *dce,
+                      struct dcerpc_pdu *pdu,
+                      struct smb2_iovec *iov, int *offset,
+                      dcerpc_uuid_t *uuid);
+int dcerpc_uint8_coder(struct dcerpc_context *ctx, struct dcerpc_pdu *pdu,
+                    struct smb2_iovec *iov, int *offset, void *ptr);
+#define DCERPC_DECODE 0
+#define DCERPC_ENCODE 1
+struct dcerpc_pdu *dcerpc_allocate_pdu(struct dcerpc_context *dce,
+                                       int direction, int payload_size);
+void dcerpc_free_pdu(struct dcerpc_context *dce, struct dcerpc_pdu *pdu);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_LIBSMB2_DCERPC_H_ */

+ 497 - 0
entry/src/main/cpp/include/smb2/libsmb2-raw.h

@@ -0,0 +1,497 @@
+/* -*-  mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil;  -*- */
+/*
+   Copyright (C) 2016 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBSMB2_RAW_H_
+#define _LIBSMB2_RAW_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Low level RAW SMB2 interface
+ */
+/*
+ * Magic file Id used for compound commands.
+ */
+extern const smb2_file_id compound_file_id;
+
+/*
+ * This function is used to free the data returned by the query functions.
+ */
+void smb2_free_data(struct smb2_context *smb2, void *ptr);
+
+/*
+ * Asynchronous SMB2 Negotiate
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the negotiate will be reported through the callback
+ *        function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Returns:
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Negotiate was successful.
+ *            Command_data is a struct smb2_negotiate_reply.
+ *
+ *   !0     : Status is NT status code. Command_data is NULL.
+ */
+struct smb2_pdu *smb2_cmd_negotiate_async(struct smb2_context *smb2,
+                                          struct smb2_negotiate_request *req,
+                                          smb2_command_cb cb, void *cb_data);
+
+
+struct smb2_pdu *smb2_cmd_negotiate_reply_async(struct smb2_context *smb2,
+                                          struct smb2_negotiate_reply *rep,
+                                          smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Session Setup
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the session setup will be reported through the callback
+ *        function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Session setup was successful.
+ *            Command_data is a struct smb2_session_setup_reply.
+ *
+ *   !0     : Status is NT status code.
+ */
+struct smb2_pdu *smb2_cmd_session_setup_async(struct smb2_context *smb2,
+                                 struct smb2_session_setup_request *req,
+                                 smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_session_setup_reply_async(struct smb2_context *smb2,
+                                struct smb2_session_setup_reply *rep,
+                                smb2_command_cb cb, void *cb_data);
+/*
+ * Asynchronous SMB2 Tree Connect
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the tree connect will be reported through the callback
+ *        function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Tree Connect was successful.
+ *            Command_data is a struct smb2_tree_connect_reply.
+ *
+ *   !0     : Status is NT status code. Command_data is NULL.
+ */
+struct smb2_pdu *smb2_cmd_tree_connect_async(struct smb2_context *smb2,
+                                struct smb2_tree_connect_request *req,
+                                smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_tree_connect_reply_async(struct smb2_context *smb2,
+                            struct smb2_tree_connect_reply *rep,
+                            uint32_t tree_id,
+                            smb2_command_cb cb, void *cb_data);
+/*
+ * Asynchronous SMB2 Tree Disconnect
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the tree connect will be reported through the callback
+ *        function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Tree Disconnect was successful.
+ *
+ *   !0     : Status is NT status code.
+ *
+ * Command_data is always NULL.
+ */
+struct smb2_pdu *smb2_cmd_tree_disconnect_async(struct smb2_context *smb2,
+                                  smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_tree_disconnect_reply_async(struct smb2_context *smb2,
+                                  smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Create
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the create will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Create was successful.
+ *            Command_data is a struct smb2_create_reply.
+ *
+ *   !0     : Status is NT status code. Command_data is NULL.
+ */
+struct smb2_pdu *smb2_cmd_create_async(struct smb2_context *smb2,
+                                       struct smb2_create_request *req,
+                                       smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_create_reply_async(struct smb2_context *smb2,
+                                       struct smb2_create_reply *rep,
+                                       smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Close
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the close will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Close was successful.
+ *            Command_data is a struct smb2_close_reply.
+ *
+ *   !0     : Status is NT status code. Command_data is NULL.
+ */
+struct smb2_pdu *smb2_cmd_close_async(struct smb2_context *smb2,
+                                      struct smb2_close_request *req,
+                                      smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_close_reply_async(struct smb2_context *smb2,
+                                      struct smb2_close_reply *rep,
+                                      smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Read
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the read will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Read was successful.
+ *
+ *   !0     : Status is NT status code.
+ *
+ * command_data is always NULL.
+ */
+struct smb2_pdu *smb2_cmd_read_async(struct smb2_context *smb2,
+                                     struct smb2_read_request *req,
+                                     smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_read_reply_async(struct smb2_context *smb2,
+                                     struct smb2_read_reply *rep,
+                                     smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Write
+ *
+ * use pass_buf_ownerhip non-0 to allow the request's buf to be
+ * freed along with the pdu when it is freed, use 0 to retain
+ * req->buf
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the write will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Write was successful.
+ *
+ *   !0     : Status is NT status code.
+ *
+ * command_data is always NULL.
+ */
+struct smb2_pdu *smb2_cmd_write_async(struct smb2_context *smb2,
+                                      struct smb2_write_request *req,
+                                      int pass_buf_ownership,
+                                      smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_write_reply_async(struct smb2_context *smb2,
+                                      struct smb2_write_reply *rep,
+                                      smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Query Directory
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the QD will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Query was successful.
+ *            Command_data is a struct smb2_query_directory_reply.
+ *
+ *   !0     : Status is NT status code. Command_data is NULL.
+ */
+struct smb2_pdu *smb2_cmd_query_directory_async(struct smb2_context *smb2,
+                             struct smb2_query_directory_request *req,
+                             smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_query_directory_reply_async(struct smb2_context *smb2,
+                             struct smb2_query_directory_request *req,
+                             struct smb2_query_directory_reply *rep,
+                             smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Change Notify
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the close will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Close was successful.
+ *            Command_data is a struct smb2_close_reply.
+ *
+ *   !0     : Status is NT status code. Command_data is NULL.
+ */
+struct smb2_pdu *smb2_cmd_change_notify_async(struct smb2_context *smb2,
+                                      struct smb2_change_notify_request *req,
+                                      smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_change_notify_reply_async(struct smb2_context *smb2,
+                                      struct smb2_change_notify_reply *rep,
+                                      smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Query Info
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the QI will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Query was successful.
+ *            Command_data is a struct struct smb2_query_info_reply *
+ *            This structure contains a pointer to the requested data
+ *            structure in ->output_buffer.
+ *            Output_buffer must be freed by calling smb2_free_data()
+ *
+ *   !0     : Status is NT status code. Command_data is NULL.
+ */
+struct smb2_pdu *smb2_cmd_query_info_async(struct smb2_context *smb2,
+                                           struct smb2_query_info_request *req,
+                                           smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_query_info_reply_async(struct smb2_context *smb2,
+                                           struct smb2_query_info_request *req,
+                                           struct smb2_query_info_reply *rep,
+                                           smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Set Info
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the QI will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Query was successful.
+ *            Command_data is a struct struct smb2_query_info_reply *
+ *
+ *   !0     : Status is NT status code. Command_data is NULL.
+ */
+struct smb2_pdu *smb2_cmd_set_info_async(struct smb2_context *smb2,
+                                         struct smb2_set_info_request *req,
+                                         smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_set_info_reply_async(struct smb2_context *smb2,
+                                         struct smb2_set_info_request *req,
+                                         smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Ioctl
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the Ioctl will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Query was successful.
+ *            Command_data is a struct struct smb2_ioctl_reply *
+ *            This structure contains a pointer to the requested data
+ *            structure in ->output.
+ *            Output must be freed by calling smb2_free_data()
+ *
+ *   !0     : Status is NT status code. Command_data is NULL.
+ */
+struct smb2_pdu *smb2_cmd_ioctl_async(struct smb2_context *smb2,
+                                      struct smb2_ioctl_request *req,
+                                      smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_ioctl_reply_async(struct smb2_context *smb2,
+                                      struct smb2_ioctl_reply *rep,
+                                      smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Echo
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the echo will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Echo was successful.
+ *
+ *   !0     : Status is NT status code.
+ *
+ * command_data is always NULL.
+ */
+struct smb2_pdu *smb2_cmd_echo_async(struct smb2_context *smb2,
+                                     smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_echo_reply_async(struct smb2_context *smb2,
+                                     smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Lock
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the logoff will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Lock was successful.
+ *
+ *   !0     : Status is NT status code.
+ *
+ * command_data is always NULL.
+ */
+struct smb2_pdu *smb2_cmd_lock_async(struct smb2_context *smb2,
+                                       struct smb2_lock_request *req,
+                                       smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_lock_reply_async(struct smb2_context *smb2,
+                                       smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Logoff
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the logoff will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Logoff was successful.
+ *
+ *   !0     : Status is NT status code.
+ *
+ * command_data is always NULL.
+ */
+struct smb2_pdu *smb2_cmd_logoff_async(struct smb2_context *smb2,
+                                       smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_logoff_reply_async(struct smb2_context *smb2,
+                                       smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 Flush
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the flush will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Flush was successful.
+ *
+ *   !0     : Status is NT status code.
+ *
+ * command_data is always NULL.
+ */
+struct smb2_pdu *smb2_cmd_flush_async(struct smb2_context *smb2,
+                                      struct smb2_flush_request *req,
+                                      smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_flush_reply_async(struct smb2_context *smb2,
+                                      smb2_command_cb cb, void *cb_data);
+
+/*
+ * Asynchronous SMB2 oplock-break;
+ *
+ * Returns:
+ * pdu  : If the call was initiated and a connection will be attempted.
+ *        Result of the flush will be reported through the callback function.
+ * NULL : If there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : successful.
+ *
+ *   !0     : Status is NT status code.
+ *
+ * command_data is always NULL.
+ */
+struct smb2_pdu *smb2_cmd_oplock_break_async(struct smb2_context *smb2,
+                                      struct smb2_oplock_break_acknowledgement *req,
+                                      smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_oplock_break_reply_async(struct smb2_context *smb2,
+                                      struct smb2_oplock_break_reply *rep,
+                                      smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_oplock_break_notification_async(struct smb2_context *smb2,
+                                      struct smb2_oplock_break_notification *rep,
+                                      smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_lease_break_async(struct smb2_context *smb2,
+                                      struct smb2_lease_break_acknowledgement *req,
+                                      smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_lease_break_reply_async(struct smb2_context *smb2,
+                                      struct smb2_lease_break_reply *rep,
+                                      smb2_command_cb cb, void *cb_data);
+
+struct smb2_pdu *smb2_cmd_lease_break_notification_async(struct smb2_context *smb2,
+                                      struct smb2_lease_break_notification *req,
+                                      smb2_command_cb cb, void *cb_data);
+/*
+ *
+ */
+struct smb2_pdu *smb2_cmd_error_reply_async(struct smb2_context *smb2,
+                                      struct smb2_error_reply *rep,
+                                      uint8_t causing_command,
+                                      int status,
+                                      smb2_command_cb cb, void *cb_data);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_LIBSMB2_RAW_H_ */

+ 1406 - 0
entry/src/main/cpp/include/smb2/libsmb2.h

@@ -0,0 +1,1406 @@
+/* -*-  mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil;  -*- */
+/*
+   Copyright (C) 2016 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBSMB2_H_
+#define _LIBSMB2_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LIBSMB2_SHARE_ENUM_V2 1
+
+struct smb2_iovec {
+        uint8_t *buf;
+        size_t len;
+        void (*free)(void *);
+};
+
+struct smb2_context;
+
+/*
+ * Generic callback for completion of smb2_*_async().
+ * command_data depends on status.
+ */
+typedef void (*smb2_command_cb)(struct smb2_context *smb2, int status,
+                                void *command_data, void *cb_data);
+
+/*
+ * callback for getting error information when errors are set
+ * command_data depends on status.
+ */
+typedef void (*smb2_error_cb)(struct smb2_context *smb2,
+                                const char *error_string);
+
+/*
+ * callback for server accepting a new connection
+ */
+typedef int (*smb2_accepted_cb)(const int fd, void *cb_data);
+
+/*
+ * callback when a new connection is made to setup context
+ */
+typedef void (*smb2_client_connection)(struct smb2_context *smb2, void *cb_data);
+
+/*
+ * callback when a server notifies of an oplock or lease break
+ * the type of break is determined from the stuct_size in the request
+ * (notification) passed in.  the app can set the new oplock level
+ * or new lease state for the acknowledgement that will be sent back
+ */
+struct smb2_oplock_or_lease_break_reply;
+
+typedef void (*smb2_oplock_or_lease_break_cb)(struct smb2_context *smb2,
+           int status,
+           struct smb2_oplock_or_lease_break_reply *rep,
+           uint8_t *new_oplock_level,
+           uint32_t *new_lease_state);
+
+/* Stat structure */
+#define SMB2_TYPE_FILE      0x00000000
+#define SMB2_TYPE_DIRECTORY 0x00000001
+#define SMB2_TYPE_LINK      0x00000002
+struct smb2_stat_64 {
+        uint32_t smb2_type;
+        uint32_t smb2_nlink;
+        uint64_t smb2_ino;
+        uint64_t smb2_size;
+        uint64_t smb2_atime;
+        uint64_t smb2_atime_nsec;
+        uint64_t smb2_mtime;
+        uint64_t smb2_mtime_nsec;
+        uint64_t smb2_ctime;
+        uint64_t smb2_ctime_nsec;
+        uint64_t smb2_btime;
+        uint64_t smb2_btime_nsec;
+};
+
+struct smb2_statvfs {
+        uint32_t        f_bsize;
+        uint32_t        f_frsize;
+        uint64_t        f_blocks;
+        uint64_t        f_bfree;
+        uint64_t        f_bavail;
+        uint32_t        f_files;
+        uint32_t        f_ffree;
+        uint32_t        f_favail;
+        uint32_t        f_fsid;
+        uint32_t        f_flag;
+        uint32_t        f_namemax;
+};
+
+struct smb2dirent {
+        const char *name;
+        struct smb2_stat_64 st;
+};
+
+#if defined(_WINDOWS)
+#ifdef __USE_WINSOCK__
+#include <winsock.h>
+#else
+#include <ws2tcpip.h>
+#include <winsock2.h>
+#endif
+#elif defined(_XBOX)
+#include <xtl.h>
+#include <winsockx.h>
+#endif
+
+#if defined(_WINDOWS) || defined(_XBOX)
+typedef SOCKET t_socket;
+#else
+typedef int t_socket;
+#endif
+
+/*
+ * Create an SMB2 context.
+ * Function returns
+ *  NULL  : Failed to create a context.
+ *  *smb2 : A pointer to an smb2 context.
+ */
+struct smb2_context *smb2_init_context(void);
+
+/*
+ * Close an SMB2 context
+ *
+ * closes socket if open, and clears keys but leave
+ * context allocated.  the context will be destroyed
+ * at a time later when it won't be in-use
+ */
+void smb2_close_context(struct smb2_context *smb2);
+
+/*
+ * Destroy an smb2 context.
+ *
+ * Any open "struct smb2fh" will automatically be freed. You can not reference
+ * any "struct smb2fh" after the context is destroyed.
+ * Any open "struct smb2dir" will automatically be freed. You can not reference
+ * any "struct smb2dir" after the context is destroyed.
+ * Any pending async commands will be aborted with -ECONNRESET.
+ */
+void smb2_destroy_context(struct smb2_context *smb2);
+
+/*
+ * Get the list of currently allocated contexts
+ */
+struct smb2_context *smb2_active_contexts(void);
+
+/*
+ * Determine of the context is currently active.  This lets
+ * code know if the context was destroyed in a callback for example
+ */
+int smb2_context_active(struct smb2_context *smb2);
+
+/*
+ * EVENT SYSTEM INTEGRATION
+ * ========================
+ * The following functions are used to integrate libsmb2 in an event
+ * system.
+ *
+ * The simplest way is by using smb2_get_fd() and smb2_which_events()
+ * in every loop of the event system to detect which fd to use (it can change)
+ * and which events should be waited for.
+ * This is very simple to use but has the drawback of the overhead having to
+ * call these two functions for every loop.
+ *
+ * This is suitable for trivial apps where you roll your event system
+ * using select() or poll().
+ *
+ * See for example smb2-cat-async.c for an example on how to use these
+ * two functions in an event loop.
+ */
+/*
+ * Returns the file descriptor that libsmb2 uses.
+ */
+t_socket smb2_get_fd(struct smb2_context *smb2);
+/*
+ * Returns which events that we need to poll for for the smb2 file descriptor.
+ */
+int smb2_which_events(struct smb2_context *smb2);
+
+/*
+ * Returns file descriptors that libsmb2 use or is trying to connect to
+ *
+ * This function should be used when trying to connect with more than one
+ * addresses in parallel, cf. rfc8305: Happy Eyeballs.
+ *
+ * The timeout, in ms, is valid during the socket connection step. The caller
+ * should call smb2_service_fd() with fd = -1 when the timeout is reached.
+ * This will trigger a new socket connection on the next resolved address. All
+ * connecting fds will be closed when the first fd is connected. The timeout
+ * will be -1 (infinite) once connected or if there is no next addresses to
+ * connect to.
+ */
+const t_socket *
+smb2_get_fds(struct smb2_context *smb2, size_t *fd_count, int *timeout);
+
+/*
+ * A much more scalable way to use smb2_fd_event_callbacks() to register
+ * callbacks for libsmb2 to call anytime a filedescriptor is changed or when
+ * the  events we are waiting for changes.
+ * This way libsmb2 will do callbacks back into the application to inform
+ * when fd or events change.
+ *
+ * This is suitable when you want to plug libsmb2 into a more sophisticated
+ * eventsystem or if you use epoll() or similar.
+ *
+ * See for smb2-ls-async.c for a trivial example of using these callbacks.
+ */
+#define SMB2_ADD_FD 0
+#define SMB2_DEL_FD 1
+typedef void (*smb2_change_fd_cb)(struct smb2_context *smb2, t_socket fd, int cmd);
+typedef void (*smb2_change_events_cb)(struct smb2_context *smb2, t_socket fd,
+                                      int events);
+void smb2_fd_event_callbacks(struct smb2_context *smb2,
+                             smb2_change_fd_cb change_fd,
+                             smb2_change_events_cb change_events);
+
+/*
+ * Called to process the events when events become available for the smb2
+ * file descriptor.
+ *
+ * Returns:
+ *  0 : Success
+ * <0 : Unrecoverable failure. At this point the context can no longer be
+ *      used and must be freed by calling smb2_destroy_context().
+ *
+ */
+int smb2_service(struct smb2_context *smb2, int revents);
+
+/*
+ * Called to process the events when events become available for the smb2
+ * file descriptor.
+ *
+ * Behave like smb2_service() with some differences:
+ *  - must be called with a fd returned by smb2_get_fd() or smb2_get_fds(),
+ *  - passing -1 as fd will trigger a new connection attempt on the next
+ *  resolved address, cf. smb2_get_fds().
+ *
+ * Returns:
+ *  0 : Success
+ * <0 : Unrecoverable failure. At this point the context can no longer be
+ *      used and must be freed by calling smb2_destroy_context().
+ *
+ */
+int smb2_service_fd(struct smb2_context *smb2, t_socket fd, int revents);
+
+/*
+ * Set the timeout in seconds after which a command will be aborted with
+ * SMB2_STATUS_IO_TIMEOUT.
+ * If you use timeouts with the async API you must make sure to call
+ * smb2_service() at least once every second.
+ *
+ * Default is 0: No timeout.
+ */
+void smb2_set_timeout(struct smb2_context *smb2, int seconds);
+
+/*
+ * Set passthrough-enable.  Passthrough allows command packers
+ * and unpackers to keep the extra data on complex commands
+ * in its on-the-wire format without any interpretation. this
+ * is useful for proxy use cases where there might be no need
+ * to fully parse things like query-info replies or ioctl
+ * requests.  If passthrough is not set, any command that
+ * that is processed that can't interpret the data will fail
+ * Most client use case will not need this
+ *
+ * Default is 0: no-passthrough
+ */
+void smb2_set_passthrough(struct smb2_context *smb2,
+                      int passthrough);
+
+/*
+ * Get the current passthrough setting
+ */
+void smb2_get_passthrough(struct smb2_context *smb2,
+                      int *passthrough);
+
+/*
+ * Set which version of SMB to negotiate.
+ * Default is to let the server pick the version.
+ */
+enum smb2_negotiate_version {
+        SMB2_VERSION_ANY  = 0,
+        SMB2_VERSION_ANY2 = 2,
+        SMB2_VERSION_ANY3 = 3,
+        SMB2_VERSION_0202 = 0x0202,
+        SMB2_VERSION_0210 = 0x0210,
+        SMB2_VERSION_0300 = 0x0300,
+        SMB2_VERSION_0302 = 0x0302,
+        SMB2_VERSION_0311 = 0x0311,
+};
+
+#define SMB2_VERSION_WILDCARD 0x02FF
+
+void smb2_set_version(struct smb2_context *smb2,
+                      enum smb2_negotiate_version version);
+
+/*
+ * Sets which version libsmb2 uses.
+*/
+#define LIBSMB2_MAJOR_VERSION 4
+#define LIBSMB2_MINOR_VERSION 0
+#define LIBSMB2_PATCH_VERSION 0
+
+struct smb2_libversion
+{
+     uint8_t major_version;
+     uint8_t minor_version;
+     uint8_t patch_version;
+};
+
+/*
+ * Gets the libsmb2 version being linked while used.
+ * This function will be available on 5.x
+ * @param struct smb2_libversion
+*/
+void smb2_get_libsmb2Version(struct smb2_libversion *smb2_ver);
+
+/*
+ * gets the (currently) negotiated dialect
+ */
+uint16_t smb2_get_dialect(struct smb2_context *smb2);
+
+/*
+ * Set the security mode for the connection.
+ * This is a combination of the flags SMB2_NEGOTIATE_SIGNING_ENABLED
+ * and  SMB2_NEGOTIATE_SIGNING_REQUIRED
+ * Default is 0.
+ */
+void smb2_set_security_mode(struct smb2_context *smb2, uint16_t security_mode);
+
+/*
+ * Set whether smb3 encryption should be used or not.
+ * 0  : disable encryption. This is the default.
+ * !0 : enable encryption.
+ */
+void smb2_set_seal(struct smb2_context *smb2, int val);
+
+/*
+ * Set whether smb2 signing should be required or not
+ * 0  : do not require signing. This is the default.
+ * !0 : require signing.
+ */
+void smb2_set_sign(struct smb2_context *smb2, int val);
+
+enum smb2_sec {
+        SMB2_SEC_UNDEFINED = 0,
+        SMB2_SEC_NTLMSSP,
+        SMB2_SEC_KRB5,
+};
+
+/*
+ * Set authentication method.
+ * SMB2_SEC_UNDEFINED (use KRB if available or NTLM if not)
+ * SMB2_SEC_NTLMSSP
+ * SMB2_SEC_KRB5
+ */
+void smb2_set_authentication(struct smb2_context *smb2, int val);
+
+/*
+ * Set the username that we will try to authenticate as.
+ * Default is to try to authenticate as the current user.
+ */
+void smb2_set_user(struct smb2_context *smb2, const char *user);
+
+/*
+ * Get the username associated with a context.
+ * returns NULL if none
+ */
+const char *smb2_get_user(struct smb2_context *smb2);
+
+/*
+ * Set the password that we will try to authenticate as.
+ * This function is only needed when libsmb2 is built --without-libkrb5
+ */
+void smb2_set_password(struct smb2_context *smb2, const char *password);
+
+/*
+ * Convert a win timestamp to a unix timeval
+ */
+void smb2_win_to_timeval(uint64_t smb2_time, struct smb2_timeval *tv);
+
+/*
+ * Convert unit timeval to a win timestamp
+ */
+uint64_t smb2_timeval_to_win(struct smb2_timeval *tv);
+
+/*
+ * set the context error string
+ */
+void smb2_set_error(struct smb2_context *smb2,
+                    const char *error_string, ...);
+
+/*
+ * Register an error callback, so any calls to smb2_set_error will call this
+ * function with the error string generated
+ */
+void smb2_register_error_callback(struct smb2_context *smb,
+                    smb2_error_cb error_cb);
+
+/*
+ * register for oplock or lease break callbacks
+ */
+void smb2_set_oplock_or_lease_break_callback(struct smb2_context *smb2,
+                    smb2_oplock_or_lease_break_cb cb);
+
+/*
+ * Set the smb2 context passworkd from a file (see NTLM_USER_FILE)
+ * depends on user/domain being already set in smb2 context
+ */
+void smb2_set_password_from_file(struct smb2_context *smb2);
+
+/*
+ * Set the domain when authenticating.
+ * This function is only needed when libsmb2 is built --without-libkrb5
+ */
+void smb2_set_domain(struct smb2_context *smb2, const char *domain);
+
+/*
+ * Get the domain associated with a context.
+ * returns NULL if none
+ */
+const char *smb2_get_domain(struct smb2_context *smb2);
+/*
+ * Set the workstation when authenticating.
+ * This function is only needed when libsmb2 is built --without-libkrb5
+ */
+void smb2_set_workstation(struct smb2_context *smb2, const char *workstation);
+
+/*
+ * Get the workstation associated with a context.
+ * returns NULL if none
+ */
+const char *smb2_get_workstation(struct smb2_context *smb2);
+
+/*
+ * Sets the address to some user defined object. May be used to make
+ * additional context data available in the async callbacks.
+ */
+void smb2_set_opaque(struct smb2_context *smb2, void *opaque);
+
+/*
+ * Returns the opaque pointer set with smb2_set_opaque.
+ */
+void *smb2_get_opaque(struct smb2_context *smb2);
+
+/*
+ * copy credential handle from one context to another (and set
+ * it NULL in source context)
+ * returns 0 if handle transferred,
+ * or -1 if not (no handle or no context, or not applicable)
+ */
+int smb2_delegate_credentials(struct smb2_context *in, struct smb2_context *out);
+
+/*
+ * Sets the client_guid for this context.
+ */
+void smb2_set_client_guid(struct smb2_context *smb2, const uint8_t guid[SMB2_GUID_SIZE]);
+
+/*
+ * Returns the client_guid for this context.
+ */
+const char *smb2_get_client_guid(struct smb2_context *smb2);
+
+/*
+ * Asynchronous call to connect a TCP connection to the server
+ *
+ * Returns:
+ *  0 if the call was initiated and a connection will be attempted. Result of
+ * the connection will be reported through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Connection was successful. Command_data is NULL.
+ *
+ *   -errno : Failed to establish the connection. Command_data is NULL.
+ */
+int smb2_connect_async(struct smb2_context *smb2, const char *server,
+                       smb2_command_cb cb, void *cb_data);
+
+/*
+ * Async call to connect to a share.
+ * On unix, if user is NULL then default to the current user.
+ *
+ * Returns:
+ *  0 if the call was initiated and a connection will be attempted. Result of
+ * the connection will be reported through the callback function.
+ * -errno if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Connection was successful. Command_data is NULL.
+ *
+ *   -errno : Failed to connect to the share. Command_data is NULL.
+ */
+int smb2_connect_share_async(struct smb2_context *smb2,
+                             const char *server,
+                             const char *share,
+                             const char *user,
+                             smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync call to connect to a share.
+ * On unix, if user is NULL then default to the current user.
+ *
+ * Returns:
+ * 0      : Connected to the share successfully.
+ * -errno : Failure.
+ */
+int smb2_connect_share(struct smb2_context *smb2,
+                       const char *server,
+                       const char *share,
+                       const char *user);
+
+/*
+ * Async call to disconnect from a share/
+ *
+ * Returns:
+ *  0 if the call was initiated and a connection will be attempted. Result of
+ * the disconnect will be reported through the callback function.
+ * -errno if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    0     : Connection was successful. Command_data is NULL.
+ *
+ *   -errno : Failed to disconnect the share. Command_data is NULL.
+ */
+int smb2_disconnect_share_async(struct smb2_context *smb2,
+                                smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync call to disconnect from a share/
+ *
+ * Returns:
+ * 0      : Disconnected from the share successfully.
+ * -errno : Failure.
+ */
+int smb2_disconnect_share(struct smb2_context *smb2);
+
+/*
+ * Select a tree id that was previously connected. Sets the tree_id
+ * in the context to be used for subsequent requests
+ *
+ * Returns:
+ * 0      : OK
+ * -errno : tree wasn't connected
+ */
+int smb2_select_tree_id(struct smb2_context *smb2, uint32_t tree_id);
+
+struct smb2_pdu;
+
+/*
+ * Get/Set the tree id of a pdu
+ *
+ * Returns:
+ * 0      : OK
+ * -errno : tree wasn't connected | no pdu | no context
+ */
+int smb2_get_tree_id_for_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu, uint32_t *tree_id);
+int smb2_set_tree_id_for_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu, uint32_t tree_id);
+
+/*
+ * Get session id
+ *
+ * Returns:
+ * 0      : OK
+ * -errno :
+ */
+int smb2_get_session_id(struct smb2_context *smb2, uint64_t *session_id);
+
+/*
+ * This function returns a description of the last encountered error.
+ */
+const char *smb2_get_error(struct smb2_context *smb2);
+
+int smb2_get_nterror(struct smb2_context *smb2);
+
+struct smb2_url {
+        const char *domain;
+        const char *user;
+        const char *server;
+        const char *share;
+        const char *path;
+};
+
+/* Convert an smb2/nt error code into a string */
+const char *nterror_to_str(uint32_t status);
+
+/* Convert an smb2/nt error code into an errno value */
+int nterror_to_errno(uint32_t status);
+
+/*
+ * This function is used to parse an SMB2 URL into as smb2_url structure.
+ * SMB2 URL format:
+ *   smb2://[<domain;][<username>@]<server>/<share>/<path>
+ * where <server> has the format:
+ *   <host>[:<port>].
+ *
+ * Function will return a pointer to an iscsi smb2 structure if successful,
+ * or it will return NULL and set smb2_get_error() accordingly if there was
+ * a problem with the URL.
+ *
+ * The returned structure is freed by calling smb2_destroy_url()
+ */
+struct smb2_url *smb2_parse_url(struct smb2_context *smb2, const char *url);
+void smb2_destroy_url(struct smb2_url *url);
+
+/*
+ * These functions are used when creating compound low level commands.
+ * The general pattern for compound chains is
+ * 1, pdu = smb2_cmd_*_async(smb2, ...)
+ *
+ * 2, next = smb2_cmd_*_async(smb2, ...)
+ * 3, smb2_add_compound_pdu(smb2, pdu, next);
+ *
+ * 4, next = smb2_cmd_*_async(smb2, ...)
+ * 5, smb2_add_compound_pdu(smb2, pdu, next);
+ * ...
+ * *, smb2_queue_pdu(smb2, pdu);
+ *
+ * See libsmb2.c and smb2-raw-stat-async.c for examples on how to use
+ * this interface.
+ */
+void smb2_add_compound_pdu(struct smb2_context *smb2,
+                           struct smb2_pdu *pdu, struct smb2_pdu *next_pdu);
+void smb2_free_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu);
+void smb2_queue_pdu(struct smb2_context *smb2, struct smb2_pdu *pdu);
+
+/*
+ * These are used to access/modify pdus from application level
+ * useful for proxies, etc.
+ */
+struct smb2_pdu *smb2_get_compound_pdu(struct smb2_context *smb2,
+                           struct smb2_pdu *pdu);
+void smb2_set_pdu_status(struct smb2_context *smb2, struct smb2_pdu *pdu, int status);
+void smb2_set_pdu_message_id(struct smb2_context *smb2, struct smb2_pdu *pdu, uint64_t message_id);
+uint64_t smb2_get_pdu_message_id(struct smb2_context *smb2, struct smb2_pdu *pdu);
+uint64_t smb2_get_last_request_message_id(struct smb2_context *smb2);
+uint64_t smb2_get_last_reply_message_id(struct smb2_context *smb2);
+int smb2_pdu_is_compound(struct smb2_context *smb2);
+
+/*
+ * OPENDIR
+ */
+struct smb2dir;
+
+/*
+ * Async opendir()
+ *
+ * Returns
+ * pdu :    The operation was initiated. Result of the operation will be reported
+ *          through the callback function.
+ *          The pdu must be freed by smb2_free_pdu().
+ *          The pdu can be cancelled before the callback is invoked by freeing
+ *          it.
+ * NULL :   There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ *          Command_data is struct smb2dir.
+ *          This structure is freed using smb2_closedir().
+ * -errno : An error occurred.
+ *          Command_data is NULL.
+ */
+struct smb2_pdu *
+smb2_opendir_async_pdu(struct smb2_context *smb2, const char *path,
+                       smb2_command_cb cb, void *cb_data, void (*free_cb)(void *));
+
+/*
+ * Sync opendir()
+ *
+ * Returns NULL on failure.
+ */
+struct smb2dir *smb2_opendir(struct smb2_context *smb2, const char *path);
+
+int smb2_opendir_async(struct smb2_context *smb2, const char *path,
+                       smb2_command_cb cb, void *cb_data);
+        
+/*
+ * closedir()
+ */
+/*
+ * smb2_closedir() never blocks, thus no async version is needed.
+ */
+void smb2_closedir(struct smb2_context *smb2, struct smb2dir *smb2dir);
+
+/*
+ * readdir()
+ */
+/*
+ * smb2_readdir() never blocks, thus no async version is needed.
+ */
+struct smb2dirent *smb2_readdir(struct smb2_context *smb2,
+                                struct smb2dir *smb2dir);
+
+/*
+ * rewinddir()
+ */
+/*
+ * smb2_rewinddir() never blocks, thus no async version is needed.
+ */
+void smb2_rewinddir(struct smb2_context *smb2, struct smb2dir *smb2dir);
+
+/*
+ * telldir()
+ */
+/*
+ * smb2_telldir() never blocks, thus no async version is needed.
+ */
+long smb2_telldir(struct smb2_context *smb2, struct smb2dir *smb2dir);
+
+/*
+ * seekdir()
+ */
+/*
+ * smb2_seekdir() never blocks, thus no async version is needed.
+ */
+void smb2_seekdir(struct smb2_context *smb2, struct smb2dir *smb2dir,
+                  long loc);
+
+/*
+ * OPEN
+ */
+struct smb2fh;
+/*
+ * Async open()
+ *
+ * Returns
+ * pdu :    The operation was initiated. Result of the operation will be reported
+ *          through the callback function.
+ *          The pdu must be freed by smb2_free_pdu().
+ *          The pdu can be cancelled before the callback is invoked by freeing
+ *          it.
+ * NULL :   There was an error. The callback function will not be invoked.
+ *
+ * Opens or creates a file.
+ * Supported flags are:
+ * O_RDONLY
+ * O_WRONLY
+ * O_RDWR
+ * O_SYNC
+ * O_CREAT
+ * O_EXCL
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ *          Command_data is struct smb2fh.
+ *          This structure is freed using smb2_close().
+ * -errno : An error occurred.
+ *          Command_data is NULL.
+ */
+struct smb2_pdu *
+smb2_open_async_pdu(struct smb2_context *smb2, const char *path, int flags,
+                    smb2_command_cb cb, void *cb_data, void (*free_cb)(void *));
+        
+/*
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ */  
+int smb2_open_async_with_oplock_or_lease(struct smb2_context *smb2, const char *path, int flags,
+                    uint8_t oplock_level, uint32_t lease_state, smb2_lease_key lease_key,
+                    smb2_command_cb cb, void *cb_data);
+
+int smb2_open_async(struct smb2_context *smb2, const char *path, int flags,
+                    smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync open()
+ *
+ * Returns NULL on failure.
+ */
+struct smb2fh *smb2_open(struct smb2_context *smb2, const char *path, int flags);
+
+/*
+ * CLOSE
+ */
+/*
+ * Async close()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ * -errno : An error occurred.
+ *
+ * Command_data is always NULL.
+ */
+int smb2_close_async(struct smb2_context *smb2, struct smb2fh *fh,
+                     smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync close()
+ */
+int smb2_close(struct smb2_context *smb2, struct smb2fh *fh);
+
+/*
+ * FSYNC
+ */
+/*
+ * Async fsync()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ * -errno : An error occurred.
+ *
+ * Command_data is always NULL.
+ */
+int smb2_fsync_async(struct smb2_context *smb2, struct smb2fh *fh,
+                     smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync fsync()
+ */
+int smb2_fsync(struct smb2_context *smb2, struct smb2fh *fh);
+
+/*
+ * GetMaxReadWriteSize
+ * SMB2 servers have a maximum size for read/write data that they support.
+ */
+uint32_t smb2_get_max_read_size(struct smb2_context *smb2);
+uint32_t smb2_get_max_write_size(struct smb2_context *smb2);
+
+struct smb2_read_cb_data {
+        struct smb2fh *fh;
+        uint8_t *buf;
+        uint32_t count;
+        uint64_t offset;
+};
+
+struct smb2_write_cb_data {
+        struct smb2fh *fh;
+        const uint8_t *buf;
+        uint32_t count;
+        uint64_t offset;
+};
+
+/*
+ * PREAD
+ */
+/*
+ * Async pread()
+ * Use smb2_get_max_read_size to discover the maximum data size that the
+ * server supports.
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *    >=0 : Number of bytes read.
+ * -errno : An error occurred.
+ *
+ * Command_data is struct smb2_read_cb_data, which holds the arguments
+ * that were given to smb2_pread_async.
+ * This structure is automatically freed.
+ */
+int smb2_pread_async(struct smb2_context *smb2, struct smb2fh *fh,
+                     uint8_t *buf, uint32_t count, uint64_t offset,
+                     smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync pread()
+ * Use smb2_get_max_read_size to discover the maximum data size that the
+ * server supports.
+ */
+int smb2_pread(struct smb2_context *smb2, struct smb2fh *fh,
+               uint8_t *buf, uint32_t count, uint64_t offset);
+
+/*
+ * PWRITE
+ */
+/*
+ * Async pwrite()
+ * Use smb2_get_max_write_size to discover the maximum data size that the
+ * server supports.
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *    >=0 : Number of bytes written.
+ * -errno : An error occurred.
+ *
+ * Command_data is struct smb2_write_cb_data, which holds the arguments
+ * that were given to smb2_pwrite_async.
+ * This structure is automatically freed.
+ */
+int smb2_pwrite_async(struct smb2_context *smb2, struct smb2fh *fh,
+                      const uint8_t *buf, uint32_t count, uint64_t offset,
+                      smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync pwrite()
+ * Use smb2_get_max_write_size to discover the maximum data size that the
+ * server supports.
+ */
+int smb2_pwrite(struct smb2_context *smb2, struct smb2fh *fh,
+                const uint8_t *buf, uint32_t count, uint64_t offset);
+
+/*
+ * READ
+ */
+/*
+ * Async read()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *    >=0 : Number of bytes read.
+ * -errno : An error occurred.
+ *
+ * Command_data is struct smb2_read_cb_data, which holds the arguments
+ * that were given to smb2_read_async. offset denotes the offset in the file
+ * at which the read took place.
+ * This structure is automatically freed.
+ */
+int smb2_read_async(struct smb2_context *smb2, struct smb2fh *fh,
+                    uint8_t *buf, uint32_t count,
+                    smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync read()
+ */
+int smb2_read(struct smb2_context *smb2, struct smb2fh *fh,
+              uint8_t *buf, uint32_t count);
+
+/*
+ * WRITE
+ */
+/*
+ * Async write()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *    >=0 : Number of bytes written.
+ * -errno : An error occurred.
+ *
+ * Command_data is struct smb2_write_cb_data, which holds the arguments
+ * that were given to smb2_write_async. offset denotes the offset in the file
+ * at which the write took place.
+ * This structure is automatically freed.
+ */
+int smb2_write_async(struct smb2_context *smb2, struct smb2fh *fh,
+                     const uint8_t *buf, uint32_t count,
+                     smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync write()
+ */
+int smb2_write(struct smb2_context *smb2, struct smb2fh *fh,
+               const uint8_t *buf, uint32_t count);
+
+/*
+ * Sync lseek()
+ */
+/*
+ * smb2_seek() SEEK_SET and SEEK_CUR are fully supported.
+ * SEEK_END only returns the end-of-file from the original open.
+ * (it will not call fstat to discover the current file size and will not block)
+ */
+int64_t smb2_lseek(struct smb2_context *smb2, struct smb2fh *fh,
+                   int64_t offset, int whence, uint64_t *current_offset);
+
+/*
+ * UNLINK
+ */
+/*
+ * Async unlink()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ * -errno : An error occurred.
+ *
+ * Command_data is always NULL.
+ */
+int smb2_unlink_async(struct smb2_context *smb2, const char *path,
+                      smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync unlink()
+ */
+int smb2_unlink(struct smb2_context *smb2, const char *path);
+
+/*
+ * RMDIR
+ */
+/*
+ * Async rmdir()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ * -errno : An error occurred.
+ *
+ * Command_data is always NULL.
+ */
+int smb2_rmdir_async(struct smb2_context *smb2, const char *path,
+                     smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync rmdir()
+ */
+int smb2_rmdir(struct smb2_context *smb2, const char *path);
+
+/*
+ * MKDIR
+ */
+/*
+ * Async mkdir()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ * -errno : An error occurred.
+ *
+ * Command_data is always NULL.
+ */
+int smb2_mkdir_async(struct smb2_context *smb2, const char *path,
+                     smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync mkdir()
+ */
+int smb2_mkdir(struct smb2_context *smb2, const char *path);
+
+/*
+ * STATVFS
+ */
+/*
+ * Async statvfs()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success. Command_data is struct smb2_statvfs
+ * -errno : An error occurred.
+ */
+int smb2_statvfs_async(struct smb2_context *smb2, const char *path,
+                       struct smb2_statvfs *statvfs,
+                       smb2_command_cb cb, void *cb_data);
+/*
+ * Sync statvfs()
+ */
+int smb2_statvfs(struct smb2_context *smb2, const char *path,
+                 struct smb2_statvfs *statvfs);
+
+/*
+ * FSTAT
+ */
+/*
+ * Async fstat()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success. Command_data is struct smb2_stat_64
+ * -errno : An error occurred.
+ */
+int smb2_fstat_async(struct smb2_context *smb2, struct smb2fh *fh,
+                     struct smb2_stat_64 *st,
+                     smb2_command_cb cb, void *cb_data);
+/*
+ * Sync fstat()
+ */
+int smb2_fstat(struct smb2_context *smb2, struct smb2fh *fh,
+               struct smb2_stat_64 *st);
+
+/*
+ * Async stat()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success. Command_data is struct smb2_stat_64
+ * -errno : An error occurred.
+ */
+int smb2_stat_async(struct smb2_context *smb2, const char *path,
+                    struct smb2_stat_64 *st,
+                    smb2_command_cb cb, void *cb_data);
+/*
+ * Sync stat()
+ */
+int smb2_stat(struct smb2_context *smb2, const char *path,
+              struct smb2_stat_64 *st);
+
+/*
+ * Async rename()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ * -errno : An error occurred.
+ */
+int smb2_rename_async(struct smb2_context *smb2, const char *oldpath,
+                      const char *newpath, smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync rename()
+ */
+int smb2_rename(struct smb2_context *smb2, const char *oldpath,
+              const char *newpath);
+
+/*
+ * Async truncate()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ * -errno : An error occurred.
+ */
+int smb2_truncate_async(struct smb2_context *smb2, const char *path,
+                        uint64_t length, smb2_command_cb cb, void *cb_data);
+/*
+ * Sync truncate()
+ * Function returns
+ *      0 : Success
+ * -errno : An error occurred.
+ */
+int smb2_truncate(struct smb2_context *smb2, const char *path,
+                  uint64_t length);
+
+/*
+ * Async ftruncate()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ * -errno : An error occurred.
+ */
+int smb2_ftruncate_async(struct smb2_context *smb2, struct smb2fh *fh,
+                         uint64_t length, smb2_command_cb cb, void *cb_data);
+/*
+ * Sync ftruncate()
+ * Function returns
+ *      0 : Success
+ * -errno : An error occurred.
+ */
+int smb2_ftruncate(struct smb2_context *smb2, struct smb2fh *fh,
+                   uint64_t length);
+
+
+/*
+ * READLINK
+ */
+/*
+ * Async readlink()
+ *
+ * Returns
+ *  0     : The operation was initiated. The link content will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success. Command_data is the link content.
+ * -errno : An error occurred.
+ */
+int smb2_readlink_async(struct smb2_context *smb2, const char *path,
+                        smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync readlink()
+ */
+int smb2_readlink(struct smb2_context *smb2, const char *path, char *buf, uint32_t bufsiz);
+
+/*
+ * Async echo()
+ *
+ * Returns
+ *  0     : The operation was initiated. Result of the operation will be
+ *          reported through the callback function.
+ * -errno : There was an error. The callback function will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ * -errno : An error occurred.
+ */
+int smb2_echo_async(struct smb2_context *smb2,
+                    smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync echo()
+ *
+ * Returns:
+ * 0      : successfully send the message and received a reply.
+ * -errno : Failure.
+ */
+int smb2_echo(struct smb2_context *smb2);
+
+void
+free_smb2_file_notify_change_information(struct smb2_context *smb2, struct smb2_file_notify_change_information *fnc);
+
+int smb2_notify_change_async(struct smb2_context *smb2, const char *path, uint16_t flags, uint32_t filter, int loop,
+                       smb2_command_cb cb, void *cb_data);
+
+int smb2_notify_change_filehandle_async(struct smb2_context *smb2, struct smb2fh *smb2_dir_fh, uint16_t flags, uint32_t filter, int loop,
+                       smb2_command_cb cb, void *cb_data);
+
+/*
+ * Sync notify_change()
+ *
+ */
+struct smb2_file_notify_change_information *smb2_notify_change(struct smb2_context *smb2, const char *path, uint16_t flags, uint32_t filter);
+
+/* Utilities that help by being public
+*/
+
+/* SMB's UTF-16 is always in Little Endian */
+struct smb2_utf16 {
+        int len;
+        uint16_t val[1];
+};
+
+/* Returns a string converted to UTF-16 format. Use free() to release
+ * the utf16 string.
+ */
+struct smb2_utf16 *smb2_utf8_to_utf16(const char *utf8);
+
+/* Returns a string converted to UTF8 format. Use free() to release
+ * the utf8 string.
+ */
+const char *smb2_utf16_to_utf8(const uint16_t *str, size_t len);
+
+/************* Server-side API **********************************************/
+struct smb2_server;
+
+/* pdu handlers in general take the request from the client, and return
+ * < 0  on error, and the library should create an error reply
+ * == 0 on OK, and the library should use the reply struct (if needed) to create a reply
+ * > 0  if the handler created and queued a reply itself
+ */
+struct smb2_server_request_handlers {
+        int (*destruction_event)(struct smb2_server *srvr, struct smb2_context *smb2);
+        int (*authorize_user)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            const char *user,
+                            const char *domain,
+                            const char *workstation);
+        int (*session_established)(struct smb2_server *srvr, struct smb2_context *smb2);
+        int (*logoff_cmd)(struct smb2_server *srvr, struct smb2_context *smb2);
+        int (*tree_connect_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_tree_connect_request *req,
+                            struct smb2_tree_connect_reply *rep);
+        int (*tree_disconnect_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            const uint32_t tree_id);
+        int (*create_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_create_request *req,
+                            struct smb2_create_reply *rep);
+        int (*close_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_close_request *req,
+                            struct smb2_close_reply *rep);
+        int (*flush_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_flush_request *req);
+        int (*read_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_read_request *req,
+                            struct smb2_read_reply *rep);
+        int (*write_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_write_request *req,
+                            struct smb2_write_reply *rep);
+        int (*oplock_break_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_oplock_break_acknowledgement *req);
+        int (*lease_break_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_lease_break_acknowledgement *req);
+        int (*lock_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_lock_request *req);
+        int (*ioctl_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_ioctl_request *req,
+                            struct smb2_ioctl_reply *rep);
+        int (*cancel_cmd)(struct smb2_server *srvr, struct smb2_context *smb2);
+        int (*echo_cmd)(struct smb2_server *srvr, struct smb2_context *smb2);
+        int (*query_directory_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_query_directory_request *req,
+                            struct smb2_query_directory_reply *rep);
+        int (*change_notify_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_change_notify_request *req,
+                            struct smb2_change_notify_reply *rep);
+        int (*query_info_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_query_info_request *req,
+                            struct smb2_query_info_reply *rep);
+        int (*set_info_cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_set_info_request *req);
+        /*
+        int (*oplock_break cmd)(struct smb2_server *srvr, struct smb2_context *smb2,
+                            struct smb2_oplock_break_request *req);
+        */
+};
+
+struct smb2_server {
+        uint8_t guid[16];
+        char hostname[128];
+        char domain[128];
+        int fd;
+        uint16_t port;
+        uint64_t session_counter;
+        struct smb2_server_request_handlers *handlers;
+        uint32_t max_transact_size;
+        uint32_t max_read_size;
+        uint32_t max_write_size;
+        int signing_enabled;
+        int allow_anonymous;
+        /* this can be set non-0 to delegate client authentication to
+         * another client and allow any authentication to this server */
+        int proxy_authentication;
+        /* saved from negotiate to be used in validate negotiate info */
+        uint32_t capabilities;
+        uint32_t security_mode;
+        /* for kerberos, credential context */
+        char keytab_path[256];
+        char error[128];
+        void *auth_data;
+};
+
+int smb2_bind_and_listen(const uint16_t port, const int max_connections, int *out_fd);
+int smb2_accept_connection_async(const int fd, const int to_msecs, smb2_accepted_cb cb, void *cb_data);
+int smb2_serve_port_async(const int fd, const int to_msecs, struct smb2_context **out_smb2);
+
+/*
+ * Sync serve port()
+ *
+ * Returns
+ *  0     : The server is complete by exiting its loop normally (shouldnt happen)
+ * -errno : There was an error causing server loop to exit
+ */
+int smb2_serve_port(struct smb2_server *server, const int max_connections, smb2_client_connection cb, void *cb_data);
+
+/*
+ * Some symbols have moved over to a different header file to allow better
+ * separation between dcerpc and smb2, so we need to include this header
+ * here to retain compatibility for apps that depend on those symbols.
+ */
+#include <smb2/libsmb2-dcerpc-srvsvc.h>
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !_LIBSMB2_H_ */

+ 549 - 0
entry/src/main/cpp/include/smb2/smb2-errors.h

@@ -0,0 +1,549 @@
+/* -*-  mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil;  -*- */
+/*
+   Copyright (C) 2016 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * NTSTATUS fields
+ */
+#define SMB2_STATUS_SEVERITY_MASK    0xc0000000
+#define SMB2_STATUS_SEVERITY_SUCCESS 0x00000000
+#define SMB2_STATUS_SEVERITY_INFO    0x40000000
+#define SMB2_STATUS_SEVERITY_WARNING 0x80000000
+#define SMB2_STATUS_SEVERITY_ERROR   0xc0000000
+
+#define SMB2_STATUS_CUSTOMER_MASK    0x20000000
+
+#define SMB2_STATUS_FACILITY_MASK    0x0fff0000
+
+#define SMB2_STATUS_CODE_MASK        0x0000ffff
+
+
+/* Error codes */
+#define SMB2_STATUS_SUCCESS                            0x00000000
+#define SMB2_STATUS_SHUTDOWN                           0xffffffff
+#define SMB2_STATUS_PENDING                            0x00000103
+#define SMB2_STATUS_SMB_BAD_FID                        0x00060001
+#define SMB2_STATUS_NO_MORE_FILES                      0x80000006
+#define SMB2_STATUS_UNSUCCESSFUL                       0xC0000001
+#define SMB2_STATUS_NOT_IMPLEMENTED                    0xC0000002
+#define SMB2_STATUS_INVALID_INFO_CLASS                 0xC0000003
+#define SMB2_STATUS_INFO_LENGTH_MISMATCH               0xC0000004
+#define SMB2_STATUS_ACCESS_VIOLATION                   0xC0000005
+#define SMB2_STATUS_IN_PAGE_ERROR                      0xC0000006
+#define SMB2_STATUS_PAGEFILE_QUOTA                     0xC0000007
+#define SMB2_STATUS_INVALID_HANDLE                     0xC0000008
+#define SMB2_STATUS_BAD_INITIAL_STACK                  0xC0000009
+#define SMB2_STATUS_BAD_INITIAL_PC                     0xC000000A
+#define SMB2_STATUS_INVALID_CID                        0xC000000B
+#define SMB2_STATUS_TIMER_NOT_CANCELED                 0xC000000C
+#define SMB2_STATUS_INVALID_PARAMETER                  0xC000000D
+#define SMB2_STATUS_NO_SUCH_DEVICE                     0xC000000E
+#define SMB2_STATUS_NO_SUCH_FILE                       0xC000000F
+#define SMB2_STATUS_INVALID_DEVICE_REQUEST             0xC0000010
+#define SMB2_STATUS_END_OF_FILE                        0xC0000011
+#define SMB2_STATUS_WRONG_VOLUME                       0xC0000012
+#define SMB2_STATUS_NO_MEDIA_IN_DEVICE                 0xC0000013
+#define SMB2_STATUS_UNRECOGNIZED_MEDIA                 0xC0000014
+#define SMB2_STATUS_NONEXISTENT_SECTOR                 0xC0000015
+#define SMB2_STATUS_MORE_PROCESSING_REQUIRED           0xC0000016
+#define SMB2_STATUS_NO_MEMORY                          0xC0000017
+#define SMB2_STATUS_CONFLICTING_ADDRESSES              0xC0000018
+#define SMB2_STATUS_NOT_MAPPED_VIEW                    0xC0000019
+#define SMB2_STATUS_UNABLE_TO_FREE_VM                  0xC000001A
+#define SMB2_STATUS_UNABLE_TO_DELETE_SECTION           0xC000001B
+#define SMB2_STATUS_INVALID_SYSTEM_SERVICE             0xC000001C
+#define SMB2_STATUS_ILLEGAL_INSTRUCTION                0xC000001D
+#define SMB2_STATUS_INVALID_LOCK_SEQUENCE              0xC000001E
+#define SMB2_STATUS_INVALID_VIEW_SIZE                  0xC000001F
+#define SMB2_STATUS_INVALID_FILE_FOR_SECTION           0xC0000020
+#define SMB2_STATUS_ALREADY_COMMITTED                  0xC0000021
+#define SMB2_STATUS_ACCESS_DENIED                      0xC0000022
+#define SMB2_STATUS_BUFFER_TOO_SMALL                   0xC0000023
+#define SMB2_STATUS_OBJECT_TYPE_MISMATCH               0xC0000024
+#define SMB2_STATUS_NONCONTINUABLE_EXCEPTION           0xC0000025
+#define SMB2_STATUS_INVALID_DISPOSITION                0xC0000026
+#define SMB2_STATUS_UNWIND                             0xC0000027
+#define SMB2_STATUS_BAD_STACK                          0xC0000028
+#define SMB2_STATUS_INVALID_UNWIND_TARGET              0xC0000029
+#define SMB2_STATUS_NOT_LOCKED                         0xC000002A
+#define SMB2_STATUS_PARITY_ERROR                       0xC000002B
+#define SMB2_STATUS_UNABLE_TO_DECOMMIT_VM              0xC000002C
+#define SMB2_STATUS_NOT_COMMITTED                      0xC000002D
+#define SMB2_STATUS_INVALID_PORT_ATTRIBUTES            0xC000002E
+#define SMB2_STATUS_PORT_MESSAGE_TOO_LONG              0xC000002F
+#define SMB2_STATUS_INVALID_PARAMETER_MIX              0xC0000030
+#define SMB2_STATUS_INVALID_QUOTA_LOWER                0xC0000031
+#define SMB2_STATUS_DISK_CORRUPT_ERROR                 0xC0000032
+#define SMB2_STATUS_OBJECT_NAME_INVALID                0xC0000033
+#define SMB2_STATUS_OBJECT_NAME_NOT_FOUND              0xC0000034
+#define SMB2_STATUS_OBJECT_NAME_COLLISION              0xC0000035
+#define SMB2_STATUS_HANDLE_NOT_WAITABLE                0xC0000036
+#define SMB2_STATUS_PORT_DISCONNECTED                  0xC0000037
+#define SMB2_STATUS_DEVICE_ALREADY_ATTACHED            0xC0000038
+#define SMB2_STATUS_OBJECT_PATH_INVALID                0xC0000039
+#define SMB2_STATUS_OBJECT_PATH_NOT_FOUND              0xC000003A
+#define SMB2_STATUS_OBJECT_PATH_SYNTAX_BAD             0xC000003B
+#define SMB2_STATUS_DATA_OVERRUN                       0xC000003C
+#define SMB2_STATUS_DATA_LATE_ERROR                    0xC000003D
+#define SMB2_STATUS_DATA_ERROR                         0xC000003E
+#define SMB2_STATUS_CRC_ERROR                          0xC000003F
+#define SMB2_STATUS_SECTION_TOO_BIG                    0xC0000040
+#define SMB2_STATUS_PORT_CONNECTION_REFUSED            0xC0000041
+#define SMB2_STATUS_INVALID_PORT_HANDLE                0xC0000042
+#define SMB2_STATUS_SHARING_VIOLATION                  0xC0000043
+#define SMB2_STATUS_QUOTA_EXCEEDED                     0xC0000044
+#define SMB2_STATUS_INVALID_PAGE_PROTECTION            0xC0000045
+#define SMB2_STATUS_MUTANT_NOT_OWNED                   0xC0000046
+#define SMB2_STATUS_SEMAPHORE_LIMIT_EXCEEDED           0xC0000047
+#define SMB2_STATUS_PORT_ALREADY_SET                   0xC0000048
+#define SMB2_STATUS_SECTION_NOT_IMAGE                  0xC0000049
+#define SMB2_STATUS_SUSPEND_COUNT_EXCEEDED             0xC000004A
+#define SMB2_STATUS_THREAD_IS_TERMINATING              0xC000004B
+#define SMB2_STATUS_BAD_WORKING_SET_LIMIT              0xC000004C
+#define SMB2_STATUS_INCOMPATIBLE_FILE_MAP              0xC000004D
+#define SMB2_STATUS_SECTION_PROTECTION                 0xC000004E
+#define SMB2_STATUS_EAS_NOT_SUPPORTED                  0xC000004F
+#define SMB2_STATUS_EA_TOO_LARGE                       0xC0000050
+#define SMB2_STATUS_NONEXISTENT_EA_ENTRY               0xC0000051
+#define SMB2_STATUS_NO_EAS_ON_FILE                     0xC0000052
+#define SMB2_STATUS_EA_CORRUPT_ERROR                   0xC0000053
+#define SMB2_STATUS_FILE_LOCK_CONFLICT                 0xC0000054
+#define SMB2_STATUS_LOCK_NOT_GRANTED                   0xC0000055
+#define SMB2_STATUS_DELETE_PENDING                     0xC0000056
+#define SMB2_STATUS_CTL_FILE_NOT_SUPPORTED             0xC0000057
+#define SMB2_STATUS_UNKNOWN_REVISION                   0xC0000058
+#define SMB2_STATUS_REVISION_MISMATCH                  0xC0000059
+#define SMB2_STATUS_INVALID_OWNER                      0xC000005A
+#define SMB2_STATUS_INVALID_PRIMARY_GROUP              0xC000005B
+#define SMB2_STATUS_NO_IMPERSONATION_TOKEN             0xC000005C
+#define SMB2_STATUS_CANT_DISABLE_MANDATORY             0xC000005D
+#define SMB2_STATUS_NO_LOGON_SERVERS                   0xC000005E
+#define SMB2_STATUS_NO_SUCH_LOGON_SESSION              0xC000005F
+#define SMB2_STATUS_NO_SUCH_PRIVILEGE                  0xC0000060
+#define SMB2_STATUS_PRIVILEGE_NOT_HELD                 0xC0000061
+#define SMB2_STATUS_INVALID_ACCOUNT_NAME               0xC0000062
+#define SMB2_STATUS_USER_EXISTS                        0xC0000063
+#define SMB2_STATUS_NO_SUCH_USER                       0xC0000064
+#define SMB2_STATUS_GROUP_EXISTS                       0xC0000065
+#define SMB2_STATUS_NO_SUCH_GROUP                      0xC0000066
+#define SMB2_STATUS_MEMBER_IN_GROUP                    0xC0000067
+#define SMB2_STATUS_MEMBER_NOT_IN_GROUP                0xC0000068
+#define SMB2_STATUS_LAST_ADMIN                         0xC0000069
+#define SMB2_STATUS_WRONG_PASSWORD                     0xC000006A
+#define SMB2_STATUS_ILL_FORMED_PASSWORD                0xC000006B
+#define SMB2_STATUS_PASSWORD_RESTRICTION               0xC000006C
+#define SMB2_STATUS_LOGON_FAILURE                      0xC000006D
+#define SMB2_STATUS_ACCOUNT_RESTRICTION                0xC000006E
+#define SMB2_STATUS_INVALID_LOGON_HOURS                0xC000006F
+#define SMB2_STATUS_INVALID_WORKSTATION                0xC0000070
+#define SMB2_STATUS_PASSWORD_EXPIRED                   0xC0000071
+#define SMB2_STATUS_ACCOUNT_DISABLED                   0xC0000072
+#define SMB2_STATUS_NONE_MAPPED                        0xC0000073
+#define SMB2_STATUS_TOO_MANY_LUIDS_REQUESTED           0xC0000074
+#define SMB2_STATUS_LUIDS_EXHAUSTED                    0xC0000075
+#define SMB2_STATUS_INVALID_SUB_AUTHORITY              0xC0000076
+#define SMB2_STATUS_INVALID_ACL                        0xC0000077
+#define SMB2_STATUS_INVALID_SID                        0xC0000078
+#define SMB2_STATUS_INVALID_SECURITY_DESCR             0xC0000079
+#define SMB2_STATUS_PROCEDURE_NOT_FOUND                0xC000007A
+#define SMB2_STATUS_INVALID_IMAGE_FORMAT               0xC000007B
+#define SMB2_STATUS_NO_TOKEN                           0xC000007C
+#define SMB2_STATUS_BAD_INHERITANCE_ACL                0xC000007D
+#define SMB2_STATUS_RANGE_NOT_LOCKED                   0xC000007E
+#define SMB2_STATUS_DISK_FULL                          0xC000007F
+#define SMB2_STATUS_SERVER_DISABLED                    0xC0000080
+#define SMB2_STATUS_SERVER_NOT_DISABLED                0xC0000081
+#define SMB2_STATUS_TOO_MANY_GUIDS_REQUESTED           0xC0000082
+#define SMB2_STATUS_GUIDS_EXHAUSTED                    0xC0000083
+#define SMB2_STATUS_INVALID_ID_AUTHORITY               0xC0000084
+#define SMB2_STATUS_AGENTS_EXHAUSTED                   0xC0000085
+#define SMB2_STATUS_INVALID_VOLUME_LABEL               0xC0000086
+#define SMB2_STATUS_SECTION_NOT_EXTENDED               0xC0000087
+#define SMB2_STATUS_NOT_MAPPED_DATA                    0xC0000088
+#define SMB2_STATUS_RESOURCE_DATA_NOT_FOUND            0xC0000089
+#define SMB2_STATUS_RESOURCE_TYPE_NOT_FOUND            0xC000008A
+#define SMB2_STATUS_RESOURCE_NAME_NOT_FOUND            0xC000008B
+#define SMB2_STATUS_ARRAY_BOUNDS_EXCEEDED              0xC000008C
+#define SMB2_STATUS_FLOAT_DENORMAL_OPERAND             0xC000008D
+#define SMB2_STATUS_FLOAT_DIVIDE_BY_ZERO               0xC000008E
+#define SMB2_STATUS_FLOAT_INEXACT_RESULT               0xC000008F
+#define SMB2_STATUS_FLOAT_INVALID_OPERATION            0xC0000090
+#define SMB2_STATUS_FLOAT_OVERFLOW                     0xC0000091
+#define SMB2_STATUS_FLOAT_STACK_CHECK                  0xC0000092
+#define SMB2_STATUS_FLOAT_UNDERFLOW                    0xC0000093
+#define SMB2_STATUS_INTEGER_DIVIDE_BY_ZERO             0xC0000094
+#define SMB2_STATUS_INTEGER_OVERFLOW                   0xC0000095
+#define SMB2_STATUS_PRIVILEGED_INSTRUCTION             0xC0000096
+#define SMB2_STATUS_TOO_MANY_PAGING_FILES              0xC0000097
+#define SMB2_STATUS_FILE_INVALID                       0xC0000098
+#define SMB2_STATUS_ALLOTTED_SPACE_EXCEEDED            0xC0000099
+#define SMB2_STATUS_INSUFFICIENT_RESOURCES             0xC000009A
+#define SMB2_STATUS_DFS_EXIT_PATH_FOUND                0xC000009B
+#define SMB2_STATUS_DEVICE_DATA_ERROR                  0xC000009C
+#define SMB2_STATUS_DEVICE_NOT_CONNECTED               0xC000009D
+#define SMB2_STATUS_DEVICE_POWER_FAILURE               0xC000009E
+#define SMB2_STATUS_FREE_VM_NOT_AT_BASE                0xC000009F
+#define SMB2_STATUS_MEMORY_NOT_ALLOCATED               0xC00000A0
+#define SMB2_STATUS_WORKING_SET_QUOTA                  0xC00000A1
+#define SMB2_STATUS_MEDIA_WRITE_PROTECTED              0xC00000A2
+#define SMB2_STATUS_DEVICE_NOT_READY                   0xC00000A3
+#define SMB2_STATUS_INVALID_GROUP_ATTRIBUTES           0xC00000A4
+#define SMB2_STATUS_BAD_IMPERSONATION_LEVEL            0xC00000A5
+#define SMB2_STATUS_CANT_OPEN_ANONYMOUS                0xC00000A6
+#define SMB2_STATUS_BAD_VALIDATION_CLASS               0xC00000A7
+#define SMB2_STATUS_BAD_TOKEN_TYPE                     0xC00000A8
+#define SMB2_STATUS_BAD_MASTER_BOOT_RECORD             0xC00000A9
+#define SMB2_STATUS_INSTRUCTION_MISALIGNMENT           0xC00000AA
+#define SMB2_STATUS_INSTANCE_NOT_AVAILABLE             0xC00000AB
+#define SMB2_STATUS_PIPE_NOT_AVAILABLE                 0xC00000AC
+#define SMB2_STATUS_INVALID_PIPE_STATE                 0xC00000AD
+#define SMB2_STATUS_PIPE_BUSY                          0xC00000AE
+#define SMB2_STATUS_ILLEGAL_FUNCTION                   0xC00000AF
+#define SMB2_STATUS_PIPE_DISCONNECTED                  0xC00000B0
+#define SMB2_STATUS_PIPE_CLOSING                       0xC00000B1
+#define SMB2_STATUS_PIPE_CONNECTED                     0xC00000B2
+#define SMB2_STATUS_PIPE_LISTENING                     0xC00000B3
+#define SMB2_STATUS_INVALID_READ_MODE                  0xC00000B4
+#define SMB2_STATUS_IO_TIMEOUT                         0xC00000B5
+#define SMB2_STATUS_FILE_FORCED_CLOSED                 0xC00000B6
+#define SMB2_STATUS_PROFILING_NOT_STARTED              0xC00000B7
+#define SMB2_STATUS_PROFILING_NOT_STOPPED              0xC00000B8
+#define SMB2_STATUS_COULD_NOT_INTERPRET                0xC00000B9
+#define SMB2_STATUS_FILE_IS_A_DIRECTORY                0xC00000BA
+#define SMB2_STATUS_NOT_SUPPORTED                      0xC00000BB
+#define SMB2_STATUS_REMOTE_NOT_LISTENING               0xC00000BC
+#define SMB2_STATUS_DUPLICATE_NAME                     0xC00000BD
+#define SMB2_STATUS_BAD_NETWORK_PATH                   0xC00000BE
+#define SMB2_STATUS_NETWORK_BUSY                       0xC00000BF
+#define SMB2_STATUS_DEVICE_DOES_NOT_EXIST              0xC00000C0
+#define SMB2_STATUS_TOO_MANY_COMMANDS                  0xC00000C1
+#define SMB2_STATUS_ADAPTER_HARDWARE_ERROR             0xC00000C2
+#define SMB2_STATUS_INVALID_NETWORK_RESPONSE           0xC00000C3
+#define SMB2_STATUS_UNEXPECTED_NETWORK_ERROR           0xC00000C4
+#define SMB2_STATUS_BAD_REMOTE_ADAPTER                 0xC00000C5
+#define SMB2_STATUS_PRINT_QUEUE_FULL                   0xC00000C6
+#define SMB2_STATUS_NO_SPOOL_SPACE                     0xC00000C7
+#define SMB2_STATUS_PRINT_CANCELLED                    0xC00000C8
+#define SMB2_STATUS_NETWORK_NAME_DELETED               0xC00000C9
+#define SMB2_STATUS_NETWORK_ACCESS_DENIED              0xC00000CA
+#define SMB2_STATUS_BAD_DEVICE_TYPE                    0xC00000CB
+#define SMB2_STATUS_BAD_NETWORK_NAME                   0xC00000CC
+#define SMB2_STATUS_TOO_MANY_NAMES                     0xC00000CD
+#define SMB2_STATUS_TOO_MANY_SESSIONS                  0xC00000CE
+#define SMB2_STATUS_SHARING_PAUSED                     0xC00000CF
+#define SMB2_STATUS_REQUEST_NOT_ACCEPTED               0xC00000D0
+#define SMB2_STATUS_REDIRECTOR_PAUSED                  0xC00000D1
+#define SMB2_STATUS_NET_WRITE_FAULT                    0xC00000D2
+#define SMB2_STATUS_PROFILING_AT_LIMIT                 0xC00000D3
+#define SMB2_STATUS_NOT_SAME_DEVICE                    0xC00000D4
+#define SMB2_STATUS_FILE_RENAMED                       0xC00000D5
+#define SMB2_STATUS_VIRTUAL_CIRCUIT_CLOSED             0xC00000D6
+#define SMB2_STATUS_NO_SECURITY_ON_OBJECT              0xC00000D7
+#define SMB2_STATUS_CANT_WAIT                          0xC00000D8
+#define SMB2_STATUS_PIPE_EMPTY                         0xC00000D9
+#define SMB2_STATUS_CANT_ACCESS_DOMAIN_INFO            0xC00000DA
+#define SMB2_STATUS_CANT_TERMINATE_SELF                0xC00000DB
+#define SMB2_STATUS_INVALID_SERVER_STATE               0xC00000DC
+#define SMB2_STATUS_INVALID_DOMAIN_STATE               0xC00000DD
+#define SMB2_STATUS_INVALID_DOMAIN_ROLE                0xC00000DE
+#define SMB2_STATUS_NO_SUCH_DOMAIN                     0xC00000DF
+#define SMB2_STATUS_DOMAIN_EXISTS                      0xC00000E0
+#define SMB2_STATUS_DOMAIN_LIMIT_EXCEEDED              0xC00000E1
+#define SMB2_STATUS_OPLOCK_NOT_GRANTED                 0xC00000E2
+#define SMB2_STATUS_INVALID_OPLOCK_PROTOCOL            0xC00000E3
+#define SMB2_STATUS_INTERNAL_DB_CORRUPTION             0xC00000E4
+#define SMB2_STATUS_INTERNAL_ERROR                     0xC00000E5
+#define SMB2_STATUS_GENERIC_NOT_MAPPED                 0xC00000E6
+#define SMB2_STATUS_BAD_DESCRIPTOR_FORMAT              0xC00000E7
+#define SMB2_STATUS_INVALID_USER_BUFFER                0xC00000E8
+#define SMB2_STATUS_UNEXPECTED_IO_ERROR                0xC00000E9
+#define SMB2_STATUS_UNEXPECTED_MM_CREATE_ERR           0xC00000EA
+#define SMB2_STATUS_UNEXPECTED_MM_MAP_ERROR            0xC00000EB
+#define SMB2_STATUS_UNEXPECTED_MM_EXTEND_ERR           0xC00000EC
+#define SMB2_STATUS_NOT_LOGON_PROCESS                  0xC00000ED
+#define SMB2_STATUS_LOGON_SESSION_EXISTS               0xC00000EE
+#define SMB2_STATUS_INVALID_PARAMETER_1                0xC00000EF
+#define SMB2_STATUS_INVALID_PARAMETER_2                0xC00000F0
+#define SMB2_STATUS_INVALID_PARAMETER_3                0xC00000F1
+#define SMB2_STATUS_INVALID_PARAMETER_4                0xC00000F2
+#define SMB2_STATUS_INVALID_PARAMETER_5                0xC00000F3
+#define SMB2_STATUS_INVALID_PARAMETER_6                0xC00000F4
+#define SMB2_STATUS_INVALID_PARAMETER_7                0xC00000F5
+#define SMB2_STATUS_INVALID_PARAMETER_8                0xC00000F6
+#define SMB2_STATUS_INVALID_PARAMETER_9                0xC00000F7
+#define SMB2_STATUS_INVALID_PARAMETER_10               0xC00000F8
+#define SMB2_STATUS_INVALID_PARAMETER_11               0xC00000F9
+#define SMB2_STATUS_INVALID_PARAMETER_12               0xC00000FA
+#define SMB2_STATUS_REDIRECTOR_NOT_STARTED             0xC00000FB
+#define SMB2_STATUS_REDIRECTOR_STARTED                 0xC00000FC
+#define SMB2_STATUS_STACK_OVERFLOW                     0xC00000FD
+#define SMB2_STATUS_NO_SUCH_PACKAGE                    0xC00000FE
+#define SMB2_STATUS_BAD_FUNCTION_TABLE                 0xC00000FF
+#define SMB2_STATUS_DIRECTORY_NOT_EMPTY                0xC0000101
+#define SMB2_STATUS_FILE_CORRUPT_ERROR                 0xC0000102
+#define SMB2_STATUS_NOT_A_DIRECTORY                    0xC0000103
+#define SMB2_STATUS_BAD_LOGON_SESSION_STATE            0xC0000104
+#define SMB2_STATUS_LOGON_SESSION_COLLISION            0xC0000105
+#define SMB2_STATUS_NAME_TOO_LONG                      0xC0000106
+#define SMB2_STATUS_FILES_OPEN                         0xC0000107
+#define SMB2_STATUS_CONNECTION_IN_USE                  0xC0000108
+#define SMB2_STATUS_MESSAGE_NOT_FOUND                  0xC0000109
+#define SMB2_STATUS_PROCESS_IS_TERMINATING             0xC000010A
+#define SMB2_STATUS_INVALID_LOGON_TYPE                 0xC000010B
+#define SMB2_STATUS_NO_GUID_TRANSLATION                0xC000010C
+#define SMB2_STATUS_CANNOT_IMPERSONATE                 0xC000010D
+#define SMB2_STATUS_IMAGE_ALREADY_LOADED               0xC000010E
+#define SMB2_STATUS_ABIOS_NOT_PRESENT                  0xC000010F
+#define SMB2_STATUS_ABIOS_LID_NOT_EXIST                0xC0000110
+#define SMB2_STATUS_ABIOS_LID_ALREADY_OWNED            0xC0000111
+#define SMB2_STATUS_ABIOS_NOT_LID_OWNER                0xC0000112
+#define SMB2_STATUS_ABIOS_INVALID_COMMAND              0xC0000113
+#define SMB2_STATUS_ABIOS_INVALID_LID                  0xC0000114
+#define SMB2_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE       0xC0000115
+#define SMB2_STATUS_ABIOS_INVALID_SELECTOR             0xC0000116
+#define SMB2_STATUS_NO_LDT                             0xC0000117
+#define SMB2_STATUS_INVALID_LDT_SIZE                   0xC0000118
+#define SMB2_STATUS_INVALID_LDT_OFFSET                 0xC0000119
+#define SMB2_STATUS_INVALID_LDT_DESCRIPTOR             0xC000011A
+#define SMB2_STATUS_INVALID_IMAGE_NE_FORMAT            0xC000011B
+#define SMB2_STATUS_RXACT_INVALID_STATE                0xC000011C
+#define SMB2_STATUS_RXACT_COMMIT_FAILURE               0xC000011D
+#define SMB2_STATUS_MAPPED_FILE_SIZE_ZERO              0xC000011E
+#define SMB2_STATUS_TOO_MANY_OPENED_FILES              0xC000011F
+#define SMB2_STATUS_CANCELLED                          0xC0000120
+#define SMB2_STATUS_CANNOT_DELETE                      0xC0000121
+#define SMB2_STATUS_INVALID_COMPUTER_NAME              0xC0000122
+#define SMB2_STATUS_FILE_DELETED                       0xC0000123
+#define SMB2_STATUS_SPECIAL_ACCOUNT                    0xC0000124
+#define SMB2_STATUS_SPECIAL_GROUP                      0xC0000125
+#define SMB2_STATUS_SPECIAL_USER                       0xC0000126
+#define SMB2_STATUS_MEMBERS_PRIMARY_GROUP              0xC0000127
+#define SMB2_STATUS_FILE_CLOSED                        0xC0000128
+#define SMB2_STATUS_TOO_MANY_THREADS                   0xC0000129
+#define SMB2_STATUS_THREAD_NOT_IN_PROCESS              0xC000012A
+#define SMB2_STATUS_TOKEN_ALREADY_IN_USE               0xC000012B
+#define SMB2_STATUS_PAGEFILE_QUOTA_EXCEEDED            0xC000012C
+#define SMB2_STATUS_COMMITMENT_LIMIT                   0xC000012D
+#define SMB2_STATUS_INVALID_IMAGE_LE_FORMAT            0xC000012E
+#define SMB2_STATUS_INVALID_IMAGE_NOT_MZ               0xC000012F
+#define SMB2_STATUS_INVALID_IMAGE_PROTECT              0xC0000130
+#define SMB2_STATUS_INVALID_IMAGE_WIN_16               0xC0000131
+#define SMB2_STATUS_LOGON_SERVER_CONFLICT              0xC0000132
+#define SMB2_STATUS_TIME_DIFFERENCE_AT_DC              0xC0000133
+#define SMB2_STATUS_SYNCHRONIZATION_REQUIRED           0xC0000134
+#define SMB2_STATUS_DLL_NOT_FOUND                      0xC0000135
+#define SMB2_STATUS_OPEN_FAILED                        0xC0000136
+#define SMB2_STATUS_IO_PRIVILEGE_FAILED                0xC0000137
+#define SMB2_STATUS_ORDINAL_NOT_FOUND                  0xC0000138
+#define SMB2_STATUS_ENTRYPOINT_NOT_FOUND               0xC0000139
+#define SMB2_STATUS_CONTROL_C_EXIT                     0xC000013A
+#define SMB2_STATUS_LOCAL_DISCONNECT                   0xC000013B
+#define SMB2_STATUS_REMOTE_DISCONNECT                  0xC000013C
+#define SMB2_STATUS_REMOTE_RESOURCES                   0xC000013D
+#define SMB2_STATUS_LINK_FAILED                        0xC000013E
+#define SMB2_STATUS_LINK_TIMEOUT                       0xC000013F
+#define SMB2_STATUS_INVALID_CONNECTION                 0xC0000140
+#define SMB2_STATUS_INVALID_ADDRESS                    0xC0000141
+#define SMB2_STATUS_DLL_INIT_FAILED                    0xC0000142
+#define SMB2_STATUS_MISSING_SYSTEMFILE                 0xC0000143
+#define SMB2_STATUS_UNHANDLED_EXCEPTION                0xC0000144
+#define SMB2_STATUS_APP_INIT_FAILURE                   0xC0000145
+#define SMB2_STATUS_PAGEFILE_CREATE_FAILED             0xC0000146
+#define SMB2_STATUS_NO_PAGEFILE                        0xC0000147
+#define SMB2_STATUS_INVALID_LEVEL                      0xC0000148
+#define SMB2_STATUS_WRONG_PASSWORD_CORE                0xC0000149
+#define SMB2_STATUS_ILLEGAL_FLOAT_CONTEXT              0xC000014A
+#define SMB2_STATUS_PIPE_BROKEN                        0xC000014B
+#define SMB2_STATUS_REGISTRY_CORRUPT                   0xC000014C
+#define SMB2_STATUS_REGISTRY_IO_FAILED                 0xC000014D
+#define SMB2_STATUS_NO_EVENT_PAIR                      0xC000014E
+#define SMB2_STATUS_UNRECOGNIZED_VOLUME                0xC000014F
+#define SMB2_STATUS_SERIAL_NO_DEVICE_INITED            0xC0000150
+#define SMB2_STATUS_NO_SUCH_ALIAS                      0xC0000151
+#define SMB2_STATUS_MEMBER_NOT_IN_ALIAS                0xC0000152
+#define SMB2_STATUS_MEMBER_IN_ALIAS                    0xC0000153
+#define SMB2_STATUS_ALIAS_EXISTS                       0xC0000154
+#define SMB2_STATUS_LOGON_NOT_GRANTED                  0xC0000155
+#define SMB2_STATUS_TOO_MANY_SECRETS                   0xC0000156
+#define SMB2_STATUS_SECRET_TOO_LONG                    0xC0000157
+#define SMB2_STATUS_INTERNAL_DB_ERROR                  0xC0000158
+#define SMB2_STATUS_FULLSCREEN_MODE                    0xC0000159
+#define SMB2_STATUS_TOO_MANY_CONTEXT_IDS               0xC000015A
+#define SMB2_STATUS_LOGON_TYPE_NOT_GRANTED             0xC000015B
+#define SMB2_STATUS_NOT_REGISTRY_FILE                  0xC000015C
+#define SMB2_STATUS_NT_CROSS_ENCRYPTION_REQUIRED       0xC000015D
+#define SMB2_STATUS_DOMAIN_CTRLR_CONFIG_ERROR          0xC000015E
+#define SMB2_STATUS_FT_MISSING_MEMBER                  0xC000015F
+#define SMB2_STATUS_ILL_FORMED_SERVICE_ENTRY           0xC0000160
+#define SMB2_STATUS_ILLEGAL_CHARACTER                  0xC0000161
+#define SMB2_STATUS_UNMAPPABLE_CHARACTER               0xC0000162
+#define SMB2_STATUS_UNDEFINED_CHARACTER                0xC0000163
+#define SMB2_STATUS_FLOPPY_VOLUME                      0xC0000164
+#define SMB2_STATUS_FLOPPY_ID_MARK_NOT_FOUND           0xC0000165
+#define SMB2_STATUS_FLOPPY_WRONG_CYLINDER              0xC0000166
+#define SMB2_STATUS_FLOPPY_UNKNOWN_ERROR               0xC0000167
+#define SMB2_STATUS_FLOPPY_BAD_REGISTERS               0xC0000168
+#define SMB2_STATUS_DISK_RECALIBRATE_FAILED            0xC0000169
+#define SMB2_STATUS_DISK_OPERATION_FAILED              0xC000016A
+#define SMB2_STATUS_DISK_RESET_FAILED                  0xC000016B
+#define SMB2_STATUS_SHARED_IRQ_BUSY                    0xC000016C
+#define SMB2_STATUS_FT_ORPHANING                       0xC000016D
+#define SMB2_STATUS_PARTITION_FAILURE                  0xC0000172
+#define SMB2_STATUS_INVALID_BLOCK_LENGTH               0xC0000173
+#define SMB2_STATUS_DEVICE_NOT_PARTITIONED             0xC0000174
+#define SMB2_STATUS_UNABLE_TO_LOCK_MEDIA               0xC0000175
+#define SMB2_STATUS_UNABLE_TO_UNLOAD_MEDIA             0xC0000176
+#define SMB2_STATUS_EOM_OVERFLOW                       0xC0000177
+#define SMB2_STATUS_NO_MEDIA                           0xC0000178
+#define SMB2_STATUS_NO_SUCH_MEMBER                     0xC000017A
+#define SMB2_STATUS_INVALID_MEMBER                     0xC000017B
+#define SMB2_STATUS_KEY_DELETED                        0xC000017C
+#define SMB2_STATUS_NO_LOG_SPACE                       0xC000017D
+#define SMB2_STATUS_TOO_MANY_SIDS                      0xC000017E
+#define SMB2_STATUS_LM_CROSS_ENCRYPTION_REQUIRED       0xC000017F
+#define SMB2_STATUS_KEY_HAS_CHILDREN                   0xC0000180
+#define SMB2_STATUS_CHILD_MUST_BE_VOLATILE             0xC0000181
+#define SMB2_STATUS_DEVICE_CONFIGURATION_ERROR         0xC0000182
+#define SMB2_STATUS_DRIVER_INTERNAL_ERROR              0xC0000183
+#define SMB2_STATUS_INVALID_DEVICE_STATE               0xC0000184
+#define SMB2_STATUS_IO_DEVICE_ERROR                    0xC0000185
+#define SMB2_STATUS_DEVICE_PROTOCOL_ERROR              0xC0000186
+#define SMB2_STATUS_BACKUP_CONTROLLER                  0xC0000187
+#define SMB2_STATUS_LOG_FILE_FULL                      0xC0000188
+#define SMB2_STATUS_TOO_LATE                           0xC0000189
+#define SMB2_STATUS_NO_TRUST_LSA_SECRET                0xC000018A
+#define SMB2_STATUS_NO_TRUST_SAM_ACCOUNT               0xC000018B
+#define SMB2_STATUS_TRUSTED_DOMAIN_FAILURE             0xC000018C
+#define SMB2_STATUS_TRUSTED_RELATIONSHIP_FAILURE       0xC000018D
+#define SMB2_STATUS_EVENTLOG_FILE_CORRUPT              0xC000018E
+#define SMB2_STATUS_EVENTLOG_CANT_START                0xC000018F
+#define SMB2_STATUS_TRUST_FAILURE                      0xC0000190
+#define SMB2_STATUS_MUTANT_LIMIT_EXCEEDED              0xC0000191
+#define SMB2_STATUS_NETLOGON_NOT_STARTED               0xC0000192
+#define SMB2_STATUS_ACCOUNT_EXPIRED                    0xC0000193
+#define SMB2_STATUS_POSSIBLE_DEADLOCK                  0xC0000194
+#define SMB2_STATUS_NETWORK_CREDENTIAL_CONFLICT        0xC0000195
+#define SMB2_STATUS_REMOTE_SESSION_LIMIT               0xC0000196
+#define SMB2_STATUS_EVENTLOG_FILE_CHANGED              0xC0000197
+#define SMB2_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT  0xC0000198
+#define SMB2_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT  0xC0000199
+#define SMB2_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT       0xC000019A
+#define SMB2_STATUS_DOMAIN_TRUST_INCONSISTENT          0xC000019B
+#define SMB2_STATUS_FS_DRIVER_REQUIRED                 0xC000019C
+#define SMB2_STATUS_NO_USER_SESSION_KEY                0xC0000202
+#define SMB2_STATUS_USER_SESSION_DELETED               0xC0000203
+#define SMB2_STATUS_RESOURCE_LANG_NOT_FOUND            0xC0000204
+#define SMB2_STATUS_INSUFF_SERVER_RESOURCES            0xC0000205
+#define SMB2_STATUS_INVALID_BUFFER_SIZE                0xC0000206
+#define SMB2_STATUS_INVALID_ADDRESS_COMPONENT          0xC0000207
+#define SMB2_STATUS_INVALID_ADDRESS_WILDCARD           0xC0000208
+#define SMB2_STATUS_TOO_MANY_ADDRESSES                 0xC0000209
+#define SMB2_STATUS_ADDRESS_ALREADY_EXISTS             0xC000020A
+#define SMB2_STATUS_ADDRESS_CLOSED                     0xC000020B
+#define SMB2_STATUS_CONNECTION_DISCONNECTED            0xC000020C
+#define SMB2_STATUS_CONNECTION_RESET                   0xC000020D
+#define SMB2_STATUS_TOO_MANY_NODES                     0xC000020E
+#define SMB2_STATUS_TRANSACTION_ABORTED                0xC000020F
+#define SMB2_STATUS_TRANSACTION_TIMED_OUT              0xC0000210
+#define SMB2_STATUS_TRANSACTION_NO_RELEASE             0xC0000211
+#define SMB2_STATUS_TRANSACTION_NO_MATCH               0xC0000212
+#define SMB2_STATUS_TRANSACTION_RESPONDED              0xC0000213
+#define SMB2_STATUS_TRANSACTION_INVALID_ID             0xC0000214
+#define SMB2_STATUS_TRANSACTION_INVALID_TYPE           0xC0000215
+#define SMB2_STATUS_NOT_SERVER_SESSION                 0xC0000216
+#define SMB2_STATUS_NOT_CLIENT_SESSION                 0xC0000217
+#define SMB2_STATUS_CANNOT_LOAD_REGISTRY_FILE          0xC0000218
+#define SMB2_STATUS_DEBUG_ATTACH_FAILED                0xC0000219
+#define SMB2_STATUS_SYSTEM_PROCESS_TERMINATED          0xC000021A
+#define SMB2_STATUS_DATA_NOT_ACCEPTED                  0xC000021B
+#define SMB2_STATUS_NO_BROWSER_SERVERS_FOUND           0xC000021C
+#define SMB2_STATUS_VDM_HARD_ERROR                     0xC000021D
+#define SMB2_STATUS_DRIVER_CANCEL_TIMEOUT              0xC000021E
+#define SMB2_STATUS_REPLY_MESSAGE_MISMATCH             0xC000021F
+#define SMB2_STATUS_MAPPED_ALIGNMENT                   0xC0000220
+#define SMB2_STATUS_IMAGE_CHECKSUM_MISMATCH            0xC0000221
+#define SMB2_STATUS_LOST_WRITEBEHIND_DATA              0xC0000222
+#define SMB2_STATUS_CLIENT_SERVER_PARAMETERS_INVALID   0xC0000223
+#define SMB2_STATUS_PASSWORD_MUST_CHANGE               0xC0000224
+#define SMB2_STATUS_NOT_FOUND                          0xC0000225
+#define SMB2_STATUS_NOT_TINY_STREAM                    0xC0000226
+#define SMB2_STATUS_RECOVERY_FAILURE                   0xC0000227
+#define SMB2_STATUS_STACK_OVERFLOW_READ                0xC0000228
+#define SMB2_STATUS_FAIL_CHECK                         0xC0000229
+#define SMB2_STATUS_DUPLICATE_OBJECTID                 0xC000022A
+#define SMB2_STATUS_OBJECTID_EXISTS                    0xC000022B
+#define SMB2_STATUS_CONVERT_TO_LARGE                   0xC000022C
+#define SMB2_STATUS_RETRY                              0xC000022D
+#define SMB2_STATUS_FOUND_OUT_OF_SCOPE                 0xC000022E
+#define SMB2_STATUS_ALLOCATE_BUCKET                    0xC000022F
+#define SMB2_STATUS_PROPSET_NOT_FOUND                  0xC0000230
+#define SMB2_STATUS_MARSHALL_OVERFLOW                  0xC0000231
+#define SMB2_STATUS_INVALID_VARIANT                    0xC0000232
+#define SMB2_STATUS_DOMAIN_CONTROLLER_NOT_FOUND        0xC0000233
+#define SMB2_STATUS_ACCOUNT_LOCKED_OUT                 0xC0000234
+#define SMB2_STATUS_HANDLE_NOT_CLOSABLE                0xC0000235
+#define SMB2_STATUS_CONNECTION_REFUSED                 0xC0000236
+#define SMB2_STATUS_GRACEFUL_DISCONNECT                0xC0000237
+#define SMB2_STATUS_ADDRESS_ALREADY_ASSOCIATED         0xC0000238
+#define SMB2_STATUS_ADDRESS_NOT_ASSOCIATED             0xC0000239
+#define SMB2_STATUS_CONNECTION_INVALID                 0xC000023A
+#define SMB2_STATUS_CONNECTION_ACTIVE                  0xC000023B
+#define SMB2_STATUS_NETWORK_UNREACHABLE                0xC000023C
+#define SMB2_STATUS_HOST_UNREACHABLE                   0xC000023D
+#define SMB2_STATUS_PROTOCOL_UNREACHABLE               0xC000023E
+#define SMB2_STATUS_PORT_UNREACHABLE                   0xC000023F
+#define SMB2_STATUS_REQUEST_ABORTED                    0xC0000240
+#define SMB2_STATUS_CONNECTION_ABORTED                 0xC0000241
+#define SMB2_STATUS_BAD_COMPRESSION_BUFFER             0xC0000242
+#define SMB2_STATUS_USER_MAPPED_FILE                   0xC0000243
+#define SMB2_STATUS_AUDIT_FAILED                       0xC0000244
+#define SMB2_STATUS_TIMER_RESOLUTION_NOT_SET           0xC0000245
+#define SMB2_STATUS_CONNECTION_COUNT_LIMIT             0xC0000246
+#define SMB2_STATUS_LOGIN_TIME_RESTRICTION             0xC0000247
+#define SMB2_STATUS_LOGIN_WKSTA_RESTRICTION            0xC0000248
+#define SMB2_STATUS_IMAGE_MP_UP_MISMATCH               0xC0000249
+#define SMB2_STATUS_INSUFFICIENT_LOGON_INFO            0xC0000250
+#define SMB2_STATUS_BAD_DLL_ENTRYPOINT                 0xC0000251
+#define SMB2_STATUS_BAD_SERVICE_ENTRYPOINT             0xC0000252
+#define SMB2_STATUS_LPC_REPLY_LOST                     0xC0000253
+#define SMB2_STATUS_IP_ADDRESS_CONFLICT1               0xC0000254
+#define SMB2_STATUS_IP_ADDRESS_CONFLICT2               0xC0000255
+#define SMB2_STATUS_REGISTRY_QUOTA_LIMIT               0xC0000256
+#define SMB2_STATUS_PATH_NOT_COVERED                   0xC0000257
+#define SMB2_STATUS_NO_CALLBACK_ACTIVE                 0xC0000258
+#define SMB2_STATUS_LICENSE_QUOTA_EXCEEDED             0xC0000259
+#define SMB2_STATUS_PWD_TOO_SHORT                      0xC000025A
+#define SMB2_STATUS_PWD_TOO_RECENT                     0xC000025B
+#define SMB2_STATUS_PWD_HISTORY_CONFLICT               0xC000025C
+#define SMB2_STATUS_PLUGPLAY_NO_DEVICE                 0xC000025E
+#define SMB2_STATUS_UNSUPPORTED_COMPRESSION            0xC000025F
+#define SMB2_STATUS_INVALID_HW_PROFILE                 0xC0000260
+#define SMB2_STATUS_INVALID_PLUGPLAY_DEVICE_PATH       0xC0000261
+#define SMB2_STATUS_DRIVER_ORDINAL_NOT_FOUND           0xC0000262
+#define SMB2_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND        0xC0000263
+#define SMB2_STATUS_RESOURCE_NOT_OWNED                 0xC0000264
+#define SMB2_STATUS_TOO_MANY_LINKS                     0xC0000265
+#define SMB2_STATUS_QUOTA_LIST_INCONSISTENT            0xC0000266
+#define SMB2_STATUS_FILE_IS_OFFLINE                    0xC0000267
+#define SMB2_STATUS_VOLUME_DISMOUNTED                  0xC000026E
+#define SMB2_STATUS_NOT_A_REPARSE_POINT                0xC0000275
+#define SMB2_STATUS_SERVER_UNAVAILABLE                 0xC0000466
+
+/* Warning codes */
+#define SMB2_STATUS_BUFFER_OVERFLOW                    0x80000005
+#define SMB2_STATUS_STOPPED_ON_SYMLINK                 0x8000002D
+

+ 83 - 0
entry/src/main/cpp/include/smb2/smb2-ioctl.h

@@ -0,0 +1,83 @@
+/*
+ * Copyright 2025 Ronnie Sahlberg <ronniesahberg@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the “Software”), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * Or optionally just relicense this file to whatever license you want.
+ */
+#ifndef _SMB2_IOCTL_H_
+#define _SMB2_IOCTL_H_
+
+#define FSCTL_CREATE_OR_GET_OBJECT_ID              0x000900C0
+#define FSCTL_DELETE_OBJECT_ID                     0x000900A0
+#define FSCTL_DELETE_REPARSE_POINT                 0x000900AC
+#define FSCTL_DUPLICATE_EXTENTS_TO_FILE            0x00098344
+#define FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX         0x000983E8 
+#define FSCTL_FILESYSTEM_GET_STATISTICS            0x00090060
+#define FSCTL_FILE_LEVEL_TRIM                      0x00098208
+#define FSCTL_FIND_FILES_BY_SID                    0x0009008F
+#define FSCTL_GET_COMPRESSION                      0x0009003C
+#define FSCTL_GET_INTEGRITY_INFORMATION            0x0009027C
+#define FSCTL_GET_NTFS_VOLUME_DATA                 0x00090064
+#define FSCTL_GET_REFS_VOLUME_DATA                 0x000902D8
+#define FSCTL_GET_OBJECT_ID                        0x0009009C
+#define FSCTL_GET_REPARSE_POINT                    0x000900A8
+#define FSCTL_GET_RETRIEVAL_POINTER_COUNT          0x0009042B
+#define FSCTL_GET_RETRIEVAL_POINTERS               0x00090073
+#define FSCTL_GET_RETRIEVAL_POINTERS_AND_REFCOUNT  0x000903D3
+#define FSCTL_IS_PATHNAME_VALID                    0x0009002C
+#define FSCTL_LMR_SET_LINK_TRACKING_INFORMATION    0x001400EC
+#define FSCTL_MARK_HANDLE                          0x000900FC
+#define FSCTL_OFFLOAD_READ                         0x00094264
+#define FSCTL_OFFLOAD_WRITE                        0x00098268
+#define FSCTL_PIPE_PEEK                            0x0011400C
+#define FSCTL_PIPE_TRANSCEIVE                      0x0011C017
+#define FSCTL_PIPE_WAIT                            0x00110018
+#define FSCTL_QUERY_ALLOCATED_RANGES               0x000940CF
+#define FSCTL_QUERY_FAT_BPB                        0x00090058
+#define FSCTL_QUERY_FILE_REGIONS                   0x00090284
+#define FSCTL_QUERY_ON_DISK_VOLUME_INFO            0x0009013C
+#define FSCTL_QUERY_SPARING_INFO                   0x00090138
+#define FSCTL_READ_FILE_USN_DATA                   0x000900EB
+#define FSCTL_RECALL_FILE                          0x00090117
+#define FSCTL_REFS_STREAM_SNAPSHOT_MANAGEMENT      0x00090440
+#define FSCTL_SET_COMPRESSION                      0x0009C040
+#define FSCTL_SET_DEFECT_MANAGEMENT                0x00098134
+#define FSCTL_SET_ENCRYPTION                       0x000900D7
+#define FSCTL_SET_INTEGRITY_INFORMATION            0x0009C280
+#define FSCTL_SET_INTEGRITY_INFORMATION_EX         0x00090380
+#define FSCTL_SET_OBJECT_ID                        0x00090098
+#define FSCTL_SET_OBJECT_ID_EXTENDED               0x000900BC
+#define FSCTL_SET_REPARSE_POINT                    0x000900A4
+#define FSCTL_SET_SPARSE                           0x000900C4
+#define FSCTL_SET_ZERO_DATA                        0x000980C8
+#define FSCTL_SET_ZERO_ON_DEALLOCATION             0x00090194
+#define FSCTL_SIS_COPYFILE                         0x00090100
+#define FSCTL_WRITE_USN_CLOSE_RECORD               0x000900EF
+
+#define FSCTL_SRV_ENUMERATE_SNAPSHOTS              0x00144064
+#define FSCTL_GET_SHADOW_COPY_DATA                 0x00144064
+
+
+#endif /* _SMB2_IOCTL_H_ */

+ 1251 - 0
entry/src/main/cpp/include/smb2/smb2.h

@@ -0,0 +1,1251 @@
+/* -*-  mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil;  -*- */
+/*
+   Copyright (C) 2016 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SMB2_H_
+#define _SMB2_H_
+
+#include <smb2/smb2-errors.h>
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct smb2_timeval {
+        time_t tv_sec;
+        long tv_usec;
+};
+#define SMB2_ERROR_REPLY_SIZE 9
+
+struct smb2_error_reply {
+        uint8_t error_context_count;
+        uint32_t byte_count;
+        uint8_t *error_data;
+};
+
+
+#define SMB2_FLAGS_SERVER_TO_REDIR    0x00000001
+#define SMB2_FLAGS_ASYNC_COMMAND      0x00000002
+#define SMB2_FLAGS_RELATED_OPERATIONS 0x00000004
+#define SMB2_FLAGS_SIGNED             0x00000008
+#define SMB2_FLAGS_PRIORITY_MASK      0x00000070
+#define SMB2_FLAGS_DFS_OPERATIONS     0x10000000
+#define SMB2_FLAGS_REPLAY_OPERATION   0x20000000
+
+enum smb2_command {
+        SMB2_NEGOTIATE       = 0,
+        SMB2_SESSION_SETUP   = 1,
+        SMB2_LOGOFF          = 2,
+        SMB2_TREE_CONNECT    = 3,
+        SMB2_TREE_DISCONNECT = 4,
+        SMB2_CREATE          = 5,
+        SMB2_CLOSE           = 6,
+        SMB2_FLUSH           = 7,
+        SMB2_READ            = 8,
+        SMB2_WRITE           = 9,
+        SMB2_LOCK            = 10,
+        SMB2_IOCTL           = 11,
+        SMB2_CANCEL          = 12,
+        SMB2_ECHO            = 13,
+        SMB2_QUERY_DIRECTORY = 14,
+        SMB2_CHANGE_NOTIFY   = 15,
+        SMB2_QUERY_INFO      = 16,
+        SMB2_SET_INFO        = 17,
+        SMB2_OPLOCK_BREAK    = 18,
+
+        SMB1_NEGOTIATE       = 114,
+};
+
+/*
+ * SMB2 NEGOTIATE
+ */
+#define SMB2_NEGOTIATE_SIGNING_ENABLED  0x0001
+#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002
+
+#define SMB2_GLOBAL_CAP_DFS                0x00000001
+#define SMB2_GLOBAL_CAP_LEASING            0x00000002
+#define SMB2_GLOBAL_CAP_LARGE_MTU          0x00000004
+#define SMB2_GLOBAL_CAP_MULTI_CHANNEL      0x00000008
+#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010
+#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING  0x00000020
+#define SMB2_GLOBAL_CAP_ENCRYPTION         0x00000040
+
+#define SMB2_PREAUTH_INTEGRITY_CAP         0x0001
+#define SMB2_ENCRYPTION_CAP                0x0002
+#define SMB2_COMPRESSION_CAP               0x0003
+#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID  0x0005
+#define SMB2_TRANSPORT_CAP                 0x0006
+#define SMB2_RDMA_TRANSFORM_CAP            0x0007
+#define SMB2_SIGNING_CAP                   0x0008
+#define SMB2_CONTEXTTYPE_RESERVED          0x0100
+
+#define SMB2_HASH_SHA_512                  0x0001
+#define SMB2_PREAUTH_HASH_SIZE             64
+
+#define SMB2_ENCRYPTION_AES_128_CCM        0x0001
+#define SMB2_ENCRYPTION_AES_128_GCM        0x0002
+
+#define SMB2_NEGOTIATE_MAX_DIALECTS 10
+
+#define SMB2_NEGOTIATE_REQUEST_SIZE 36
+
+#define SMB2_GUID_SIZE 16
+typedef uint8_t smb2_guid[SMB2_GUID_SIZE];
+
+struct smb2_negotiate_request {
+        uint16_t dialect_count;
+        uint16_t security_mode;
+        uint32_t capabilities;
+        smb2_guid client_guid;
+        uint32_t negotiate_context_offset;
+        uint16_t negotiate_context_count;
+        uint16_t dialects[SMB2_NEGOTIATE_MAX_DIALECTS];
+};
+
+#define SMB2_NEGOTIATE_REPLY_SIZE 65
+
+struct smb2_negotiate_reply {
+        uint16_t security_mode;
+        uint16_t dialect_revision;
+        uint16_t cypher;
+        smb2_guid server_guid;
+        uint32_t capabilities;
+        uint32_t max_transact_size;
+        uint32_t max_read_size;
+        uint32_t max_write_size;
+        uint64_t system_time;
+        uint64_t server_start_time;
+        uint32_t negotiate_context_offset;
+        uint16_t negotiate_context_count;
+        uint16_t security_buffer_length;
+        uint16_t security_buffer_offset;
+        uint8_t *security_buffer;
+};
+
+/* session setup flags */
+#define SMB2_SESSION_FLAG_BINDING 0x01
+
+/* session setup capabilities */
+#define SMB2_GLOBAL_CAP_DFS     0x00000001
+#define SMB2_GLOBAL_CAP_UNUSED1 0x00000002
+#define SMB2_GLOBAL_CAP_UNUSED2 0x00000004
+#define SMB2_GLOBAL_CAP_UNUSED4 0x00000008
+
+#define SMB2_SESSION_SETUP_REQUEST_SIZE 25
+
+struct smb2_session_setup_request {
+        uint8_t flags;
+        uint8_t security_mode;
+        uint32_t capabilities;
+        uint32_t channel;
+        uint64_t previous_session_id;
+        uint16_t security_buffer_length;
+        uint8_t *security_buffer;
+};
+
+#define SMB2_SESSION_FLAG_IS_GUEST        0x0001
+#define SMB2_SESSION_FLAG_IS_NULL         0x0002
+#define SMB2_SESSION_FLAG_IS_ENCRYPT_DATA 0x0004
+
+#define SMB2_SESSION_SETUP_REPLY_SIZE 9
+
+struct smb2_session_setup_reply {
+        uint16_t session_flags;
+        uint16_t security_buffer_length;
+        uint16_t security_buffer_offset;
+        uint8_t *security_buffer;
+};
+
+#define SMB2_TREE_CONNECT_REQUEST_SIZE 9
+
+#define SMB2_SHAREFLAG_CLUSTER_RECONNECT 0x0001
+
+struct smb2_tree_connect_request {
+        uint16_t flags;
+        uint16_t path_offset;
+        uint16_t path_length;
+        uint16_t *path;
+};
+
+#define SMB2_SHARE_TYPE_DISK  0x01
+#define SMB2_SHARE_TYPE_PIPE  0x02
+#define SMB2_SHARE_TYPE_PRINT 0x03
+
+#define SMB2_SHAREFLAG_MANUAL_CACHING              0x00000000
+#define SMB2_SHAREFLAG_DFS                         0x00000001
+#define SMB2_SHAREFLAG_DFS_ROOT                    0x00000002
+#define SMB2_SHAREFLAG_AUTO_CACHING                0x00000010
+#define SMB2_SHAREFLAG_VDO_CACHING                 0x00000020
+#define SMB2_SHAREFLAG_NO_CACHING                  0x00000030
+#define SMB2_SHAREFLAG_RESTRICT_EXCLUSIVE_OPENS    0x00000100
+#define SMB2_SHAREFLAG_FORCE_SHARED_DELETE         0x00000200
+#define SMB2_SHAREFLAG_ALLOW_NAMESPACE_CACHING     0x00000400
+#define SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM 0x00000800
+#define SMB2_SHAREFLAG_FORCE_LEVELII_OPLOCK        0x00001000
+#define SMB2_SHAREFLAG_ENABLE_HASH_V1              0x00002000
+#define SMB2_SHAREFLAG_ENABLE_HASH_V2              0x00004000
+#define SMB2_SHAREFLAG_ENCRYPT_DATA                0x00008000
+
+#define SMB2_SHARE_CAP_DFS                         0x00000008
+#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY     0x00000010
+#define SMB2_SHARE_CAP_SCALEOUT                    0x00000020
+#define SMB2_SHARE_CAP_CLUSTER                     0x00000040
+#define SMB2_SHARE_CAP_ASYMMETRIC                  0x00000080
+
+#define SMB2_TREE_CONNECT_REPLY_SIZE 16
+
+struct smb2_tree_connect_reply {
+        uint8_t share_type;
+        uint32_t share_flags;
+        uint32_t capabilities;
+        uint32_t maximal_access;
+};
+
+#define SMB2_CREATE_REQUEST_SIZE 57
+
+#define SMB2_OPLOCK_LEVEL_NONE      0x00
+#define SMB2_OPLOCK_LEVEL_II        0x01
+#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
+#define SMB2_OPLOCK_LEVEL_BATCH     0x09
+#define SMB2_OPLOCK_LEVEL_LEASE     0xff
+
+#define SMB2_CREATE_REQUEST_LEASE_SIZE  32
+
+#define SMB2_IMPERSONATION_ANONYMOUS      0x00000000
+#define SMB2_IMPERSONATION_IDENTIFICATION 0x00000001
+#define SMB2_IMPERSONATION_IMPERSONATION  0x00000002
+#define SMB2_IMPERSONATION_DELEGATE       0x00000003
+
+/* Access mask common to all objects */
+#define SMB2_FILE_READ_EA           0x00000008
+#define SMB2_FILE_WRITE_EA          0x00000010
+#define SMB2_FILE_DELETE_CHILD      0x00000040
+#define SMB2_FILE_READ_ATTRIBUTES   0x00000080
+#define SMB2_FILE_WRITE_ATTRIBUTES  0x00000100
+#define SMB2_DELETE                 0x00010000
+#define SMB2_READ_CONTROL           0x00020000
+#define SMB2_WRITE_DACL             0x00040000
+#define SMB2_WRITE_OWNER            0x00080000
+#define SMB2_SYNCHRONIZE            0x00100000
+#define SMB2_ACCESS_SYSTEM_SECURITY 0x01000000
+#define SMB2_MAXIMUM_ALLOWED        0x02000000
+#define SMB2_GENERIC_ALL            0x10000000
+#define SMB2_GENERIC_EXECUTE        0x20000000
+#define SMB2_GENERIC_WRITE          0x40000000
+#define SMB2_GENERIC_READ           0x80000000
+
+/* Access mask unique for file/pipe/printer */
+#define SMB2_FILE_READ_DATA         0x00000001
+#define SMB2_FILE_WRITE_DATA        0x00000002
+#define SMB2_FILE_APPEND_DATA       0x00000004
+#define SMB2_FILE_EXECUTE           0x00000020
+
+/* Access mask unique for directories */
+#define SMB2_FILE_LIST_DIRECTORY    0x00000001
+#define SMB2_FILE_ADD_FILE          0x00000002
+#define SMB2_FILE_ADD_SUBDIRECTORY  0x00000004
+#define SMB2_FILE_TRAVERSE          0x00000020
+
+/* File attributes */
+#define SMB2_FILE_ATTRIBUTE_READONLY            0x00000001
+#define SMB2_FILE_ATTRIBUTE_HIDDEN              0x00000002
+#define SMB2_FILE_ATTRIBUTE_SYSTEM              0x00000004
+#define SMB2_FILE_ATTRIBUTE_DIRECTORY           0x00000010
+#define SMB2_FILE_ATTRIBUTE_ARCHIVE             0x00000020
+#define SMB2_FILE_ATTRIBUTE_NORMAL              0x00000080
+#define SMB2_FILE_ATTRIBUTE_TEMPORARY           0x00000100
+#define SMB2_FILE_ATTRIBUTE_SPARSE_FILE         0x00000200
+#define SMB2_FILE_ATTRIBUTE_REPARSE_POINT       0x00000400
+#define SMB2_FILE_ATTRIBUTE_COMPRESSED          0x00000800
+#define SMB2_FILE_ATTRIBUTE_OFFLINE             0x00001000
+#define SMB2_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
+#define SMB2_FILE_ATTRIBUTE_ENCRYPTED           0x00004000
+#define SMB2_FILE_ATTRIBUTE_INTEGRITY_STREAM    0x00008000
+#define SMB2_FILE_ATTRIBUTE_NO_SCRUB_DATA       0x00020000
+
+/* Share access */
+#define SMB2_FILE_SHARE_READ 0x00000001
+#define SMB2_FILE_SHARE_WRITE 0x00000002
+#define SMB2_FILE_SHARE_DELETE 0x00000004
+
+/* Create disposition */
+#define SMB2_FILE_SUPERSEDE    0x00000000
+#define SMB2_FILE_OPEN         0x00000001
+#define SMB2_FILE_CREATE       0x00000002
+#define SMB2_FILE_OPEN_IF      0x00000003
+#define SMB2_FILE_OVERWRITE    0x00000004
+#define SMB2_FILE_OVERWRITE_IF 0x00000005
+
+/* Create options */
+#define SMB2_FILE_DIRECTORY_FILE            0x00000001
+#define SMB2_FILE_WRITE_THROUGH             0x00000002
+#define SMB2_FILE_SEQUENTIAL_ONLY           0x00000004
+#define SMB2_FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
+#define SMB2_FILE_SYNCHRONOUS_IO_ALERT      0x00000010
+#define SMB2_FILE_SYNCHRONOUS_IO_NONALERT   0x00000020
+#define SMB2_FILE_NON_DIRECTORY_FILE        0x00000040
+#define SMB2_FILE_COMPLETE_IF_OPLOCKED      0x00000100
+#define SMB2_FILE_NO_EA_KNOWLEDGE           0x00000200
+#define SMB2_FILE_RANDOM_ACCESS             0x00000800
+#define SMB2_FILE_DELETE_ON_CLOSE           0x00001000
+#define SMB2_FILE_OPEN_BY_FILE_ID           0x00002000
+#define SMB2_FILE_OPEN_FOR_BACKUP_INTENT    0x00004000
+#define SMB2_FILE_NO_COMPRESSION            0x00008000
+#define SMB2_FILE_OPEN_REMOTE_INSTANCE      0x00000400
+#define SMB2_FILE_OPEN_REQUIRING_OPLOCK     0x00010000
+#define SMB2_FILE_DISALLOW_EXCLUSIVE        0x00020000
+#define SMB2_FILE_RESERVE_OPFILTER          0x00100000
+#define SMB2_FILE_OPEN_REPARSE_POINT        0x00200000
+#define SMB2_FILE_OPEN_NO_RECALL            0x00400000
+#define SMB2_FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
+
+struct smb2_create_request {
+        uint8_t security_flags;
+        uint8_t requested_oplock_level;
+        uint32_t impersonation_level;
+        uint64_t smb_create_flags;
+        uint32_t desired_access;
+        uint32_t file_attributes;
+        uint32_t share_access;
+        uint32_t create_disposition;
+        uint32_t create_options;
+        uint16_t name_offset;
+        uint16_t name_length;
+        const char *name;       /* name in UTF8 */
+        uint32_t create_context_offset;
+        uint32_t create_context_length;
+        uint8_t *create_context;
+};
+
+#define SMB2_CREATE_REPLY_SIZE 89
+
+#define SMB2_FD_SIZE 16
+typedef uint8_t smb2_file_id[SMB2_FD_SIZE];
+
+#define SMB2_LEASE_KEY_SIZE 16
+typedef uint8_t smb2_lease_key[SMB2_LEASE_KEY_SIZE];
+
+struct smb2fh;
+smb2_file_id *smb2_get_file_id(struct smb2fh *fh);
+
+/*
+ * This creates a new smb2fh based on fileid.
+ * Free it with smb2_close_async()
+ */
+struct smb2_context;
+struct smb2fh *smb2_fh_from_file_id(struct smb2_context *smb2,
+                                    smb2_file_id *fileid);
+
+struct smb2_create_reply {
+        uint8_t oplock_level;
+        uint8_t flags;
+        uint32_t create_action;
+        uint64_t creation_time;
+        uint64_t last_access_time;
+        uint64_t last_write_time;
+        uint64_t change_time;
+        uint64_t allocation_size;
+        uint64_t end_of_file;
+        uint32_t file_attributes;
+        smb2_file_id file_id;
+        uint32_t create_context_length;
+        uint32_t create_context_offset;
+        uint8_t *create_context;
+};
+
+#define SMB2_CLOSE_REQUEST_SIZE 24
+
+#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB 0x0001
+
+struct smb2_close_request {
+        uint16_t flags;
+        smb2_file_id file_id;
+};
+
+#define SMB2_CLOSE_REPLY_SIZE 60
+
+struct smb2_close_reply {
+        uint16_t flags;
+        uint64_t creation_time;
+        uint64_t last_access_time;
+        uint64_t last_write_time;
+        uint64_t change_time;
+        uint64_t allocation_size;
+        uint64_t end_of_file;
+        uint32_t file_attributes;
+};
+
+#define SMB2_FLUSH_REQUEST_SIZE 24
+
+struct smb2_flush_request {
+        smb2_file_id file_id;
+};
+
+#define SMB2_LOGFF_REQUEST_SIZE 4
+
+struct smb2_logoff_request {
+        uint16_t reserved;
+};
+
+#define SMB2_ECHO_REQUEST_SIZE 4
+
+struct smb2_echo_request {
+        uint16_t reserved;
+};
+
+#define SMB2_FLUSH_REPLY_SIZE 4
+
+#define SMB2_QUERY_DIRECTORY_REQUEST_SIZE 33
+
+/* File information class */
+#define SMB2_FILE_DIRECTORY_INFORMATION         0x01
+#define SMB2_FILE_FULL_DIRECTORY_INFORMATION    0x02
+#define SMB2_FILE_BOTH_DIRECTORY_INFORMATION    0x03
+#define SMB2_FILE_ID_BOTH_DIRECTORY_INFORMATION 0x25
+#define SMB2_FILE_ID_FULL_DIRECTORY_INFORMATION 0x26
+
+/* query flags */
+#define SMB2_RESTART_SCANS       0x01
+#define SMB2_RETURN_SINGLE_ENTRY 0x02
+#define SMB2_INDEX_SPECIFIED     0x04
+#define SMB2_REOPEN              0x10
+
+#define SMB2_FILEID_FULL_DIRECTORY_INFORMATION_SIZE  80
+
+/* Structure for SMB2_FILE_ID_FULL_DIRECTORY_INFORMATION.
+ * This is also used as the dirent content.
+ */
+struct smb2_fileidfulldirectoryinformation {
+        uint32_t next_entry_offset;
+        uint32_t file_index;
+        struct smb2_timeval creation_time;
+        struct smb2_timeval last_access_time;
+        struct smb2_timeval last_write_time;
+        struct smb2_timeval change_time;
+        uint64_t end_of_file;
+        uint64_t allocation_size;
+        uint32_t file_attributes;
+        uint32_t file_name_length;
+        uint32_t ea_size;
+        uint64_t file_id;
+        const char *name; /* or "reserved" for replys */
+};
+
+#define SMB2_FILEID_BOTH_DIRECTORY_INFORMATION_SIZE  104
+
+/* Structure for SMB2_FILE_ID_BOTH_DIRECTORY_INFORMATION.
+ */
+struct smb2_fileidbothdirectoryinformation {
+        uint32_t next_entry_offset;
+        uint32_t file_index;
+        struct smb2_timeval creation_time;
+        struct smb2_timeval last_access_time;
+        struct smb2_timeval last_write_time;
+        struct smb2_timeval change_time;
+        uint64_t end_of_file;
+        uint64_t allocation_size;
+        uint32_t file_attributes;
+        uint32_t file_name_length;
+        uint32_t ea_size;
+        uint8_t short_name_length;
+        uint8_t short_name[24];
+        uint64_t file_id;
+        const char *name; /* or "reserved" for replys */
+};
+
+struct smb2_iovec;
+int smb2_decode_fileidfulldirectoryinformation(
+        struct smb2_context *smb2,
+        struct smb2_fileidfulldirectoryinformation *fs,
+        struct smb2_iovec *vec);
+
+struct smb2_query_directory_request {
+        uint8_t file_information_class;
+        uint8_t flags;
+        uint32_t file_index;
+        smb2_file_id file_id;
+        uint32_t output_buffer_length;
+        uint16_t file_name_offset;
+        uint16_t file_name_length;
+        const char *name;       /* name in UTF8 */
+};
+
+#define SMB2_QUERY_DIRECTORY_REPLY_SIZE 9
+
+struct smb2_query_directory_reply {
+        uint16_t output_buffer_offset;
+        uint32_t output_buffer_length;
+        uint8_t *output_buffer;
+};
+
+#define SMB2_READ_REQUEST_SIZE 49
+
+#define SMB2_READFLAG_READ_UNBUFFERED 0x01
+
+#define SMB2_CHANNEL_NONE               0x00000000
+#define SMB2_CHANNEL_RDMA_V1            0x00000001
+#define SMB2_CHANNEL_RDMA_V1_INVALIDATE 0x00000002
+
+struct smb2_read_request {
+        uint8_t flags;
+        uint32_t length;
+        uint64_t offset;
+        uint8_t *buf;
+        smb2_file_id file_id;
+        uint32_t minimum_count;
+        uint32_t channel;
+        uint32_t remaining_bytes;
+        uint16_t read_channel_info_offset;
+        uint16_t read_channel_info_length;
+        uint8_t *read_channel_info;
+};
+
+#define SMB2_READ_REPLY_SIZE 17
+
+struct smb2_read_reply {
+        uint8_t data_offset;
+        uint32_t data_length;
+        uint32_t data_remaining;
+        uint8_t *data;
+};
+
+#define SMB2_QUERY_INFO_REQUEST_SIZE 41
+
+/* info type */
+#define SMB2_0_INFO_FILE       0x01
+#define SMB2_0_INFO_FILESYSTEM 0x02
+#define SMB2_0_INFO_SECURITY   0x03
+#define SMB2_0_INFO_QUOTA      0x04
+
+/* File information class : for SMB2_0_INFO_FILE */
+#define SMB2_FILE_DIRECTORY_INFORMATION         0x01
+#define SMB2_FILE_FULL_DIRECTORY_INFORMATION    0x02
+#define SMB2_FILE_BOTH_DIRECTORY_INFORMATION    0x03
+#define SMB2_FILE_BASIC_INFORMATION             0x04
+#define SMB2_FILE_STANDARD_INFORMATION          0x05
+#define SMB2_FILE_INTERNAL_INFORMATION          0x06
+#define SMB2_FILE_EA_INFORMATION                0x07
+#define SMB2_FILE_ACCESS_INFORMATION            0x08
+#define SMB2_FILE_NAME_INFORMATION              0x09
+#define SMB2_FILE_RENAME_INFORMATION            0x0A
+#define SMB2_FILE_LINK_INFORMATION              0x0B
+#define SMB2_FILE_NAMES_INFORMATION             0x0C
+#define SMB2_FILE_DISPOSITION_INFORMATION       0x0D
+#define SMB2_FILE_POSITION_INFORMATION          0x0E
+#define SMB2_FILE_FULL_EA_INFORMATION           0x0F
+#define SMB2_FILE_MODE_INFORMATION              0x10
+#define SMB2_FILE_ALIGNMENT_INFORMATION         0x11
+#define SMB2_FILE_ALL_INFORMATION               0x12
+#define SMB2_FILE_ALLOCATION_INFORMATION        0x13
+#define SMB2_FILE_END_OF_FILE_INFORMATION       0x14
+#define SMB2_FILE_ALTERNATE_NAME_INFORMATION    0x15
+#define SMB2_FILE_OBJECT_ID_INFORMATION         0x1D
+#define SMB2_FILE_ATTRIBUTE_TAG_INFORMATION     0x23
+#define SMB2_FILE_VALID_DATA_LENGTH_INFORMATION 0x27
+#define SMB2_FILE_NORMALIZED_NAME_INFORMATION   0x30
+#define SMB2_FILE_ID_INFORMATION                0x3B
+
+#define SMB2_FILE_STREAM_INFORMATION            0x16
+#define SMB2_FILE_PIPE_INFORMATION              0x17
+#define SMB2_FILE_PIPE_LOCAL_INFORMATION        0x18
+#define SMB2_FILE_PIPE_REMOTE_INFORMATION       0x19
+#define SMB2_FILE_MAILSLOT_QUERY_INFORMATION    0x1A
+#define SMB2_FILE_MAILSLOT_SET_INFORMATION      0x1B
+#define SMB2_FILE_COMPRESSION_INFORMATION       0x1C
+#define SMB2_FILE_OBJECT_ID_INFORMATION         0x1D
+#define SMB2_FILE_QUOTA_INFORMATION             0x20
+#define SMB2_FILE_REPARSE_POINT_INFORMATION     0x21
+#define SMB2_FILE_NETWORK_OPEN_INFORMATION      0x22
+
+/* Filesystem information class : for SMB2_0_INFO_FILESYSTEM */
+#define SMB2_FILE_FS_VOLUME_INFORMATION            1
+#define SMB2_FILE_FS_SIZE_INFORMATION              3
+#define SMB2_FILE_FS_DEVICE_INFORMATION            4
+#define SMB2_FILE_FS_ATTRIBUTE_INFORMATION         5
+#define SMB2_FILE_FS_CONTROL_INFORMATION           6
+#define SMB2_FILE_FS_FULL_SIZE_INFORMATION         7
+#define SMB2_FILE_FS_OBJECT_ID_INFORMATION         8
+#define SMB2_FILE_FS_SECTOR_SIZE_INFORMATION      11
+
+#define SMB2_FILE_INFO_CLASS_RESERVED           0x40
+
+/* additional info */
+#define SMB2_OWNER_SECURITY_INFORMATION     0x00000001
+#define SMB2_GROUP_SECURITY_INFORMATION     0x00000002
+#define SMB2_DACL_SECURITY_INFORMATION      0x00000004
+#define SMB2_SACL_SECURITY_INFORMATION      0x00000008
+#define SMB2_LABEL_SECURITY_INFORMATION     0x00000010
+#define SMB2_ATTRIBUTE_SECURITY_INFORMATION 0x00000020
+#define SMB2_SCOPE_SECURITY_INFORMATION     0x00000040
+#define SMB2_BACKUP_SECURITY_INFORMATION    0x00010000
+
+/* flags */
+#define SL_RESTART_SCAN        0x00000001
+#define SL_RETURN_SINGLE_ENTRY 0x00000002
+#define SL_INDEX_SPECIFIED     0x00000004
+
+/*
+ * FILE_BASIC_INFORMATION
+ */
+struct smb2_file_basic_info {
+        struct smb2_timeval creation_time;
+        struct smb2_timeval last_access_time;
+        struct smb2_timeval last_write_time;
+        struct smb2_timeval change_time;
+        uint32_t file_attributes;
+};
+
+/*
+ * FILE_STANDARD_INFORMATION
+ */
+struct smb2_file_standard_info {
+        uint64_t allocation_size;
+        uint64_t end_of_file;
+        uint32_t number_of_links;
+        uint8_t delete_pending;
+        uint8_t directory;
+};
+
+/*
+ * FILE_STREAM_INFORMATION
+ */
+struct smb2_file_stream_info {
+        uint32_t next_entry_offset;
+        uint32_t stream_name_length;
+        uint64_t stream_size;
+        uint64_t stream_allocation_size;
+        const char *stream_name;
+};
+
+/*
+ * FILE_POSITION_INFORMATION
+ */
+struct smb2_file_position_info {
+        uint64_t current_byte_offset;
+};
+
+/*
+ * FILE_NAME_INFORMATION
+ */
+struct smb2_file_name_info {
+        uint32_t file_name_length;
+        const uint8_t *name;
+};
+
+/*
+ * FILE_ALL_INFORMATION.
+ */
+struct smb2_file_all_info {
+        struct smb2_file_basic_info basic;
+        struct smb2_file_standard_info standard;
+        uint64_t index_number;
+        uint32_t ea_size;
+        uint32_t access_flags;
+        uint64_t current_byte_offset;
+        uint32_t mode;
+        uint32_t alignment_requirement;
+        const uint8_t *name;
+};
+
+struct smb2_query_info_request {
+        uint8_t info_type;
+        uint8_t file_info_class;
+        uint32_t output_buffer_length;
+        uint16_t input_buffer_offset;
+        uint32_t input_buffer_length;
+        uint8_t *input_buffer;
+        uint32_t additional_information;
+        uint32_t flags;
+        smb2_file_id file_id;
+        const uint8_t *input;
+};
+
+/*
+ * FILE_END_OF_FILE_INFORMATION.
+ */
+struct smb2_file_end_of_file_info {
+        uint64_t end_of_file;
+};
+
+/*
+ * FILE_DISPOSITION_INFORMATION.
+ */
+struct smb2_file_disposition_info {
+        uint8_t delete_pending;
+};
+
+/*
+ * SMB2_FILE_RENAME_INFORMATION.
+ */
+struct smb2_file_rename_info {
+        uint8_t replace_if_exist;
+        const uint8_t* file_name;
+};
+
+/*
+ * FILE_NETWORK_OPEN_INFORMATION
+ */
+struct smb2_file_network_open_info {
+        struct smb2_timeval creation_time;
+        struct smb2_timeval last_access_time;
+        struct smb2_timeval last_write_time;
+        struct smb2_timeval change_time;
+        uint64_t allocation_size;
+        uint64_t end_of_file;
+        uint32_t file_attributes;
+};
+
+#define SMB2_SET_INFO_REQUEST_SIZE 33
+
+struct smb2_set_info_request {
+        uint8_t info_type;
+        uint8_t file_info_class;
+        uint32_t buffer_length;
+        uint16_t buffer_offset;
+        uint32_t additional_information;
+        smb2_file_id file_id;
+        void *input_data;
+};
+
+#define SMB2_SET_INFO_REPLY_SIZE 2
+
+/*
+ * SID
+ */
+#define SID_ID_AUTH_LEN 6
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning( disable : 4200 ) /* Silence c4200 warning. */
+#endif
+
+struct smb2_sid {
+        uint8_t revision;
+        uint8_t sub_auth_count;
+        uint8_t id_auth[SID_ID_AUTH_LEN];
+        uint32_t sub_auth[0];
+};
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+/*
+ * ACE
+ */
+/* ace type */
+#define SMB2_ACCESS_ALLOWED_ACE_TYPE                 0x00
+#define SMB2_ACCESS_DENIED_ACE_TYPE                  0x01
+#define SMB2_SYSTEM_AUDIT_ACE_TYPE                   0x02
+/*
+ * Reserved for future use
+ * #define SMB2_SYSTEM_ALARM_ACE_TYPE                   0x03
+ */
+#define SMB2_ACCESS_ALLOWED_OBJECT_ACE_TYPE          0x05
+#define SMB2_ACCESS_DENIED_OBJECT_ACE_TYPE           0x06
+#define SMB2_SYSTEM_AUDIT_OBJECT_ACE_TYPE            0x07
+/*
+ * Reserved for future use
+ * #define SMB2_SYSTEM_ALARM_OBJECT_ACE_TYPE            0x08
+ */
+#define SMB2_ACCESS_ALLOWED_CALLBACK_ACE_TYPE        0x09
+#define SMB2_ACCESS_DENIED_CALLBACK_ACE_TYPE         0x10
+#define SMB2_SYSTEM_MANDATORY_LABEL_ACE_TYPE         0x11
+#define SMB2_SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE      0x12
+#define SMB2_SYSTEM_SCOPED_POLICY_ID_ACE_TYPE        0x13
+
+/* ace flags */
+#define SMB2_OBJECT_INHERIT_ACE         0x01
+#define SMB2_CONTAINER_INHERIT_ACE      0x02
+#define SMB2_NO_PROPAGATE_INHERIT_ACE   0x04
+#define SMB2_INHERIT_ONLY_ACE           0x08
+#define SMB2_INHERITED_ACE              0x10
+#define SMB2_SUCCESSFUL_ACCESS_ACE_FLAG 0x40
+#define SMB2_FAILED_ACCESS_ACE_FLAG     0x80
+
+#define SMB2_OBJECT_TYPE_SIZE 16
+
+struct smb2_ace {
+        struct smb2_ace *next;
+
+        uint8_t ace_type;
+        uint8_t ace_flags;
+        uint16_t ace_size;
+
+        /* Which fields are valid depends on the ace type */
+        uint32_t mask;
+        uint32_t flags;
+        struct smb2_sid *sid;
+        uint8_t object_type[SMB2_OBJECT_TYPE_SIZE];
+        uint8_t inherited_object_type[SMB2_OBJECT_TYPE_SIZE];
+
+        /* ApplicationData/AttributeData. Used by
+         * SMB2_ACCESS_ALLOWED_CALLBACK_ACE_TYPE,
+         * SMB2_DENIED_ALLOWED_CALLBACK_ACE_TYPE,
+         * SMB2_SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE
+         */
+        size_t ad_len;
+        char *ad_data;
+
+        /* raw blob, used for unknown ACE types */
+        size_t   raw_len;
+        char *raw_data;
+};
+
+/*
+ * ACL
+ */
+#define SMB2_ACL_REVISION    0x02
+#define SMB2_ACL_REVISION_DS 0x04
+
+struct smb2_acl {
+        uint8_t revision;
+        uint16_t ace_count;
+        struct smb2_ace *aces;
+};
+
+/*
+ * SECURITY_DESCRIPTOR
+ */
+/* Security descriptor control flags */
+#define SMB2_SD_CONTROL_OD 0x0001
+#define SMB2_SD_CONTROL_GD 0x0002
+#define SMB2_SD_CONTROL_DP 0x0004
+#define SMB2_SD_CONTROL_DD 0x0008
+#define SMB2_SD_CONTROL_SP 0x0010
+#define SMB2_SD_CONTROL_SD 0x0020
+#define SMB2_SD_CONTROL_SS 0x0040
+#define SMB2_SD_CONTROL_DT 0x0080
+#define SMB2_SD_CONTROL_DC 0x0100
+#define SMB2_SD_CONTROL_SC 0x0200
+#define SMB2_SD_CONTROL_DI 0x0400
+#define SMB2_SD_CONTROL_SI 0x0800
+#define SMB2_SD_CONTROL_PD 0x1000
+#define SMB2_SD_CONTROL_PS 0x2000
+#define SMB2_SD_CONTROL_RM 0x4000
+#define SMB2_SD_CONTROL_SR 0x8000
+
+struct smb2_security_descriptor {
+        uint8_t revision;
+        uint16_t control;
+        struct smb2_sid *owner;
+        struct smb2_sid *group;
+        struct smb2_acl *dacl;
+};
+
+struct smb2_file_fs_volume_info {
+        struct smb2_timeval creation_time;
+        uint32_t volume_serial_number;
+        uint32_t volume_label_length;
+        uint8_t supports_objects;
+        uint8_t reserved;
+        const uint8_t *volume_label;
+};
+
+struct smb2_file_fs_size_info {
+        uint64_t total_allocation_units;
+        uint64_t available_allocation_units;
+        uint32_t sectors_per_allocation_unit;
+        uint32_t bytes_per_sector;
+};
+
+struct smb2_file_fs_attribute_info {
+        uint32_t filesystem_attributes;
+        uint32_t maximum_component_name_length;
+        uint32_t filesystem_name_length;
+        const uint8_t *filesystem_name;
+};
+
+/* Device type */
+#define FILE_DEVICE_CD_ROM 0x00000002
+#define FILE_DEVICE_DISK   0x00000007
+
+/* Characteristics */
+#define FILE_REMOVABLE_MEDIA                     0x00000001
+#define FILE_READ_ONLY_DEVICE                    0x00000002
+#define FILE_FLOPPY_DISKETTE                     0x00000004
+#define FILE_WRITE_ONCE_MEDIA                    0x00000008
+#define FILE_REMOTE_DEVICE                       0x00000010
+#define FILE_DEVICE_IS_MOUNTED                   0x00000020
+#define FILE_VIRTUAL_VOLUME                      0x00000040
+#define FILE_DEVICE_SECURE_OPEN                  0x00000100
+#define FILE_CHARACTERISTIC_TS_DEVICE            0x00001000
+#define FILE_CHARACTERISTIC_WEBDAV_DEVICE        0x00002000
+#define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000
+#define FILE_PORTABLE_DEVICE                     0x00040000
+
+struct smb2_file_fs_device_info {
+        uint32_t device_type;
+        uint32_t characteristics;
+};
+
+/* File System Control Flags */
+#define FILE_VC_QUOTA_TRACK            0x00000001
+#define FILE_VC_QUOTA_ENFORCE          0x00000002
+#define FILE_VC_CONTENT_INDEX_DISABLED 0x00000008
+#define FILE_VC_LOG_QUOTA_THRESHOLD    0x00000010
+#define FILE_VC_LOG_QUOTA_LIMIT        0x00000020
+#define FILE_VC_LOG_VOLUME_THRESHOLD   0x00000040
+#define FILE_VC_LOG_VOLUME_LIMIT       0x00000080
+#define FILE_VC_QUOTAS_INCOMPLETE      0x00000100
+#define FILE_VC_QUOTAS_REBUILDING      0x00000200
+
+struct smb2_file_fs_control_info {
+        uint64_t free_space_start_filtering;
+        uint64_t free_space_threshold;
+        uint64_t free_space_stop_filtering;
+        uint64_t default_quota_threshold;
+        uint64_t default_quota_limit;
+        uint32_t file_system_control_flags;
+};
+
+struct smb2_file_fs_full_size_info {
+        uint64_t total_allocation_units;
+        uint64_t caller_available_allocation_units;
+        uint64_t actual_available_allocation_units;
+        uint32_t sectors_per_allocation_unit;
+        uint32_t bytes_per_sector;
+};
+
+struct smb2_file_fs_object_id_info {
+        smb2_guid object_id;
+        uint8_t extended_info[48];
+};
+
+/* Flags */
+#define SSINFO_FLAGS_ALIGNED_DEVICE              0x00000001
+#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002
+#define SSINFO_FLAGS_NO_SEEK_PENALTY             0x00000004
+#define SSINFO_FLAGS_TRIM_ENABLED                0x00000008
+
+struct smb2_file_fs_sector_size_info {
+        uint32_t logical_bytes_per_sector;
+        uint32_t physical_bytes_per_sector_for_atomicity;
+        uint32_t physical_bytes_per_sector_for_performance;
+        uint32_t file_system_effective_physical_bytes_per_sector_for_atomicity;
+        uint32_t flags;
+        uint32_t byte_offset_for_sector_alignment;
+        uint32_t byte_offset_for_partition_alignment;
+};
+
+#define SMB2_QUERY_INFO_REPLY_SIZE 9
+
+struct smb2_query_info_reply {
+        uint16_t output_buffer_offset;
+        uint32_t output_buffer_length;
+        void *output_buffer;
+};
+
+#define SMB2_IOCTL_REQUEST_SIZE 57
+
+/* CtlCode */
+#define SMB2_FSCTL_DFS_GET_REFERRALS            0x00060194
+#define SMB2_FSCTL_PIPE_PEEK                    0x0011400C
+#define SMB2_FSCTL_PIPE_WAIT                    0x00110018
+#define SMB2_FSCTL_PIPE_TRANSCEIVE              0x0011C017
+#define SMB2_FSCTL_SRV_COPYCHUNK                0x001440F2
+#define SMB2_FSCTL_SRV_ENUMERATE_SNAPSHOTS      0x00144064
+#define SMB2_FSCTL_SRV_REQUEST_RESUME_KEY       0x00140078
+#define SMB2_FSCTL_SRV_READ_HASH                0x001441bb
+#define SMB2_FSCTL_SRV_COPYCHUNK_WRITE          0x001480F2
+#define SMB2_FSCTL_LMR_REQUEST_RESILIENCY       0x001401D4
+#define SMB2_FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC
+#define SMB2_FSCTL_SET_REPARSE_POINT            0x000900A4
+#define SMB2_FSCTL_GET_REPARSE_POINT            0X000900A8
+#define SMB2_FSCTL_DFS_GET_REFERRALS_EX         0x000601B0
+#define SMB2_FSCTL_FILE_LEVEL_TRIM              0x00098208
+#define SMB2_FSCTL_VALIDATE_NEGOTIATE_INFO      0x00140204
+
+/* Flags */
+#define SMB2_0_IOCTL_IS_FSCTL                   0x00000001
+
+#define SMB2_SYMLINK_FLAG_RELATIVE 0x00000001
+struct smb2_symlink_reparse_buffer {
+        uint32_t flags;
+        char *subname;
+        char *printname;
+};
+
+#define SMB2_REPARSE_TAG_SYMLINK                0xa000000c
+/*
+ * Reparse_data_buffer
+ */
+struct smb2_reparse_data_buffer {
+        uint32_t reparse_tag;
+        uint16_t reparse_data_length;
+        union {
+                struct smb2_symlink_reparse_buffer symlink;
+        };
+};
+
+struct smb2_ioctl_request {
+        uint32_t ctl_code;
+        smb2_file_id file_id;
+        uint32_t input_offset;
+        uint32_t input_count;
+        uint32_t max_input_response;
+        uint32_t output_offset;
+        uint32_t output_count;
+        uint32_t max_output_response;
+        uint32_t flags;
+        void *input;
+};
+
+#define SMB2_IOCTL_REPLY_SIZE 49
+
+struct smb2_ioctl_reply {
+        uint32_t ctl_code;
+        smb2_file_id file_id;
+        uint32_t input_offset;
+        uint32_t input_count;
+        uint32_t output_offset;
+        uint32_t output_count;
+        uint32_t flags;
+        void *output;
+};
+
+#define SMB2_IOCTL_VALIDIATE_NEGOTIATE_INFO_SIZE 24
+
+struct  smb2_ioctl_validate_negotiate_info {
+        uint32_t capabilities;
+        uint8_t  guid[16];
+        uint16_t security_mode;
+        uint16_t dialect;
+};
+
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_FILE_NAME    0x00000001
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_DIR_NAME     0x00000002
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_ATTRIBUTES   0x00000004
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_SIZE         0x00000008
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_LAST_WRITE   0x00000010
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_LAST_ACCESS  0x00000020
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_CREATION     0x00000040
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_EA           0x00000080
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_SECURITY     0x00000100
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_STREAM_NAME  0x00000200
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_STREAM_SIZE  0x00000400
+#define SMB2_CHANGE_NOTIFY_FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800
+
+#define SMB2_CHANGE_NOTIFY_WATCH_TREE    0x0001
+
+#define SMB2_CHANGE_NOTIFY_REQUEST_SIZE 32
+
+#define SMB2_NOTIFY_CHANGE_FILE_ACTION_ADDED                0x0001
+#define SMB2_NOTIFY_CHANGE_FILE_ACTION_REMOVED              0x0002
+#define SMB2_NOTIFY_CHANGE_FILE_ACTION_MODIFIED             0x0003
+#define SMB2_NOTIFY_CHANGE_FILE_ACTION_RENAMED_OLD_NAME     0x0004
+#define SMB2_NOTIFY_CHANGE_FILE_ACTION_RENAMED_NEW_NAME     0x0005
+#define SMB2_NOTIFY_CHANGE_FILE_ACTION_ADDED_STREAM         0x0006
+#define SMB2_NOTIFY_CHANGE_FILE_ACTION_REMOVED_STREAM       0x0007
+#define SMB2_NOTIFY_CHANGE_FILE_ACTION_MODIFIED_STREAM      0x0008
+
+struct smb2_change_notify_request {
+        uint16_t flags;
+        uint32_t output_buffer_length;
+        smb2_file_id file_id;
+        uint32_t completion_filter;
+};
+
+#define SMB2_CHANGE_NOTIFY_REPLY_SIZE 9
+
+struct smb2_change_notify_reply {
+        uint16_t output_buffer_offset;
+        uint32_t output_buffer_length;
+        uint8_t *output;
+};
+
+struct smb2_file_notify_change_information {
+        uint32_t action;
+        const char *name;
+        struct smb2_file_notify_change_information *next;
+};
+
+int
+smb2_decode_filenotifychangeinformation(
+    struct smb2_context *smb2,
+    struct smb2_file_notify_change_information *fnc,
+    struct smb2_iovec *vec,
+    uint32_t next_entry_offset);
+
+#define SMB2_OPLOCK_LEVEL_NONE        0x00
+#define SMB2_OPLOCK_LEVEL_II          0x01
+#define SMB2_OPLOCK_LEVEL_EXCLUSIVE   0x08
+
+#define SMB2_OPLOCK_BREAK_NOTIFICATION_SIZE 24
+
+struct smb2_oplock_break_notification {
+        uint8_t oplock_level;
+        smb2_file_id file_id;
+};
+
+#define SMB2_OPLOCK_BREAK_ACKNOWLEDGE_SIZE 24
+
+struct smb2_oplock_break_acknowledgement {
+        uint8_t oplock_level;
+        smb2_file_id file_id;
+};
+
+#define SMB2_OPLOCK_BREAK_REPLY_SIZE 24
+
+struct smb2_oplock_break_reply {
+        uint8_t oplock_level;
+        smb2_file_id file_id;
+};
+
+#define SMB2_LEASE_NONE                 0x00
+#define SMB2_LEASE_READ_CACHING         0x01
+#define SMB2_LEASE_HANDLE_CACHING       0x02
+#define SMB2_LEASE_WRITE_CACHING        0x04
+
+#define SMB2_BREAK_TYPE_OPLOCK_NOTIFICATION     0x01
+#define SMB2_BREAK_TYPE_OPLOCK_RESPONSE         0x02
+#define SMB2_BREAK_TYPE_OPLOCK_ACKNOWLEDGE      0x03
+
+#define SMB2_BREAK_TYPE_LEASE_NOTIFICATION      0x04
+#define SMB2_BREAK_TYPE_LEASE_RESPONSE          0x05
+#define SMB2_BREAK_TYPE_LEASE_ACKNOWLEDGE       0x06
+
+#define SMB2_LEASE_BREAK_NOTIFICATION_SIZE 44
+
+struct smb2_lease_break_notification {
+        uint16_t new_epoch;
+        uint32_t flags;
+        smb2_lease_key lease_key;
+        uint32_t current_lease_state;
+        uint32_t new_lease_state;
+        uint32_t break_reason;
+        uint32_t access_mask_hint;
+        uint32_t share_mask_hint;
+};
+
+#define SMB2_LEASE_BREAK_ACKNOWLEDGE_SIZE 36
+
+struct smb2_lease_break_acknowledgement {
+        uint32_t flags;
+        smb2_lease_key lease_key;
+        uint32_t lease_state;
+        uint64_t lease_duration;
+};
+
+#define SMB2_LEASE_BREAK_REPLY_SIZE 36
+
+struct smb2_lease_break_reply {
+        uint32_t flags;
+        smb2_lease_key lease_key;
+        uint32_t lease_state;
+        uint64_t lease_duration;
+};
+
+/* note that for oplocks, notifications (request) and responses (reply)
+ * come from the server, while acknowledgements come from the client
+ */
+struct smb2_oplock_or_lease_break_reply {
+        uint16_t struct_size;
+        int break_type;
+        union {
+                struct smb2_oplock_break_notification oplock;
+                struct smb2_oplock_break_reply oplockrep;
+                struct smb2_lease_break_notification lease;
+                struct smb2_lease_break_reply leaserep;
+        }
+        lock;
+};
+
+struct smb2_oplock_or_lease_break_request {
+        uint16_t struct_size;
+        int break_type;
+        union {
+                struct smb2_oplock_break_acknowledgement oplock;
+                struct smb2_lease_break_acknowledgement lease;
+        }
+        lock;
+};
+
+#define SMB2_WRITE_REQUEST_SIZE 49
+
+#define SMB2_WRITEFLAG_WRITE_THROUGH    0x00000001
+#define SMB2_WRITEFLAG_WRITE_UNBUFFERED 0x00000002
+
+struct smb2_write_request {
+        uint16_t data_offset;
+        uint32_t length;
+        uint64_t offset;
+        const uint8_t* buf;
+        smb2_file_id file_id;
+        uint32_t channel;
+        uint32_t remaining_bytes;
+        uint16_t write_channel_info_offset;
+        uint16_t write_channel_info_length;
+        uint8_t *write_channel_info;
+        uint32_t flags;
+};
+
+#define SMB2_WRITE_REPLY_SIZE 17
+
+struct smb2_write_reply {
+        uint32_t count;
+        uint32_t remaining;
+};
+
+#define SMB2_LOCK_ELEMENT_SIZE 24
+
+struct smb2_lock_element {
+        uint64_t offset;
+        uint64_t length;
+        uint32_t flags;
+        uint32_t reserved;
+};
+
+/* Note that this size includes 1 lock element */
+#define SMB2_LOCK_REQUEST_SIZE 48
+
+struct smb2_lock_request {
+        uint16_t lock_count;
+        uint8_t  lock_sequence_number;
+        uint32_t lock_sequence_index;
+        smb2_file_id file_id;
+        struct smb2_lock_element *locks;
+};
+
+#define SMB2_LOCK_REPLY_SIZE 4
+
+
+#define SMB2_ECHO_REQUEST_SIZE 4
+#define SMB2_ECHO_REPLY_SIZE 4
+#define SMB2_CANCEL_REQUEST_SIZE 4
+
+#define SMB2_LOGOFF_REQUEST_SIZE 4
+#define SMB2_LOGOFF_REPLY_SIZE 4
+
+#define SMB2_TREE_DISCONNECT_REQUEST_SIZE 4
+#define SMB2_TREE_DISCONNECT_REPLY_SIZE 4
+
+#define SMB_ENCRYPTION_AES128_CCM     0x0001
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_SMB2_H_ */

+ 139 - 0
entry/src/main/cpp/include/xbox 360/config.h

@@ -0,0 +1,139 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Whether or not TCP sockets should be allowed to linger after closure */
+#define CONFIGURE_OPTION_TCP_LINGER 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+/* #undef HAVE_ARPA_INET_H */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+/* #undef HAVE_DLFCN_H */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <gssapi/gssapi.h> header file. */
+/* #undef HAVE_GSSAPI_GSSAPI_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+/* #undef HAVE_INTTYPES_H */
+
+/* Whether we use gssapi_krb5 or not */
+/* #undef HAVE_LIBKRB5 */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Whether we have linger */
+#define HAVE_LINGER 1
+
+/* Define to 1 if you have the <netdb.h> header file. */
+/* #undef HAVE_NETDB_H */
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+/* #undef HAVE_NETINET_IN_H */
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+/* #undef HAVE_NETINET_TCP_H */
+
+/* Define to 1 if you have the <poll.h> header file. */
+/* #undef HAVE_POLL_H */
+
+/* Whether sockaddr struct has sa_len */
+/* #undef HAVE_SOCKADDR_LEN */
+
+/* Whether we have sockaddr_Storage */
+/* #undef HAVE_SOCKADDR_STORAGE */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+/* #undef HAVE_STRINGS_H 1 */
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/errno.h> header file. */
+/* #undef HAVE_SYS_ERRNO_H 1 */
+
+/* Define to 1 if you have the <sys/fcntl.h> header file. */
+/* #undef HAVE_SYS_FCNTL_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+/* #undef HAVE_SYS_IOCTL_H */
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+/* #undef HAVE_SYS_POLL_H */
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+/* #undef HAVE_SYS_SOCKET_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+/* #undef HAVE_SYS_TIME_H 1 */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+/* #undef HAVE_SYS_UIO_H */
+
+/* Define to 1 if you have the <sys/unistd.h> header file. */
+/* #undef HAVE_SYS_UNISTD_H */
+
+/* Define to 1 if you have the <sys/_iovec.h> header file. */
+/* #undef HAVE_SYS__IOVEC_H */
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+/* #undef HAVE_UNISTD_H */
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "libsmb2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "ronniesahlberg@gmail.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libsmb2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libsmb2 4.0.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libsmb2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "4.0.0"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "4.0.0"

+ 139 - 0
entry/src/main/cpp/include/xbox/config.h

@@ -0,0 +1,139 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Whether or not TCP sockets should be allowed to linger after closure */
+#define CONFIGURE_OPTION_TCP_LINGER 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+/* #undef HAVE_ARPA_INET_H */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+/* #undef HAVE_DLFCN_H */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <gssapi/gssapi.h> header file. */
+/* #undef HAVE_GSSAPI_GSSAPI_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+/* #undef HAVE_INTTYPES_H */
+
+/* Whether we use gssapi_krb5 or not */
+/* #undef HAVE_LIBKRB5 */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Whether we have linger */
+#define HAVE_LINGER 1
+
+/* Define to 1 if you have the <netdb.h> header file. */
+/* #undef HAVE_NETDB_H */
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+/* #undef HAVE_NETINET_IN_H */
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+/* #undef HAVE_NETINET_TCP_H */
+
+/* Define to 1 if you have the <poll.h> header file. */
+/* #undef HAVE_POLL_H */
+
+/* Whether sockaddr struct has sa_len */
+/* #undef HAVE_SOCKADDR_LEN */
+
+/* Whether we have sockaddr_Storage */
+#define HAVE_SOCKADDR_STORAGE 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+/* #undef HAVE_STDINT_H */
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+/* #undef HAVE_STRINGS_H */
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/errno.h> header file. */
+/* #undef HAVE_SYS_ERRNO_H */
+
+/* Define to 1 if you have the <sys/fcntl.h> header file. */
+/* #undef HAVE_SYS_FCNTL_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+/* #undef HAVE_SYS_IOCTL_H */
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+/* #undef HAVE_SYS_POLL_H */
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+/* #undef HAVE_SYS_SOCKET_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+/* #undef HAVE_SYS_TIME_H */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+/* #undef HAVE_SYS_UIO_H */
+
+/* Define to 1 if you have the <sys/unistd.h> header file. */
+/* #undef HAVE_SYS_UNISTD_H */
+
+/* Define to 1 if you have the <sys/_iovec.h> header file. */
+/* #undef HAVE_SYS__IOVEC_H */
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+/* #undef HAVE_UNISTD_H */
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "libsmb2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "ronniesahlberg@gmail.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libsmb2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libsmb2 4.0.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libsmb2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "4.0.0"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "4.0.0"

BIN
entry/src/main/cpp/libs/arm64-v8a/libsmb2.so


BIN
entry/src/main/cpp/libs/arm64-v8a/libsmb2.so.1


+ 690 - 22
entry/src/main/cpp/napi_init.cpp

@@ -1,53 +1,721 @@
 #include "napi/native_api.h"
 #include "napi/native_api.h"
 
 
-static napi_value Add(napi_env env, napi_callback_info info)
+#include <algorithm>
+#include <atomic>
+#include <cctype>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <filesystem>
+#include <fcntl.h>
+#include <system_error>
+#include <memory>
+#include <mutex>
+#include <stdexcept>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "hilog/log.h"
+
+extern "C" {
+#include "smb2/smb2.h"
+#include "smb2/libsmb2.h"
+}
+
+namespace {
+constexpr unsigned int SMB_LOG_DOMAIN = 0xD001780;
+constexpr const char *SMB_LOG_TAG = "libsmb2";
+
+#define SMB_LOGI(fmt, ...) OH_LOG_Print(LOG_APP, LOG_INFO, SMB_LOG_DOMAIN, SMB_LOG_TAG, fmt, ##__VA_ARGS__)
+#define SMB_LOGE(fmt, ...) OH_LOG_Print(LOG_APP, LOG_ERROR, SMB_LOG_DOMAIN, SMB_LOG_TAG, fmt, ##__VA_ARGS__)
+inline void NapiCheck(napi_status status, const char *message)
+{
+    if (status != napi_ok) {
+        throw std::runtime_error(message == nullptr ? "NAPI call failed" : message);
+    }
+}
+
+struct SambaClient {
+    int64_t id = 0;
+    std::string host;
+    smb2_context *ctx = nullptr;
+    bool closed = false;
+};
+
+struct SambaSession {
+    int64_t id = 0;
+    std::weak_ptr<SambaClient> client;
+    std::string username;
+    std::string password;
+    std::string domain;
+    bool closed = false;
+    int64_t activeTreeId = 0;
+};
+
+struct SambaTree {
+    int64_t id = 0;
+    std::weak_ptr<SambaSession> session;
+    std::string share;
+    bool connected = false;
+};
+
+std::atomic<int64_t> g_nextHandle{1};
+std::mutex g_mutex;
+std::unordered_map<int64_t, std::shared_ptr<SambaClient>> g_clients;
+std::unordered_map<int64_t, std::shared_ptr<SambaSession>> g_sessions;
+std::unordered_map<int64_t, std::shared_ptr<SambaTree>> g_trees;
+
+int64_t GenerateHandle()
+{
+    return g_nextHandle.fetch_add(1);
+}
+
+void Ensure(bool condition, const std::string &message)
+{
+    if (!condition) {
+        throw std::runtime_error(message);
+    }
+}
+
+std::string Trim(const std::string &value)
+{
+    if (value.empty()) {
+        return value;
+    }
+    size_t start = 0;
+    size_t end = value.size();
+    while (start < end && std::isspace(static_cast<unsigned char>(value[start])) != 0) {
+        start++;
+    }
+    if (start == end) {
+        return "";
+    }
+    while (end > start && std::isspace(static_cast<unsigned char>(value[end - 1])) != 0) {
+        end--;
+    }
+    return value.substr(start, end - start);
+}
+
+std::string NormalizeRemotePath(const std::string &raw)
+{
+    std::string trimmed = Trim(raw);
+    if (trimmed.empty() || trimmed == "/" || trimmed == "\\") {
+        return "";
+    }
+    size_t index = 0;
+    while (index < trimmed.size() && (trimmed[index] == '/' || trimmed[index] == '\\')) {
+        index++;
+    }
+    std::string normalized = trimmed.substr(index);
+    if (normalized.empty()) {
+        return "";
+    }
+    std::replace(normalized.begin(), normalized.end(), '\\', '/');
+    return normalized;
+}
+
+std::string BuildSmbErrorMessage(smb2_context *ctx, const std::string &fallback)
+{
+    const char *error = ctx != nullptr ? smb2_get_error(ctx) : nullptr;
+    if (error == nullptr || std::strlen(error) == 0) {
+        return fallback;
+    }
+    return fallback + ": " + error;
+}
+
+std::string ReadString(napi_env env, napi_value value, const char *name, bool required)
+{
+    napi_valuetype type = napi_undefined;
+    NapiCheck(napi_typeof(env, value, &type), "Failed to read argument type");
+    if (type == napi_null || type == napi_undefined) {
+        if (required) {
+            throw std::runtime_error(std::string(name) + " is required");
+        }
+        return "";
+    }
+    if (type != napi_string) {
+        throw std::runtime_error(std::string(name) + " must be a string");
+    }
+    size_t length = 0;
+    NapiCheck(napi_get_value_string_utf8(env, value, nullptr, 0, &length), "Failed to measure string length");
+    std::string result(length, '\0');
+    size_t written = 0;
+    NapiCheck(napi_get_value_string_utf8(env, value, result.data(), length + 1, &written), "Failed to read string value");
+    result.resize(written);
+    return result;
+}
+
+int64_t ReadInt64(napi_env env, napi_value value, const char *name)
+{
+    napi_valuetype type = napi_undefined;
+    NapiCheck(napi_typeof(env, value, &type), "Failed to read numeric argument type");
+    if (type != napi_number) {
+        throw std::runtime_error(std::string(name) + " must be a number");
+    }
+    int64_t result = 0;
+    NapiCheck(napi_get_value_int64(env, value, &result), "Failed to read numeric argument");
+    return result;
+}
+
+napi_value CreateInt64Value(napi_env env, int64_t value)
+{
+    napi_value result = nullptr;
+    NapiCheck(napi_create_int64(env, value, &result), "Failed to create int value");
+    return result;
+}
+
+napi_value CreateUndefined(napi_env env)
+{
+    napi_value result = nullptr;
+    NapiCheck(napi_get_undefined(env, &result), "Failed to create undefined value");
+    return result;
+}
+
+std::shared_ptr<SambaClient> RequireClient(int64_t clientId)
+{
+    std::lock_guard<std::mutex> lock(g_mutex);
+    auto it = g_clients.find(clientId);
+    if (it == g_clients.end() || it->second == nullptr || it->second->closed) {
+        throw std::runtime_error("Invalid SMB client handle");
+    }
+    return it->second;
+}
+
+std::shared_ptr<SambaSession> RequireSession(int64_t sessionId)
+{
+    std::lock_guard<std::mutex> lock(g_mutex);
+    auto it = g_sessions.find(sessionId);
+    if (it == g_sessions.end() || it->second == nullptr || it->second->closed) {
+        throw std::runtime_error("Invalid SMB session handle");
+    }
+    return it->second;
+}
+
+std::shared_ptr<SambaTree> RequireTree(int64_t treeId)
+{
+    std::lock_guard<std::mutex> lock(g_mutex);
+    auto it = g_trees.find(treeId);
+    if (it == g_trees.end() || it->second == nullptr) {
+        throw std::runtime_error("Invalid SMB tree handle");
+    }
+    return it->second;
+}
+
+void RemoveTreeEntry(int64_t treeId)
+{
+    std::lock_guard<std::mutex> lock(g_mutex);
+    g_trees.erase(treeId);
+}
+
+void ResetSessionTree(const std::shared_ptr<SambaSession> &session, int64_t treeId)
 {
 {
-    size_t argc = 2;
-    napi_value args[2] = {nullptr};
+    if (session == nullptr) {
+        return;
+    }
+    std::lock_guard<std::mutex> lock(g_mutex);
+    if (session->activeTreeId == treeId) {
+        session->activeTreeId = 0;
+    }
+}
 
 
-    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
+void CloseTreeInternal(const std::shared_ptr<SambaTree> &tree)
+{
+    if (!tree) {
+        return;
+    }
+    auto session = tree->session.lock();
+    auto client = session ? session->client.lock() : nullptr;
+    if (client && client->ctx && tree->connected) {
+        smb2_disconnect_share(client->ctx);
+    }
+    tree->connected = false;
+    ResetSessionTree(session, tree->id);
+}
 
 
-    napi_valuetype valuetype0;
-    napi_typeof(env, args[0], &valuetype0);
+void RemoveTreesForSession(int64_t sessionId)
+{
+    std::vector<std::shared_ptr<SambaTree>> ownedTrees;
+    {
+        std::lock_guard<std::mutex> lock(g_mutex);
+        for (auto it = g_trees.begin(); it != g_trees.end(); ) {
+            auto session = it->second ? it->second->session.lock() : nullptr;
+            if (!session || session->id == sessionId) {
+                ownedTrees.push_back(it->second);
+                it = g_trees.erase(it);
+                continue;
+            }
+            ++it;
+        }
+    }
+    for (const auto &tree : ownedTrees) {
+        CloseTreeInternal(tree);
+    }
+}
 
 
-    napi_valuetype valuetype1;
-    napi_typeof(env, args[1], &valuetype1);
+void RemoveSessionsForClient(int64_t clientId)
+{
+    std::vector<std::shared_ptr<SambaSession>> sessions;
+    {
+        std::lock_guard<std::mutex> lock(g_mutex);
+        for (auto it = g_sessions.begin(); it != g_sessions.end(); ) {
+            auto client = it->second ? it->second->client.lock() : nullptr;
+            if (!client || client->id == clientId) {
+                it->second->closed = true;
+                sessions.push_back(it->second);
+                it = g_sessions.erase(it);
+                continue;
+            }
+            ++it;
+        }
+    }
+    for (const auto &session : sessions) {
+        RemoveTreesForSession(session->id);
+    }
+}
 
 
-    double value0;
-    napi_get_value_double(env, args[0], &value0);
+void CloseClientContext(const std::shared_ptr<SambaClient> &client)
+{
+    if (!client || client->ctx == nullptr) {
+        return;
+    }
+    smb2_disconnect_share(client->ctx);
+    smb2_destroy_context(client->ctx);
+    client->ctx = nullptr;
+    client->closed = true;
+}
 
 
-    double value1;
-    napi_get_value_double(env, args[1], &value1);
+napi_value CreateClient(napi_env env, napi_callback_info info)
+{
+    try {
+        size_t argc = 1;
+        napi_value args[1] = {nullptr};
+        NapiCheck(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "Failed to get arguments for createClient");
+        if (argc < 1) {
+            throw std::runtime_error("host argument is required");
+        }
+        std::string host = Trim(ReadString(env, args[0], "host", true));
+        Ensure(!host.empty(), "host must not be empty");
 
 
-    napi_value sum;
-    napi_create_double(env, value0 + value1, &sum);
+        smb2_context *ctx = smb2_init_context();
+        if (ctx == nullptr) {
+            throw std::runtime_error("Unable to initialize libsmb2 context");
+        }
+        smb2_set_timeout(ctx, 15);
+        smb2_set_authentication(ctx, SMB2_SEC_NTLMSSP);
 
 
-    return sum;
+        auto client = std::make_shared<SambaClient>();
+        client->id = GenerateHandle();
+        client->host = host;
+        client->ctx = ctx;
+        {
+            std::lock_guard<std::mutex> lock(g_mutex);
+            g_clients[client->id] = client;
+        }
+        return CreateInt64Value(env, client->id);
+    } catch (const std::exception &error) {
+        napi_throw_error(env, nullptr, error.what());
+        return nullptr;
+    }
+}
 
 
+napi_value DestroyClient(napi_env env, napi_callback_info info)
+{
+    try {
+        size_t argc = 1;
+        napi_value args[1] = {nullptr};
+        NapiCheck(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "Failed to get arguments for destroyClient");
+        if (argc < 1) {
+            return CreateUndefined(env);
+        }
+        int64_t clientId = ReadInt64(env, args[0], "clientId");
+        std::shared_ptr<SambaClient> client;
+        {
+            std::lock_guard<std::mutex> lock(g_mutex);
+            auto it = g_clients.find(clientId);
+            if (it != g_clients.end()) {
+                client = it->second;
+                g_clients.erase(it);
+            }
+        }
+        if (client) {
+            RemoveSessionsForClient(client->id);
+            CloseClientContext(client);
+        }
+        return CreateUndefined(env);
+    } catch (const std::exception &error) {
+        napi_throw_error(env, nullptr, error.what());
+        return nullptr;
+    }
+}
+
+napi_value Authenticate(napi_env env, napi_callback_info info)
+{
+    try {
+        size_t argc = 4;
+        napi_value args[4] = {nullptr};
+        NapiCheck(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "Failed to get arguments for authenticate");
+        if (argc < 3) {
+            throw std::runtime_error("authenticate requires clientId, username and password");
+        }
+        int64_t clientId = ReadInt64(env, args[0], "clientId");
+        std::string username = Trim(ReadString(env, args[1], "username", true));
+        std::string password = ReadString(env, args[2], "password", true);
+        std::string domain;
+        if (argc >= 4) {
+            domain = Trim(ReadString(env, args[3], "domain", false));
+        }
+        Ensure(!username.empty(), "username must not be empty");
+
+        auto client = RequireClient(clientId);
+        auto session = std::make_shared<SambaSession>();
+        session->id = GenerateHandle();
+        session->client = client;
+        session->username = username;
+        session->password = password;
+        session->domain = domain;
+        {
+            std::lock_guard<std::mutex> lock(g_mutex);
+            g_sessions[session->id] = session;
+        }
+        return CreateInt64Value(env, session->id);
+    } catch (const std::exception &error) {
+        napi_throw_error(env, nullptr, error.what());
+        return nullptr;
+    }
+}
+
+napi_value DisconnectSession(napi_env env, napi_callback_info info)
+{
+    try {
+        size_t argc = 1;
+        napi_value args[1] = {nullptr};
+        NapiCheck(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "Failed to get arguments for disconnectSession");
+        if (argc < 1) {
+            return CreateUndefined(env);
+        }
+        int64_t sessionId = ReadInt64(env, args[0], "sessionId");
+        std::shared_ptr<SambaSession> session;
+        {
+            std::lock_guard<std::mutex> lock(g_mutex);
+            auto it = g_sessions.find(sessionId);
+            if (it != g_sessions.end()) {
+                session = it->second;
+                g_sessions.erase(it);
+            }
+        }
+        if (session) {
+            session->closed = true;
+            RemoveTreesForSession(session->id);
+        }
+        return CreateUndefined(env);
+    } catch (const std::exception &error) {
+        napi_throw_error(env, nullptr, error.what());
+        return nullptr;
+    }
+}
+
+napi_value ConnectTree(napi_env env, napi_callback_info info)
+{
+    try {
+        size_t argc = 2;
+        napi_value args[2] = {nullptr};
+        NapiCheck(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "Failed to get arguments for connectTree");
+        if (argc < 2) {
+            throw std::runtime_error("connectTree requires sessionId and share name");
+        }
+        int64_t sessionId = ReadInt64(env, args[0], "sessionId");
+        std::string share = Trim(ReadString(env, args[1], "share", true));
+        Ensure(!share.empty(), "share must not be empty");
+
+        auto session = RequireSession(sessionId);
+        auto client = session->client.lock();
+        if (!client || client->closed) {
+            throw std::runtime_error("SMB client is not available");
+        }
+        Ensure(session->activeTreeId == 0, "Session already has an active tree");
+        smb2_context *ctx = client->ctx;
+        if (ctx == nullptr) {
+            throw std::runtime_error("SMB context is not initialized");
+        }
+        smb2_set_user(ctx, session->username.c_str());
+        smb2_set_password(ctx, session->password.c_str());
+        smb2_set_domain(ctx, session->domain.empty() ? nullptr : session->domain.c_str());
+
+        int rc = smb2_connect_share(ctx, client->host.c_str(), share.c_str(), session->username.c_str());
+        if (rc != 0) {
+            throw std::runtime_error(BuildSmbErrorMessage(ctx, "Failed to connect to share"));
+        }
+
+        auto tree = std::make_shared<SambaTree>();
+        tree->id = GenerateHandle();
+        tree->session = session;
+        tree->share = share;
+        tree->connected = true;
+        {
+            std::lock_guard<std::mutex> lock(g_mutex);
+            session->activeTreeId = tree->id;
+            g_trees[tree->id] = tree;
+        }
+        return CreateInt64Value(env, tree->id);
+    } catch (const std::exception &error) {
+        napi_throw_error(env, nullptr, error.what());
+        return nullptr;
+    }
+}
+
+napi_value DisconnectTree(napi_env env, napi_callback_info info)
+{
+    try {
+        size_t argc = 1;
+        napi_value args[1] = {nullptr};
+        NapiCheck(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "Failed to get arguments for disconnectTree");
+        if (argc < 1) {
+            return CreateUndefined(env);
+        }
+        int64_t treeId = ReadInt64(env, args[0], "treeId");
+        std::shared_ptr<SambaTree> tree;
+        {
+            std::lock_guard<std::mutex> lock(g_mutex);
+            auto it = g_trees.find(treeId);
+            if (it != g_trees.end()) {
+                tree = it->second;
+                g_trees.erase(it);
+            }
+        }
+        CloseTreeInternal(tree);
+        return CreateUndefined(env);
+    } catch (const std::exception &error) {
+        napi_throw_error(env, nullptr, error.what());
+        return nullptr;
+    }
+}
+
+napi_value ReadDirectory(napi_env env, napi_callback_info info)
+{
+    try {
+        size_t argc = 2;
+        napi_value args[2] = {nullptr};
+        NapiCheck(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "Failed to get arguments for readDirectory");
+        if (argc < 2) {
+            throw std::runtime_error("readDirectory requires treeId and remote path");
+        }
+        int64_t treeId = ReadInt64(env, args[0], "treeId");
+        std::string path = ReadString(env, args[1], "path", false);
+        auto tree = RequireTree(treeId);
+        auto session = tree->session.lock();
+        auto client = session ? session->client.lock() : nullptr;
+        if (!client || client->ctx == nullptr) {
+            throw std::runtime_error("SMB client context is not available");
+        }
+        Ensure(tree->connected, "SMB tree is not connected");
+        std::string normalized = NormalizeRemotePath(path);
+        SMB_LOGI("readDirectory invoked tree=%{public}lld path=%{public}s", static_cast<long long>(treeId), normalized.c_str());
+        smb2_context *ctx = client->ctx;
+        smb2dir *dir = smb2_opendir(ctx, normalized.c_str());
+        if (dir == nullptr) {
+            const char *err = ctx != nullptr ? smb2_get_error(ctx) : "";
+            SMB_LOGE("smb2_opendir failed tree=%{public}lld path=%{public}s err=%{public}s", static_cast<long long>(treeId), normalized.c_str(), err ? err : "");
+            throw std::runtime_error(BuildSmbErrorMessage(ctx, "Failed to open remote directory"));
+        }
+        std::vector<napi_value> entries;
+        struct smb2dirent *dirent = nullptr;
+        while ((dirent = smb2_readdir(ctx, dir)) != nullptr) {
+            if (!dirent->name) {
+                continue;
+            }
+            if (std::strcmp(dirent->name, ".") == 0 || std::strcmp(dirent->name, "..") == 0) {
+                continue;
+            }
+            napi_value entry = nullptr;
+            NapiCheck(napi_create_object(env, &entry), "Failed to create directory entry object");
+            napi_value nameValue = nullptr;
+            NapiCheck(napi_create_string_utf8(env, dirent->name, NAPI_AUTO_LENGTH, &nameValue), "Failed to create directory name");
+            NapiCheck(napi_set_named_property(env, entry, "name", nameValue), "Failed to set name property");
+            NapiCheck(napi_set_named_property(env, entry, "fileName", nameValue), "Failed to set fileName property");
+
+            bool isDirectory = dirent->st.smb2_type == SMB2_TYPE_DIRECTORY;
+            bool isFile = dirent->st.smb2_type == SMB2_TYPE_FILE;
+            napi_value dirValue = nullptr;
+            NapiCheck(napi_get_boolean(env, isDirectory, &dirValue), "Failed to create directory flag");
+            NapiCheck(napi_set_named_property(env, entry, "isDirectory", dirValue), "Failed to set directory flag");
+            napi_value fileValue = nullptr;
+            NapiCheck(napi_get_boolean(env, isFile, &fileValue), "Failed to create file flag");
+            NapiCheck(napi_set_named_property(env, entry, "isFile", fileValue), "Failed to set file flag");
+            napi_value sizeValue = nullptr;
+            NapiCheck(napi_create_double(env, static_cast<double>(dirent->st.smb2_size), &sizeValue), "Failed to create size value");
+            NapiCheck(napi_set_named_property(env, entry, "size", sizeValue), "Failed to set size property");
+            entries.push_back(entry);
+        }
+        smb2_closedir(ctx, dir);
+        napi_value result = nullptr;
+        NapiCheck(napi_create_array_with_length(env, entries.size(), &result), "Failed to create directory result array");
+        for (size_t i = 0; i < entries.size(); i++) {
+            NapiCheck(napi_set_element(env, result, i, entries[i]), "Failed to append directory entry");
+        }
+        return result;
+    } catch (const std::exception &error) {
+        napi_throw_error(env, nullptr, error.what());
+        return nullptr;
+    }
+}
+
+napi_value DownloadSmbFile(napi_env env, napi_callback_info info)
+{
+    try {
+        size_t argc = 7;
+        napi_value args[7] = {nullptr};
+        NapiCheck(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "Failed to get arguments for downloadSmbFile");
+        if (argc < 7) {
+            throw std::runtime_error("downloadSmbFile requires host, share, username, password, domain, remotePath and localPath");
+        }
+
+        std::string host = ReadString(env, args[0], "host", true);
+        std::string share = ReadString(env, args[1], "share", true);
+        std::string username = ReadString(env, args[2], "username", true);
+        std::string password = ReadString(env, args[3], "password", true);
+        std::string domain = ReadString(env, args[4], "domain", false);
+        std::string remotePath = ReadString(env, args[5], "remotePath", true);
+        std::string localPath = ReadString(env, args[6], "localPath", true);
+
+        if (share.empty()) {
+            throw std::runtime_error("share must not be empty");
+        }
+        if (localPath.empty()) {
+            throw std::runtime_error("localPath must not be empty");
+        }
+
+        std::string normalizedRemotePath = NormalizeRemotePath(remotePath);
+        if (normalizedRemotePath.empty()) {
+            throw std::runtime_error("remotePath must not be empty");
+        }
+
+        std::error_code fsError;
+        std::filesystem::path targetPath(localPath);
+        auto parent = targetPath.parent_path();
+        if (!parent.empty()) {
+            std::filesystem::create_directories(parent, fsError);
+            if (fsError) {
+                throw std::runtime_error("Failed to prepare cache directory: " + fsError.message());
+            }
+        }
+
+        FILE *output = std::fopen(localPath.c_str(), "wb");
+        if (output == nullptr) {
+            throw std::runtime_error("Unable to open local file for writing");
+        }
+
+        smb2_context *ctx = smb2_init_context();
+        if (ctx == nullptr) {
+            std::fclose(output);
+            throw std::runtime_error("Unable to initialize libsmb2 context");
+        }
+
+        auto cleanupContext = [&ctx]() {
+            if (ctx != nullptr) {
+                smb2_disconnect_share(ctx);
+                smb2_destroy_context(ctx);
+                ctx = nullptr;
+            }
+        };
+
+        auto cleanupFile = [&output]() {
+            if (output != nullptr) {
+                std::fclose(output);
+                output = nullptr;
+            }
+        };
+
+        auto removeCacheFile = [&localPath]() {
+            std::remove(localPath.c_str());
+        };
+
+        smb2_set_timeout(ctx, 30);
+        smb2_set_authentication(ctx, SMB2_SEC_NTLMSSP);
+        smb2_set_user(ctx, username.c_str());
+        smb2_set_password(ctx, password.c_str());
+        smb2_set_domain(ctx, domain.empty() ? nullptr : domain.c_str());
+
+        int rc = smb2_connect_share(ctx, host.c_str(), share.c_str(), username.c_str());
+        if (rc != 0) {
+            cleanupFile();
+            cleanupContext();
+            removeCacheFile();
+            throw std::runtime_error(BuildSmbErrorMessage(ctx, "Failed to connect to share"));
+        }
+
+        smb2fh *fileHandle = smb2_open(ctx, normalizedRemotePath.c_str(), O_RDONLY);
+        if (fileHandle == nullptr) {
+            cleanupFile();
+            cleanupContext();
+            removeCacheFile();
+            throw std::runtime_error(BuildSmbErrorMessage(ctx, "Failed to open remote file"));
+        }
+
+        const size_t BUFFER_SIZE = 64 * 1024;
+        std::vector<uint8_t> buffer(BUFFER_SIZE);
+        int bytesRead = 0;
+        while ((bytesRead = smb2_read(ctx, fileHandle, buffer.data(), static_cast<uint32_t>(buffer.size()))) > 0) {
+            size_t written = std::fwrite(buffer.data(), 1, static_cast<size_t>(bytesRead), output);
+            if (written != static_cast<size_t>(bytesRead)) {
+                smb2_close(ctx, fileHandle);
+                cleanupFile();
+                cleanupContext();
+                removeCacheFile();
+                throw std::runtime_error("Failed to write to local file");
+            }
+        }
+
+        if (bytesRead < 0) {
+            std::string message = BuildSmbErrorMessage(ctx, "Failed to read remote file");
+            smb2_close(ctx, fileHandle);
+            cleanupFile();
+            cleanupContext();
+            removeCacheFile();
+            throw std::runtime_error(message);
+        }
+
+        smb2_close(ctx, fileHandle);
+        cleanupFile();
+        cleanupContext();
+
+        return CreateUndefined(env);
+    } catch (const std::exception &error) {
+        napi_throw_error(env, nullptr, error.what());
+        return nullptr;
+    }
+}
 }
 }
 
 
 EXTERN_C_START
 EXTERN_C_START
 static napi_value Init(napi_env env, napi_value exports)
 static napi_value Init(napi_env env, napi_value exports)
 {
 {
-    napi_property_descriptor desc[] = {
-        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr }
+    napi_property_descriptor descriptors[] = {
+        {"createClient", nullptr, CreateClient, nullptr, nullptr, nullptr, napi_default, nullptr},
+        {"destroyClient", nullptr, DestroyClient, nullptr, nullptr, nullptr, napi_default, nullptr},
+        {"authenticate", nullptr, Authenticate, nullptr, nullptr, nullptr, napi_default, nullptr},
+        {"disconnectSession", nullptr, DisconnectSession, nullptr, nullptr, nullptr, napi_default, nullptr},
+        {"connectTree", nullptr, ConnectTree, nullptr, nullptr, nullptr, napi_default, nullptr},
+        {"disconnectTree", nullptr, DisconnectTree, nullptr, nullptr, nullptr, napi_default, nullptr},
+        {"readDirectory", nullptr, ReadDirectory, nullptr, nullptr, nullptr, napi_default, nullptr},
+        {"downloadSmbFile", nullptr, DownloadSmbFile, nullptr, nullptr, nullptr, napi_default, nullptr}
     };
     };
-    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
+    NapiCheck(napi_define_properties(env, exports, sizeof(descriptors) / sizeof(descriptors[0]), descriptors), "Failed to define native exports");
     return exports;
     return exports;
 }
 }
 EXTERN_C_END
 EXTERN_C_END
 
 
-static napi_module demoModule = {
+static napi_module g_module = {
     .nm_version = 1,
     .nm_version = 1,
     .nm_flags = 0,
     .nm_flags = 0,
     .nm_filename = nullptr,
     .nm_filename = nullptr,
     .nm_register_func = Init,
     .nm_register_func = Init,
     .nm_modname = "entry",
     .nm_modname = "entry",
-    .nm_priv = ((void*)0),
-    .reserved = { 0 },
+    .nm_priv = nullptr,
+    .reserved = {0},
 };
 };
 
 
 extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
 extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
 {
 {
-    napi_module_register(&demoModule);
+    napi_module_register(&g_module);
 }
 }

+ 23 - 1
entry/src/main/cpp/types/libentry/Index.d.ts

@@ -1 +1,23 @@
-export const add: (a: number, b: number) => number;
+export interface NativeDirectoryEntry {
+  name?: string;
+  fileName?: string;
+  isDirectory?: boolean;
+  isFile?: boolean;
+  size?: number;
+}
+
+export interface NativeModule {
+  createClient(host: string): number;
+  destroyClient(clientId: number): void;
+  authenticate(clientId: number, username: string, password: string, domain?: string): number;
+  disconnectSession(sessionId: number): void;
+  connectTree(sessionId: number, share: string): number;
+  disconnectTree(treeId: number): void;
+  readDirectory(treeId: number, path: string): NativeDirectoryEntry[];
+  downloadSmbFile(host: string, share: string, username: string, password: string, domain: string | undefined, remotePath: string, localPath: string): void;
+}
+
+declare const libentry: NativeModule;
+
+export default libentry;
+export { NativeDirectoryEntry, NativeModule };

+ 2 - 0
entry/src/main/ets/common/constants/CommonConstants.ets

@@ -200,6 +200,8 @@ export class CommonConstants {
    */
    */
   static readonly TYPE_INTERNET: number = 1;//网络视频
   static readonly TYPE_INTERNET: number = 1;//网络视频
   static readonly TYPE_WEBDAV: number = 3;//webdav文件
   static readonly TYPE_WEBDAV: number = 3;//webdav文件
+  static readonly TYPE_SMB: number = 4;//SMB文件
+  static readonly TYPE_NAVIDROME: number = 5;//Navidrome流媒体文件
 
 
   static readonly TYPE_LOCK: number = 200;//私密视频
   static readonly TYPE_LOCK: number = 200;//私密视频
   static readonly TYPE_LIKE: number = 201;//我收藏的视频
   static readonly TYPE_LIKE: number = 201;//我收藏的视频

+ 1 - 1
entry/src/main/ets/common/enums/WebdavManagerStates.ets → entry/src/main/ets/common/enums/RemoteDriveManagerStates.ets

@@ -1,4 +1,4 @@
-export enum WebdavManagerStates{
+export enum RemoteDriveManagerStates{
   // 账户管理
   // 账户管理
   QueryAccountsSucceed = "QueryAccountsSucceed",
   QueryAccountsSucceed = "QueryAccountsSucceed",
   QueryAccountsFailed = "QueryAccountsFailed",
   QueryAccountsFailed = "QueryAccountsFailed",

+ 5 - 0
entry/src/main/ets/common/enums/RemoteDriveType.ets

@@ -0,0 +1,5 @@
+export enum RemoteDriveType {
+  WebDav = 0,
+  Smb = 1,
+  Navidrome = 2
+}

+ 2 - 1
entry/src/main/ets/common/enums/SongType.ets

@@ -1,5 +1,6 @@
 // 歌曲类型枚举
 // 歌曲类型枚举
 export enum SongType {
 export enum SongType {
   Local = 0,    // 本地歌曲
   Local = 0,    // 本地歌曲
-  WebDav = 1    // WebDAV云端歌曲
+  WebDav = 1,   // WebDAV云端歌曲
+  Smb = 2       // SMB云端歌曲
 }
 }

+ 331 - 0
entry/src/main/ets/common/network/NavidromeApi.ets

@@ -0,0 +1,331 @@
+import { http } from '@kit.NetworkKit';
+import { MD5 } from '@pura/harmony-utils';
+import { WebDavAccount } from '../../viewmodel/WebDavAccount';
+import Logger from '../util/Logger';
+
+const TAG = 'heanup NavidromeApi';
+const API_VERSION = '1.16.1';
+const CLIENT_NAME = 'TTMusic';
+
+export interface NavidromeArtist {
+  id: string;
+  name: string;
+  albumCount?: number;
+}
+
+export interface NavidromeArtistDetail extends NavidromeArtist {
+  albums: NavidromeAlbum[];
+}
+
+export interface NavidromeAlbum {
+  id: string;
+  name: string;
+  artist?: string;
+  coverArt?: string;
+  year?: number;
+  songCount?: number;
+}
+
+export interface NavidromeAlbumDetail extends NavidromeAlbum {
+  songs: NavidromeSong[];
+}
+
+export interface NavidromeSong {
+  id: string;
+  title: string;
+  artist?: string;
+  album?: string;
+  track?: number;
+  duration?: number;
+  bitRate?: number;
+  suffix?: string;
+  size?: number;
+  contentType?: string;
+  coverArt?: string;
+}
+
+interface SubsonicError {
+  code?: number;
+  message?: string;
+}
+
+interface SubsonicArtistEntry {
+  id?: string;
+  name?: string;
+  albumCount?: number;
+}
+
+interface SubsonicIndexEntry {
+  name?: string;
+  artist?: Array<SubsonicArtistEntry>;
+}
+
+interface SubsonicIndexes {
+  index?: Array<SubsonicIndexEntry>;
+}
+
+interface SubsonicAlbumEntry {
+  id?: string;
+  name?: string;
+  artist?: string;
+  coverArt?: string;
+  year?: number;
+  songCount?: number;
+  song?: Array<SubsonicSongEntry>;
+}
+
+interface SubsonicSongEntry {
+  id?: string;
+  title?: string;
+  artist?: string;
+  album?: string;
+  track?: number;
+  duration?: number;
+  bitRate?: number;
+  suffix?: string;
+  size?: number;
+  contentType?: string;
+  coverArt?: string;
+}
+
+interface SubsonicArtistBody {
+  id?: string;
+  name?: string;
+  albumCount?: number;
+  album?: Array<SubsonicAlbumEntry>;
+}
+
+interface SubsonicBody {
+  status: string;
+  error?: SubsonicError;
+  indexes?: SubsonicIndexes;
+  artist?: SubsonicArtistBody;
+  album?: SubsonicAlbumEntry;
+}
+
+interface SubsonicRoot {
+  ['subsonic-response']: SubsonicBody;
+}
+
+class QueryParam {
+  key: string;
+  value: string;
+
+  constructor(key: string, value: string) {
+    this.key = key;
+    this.value = value;
+  }
+}
+
+export class NavidromeApi {
+  async getArtists(account: WebDavAccount): Promise<NavidromeArtist[]> {
+    const response = await this.request(account, 'getIndexes', []);
+    const indexes = response.indexes;
+    const artists: NavidromeArtist[] = [];
+    if (indexes && indexes.index) {
+      for (let i = 0; i < indexes.index.length; i++) {
+        const entry = indexes.index[i];
+        const entryArtists = entry.artist;
+        if (!entryArtists) {
+          continue;
+        }
+        for (let j = 0; j < entryArtists.length; j++) {
+          const artist = entryArtists[j];
+          if (artist.id && artist.name) {
+            artists.push({
+              id: artist.id,
+              name: artist.name,
+              albumCount: artist.albumCount
+            });
+          }
+        }
+      }
+    }
+    return artists;
+  }
+
+  async getArtist(account: WebDavAccount, artistId: string): Promise<NavidromeArtistDetail> {
+    const response = await this.request(account, 'getArtist', [new QueryParam('id', artistId)]);
+    const artist = response.artist;
+    if (!artist || !artist.id || !artist.name) {
+      throw new Error('Navidrome 返回的艺术家数据无效');
+    }
+    const albums: NavidromeAlbum[] = [];
+    if (artist.album) {
+      for (let i = 0; i < artist.album.length; i++) {
+        const album = artist.album[i];
+        if (!album.id || !album.name) {
+          continue;
+        }
+        albums.push({
+          id: album.id,
+          name: album.name,
+          artist: album.artist ?? artist.name,
+          coverArt: album.coverArt,
+          year: album.year,
+          songCount: album.songCount
+        });
+      }
+    }
+    return {
+      id: artist.id,
+      name: artist.name,
+      albumCount: artist.albumCount,
+      albums
+    };
+  }
+
+  async getAlbum(account: WebDavAccount, albumId: string): Promise<NavidromeAlbumDetail> {
+    const response = await this.request(account, 'getAlbum', [new QueryParam('id', albumId)]);
+    const album = response.album;
+    if (!album || !album.id || !album.name) {
+      throw new Error('Navidrome 返回的专辑数据无效');
+    }
+    const songs: NavidromeSong[] = [];
+    if (album.song) {
+      for (let i = 0; i < album.song.length; i++) {
+        const song = album.song[i];
+        if (!song.id) {
+          continue;
+        }
+        songs.push({
+          id: song.id,
+          title: song.title ?? '未知曲目',
+          artist: song.artist,
+          album: song.album,
+          track: song.track,
+          duration: song.duration,
+          bitRate: song.bitRate,
+          suffix: song.suffix,
+          size: song.size,
+          contentType: song.contentType,
+          coverArt: song.coverArt
+        });
+      }
+    }
+    return {
+      id: album.id,
+      name: album.name,
+      artist: album.artist,
+      coverArt: album.coverArt,
+      year: album.year,
+      songCount: album.songCount,
+      songs
+    };
+  }
+
+  async buildStreamUrl(account: WebDavAccount, songId: string | undefined): Promise<string> {
+    if (!songId) {
+      throw new Error('Navidrome歌曲ID缺失');
+    }
+    const baseUrl = this.buildBaseUrl(account);
+    const params: QueryParam[] = [];
+    params.push(new QueryParam('id', songId));
+    this.appendParams(params, await this.buildAuthParams(account));
+    this.appendCommonParams(params);
+    const query = this.buildQueryString(params);
+    return `${baseUrl}/stream?${query}`;
+  }
+
+  private async request(account: WebDavAccount, endpoint: string, extraParams: Array<QueryParam>): Promise<SubsonicBody> {
+    const baseUrl = this.buildBaseUrl(account);
+    const params: QueryParam[] = [];
+    this.appendParams(params, await this.buildAuthParams(account));
+    this.appendParams(params, extraParams);
+    this.appendCommonParams(params);
+    const query = this.buildQueryString(params);
+    const url = `${baseUrl}/${endpoint}?${query}`;
+
+    const httpRequest = http.createHttp();
+    try {
+      const options: http.HttpRequestOptions = {
+        method: http.RequestMethod.GET,
+        connectTimeout: 10000,
+        readTimeout: 15000,
+        expectDataType: http.HttpDataType.STRING
+      };
+      const response = await httpRequest.request(url, options);
+      if (response.responseCode !== 200) {
+        throw new Error(`Navidrome 请求失败: HTTP ${response.responseCode}`);
+      }
+      const result = response.result as string;
+      const parsed = JSON.parse(result) as SubsonicRoot;
+      const body = Reflect.get(parsed as object, 'subsonic-response') as SubsonicBody | undefined;
+      if (!body) {
+        throw new Error('Navidrome 响应格式异常');
+      }
+      if (body.status !== 'ok') {
+        const errorInfo = body.error;
+        const message = errorInfo && errorInfo.message ? errorInfo.message : 'Navidrome 返回错误';
+        throw new Error(message);
+      }
+      return body;
+    } catch (error) {
+      const err = error as Error;
+      Logger.error(TAG, `Navidrome 请求失败: ${err.message}`);
+      throw err;
+    } finally {
+      httpRequest.destroy();
+    }
+  }
+
+
+  private appendParams(target: Array<QueryParam>, source: Array<QueryParam>): void {
+    for (let i = 0; i < source.length; i++) {
+      const param = source[i];
+      target.push(new QueryParam(param.key, param.value));
+    }
+  }
+
+  private appendCommonParams(target: Array<QueryParam>): void {
+    target.push(new QueryParam('v', API_VERSION));
+    target.push(new QueryParam('c', CLIENT_NAME));
+    target.push(new QueryParam('f', 'json'));
+  }
+
+  private async buildAuthParams(account: WebDavAccount): Promise<Array<QueryParam>> {
+    const salt = Math.random().toString(36).slice(2, 10);
+    const token = await MD5.digestSync(`${account.password ?? ''}${salt}`);
+    const params: Array<QueryParam> = [];
+    params.push(new QueryParam('u', account.account));
+    params.push(new QueryParam('t', token));
+    params.push(new QueryParam('s', salt));
+    return params;
+  }
+
+  private buildQueryString(params: Array<QueryParam>): string {
+    const parts: Array<string> = [];
+    for (let i = 0; i < params.length; i++) {
+      const param = params[i];
+      if (param.value.length === 0) {
+        continue;
+      }
+      parts.push(`${encodeURIComponent(param.key)}=${encodeURIComponent(param.value)}`);
+    }
+    return parts.join('&');
+  }
+
+  private buildBaseUrl(account: WebDavAccount): string {
+    const protocol = account.enableHttps ? 'https' : 'http';
+    const host = account.isUseLocalHost && account.localHost ? account.localHost : account.host;
+    const portPart = account.port && account.port !== 80 && account.port !== 443 ? `:${account.port}` : '';
+    const basePath = this.normalizeBasePath(account.navidromeBasePath ?? '/rest');
+    return `${protocol}://${host}${portPart}${basePath}`;
+  }
+
+  private normalizeBasePath(path: string): string {
+    if (!path || path.trim().length === 0) {
+      return '/rest';
+    }
+    let normalized = path.trim();
+    if (!normalized.startsWith('/')) {
+      normalized = `/${normalized}`;
+    }
+    if (normalized.endsWith('/')) {
+      normalized = normalized.slice(0, -1);
+    }
+    return normalized.length === 0 ? '/rest' : normalized;
+  }
+}
+
+export const navidromeApi = new NavidromeApi();

+ 122 - 0
entry/src/main/ets/common/network/RemoteSongCache.ets

@@ -0,0 +1,122 @@
+import { MD5 } from '@pura/harmony-utils';
+import { RemoteDriveManager } from '../util/RemoteDriveManager';
+import FileManager, { merge2paths } from '../util/FileManager';
+import Logger from '../util/Logger';
+import { fileIo } from '@kit.CoreFileKit';
+import { common } from '@kit.AbilityKit';
+
+const TAG = 'RemoteSongCache';
+const CACHE_ROOT_DIR = 'remote_cache';
+
+export enum RemoteCacheType {
+  WEBDAV = 'webdav',
+  SMB = 'smb'
+}
+
+export interface CachePathInfo {
+  cacheDir: string;
+  cachePath: string;
+  normalizedRelative: string;
+}
+
+function ensureContext(): common.Context {
+  const manager = RemoteDriveManager.getInstance();
+  if (!manager.context) {
+    throw new Error('应用上下文未初始化,无法访问缓存目录');
+  }
+  return manager.context;
+}
+
+async function ensureCacheRoot(): Promise<string> {
+  const context = ensureContext();
+  const baseDir = context.filesDir ?? context.cacheDir;
+  if (!baseDir) {
+    throw new Error('未找到可用的缓存根目录');
+  }
+  const cacheRoot = merge2paths(baseDir, CACHE_ROOT_DIR);
+  await FileManager.createDir(cacheRoot);
+  return cacheRoot;
+}
+
+async function ensureTypeDir(type: RemoteCacheType): Promise<string> {
+  const root = await ensureCacheRoot();
+  const typeDir = merge2paths(root, type);
+  await FileManager.createDir(typeDir);
+  return typeDir;
+}
+
+async function ensureAccountDir(type: RemoteCacheType, accountId?: string | number): Promise<string> {
+  const typeDir = await ensureTypeDir(type);
+  const accountDir = merge2paths(typeDir, accountId ? accountId.toString() : 'default');
+  await FileManager.createDir(accountDir);
+  return accountDir;
+}
+
+export function normalizeCacheRelativePath(relativePath: string): string {
+  if (!relativePath || relativePath.length === 0) {
+    return '/';
+  }
+  let normalized = relativePath.replace(/\\/g, '/').replace(/\/+/g, '/');
+  if (!normalized.startsWith('/')) {
+    normalized = `/${normalized}`;
+  }
+  if (normalized.length > 1 && normalized.endsWith('/')) {
+    normalized = normalized.slice(0, -1);
+  }
+  return normalized || '/';
+}
+
+async function buildCacheFileName(relativePath: string): Promise<string> {
+  const hash = await MD5.digestSync(relativePath ?? '');
+  const lastSlash = relativePath.lastIndexOf('/');
+  const originalName = lastSlash >= 0 ? relativePath.substring(lastSlash + 1) : relativePath;
+  const safeName = originalName ? originalName.replace(/[^a-zA-Z0-9_.-]/g, '_') : 'remote';
+  return `${hash}_${safeName}`;
+}
+
+export async function resolveCacheFilePath(
+  type: RemoteCacheType,
+  accountId: string | number | undefined,
+  relativePath: string
+): Promise<CachePathInfo> {
+  const accountDir = await ensureAccountDir(type, accountId);
+  const normalizedRelative = normalizeCacheRelativePath(relativePath);
+  const cacheFileName = await buildCacheFileName(normalizedRelative);
+  const cachePath = merge2paths(accountDir, cacheFileName);
+  return {
+    cacheDir: accountDir,
+    cachePath,
+    normalizedRelative
+  };
+}
+
+export async function findExistingCacheFile(
+  type: RemoteCacheType,
+  accountId: string | number | undefined,
+  relativePath: string
+): Promise<string | null> {
+  const pathInfo = await resolveCacheFilePath(type, accountId, relativePath);
+  const cachePath = pathInfo.cachePath;
+  const exists = await FileManager.isExist(cachePath);
+  if (!exists) {
+    return null;
+  }
+  const size = await FileManager.getFileSize(cachePath);
+  if (size <= 0) {
+    await FileManager.deleteFile(cachePath);
+    return null;
+  }
+  return cachePath;
+}
+
+export async function writeBufferToFile(data: ArrayBuffer, filePath: string): Promise<void> {
+  try {
+    const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
+    await fileIo.write(file.fd, data);
+    fileIo.closeSync(file);
+  } catch (error) {
+    const err = error as Error;
+    Logger.error(TAG, `写入缓存文件失败: ${err.message}`);
+    throw err;
+  }
+}

+ 146 - 0
entry/src/main/ets/common/network/SmbBridge.ets

@@ -0,0 +1,146 @@
+import libentry, { NativeDirectoryEntry as LibNativeDirectoryEntry, NativeModule as LibNativeModule } from 'libentry.so';
+
+export interface SmbConnectionOptions {
+  host: string;
+  share: string;
+  username: string;
+  password: string;
+  domain?: string;
+}
+
+export interface SmbListOptions extends SmbConnectionOptions {
+  path?: string;
+}
+
+export interface SmbDirectoryEntry {
+  name: string;
+  isDirectory: boolean;
+  isFile: boolean;
+  size: number;
+}
+
+export type NativeDirectoryEntry = LibNativeDirectoryEntry;
+
+const bridge: LibNativeModule = libentry as LibNativeModule;
+
+export class NativeSambaTree {
+  private closed = false;
+  private readonly treeId: number;
+
+  constructor(treeId: number) {
+    this.treeId = treeId;
+  }
+
+  private normalizePath(input?: string): string {
+    if (!input) {
+      return '';
+    }
+    const cleaned = input.replace(/^[\\/]+/, '').replace(/\\/g, '/');
+    return cleaned;
+  }
+
+  private ensureOpen(): void {
+    if (this.closed) {
+      throw new Error('SMB tree has been closed');
+    }
+  }
+
+  async readDirectory(path?: string): Promise<NativeDirectoryEntry[]> {
+    this.ensureOpen();
+    const normalized = this.normalizePath(path);
+    return bridge.readDirectory(this.treeId, normalized) ?? [];
+  }
+
+  async close(): Promise<void> {
+    if (this.closed) {
+      return;
+    }
+    bridge.disconnectTree(this.treeId);
+    this.closed = true;
+  }
+}
+
+export class NativeSambaSession {
+  private closed = false;
+  private readonly sessionId: number;
+
+  constructor(sessionId: number) {
+    this.sessionId = sessionId;
+  }
+
+  private ensureOpen(): void {
+    if (this.closed) {
+      throw new Error('SMB session has been closed');
+    }
+  }
+
+  async connectTree(share: string): Promise<NativeSambaTree> {
+    this.ensureOpen();
+    const treeId = Number(bridge.connectTree(this.sessionId, share));
+    return new NativeSambaTree(treeId);
+  }
+
+  async disconnect(): Promise<void> {
+    if (this.closed) {
+      return;
+    }
+    bridge.disconnectSession(this.sessionId);
+    this.closed = true;
+  }
+}
+
+export class NativeSambaClient {
+  private readonly clientId: number;
+  private closed = false;
+
+  constructor(host: string) {
+    this.clientId = bridge.createClient(host);
+  }
+
+  private ensureOpen(): void {
+    if (this.closed) {
+      throw new Error('SMB client has been closed');
+    }
+  }
+
+  async authenticate(options: SmbConnectionOptions): Promise<NativeSambaSession> {
+    this.ensureOpen();
+    const sessionId = Number(
+      bridge.authenticate(this.clientId, options.username, options.password, options.domain)
+    );
+    return new NativeSambaSession(sessionId);
+  }
+
+  async close(): Promise<void> {
+    if (this.closed) {
+      return;
+    }
+    bridge.destroyClient(this.clientId);
+    this.closed = true;
+  }
+}
+
+async function withSmbTree<T>(options: SmbConnectionOptions, handler: (tree: NativeSambaTree) => Promise<T>): Promise<T> {
+  const client = new NativeSambaClient(options.host);
+  let session: NativeSambaSession | undefined;
+  let tree: NativeSambaTree | undefined;
+  try {
+    session = await client.authenticate(options);
+    tree = await session.connectTree(options.share);
+    return await handler(tree);
+  } finally {
+    await tree?.close();
+    await session?.disconnect();
+    await client.close();
+  }
+}
+
+export async function listSmbDirectory(options: SmbListOptions): Promise<SmbDirectoryEntry[]> {
+  const entries = await withSmbTree(options, (tree: NativeSambaTree) => tree.readDirectory(options.path));
+  return entries.map((entry: NativeDirectoryEntry): SmbDirectoryEntry => ({
+    name: entry.name ?? entry.fileName ?? 'unknown',
+    isDirectory: Boolean(entry.isDirectory),
+    isFile: Boolean(entry.isFile),
+    size: entry.size ?? 0
+  }));
+}

+ 142 - 0
entry/src/main/ets/common/network/SmbFileCache.ets

@@ -0,0 +1,142 @@
+import { RemoteDriveManager } from '../util/RemoteDriveManager';
+import FileManager from '../util/FileManager';
+import { WebDavAccount } from '../../viewmodel/WebDavAccount';
+import Logger from '../util/Logger';
+import nativeBridge from 'libentry.so';
+import { RemoteCacheType, resolveCacheFilePath, normalizeCacheRelativePath } from './RemoteSongCache';
+
+interface SmbDownloadBinding {
+  downloadSmbFile(host: string, share: string, username: string, password: string, domain: string | undefined, remotePath: string, localPath: string): void;
+}
+
+const smbBinding: SmbDownloadBinding = nativeBridge as SmbDownloadBinding;
+
+function cleanShareName(name?: string): string {
+  return name ? name.replace(/^\/+|\/+$/g, '') : '';
+}
+
+function buildRemotePathCandidates(relativePath: string, account: WebDavAccount): string[] {
+  const ordered: string[] = [];
+  const seen = new Set<string>();
+  const pushCandidate = (value: string) => {
+    if (!value || value.length === 0) {
+      return;
+    }
+    const normalized = normalizeCacheRelativePath(value);
+    if (!seen.has(normalized)) {
+      seen.add(normalized);
+      ordered.push(normalized);
+    }
+  };
+
+  const base = normalizeCacheRelativePath(relativePath);
+  pushCandidate(base);
+
+  const trimmed = base.replace(/^\/+/, '');
+  if (trimmed.length === 0) {
+    return ordered;
+  }
+
+  const shareNames: string[] = [];
+  const accountShare = cleanShareName(account.smbShare);
+  if (accountShare.length > 0) {
+    shareNames.push(accountShare);
+  }
+
+  for (let i = 0; i < shareNames.length; i++) {
+    const share = shareNames[i];
+    const lowerShare = share.toLowerCase();
+    if (trimmed.toLowerCase().startsWith(`${lowerShare}/`)) {
+      const remainder = trimmed.substring(share.length + 1);
+      if (remainder.length > 0) {
+        pushCandidate(remainder);
+      }
+    }
+  }
+
+  const slashIndex = trimmed.indexOf('/');
+  if (slashIndex > 0) {
+    const firstSegment = trimmed.substring(0, slashIndex);
+    const remainder = trimmed.substring(slashIndex + 1);
+    if (remainder.length > 0) {
+      const matchesShare = shareNames.some(name => name.toLowerCase() === firstSegment.toLowerCase());
+      if (!matchesShare) {
+        pushCandidate(remainder);
+      }
+    }
+  }
+
+  return ordered;
+}
+
+function shouldRetryRemoteDownload(error: Error): boolean {
+  if (!error || !error.message) {
+    return false;
+  }
+  const message = error.message.toLowerCase();
+  return message.indexOf('failed to open remote file') >= 0 ||
+    message.indexOf('object_name_not_found') >= 0 ||
+    message.indexOf('no such file') >= 0;
+}
+
+export async function ensureSmbFileCached(account: WebDavAccount, relativePath: string): Promise<string> {
+  const manager = RemoteDriveManager.getInstance();
+  if (!manager.context) {
+    throw new Error('App context is not initialized');
+  }
+  if (!account.smbShare) {
+    throw new Error('SMB account missing share name');
+  }
+  const normalizedRelative = normalizeCacheRelativePath(relativePath);
+  const pathInfo = await resolveCacheFilePath(
+    RemoteCacheType.SMB,
+    account.id?.toString(),
+    normalizedRelative
+  );
+  const localPath = pathInfo.cachePath;
+  let exists = await FileManager.isExist(localPath);
+  if (exists) {
+    const currentSize = await FileManager.getFileSize(localPath);
+    if (currentSize <= 0) {
+      await FileManager.deleteFile(localPath);
+      exists = false;
+    }
+  }
+  if (!exists) {
+    const host = account.isUseLocalHost && account.localHost ? account.localHost : account.host;
+    const candidates = buildRemotePathCandidates(normalizedRelative, account);
+    let lastError: Error | undefined;
+    for (let i = 0; i < candidates.length; i++) {
+      const candidate = candidates[i];
+      try {
+        smbBinding.downloadSmbFile(
+          host,
+          account.smbShare,
+          account.account,
+          account.password,
+          account.smbDomain,
+          candidate,
+          localPath
+        );
+        if (i > 0) {
+          Logger.warn('SMB download fallback succeeded with path:', candidate);
+        }
+        lastError = undefined;
+        break;
+      } catch (error) {
+        await FileManager.deleteFile(localPath);
+        const err = error as Error;
+        lastError = err;
+        const shouldRetry = i < candidates.length - 1 && shouldRetryRemoteDownload(err);
+        if (!shouldRetry) {
+          throw err;
+        }
+        Logger.warn('SMB download failed for path, trying fallback:', candidate);
+      }
+    }
+    if (lastError) {
+      throw lastError;
+    }
+  }
+  return localPath;
+}

+ 151 - 0
entry/src/main/ets/common/network/WebDavFileCache.ets

@@ -0,0 +1,151 @@
+import Logger from '../util/Logger';
+import FileManager from '../util/FileManager';
+import { buffer } from '@kit.ArkTS';
+import { fileIo } from '@kit.CoreFileKit';
+import { rcp } from '@kit.RemoteCommunicationKit';
+import { WebDavAccount } from '../../viewmodel/WebDavAccount';
+import {
+  RemoteCacheType,
+  resolveCacheFilePath,
+  findExistingCacheFile,
+  normalizeCacheRelativePath
+} from './RemoteSongCache';
+
+const TAG = 'WebDavFileCache';
+const ongoingDownloads: Map<string, Promise<string | null>> = new Map();
+
+function normalizeRelativePath(path: string): string {
+  if (!path) {
+    return '/';
+  }
+  try {
+    return normalizeCacheRelativePath(decodeURIComponent(path));
+  } catch (error) {
+    Logger.warn(TAG, `WebDAV 相对路径解码失败,继续使用原始值: ${(error as Error).message}`);
+    return normalizeCacheRelativePath(path);
+  }
+}
+
+function buildAuthHeader(account: WebDavAccount): string | undefined {
+  if (!account.account || !account.password) {
+    return undefined;
+  }
+  const credentials = `${account.account}:${account.password}`;
+  return `Basic ${buffer.from(credentials).toString('base64')}`;
+}
+
+export async function findWebDavCacheIfExists(account: WebDavAccount, relativePath: string): Promise<string | null> {
+  const normalizedRelative = normalizeRelativePath(relativePath);
+  return findExistingCacheFile(RemoteCacheType.WEBDAV, account.id?.toString(), normalizedRelative);
+}
+
+export async function triggerWebDavCacheDownload(
+  account: WebDavAccount,
+  relativePath: string,
+  fullUrl: string
+): Promise<string | null> {
+  const pathInfo = await resolveCacheFilePath(
+    RemoteCacheType.WEBDAV,
+    account.id?.toString(),
+    normalizeRelativePath(relativePath)
+  );
+  const cachePath = pathInfo.cachePath;
+  const normalizedRelative = pathInfo.normalizedRelative;
+
+  const exists = await FileManager.isExist(cachePath);
+  if (exists) {
+    const validSize = await FileManager.getFileSize(cachePath);
+    if (validSize > 0) {
+      Logger.info(TAG, `WebDAV 缓存已存在,跳过下载: ${normalizedRelative}`);
+      return null;
+    }
+    await FileManager.deleteFile(cachePath);
+  }
+
+  if (ongoingDownloads.has(cachePath)) {
+    Logger.info(TAG, `WebDAV 缓存下载进行中,跳过重复任务: ${normalizedRelative}`);
+    const existingTask = ongoingDownloads.get(cachePath);
+    if (existingTask) {
+      return existingTask;
+    }
+    return Promise.resolve(null);
+  }
+
+  const downloadTask = (async () => {
+    const headers: Record<string, string> = {
+      'User-Agent': 'TTMusic-WebDAV/1.0',
+      'Accept': '*/*'
+    };
+    const authHeader = buildAuthHeader(account);
+    if (authHeader) {
+      headers['Authorization'] = authHeader;
+    }
+    try {
+      await downloadWebDavToFile(fullUrl, authHeader, cachePath);
+      Logger.info(TAG, `WebDAV 缓存完成: ${normalizedRelative}`);
+      return cachePath;
+    } catch (error) {
+      const err = error as Error;
+      Logger.error(TAG, `WebDAV 缓存异常: ${err.message}`);
+      await FileManager.deleteFile(cachePath);
+      throw err;
+    } finally {
+      ongoingDownloads.delete(cachePath);
+    }
+  })();
+
+  ongoingDownloads.set(cachePath, downloadTask);
+  return downloadTask;
+}
+
+async function downloadWebDavToFile(
+  url: string,
+  authHeader: string | undefined,
+  cachePath: string
+): Promise<void> {
+  const fileHandle: fileIo.File = await fileIo.open(cachePath, fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.CREATE | fileIo.OpenMode.TRUNC);
+  const headers: rcp.RequestHeaders = {
+    'User-Agent': 'TTMusic-WebDAV/1.0',
+    'Accept': '*/*',
+    'Method': 'GET'
+  };
+  if (authHeader) {
+    headers['Authorization'] = authHeader;
+  }
+  const tracingConfig: rcp.TracingConfiguration = {
+    verbose: false,
+    infoToCollect: {
+      textual: false,
+      incomingHeader: false,
+      outgoingHeader: false,
+      incomingData: false,
+      outgoingData: false,
+    },
+    collectTimeInfo: false
+  };
+  const session = rcp.createSession({
+    requestConfiguration: { tracing: tracingConfig },
+    headers: headers
+  });
+  const streamData: rcp.WriteStream = {
+    async write(buffer: ArrayBuffer): Promise<number> {
+      await fileIo.write(fileHandle.fd, buffer);
+      return buffer.byteLength;
+    }
+  };
+  const downloadToStream: rcp.DownloadToStream = {
+    kind: 'stream',
+    stream: streamData
+  };
+  return new Promise<void>((resolve, reject) => {
+    session.downloadToStream(url, downloadToStream).then(() => {
+      fileIo.closeSync(fileHandle);
+      session.close();
+      resolve();
+    }).catch((error: Error) => {
+      fileIo.closeSync(fileHandle);
+      session.close();
+      reject(error);
+    });
+  });
+}

+ 4 - 4
entry/src/main/ets/common/util/DataBaseUtil.ets

@@ -116,7 +116,7 @@ export class DataBaseUtil {
     }
     }
   }
   }
 
 
-  // 查询数据(简化版,用于WebdavManager)
+  // 查询数据(简化版,用于RemoteDriveManager)
   public async queryData(tableName: string, columns: Array<string>, predicates?: relationalStore.RdbPredicates): Promise<relationalStore.ResultSet> {
   public async queryData(tableName: string, columns: Array<string>, predicates?: relationalStore.RdbPredicates): Promise<relationalStore.ResultSet> {
     try {
     try {
       const db = await this.getDB();
       const db = await this.getDB();
@@ -133,17 +133,17 @@ export class DataBaseUtil {
     }
     }
   }
   }
 
 
-  // 插入数据(简化版,用于WebdavManager)
+  // 插入数据(简化版,用于RemoteDriveManager)
   public async insertData(tableName: string, values: relationalStore.ValuesBucket): Promise<number> {
   public async insertData(tableName: string, values: relationalStore.ValuesBucket): Promise<number> {
     return await this.insert(tableName, values);
     return await this.insert(tableName, values);
   }
   }
 
 
-  // 更新数据(简化版,用于WebdavManager)
+  // 更新数据(简化版,用于RemoteDriveManager)
   public async updateData(tableName: string, values: relationalStore.ValuesBucket, predicates: relationalStore.RdbPredicates): Promise<number> {
   public async updateData(tableName: string, values: relationalStore.ValuesBucket, predicates: relationalStore.RdbPredicates): Promise<number> {
     return await this.update(tableName, values, predicates);
     return await this.update(tableName, values, predicates);
   }
   }
 
 
-  // 删除数据(简化版,用于WebdavManager)
+  // 删除数据(简化版,用于RemoteDriveManager)
   public async deleteData(predicates: relationalStore.RdbPredicates): Promise<number> {
   public async deleteData(predicates: relationalStore.RdbPredicates): Promise<number> {
     return await this.delete(predicates);
     return await this.delete(predicates);
   }
   }

+ 13 - 9
entry/src/main/ets/common/util/MediaTable.ets

@@ -105,14 +105,18 @@ export default  class MediaTable {
         return false;
         return false;
       }
       }
 
 
-      // 将完整URL转换为相对路径用于存储
-      const relativePath = WebDavUrlUtil.toStoragePath(item.filePath);
-      if (relativePath !== item.filePath) {
-        // 只有当转换成功时才设置remote_rel_path
-        item.remote_rel_path = relativePath;
-        // 更新filePath为相对路径
-        item.filePath = relativePath;
-        Logger.info(RdbUtils.RDB_TAG, `WebDAV URL转换: 原始URL -> 存储路径: ${relativePath}`);
+      const isPureWebDav = item.type === CommonConstants.TYPE_WEBDAV;
+
+      if (isPureWebDav) {
+        // 将完整URL转换为相对路径用于存储
+        const relativePath = WebDavUrlUtil.toStoragePath(item.filePath);
+        if (relativePath !== item.filePath) {
+          // 只有当转换成功时才设置remote_rel_path
+          item.remote_rel_path = relativePath;
+          // 更新filePath为相对路径
+          item.filePath = relativePath;
+          Logger.info(RdbUtils.RDB_TAG, `WebDAV URL转换: 原始URL -> 存储路径: ${relativePath}`);
+        }
       }
       }
 
 
       const exists = await this.existsByFilePath(item.filePath);
       const exists = await this.existsByFilePath(item.filePath);
@@ -1091,4 +1095,4 @@ function generateBucket(item: VideoItem): relationalStore.ValuesBucket {
   }
   }
 
 
   return obj;
   return obj;
-}
+}

+ 33 - 0
entry/src/main/ets/common/util/RemoteDriveLabel.ets

@@ -0,0 +1,33 @@
+import { RemoteDriveType } from '../enums/RemoteDriveType';
+
+const BASE_LABEL = '网盘';
+
+export function getRemoteDriveBaseLabel(): string {
+  return BASE_LABEL;
+}
+
+export function getRemoteDriveAccountLabel(): string {
+  return `${BASE_LABEL}账户`;
+}
+
+export function getRemoteDriveProtocolLabel(type?: number): string {
+  switch (type) {
+    case RemoteDriveType.Smb:
+      return 'SMB';
+    case RemoteDriveType.Navidrome:
+      return 'Navidrome';
+    case RemoteDriveType.WebDav:
+    default:
+      return 'WebDAV';
+  }
+}
+
+export function getRemoteDriveDisplayLabel(type?: number): string {
+  const protocol = getRemoteDriveProtocolLabel(type);
+  return `${BASE_LABEL}${protocol ? `(${protocol})` : ''}`;
+}
+
+export function getRemoteDrivePlaylistPrefix(type?: number): string {
+  const protocol = getRemoteDriveProtocolLabel(type);
+  return `${BASE_LABEL}${protocol ? `-${protocol}` : ''}`;
+}

+ 450 - 170
entry/src/main/ets/common/util/WebdavManager.ets → entry/src/main/ets/common/util/RemoteDriveManager.ets

@@ -1,10 +1,10 @@
-// WebdavManager - WebDAV管理器(简化版)
+// RemoteDriveManager - WebDAV管理器(简化版)
 import { VideoItem } from '../../viewmodel/VideoItem';
 import { VideoItem } from '../../viewmodel/VideoItem';
 import { common } from '@kit.AbilityKit';
 import { common } from '@kit.AbilityKit';
 import { FileInfo } from '../../viewmodel/FileInfo';
 import { FileInfo } from '../../viewmodel/FileInfo';
 import FileManager, { merge2paths } from './FileManager';
 import FileManager, { merge2paths } from './FileManager';
 import { BusinessError } from '@kit.BasicServicesKit';
 import { BusinessError } from '@kit.BasicServicesKit';
-import { WebdavManagerStates } from '../enums/WebdavManagerStates';
+import { RemoteDriveManagerStates } from '../enums/RemoteDriveManagerStates';
 import { RcpSocket } from './RcpSocketUtil';
 import { RcpSocket } from './RcpSocketUtil';
 import { DataBaseUtil } from './DataBaseUtil';
 import { DataBaseUtil } from './DataBaseUtil';
 import { WebDavAccount } from '../../viewmodel/WebDavAccount';
 import { WebDavAccount } from '../../viewmodel/WebDavAccount';
@@ -15,8 +15,12 @@ import { buffer } from '@kit.ArkTS';
 import { CommonConstants } from '../constants/CommonConstants';
 import { CommonConstants } from '../constants/CommonConstants';
 import { GlobalContext } from '@pura/harmony-utils';
 import { GlobalContext } from '@pura/harmony-utils';
 import { MusicInfo, parseMusicFileName, Utility } from './Utility';
 import { MusicInfo, parseMusicFileName, Utility } from './Utility';
+import { RemoteDriveType } from '../enums/RemoteDriveType';
+import { listSmbDirectory, SmbDirectoryEntry } from '../network/SmbBridge';
+import { NavidromeApi, NavidromeAlbumDetail, NavidromeArtist, NavidromeArtistDetail, NavidromeSong } from '../network/NavidromeApi';
+import { WebDavUrlUtil } from './WebDavUrlUtil';
 
 
-const TAG = 'heanup WebdavManager';
+const TAG = 'heanup RemoteDriveManager';
 
 
 // WebDAV认证信息接口
 // WebDAV认证信息接口
 export interface WebDavAuthInfo {
 export interface WebDavAuthInfo {
@@ -37,11 +41,11 @@ export interface TransferTask {
 }
 }
 
 
 @Observed
 @Observed
-export class WebdavManager {
+export class RemoteDriveManager {
   public rcpSocket: RcpSocket = RcpSocket.getInstance();
   public rcpSocket: RcpSocket = RcpSocket.getInstance();
   private dataBaseUtil = DataBaseUtil.getInstance();
   private dataBaseUtil = DataBaseUtil.getInstance();
   public observers: Array<(event: string) => void> = [];
   public observers: Array<(event: string) => void> = [];
-  public static instance: WebdavManager;
+  public static instance: RemoteDriveManager;
   public context: common.Context | undefined;
   public context: common.Context | undefined;
   public DownloadDirectoryFilePath: string = '';
   public DownloadDirectoryFilePath: string = '';
   public audioExtensions = Constants.AUDIO_EXTENSIONS;
   public audioExtensions = Constants.AUDIO_EXTENSIONS;
@@ -76,6 +80,10 @@ export class WebdavManager {
   // 上传队列
   // 上传队列
   public uploadQueue: TransferTask[] = [];
   public uploadQueue: TransferTask[] = [];
   public finishUploadQueue: TransferTask[] = [];
   public finishUploadQueue: TransferTask[] = [];
+
+  private navidromeApi: NavidromeApi = new NavidromeApi();
+  private pathDisplayNames: Map<string, string> = new Map();
+  private lastAccountId: number | null = null;
   public isProcessingUploadQueue: boolean = false;
   public isProcessingUploadQueue: boolean = false;
   public currentUploadTask: TransferTask | null = null;
   public currentUploadTask: TransferTask | null = null;
   public isPauseUpload: boolean = true;
   public isPauseUpload: boolean = true;
@@ -88,11 +96,11 @@ export class WebdavManager {
     });
     });
   }
   }
 
 
-  public static getInstance(): WebdavManager {
-    if (!WebdavManager.instance) {
-      WebdavManager.instance = new WebdavManager();
+  public static getInstance(): RemoteDriveManager {
+    if (!RemoteDriveManager.instance) {
+      RemoteDriveManager.instance = new RemoteDriveManager();
     }
     }
-    return WebdavManager.instance;
+    return RemoteDriveManager.instance;
   }
   }
 
 
   public setContext(context: common.Context): void {
   public setContext(context: common.Context): void {
@@ -160,6 +168,21 @@ export class WebdavManager {
         account.enableHttps = resultSet.getLong(resultSet.getColumnIndex('enableHttps')) === 1;
         account.enableHttps = resultSet.getLong(resultSet.getColumnIndex('enableHttps')) === 1;
         account.coverPath = resultSet.getString(resultSet.getColumnIndex('coverPath'));
         account.coverPath = resultSet.getString(resultSet.getColumnIndex('coverPath'));
         account.webType = resultSet.getLong(resultSet.getColumnIndex('webType'));
         account.webType = resultSet.getLong(resultSet.getColumnIndex('webType'));
+        const smbShareIndex = resultSet.getColumnIndex('smbShare');
+        const smbDomainIndex = resultSet.getColumnIndex('smbDomain');
+        const navBaseIndex = resultSet.getColumnIndex('navidromeBasePath');
+        if (smbShareIndex >= 0) {
+          account.smbShare = resultSet.getString(smbShareIndex) ?? '';
+        }
+        if (smbDomainIndex >= 0) {
+          account.smbDomain = resultSet.getString(smbDomainIndex) ?? '';
+        }
+        if (navBaseIndex >= 0) {
+          const stored = resultSet.getString(navBaseIndex);
+          account.navidromeBasePath = stored && stored.length > 0 ? stored : '/rest';
+        } else {
+          account.navidromeBasePath = '/rest';
+        }
         account.isActivate = resultSet.getLong(resultSet.getColumnIndex('isActivate')) === 1;
         account.isActivate = resultSet.getLong(resultSet.getColumnIndex('isActivate')) === 1;
 
 
         resultSet.close();
         resultSet.close();
@@ -290,7 +313,7 @@ export class WebdavManager {
       // 查询所有TYPE_WEBDAV且webdav_account_id为空或null的记录
       // 查询所有TYPE_WEBDAV且webdav_account_id为空或null的记录
       const querySql = `
       const querySql = `
         SELECT id, filePath FROM mediaTable
         SELECT id, filePath FROM mediaTable
-        WHERE mtype = ${CommonConstants.TYPE_WEBDAV}
+        WHERE mtype IN (${CommonConstants.TYPE_WEBDAV}, ${CommonConstants.TYPE_SMB})
         AND (webdav_account_id IS NULL OR webdav_account_id = '')
         AND (webdav_account_id IS NULL OR webdav_account_id = '')
       `;
       `;
 
 
@@ -304,7 +327,7 @@ export class WebdavManager {
         const updateSql = `
         const updateSql = `
           UPDATE mediaTable
           UPDATE mediaTable
           SET webdav_account_id = ?
           SET webdav_account_id = ?
-          WHERE mtype = ${CommonConstants.TYPE_WEBDAV}
+          WHERE mtype IN (${CommonConstants.TYPE_WEBDAV}, ${CommonConstants.TYPE_SMB})
           AND (webdav_account_id IS NULL OR webdav_account_id = '')
           AND (webdav_account_id IS NULL OR webdav_account_id = '')
         `;
         `;
 
 
@@ -377,6 +400,14 @@ export class WebdavManager {
         account.enableHttps = resultSet.getLong(resultSet.getColumnIndex('enableHttps')) === 1;
         account.enableHttps = resultSet.getLong(resultSet.getColumnIndex('enableHttps')) === 1;
         account.coverPath = resultSet.getString(resultSet.getColumnIndex('coverPath'));
         account.coverPath = resultSet.getString(resultSet.getColumnIndex('coverPath'));
         account.webType = resultSet.getLong(resultSet.getColumnIndex('webType'));
         account.webType = resultSet.getLong(resultSet.getColumnIndex('webType'));
+        const smbShareIndex = resultSet.getColumnIndex('smbShare');
+        const smbDomainIndex = resultSet.getColumnIndex('smbDomain');
+        if (smbShareIndex >= 0) {
+          account.smbShare = resultSet.getString(smbShareIndex) ?? '';
+        }
+        if (smbDomainIndex >= 0) {
+          account.smbDomain = resultSet.getString(smbDomainIndex) ?? '';
+        }
         account.isActivate = resultSet.getLong(resultSet.getColumnIndex('isActivate')) === 1;
         account.isActivate = resultSet.getLong(resultSet.getColumnIndex('isActivate')) === 1;
 
 
         resultSet.close();
         resultSet.close();
@@ -410,7 +441,10 @@ export class WebdavManager {
       password TEXT,
       password TEXT,
       enableHttps INTEGER DEFAULT 0,
       enableHttps INTEGER DEFAULT 0,
       coverPath TEXT,
       coverPath TEXT,
-      webType INTEGER DEFAULT 0
+      webType INTEGER DEFAULT 0,
+      smbShare TEXT,
+      smbDomain TEXT,
+      navidromeBasePath TEXT
     )`;
     )`;
 
 
     return this.dataBaseUtil.executeSql(createTableSql)
     return this.dataBaseUtil.executeSql(createTableSql)
@@ -439,7 +473,7 @@ export class WebdavManager {
       // 如果添加字段失败(可能字段已存在),记录但不阻止应用启动
       // 如果添加字段失败(可能字段已存在),记录但不阻止应用启动
       Logger.info(TAG, 'coverPath字段可能已存在或添加失败,继续正常运行');
       Logger.info(TAG, 'coverPath字段可能已存在或添加失败,继续正常运行');
     }
     }
-    
+
     try {
     try {
       // 添加webType字段,用于标识WebDAV账户类型
       // 添加webType字段,用于标识WebDAV账户类型
       Logger.info(TAG, '检查数据库表结构,尝试添加webType字段...');
       Logger.info(TAG, '检查数据库表结构,尝试添加webType字段...');
@@ -451,6 +485,33 @@ export class WebdavManager {
       // 如果添加字段失败(可能字段已存在),记录但不阻止应用启动
       // 如果添加字段失败(可能字段已存在),记录但不阻止应用启动
       Logger.info(TAG, 'webType字段可能已存在或添加失败,继续正常运行');
       Logger.info(TAG, 'webType字段可能已存在或添加失败,继续正常运行');
     }
     }
+
+    try {
+      Logger.info(TAG, '尝试添加smbShare字段...');
+      const addSmbShareSql = `ALTER TABLE ${this.webDavTable} ADD COLUMN smbShare TEXT`;
+      await this.dataBaseUtil.executeSql(addSmbShareSql);
+      Logger.info(TAG, 'smbShare字段添加成功');
+    } catch (error) {
+      Logger.info(TAG, 'smbShare字段可能已存在或添加失败,继续运行');
+    }
+
+    try {
+      Logger.info(TAG, '尝试添加smbDomain字段...');
+      const addSmbDomainSql = `ALTER TABLE ${this.webDavTable} ADD COLUMN smbDomain TEXT`;
+      await this.dataBaseUtil.executeSql(addSmbDomainSql);
+      Logger.info(TAG, 'smbDomain字段添加成功');
+    } catch (error) {
+      Logger.info(TAG, 'smbDomain字段可能已存在或添加失败,继续运行');
+    }
+
+    try {
+      Logger.info(TAG, '尝试添加navidromeBasePath字段...');
+      const addNavBaseSql = `ALTER TABLE ${this.webDavTable} ADD COLUMN navidromeBasePath TEXT`;
+      await this.dataBaseUtil.executeSql(addNavBaseSql);
+      Logger.info(TAG, 'navidromeBasePath字段添加成功');
+    } catch (error) {
+      Logger.info(TAG, 'navidromeBasePath字段可能已存在或添加失败,继续运行');
+    }
   }
   }
 
 
   // 从数据库查询所有账户
   // 从数据库查询所有账户
@@ -463,7 +524,7 @@ export class WebdavManager {
       // 先查询基础字段(确保这些字段在旧版本中存在)
       // 先查询基础字段(确保这些字段在旧版本中存在)
       const baseColumns = ['id', 'name', 'isActivate', 'host', 'localHost', 'isUseLocalHost',
       const baseColumns = ['id', 'name', 'isActivate', 'host', 'localHost', 'isUseLocalHost',
         'port', 'filepath', 'imageFilePath', 'lyricFilePath', 'uploadFilePath',
         'port', 'filepath', 'imageFilePath', 'lyricFilePath', 'uploadFilePath',
-        'account', 'password', 'enableHttps'];
+        'account', 'password', 'enableHttps', 'coverPath', 'webType', 'smbShare', 'smbDomain', 'navidromeBasePath'];
 
 
       const resultSet = await this.dataBaseUtil.queryData(this.webDavTable, baseColumns, predicates);
       const resultSet = await this.dataBaseUtil.queryData(this.webDavTable, baseColumns, predicates);
 
 
@@ -484,48 +545,39 @@ export class WebdavManager {
         account.account = resultSet.getString(resultSet.getColumnIndex('account'));
         account.account = resultSet.getString(resultSet.getColumnIndex('account'));
         account.password = resultSet.getString(resultSet.getColumnIndex('password'));
         account.password = resultSet.getString(resultSet.getColumnIndex('password'));
         account.enableHttps = resultSet.getLong(resultSet.getColumnIndex('enableHttps')) === 1;
         account.enableHttps = resultSet.getLong(resultSet.getColumnIndex('enableHttps')) === 1;
-        // 设置coverPath和webType为默认值undefined,稍后会尝试更新
-        account.coverPath = undefined;
-        account.webType = 0;
+
+        const coverPathIndex = resultSet.getColumnIndex('coverPath');
+        if (coverPathIndex >= 0) {
+          account.coverPath = resultSet.getString(coverPathIndex) ?? undefined;
+        }
+        const webTypeIndex = resultSet.getColumnIndex('webType');
+        account.webType = webTypeIndex >= 0 ? resultSet.getLong(webTypeIndex) : 0;
+        const smbShareIndex = resultSet.getColumnIndex('smbShare');
+        if (smbShareIndex >= 0) {
+          account.smbShare = resultSet.getString(smbShareIndex) ?? '';
+        }
+        const smbDomainIndex = resultSet.getColumnIndex('smbDomain');
+        if (smbDomainIndex >= 0) {
+          account.smbDomain = resultSet.getString(smbDomainIndex) ?? '';
+        }
+        const navBaseIndex = resultSet.getColumnIndex('navidromeBasePath');
+        if (navBaseIndex >= 0) {
+          const stored = resultSet.getString(navBaseIndex);
+          account.navidromeBasePath = stored && stored.length > 0 ? stored : '/rest';
+        } else {
+          account.navidromeBasePath = '/rest';
+        }
 
 
         this.webDavAccounts.push(account);
         this.webDavAccounts.push(account);
       }
       }
       resultSet.close();
       resultSet.close();
 
 
-      // 尝试查询coverPath字段(如果升级成功)
-      try {
-        const coverPathResultSet = await this.dataBaseUtil.queryData(this.webDavTable, ['id', 'coverPath'], predicates);
-        if (coverPathResultSet.goToFirstRow()) {
-          // 创建一个映射来存储coverPath
-          const coverPathMap = new Map<number, string>();
-          do {
-            const accountId = coverPathResultSet.getLong(coverPathResultSet.getColumnIndex('id'));
-            const coverPathIndex = coverPathResultSet.getColumnIndex('coverPath');
-            const coverPath = coverPathIndex >= 0 ? coverPathResultSet.getString(coverPathIndex) : undefined;
-            if (coverPath) {
-              coverPathMap.set(accountId, coverPath);
-            }
-          } while (coverPathResultSet.goToNextRow());
-
-          // 将coverPath值赋给对应的账户
-          for (const account of this.webDavAccounts) {
-            if (coverPathMap.has(account.id)) {
-              account.coverPath = coverPathMap.get(account.id);
-            }
-          }
-        }
-        coverPathResultSet.close();
-      } catch (error) {
-        // 如果查询coverPath失败,说明字段可能不存在,忽略错误
-        Logger.info(TAG, 'coverPath字段不存在或查询失败,使用默认值');
-      }
-
       Logger.info(TAG, '从数据库加载了', this.webDavAccounts.length.toString(), '个WebDAV账户');
       Logger.info(TAG, '从数据库加载了', this.webDavAccounts.length.toString(), '个WebDAV账户');
-      this.notifyObservers(WebdavManagerStates.QueryAccountsSucceed);
+      this.notifyObservers(RemoteDriveManagerStates.QueryAccountsSucceed);
     } catch (err) {
     } catch (err) {
       const error = err as Error;
       const error = err as Error;
       Logger.error(TAG, '查询WebDAV账户失败:', error.message);
       Logger.error(TAG, '查询WebDAV账户失败:', error.message);
-      this.notifyObservers(WebdavManagerStates.QueryAccountsFailed);
+      this.notifyObservers(RemoteDriveManagerStates.QueryAccountsFailed);
       throw error;
       throw error;
     }
     }
   }
   }
@@ -545,7 +597,10 @@ export class WebdavManager {
     password: string,
     password: string,
     enableHttps: boolean,
     enableHttps: boolean,
     coverPath?: string,
     coverPath?: string,
-    webType: number = 0
+    webType: number = 0,
+    smbShare: string = '',
+    smbDomain: string = '',
+    navidromeBasePath: string = '/rest'
   ): Promise<void> {
   ): Promise<void> {
     try {
     try {
       const values: relationalStore.ValuesBucket = {
       const values: relationalStore.ValuesBucket = {
@@ -563,18 +618,21 @@ export class WebdavManager {
         'password': password,
         'password': password,
         'enableHttps': enableHttps ? 1 : 0,
         'enableHttps': enableHttps ? 1 : 0,
         'coverPath': coverPath || null,
         'coverPath': coverPath || null,
-        'webType': webType
+        'webType': webType,
+        'smbShare': smbShare,
+        'smbDomain': smbDomain,
+        'navidromeBasePath': navidromeBasePath
       };
       };
 
 
       await this.dataBaseUtil.insertData(this.webDavTable, values);
       await this.dataBaseUtil.insertData(this.webDavTable, values);
       Logger.info(TAG, '插入WebDAV账户成功:', name);
       Logger.info(TAG, '插入WebDAV账户成功:', name);
 
 
       await this.queryWebDavAccountsFromDB();
       await this.queryWebDavAccountsFromDB();
-      this.notifyObservers(WebdavManagerStates.InsertAccountSucceed);
+      this.notifyObservers(RemoteDriveManagerStates.InsertAccountSucceed);
     } catch (err) {
     } catch (err) {
       const error = err as Error;
       const error = err as Error;
       Logger.error(TAG, '插入WebDAV账户失败:', error.message);
       Logger.error(TAG, '插入WebDAV账户失败:', error.message);
-      this.notifyObservers(WebdavManagerStates.InsertAccountFailed);
+      this.notifyObservers(RemoteDriveManagerStates.InsertAccountFailed);
       throw error;
       throw error;
     }
     }
   }
   }
@@ -597,7 +655,10 @@ export class WebdavManager {
         'password': account.password,
         'password': account.password,
         'enableHttps': account.enableHttps ? 1 : 0,
         'enableHttps': account.enableHttps ? 1 : 0,
         'coverPath': account.coverPath || null,
         'coverPath': account.coverPath || null,
-        'webType': account.webType
+        'webType': account.webType,
+        'smbShare': account.smbShare,
+        'smbDomain': account.smbDomain,
+        'navidromeBasePath': account.navidromeBasePath
       };
       };
 
 
       const predicates = new relationalStore.RdbPredicates(this.webDavTable);
       const predicates = new relationalStore.RdbPredicates(this.webDavTable);
@@ -607,11 +668,11 @@ export class WebdavManager {
       Logger.info(TAG, '更新WebDAV账户成功:', account.name);
       Logger.info(TAG, '更新WebDAV账户成功:', account.name);
 
 
       await this.queryWebDavAccountsFromDB();
       await this.queryWebDavAccountsFromDB();
-      this.notifyObservers(WebdavManagerStates.EditAccountSucceed);
+      this.notifyObservers(RemoteDriveManagerStates.EditAccountSucceed);
     } catch (err) {
     } catch (err) {
       const error = err as Error;
       const error = err as Error;
       Logger.error(TAG, '更新WebDAV账户失败:', error.message);
       Logger.error(TAG, '更新WebDAV账户失败:', error.message);
-      this.notifyObservers(WebdavManagerStates.EditAccountFailed);
+      this.notifyObservers(RemoteDriveManagerStates.EditAccountFailed);
       throw error;
       throw error;
     }
     }
   }
   }
@@ -626,11 +687,11 @@ export class WebdavManager {
       Logger.info(TAG, '删除WebDAV账户成功:', account.name);
       Logger.info(TAG, '删除WebDAV账户成功:', account.name);
 
 
       await this.queryWebDavAccountsFromDB();
       await this.queryWebDavAccountsFromDB();
-      this.notifyObservers(WebdavManagerStates.RemoveAccountSucceed);
+      this.notifyObservers(RemoteDriveManagerStates.RemoveAccountSucceed);
     } catch (err) {
     } catch (err) {
       const error = err as Error;
       const error = err as Error;
       Logger.error(TAG, '删除WebDAV账户失败:', error.message);
       Logger.error(TAG, '删除WebDAV账户失败:', error.message);
-      this.notifyObservers(WebdavManagerStates.RemoveAccountFailed);
+      this.notifyObservers(RemoteDriveManagerStates.RemoveAccountFailed);
       throw error;
       throw error;
     }
     }
   }
   }
@@ -657,144 +718,205 @@ export class WebdavManager {
 
 
   // 从WebDAV加载文件列表
   // 从WebDAV加载文件列表
   public async loadFilesInfoFromWebdav(customPath?: string): Promise<void> {
   public async loadFilesInfoFromWebdav(customPath?: string): Promise<void> {
-
-    const account = this.getActivatedWebDavAccount();
+    const account = await this.getActiveWebDavAccount();
     if (!account) {
     if (!account) {
       Logger.error(TAG, '没有激活的WebDAV账户');
       Logger.error(TAG, '没有激活的WebDAV账户');
-      this.notifyObservers(WebdavManagerStates.LoadFilesInfoFailed);
+      this.notifyObservers(RemoteDriveManagerStates.LoadFilesInfoFailed);
       return;
       return;
     }
     }
+    await this.loadFilesInfoFromAccount(account, customPath);
+  }
 
 
-    try {
-      this.notifyObservers(WebdavManagerStates.LoadFilesInfoStart);
 
 
-      // 使用自定义路径或账户默认路径
-      const path = customPath !== undefined ? customPath : account.filepath;
-      this.currentPath = path;
-
-      const files = await this.rcpSocket.getFileList(
-        account.host,
-        account.localHost,
-        account.isUseLocalHost,
-        account.port,
-        path,
-        account.account,
-        account.password,
-        account.enableHttps
-      );
-
-      // 保存所有文件(包括文件夹)
-      // 直接使用从RcpSocketUtil返回的FileInfo对象
-      this.webDavFiles = [];
-      for (let i = 0; i < files.length; i++) {
-        const file = files[i];
-        // 直接添加原始文件对象
-        this.webDavFiles.push(file);
-      }
+  public async loadFilesInfoFromAccount(account: WebDavAccount,customPath?: string): Promise<void> {
+    this.currentAccount = account;
+    if (this.lastAccountId !== account.id) {
+      this.pathHistory = [];
+      this.pathDisplayNames.clear();
+      this.registerPathLabel('/', '根目录');
+    }
+    this.lastAccountId = account.id ?? null;
 
 
-      // 调试:输出获取到的文件总数
-      Logger.info(TAG, '从WebDAV获取到 ' + files.length + ' 个文件/文件夹');
+    // 更新现有WebDAV歌曲的webdav_account_id字段
+    if (account && account.id) {
+      await this.updateWebDavSongsAccountId(account.id.toString());
+    }
 
 
-      // 分别统计文件夹和音频文件
-      let folderCount = 0;
-      let audioCount = 0;
+    try {
+      this.notifyObservers(RemoteDriveManagerStates.LoadFilesInfoStart);
 
 
-      // 过滤音频文件
-      this.webDavSongs = [];
-      for (let i = 0; i < files.length; i++) {
-        const file = files[i];
-        const fileName = file.fileName;
-
-        if (file.isDirectory) {
-          folderCount++;
-        } else if (this.isAudioFile(fileName)) {
-          audioCount++;
-          const song = this.fileInfoToVideoItem(file, account);
-          this.webDavSongs.push(song);
-        }
+      // 使用自定义路径或账户默认路径
+      const normalizedFullPath = this.normalizeFullPath(customPath !== undefined ? customPath : account.filepath);
+      this.currentPath = normalizedFullPath;
+
+      if (account.webType === RemoteDriveType.Smb) {
+        await this.loadSmbFiles(account, normalizedFullPath);
+      } else if (account.webType === RemoteDriveType.Navidrome) {
+        await this.loadNavidromeFiles(account, normalizedFullPath);
+      } else {
+        await this.loadWebDavFiles(account, normalizedFullPath);
       }
       }
 
 
-      this.notifyObservers(WebdavManagerStates.LoadFilesInfoSucceed);
+      this.notifyObservers(RemoteDriveManagerStates.LoadFilesInfoSucceed);
     } catch (error) {
     } catch (error) {
       this.ErrorMessage = error as BusinessError;
       this.ErrorMessage = error as BusinessError;
-      this.notifyObservers(WebdavManagerStates.LoadFilesInfoFailed);
+      this.notifyObservers(RemoteDriveManagerStates.LoadFilesInfoFailed);
     }
     }
   }
   }
 
 
+  private normalizeFullPath(path?: string): string {
+    if (!path || path.trim().length === 0) {
+      return '/';
+    }
+    let normalized = path.trim().replace(/\\/g, '/');
+    if (!normalized.startsWith('/')) {
+      normalized = `/${normalized}`;
+    }
+    normalized = normalized.replace(/\/+/g, '/');
+    if (normalized.length > 1 && normalized.endsWith('/')) {
+      normalized = normalized.slice(0, -1);
+    }
+    return normalized || '/';
+  }
 
 
-  public async loadFilesInfoFromAccount(account: WebDavAccount,customPath?: string): Promise<void> {
-    this.currentAccount = account;
+  // 将 WebDAV 响应中的 href 统一转换为以 / 开头的相对路径
+  private normalizeRemoteHref(rawHref: string): string {
+    if (!rawHref || rawHref.trim().length === 0) {
+      return '/';
+    }
+    const trimmed = rawHref.trim();
+    const relativeFromHttp = WebDavUrlUtil.extractRelativePath(trimmed);
+    if (relativeFromHttp) {
+      return this.normalizeFullPath(relativeFromHttp);
+    }
+    if (trimmed.startsWith('//')) {
+      const hostEnd = trimmed.indexOf('/', 2);
+      if (hostEnd > 1) {
+        return this.normalizeFullPath(trimmed.substring(hostEnd));
+      }
+      return '/';
+    }
+    return this.normalizeFullPath(trimmed);
+  }
 
 
-    // 更新现有WebDAV歌曲的webdav_account_id字段
-    if (account && account.id) {
-      await this.updateWebDavSongsAccountId(account.id.toString());
+  private toRelativePath(fullPath: string): string {
+    if (!fullPath || fullPath === '/') {
+      return '';
     }
     }
+    return fullPath.replace(/^\/+/, '');
+  }
 
 
-    try {
-      this.notifyObservers(WebdavManagerStates.LoadFilesInfoStart);
+  private async loadWebDavFiles(account: WebDavAccount, fullPath: string): Promise<void> {
+    const files = await this.rcpSocket.getFileList(
+      account.host,
+      account.localHost,
+      account.isUseLocalHost,
+      account.port,
+      fullPath,
+      account.account,
+      account.password,
+      account.enableHttps
+    );
 
 
-      // 使用自定义路径或账户默认路径
-      const path = customPath !== undefined ? customPath : account.filepath;
-      this.currentPath = path;
-
-      const files = await this.rcpSocket.getFileList(
-        account.host,
-        account.localHost,
-        account.isUseLocalHost,
-        account.port,
-        path,
-        account.account,
-        account.password,
-        account.enableHttps
-      );
-
-      // 保存所有文件(包括文件夹)
-      // 直接使用从RcpSocketUtil返回的FileInfo对象
-      this.webDavFiles = [];
-      for (let i = 0; i < files.length; i++) {
-        const file = files[i];
-        // 直接添加原始文件对象
-        this.webDavFiles.push(file);
+    this.webDavFiles = files;
+    this.webDavSongs = [];
+
+    for (let i = 0; i < files.length; i++) {
+      const file = files[i];
+      if (!file.isDirectory && this.isAudioFile(file.fileName)) {
+        this.webDavSongs.push(this.fileInfoToVideoItem(file, account));
       }
       }
+    }
+    Logger.info(TAG, `从WebDAV获取到 ${files.length} 个文件/文件夹`);
+  }
 
 
-      // 调试:输出获取到的文件总数
-      Logger.info(TAG, '从WebDAV获取到 ' + files.length + ' 个文件/文件夹');
+  private async loadSmbFiles(account: WebDavAccount, fullPath: string): Promise<void> {
+    if (!account.smbShare) {
+      throw new Error('SMB账户缺少共享名称');
+    }
+    const relativePath = this.toRelativePath(fullPath);
+    const host = account.isUseLocalHost && account.localHost ? account.localHost : account.host;
+    const entries = await listSmbDirectory({
+      host,
+      share: account.smbShare,
+      username: account.account,
+      password: account.password,
+      domain: account.smbDomain,
+      path: relativePath
+    });
 
 
-      // 分别统计文件夹和音频文件
-      let folderCount = 0;
-      let audioCount = 0;
+    this.webDavFiles = [];
+    this.webDavSongs = [];
 
 
-      // 过滤音频文件
-      this.webDavSongs = [];
-      for (let i = 0; i < files.length; i++) {
-        const file = files[i];
-        const fileName = file.fileName;
-
-        if (file.isDirectory) {
-          folderCount++;
-        } else if (this.isAudioFile(fileName)) {
-          audioCount++;
-          const song = this.fileInfoToVideoItem(file, account);
-          this.webDavSongs.push(song);
-        }
+    for (let i = 0; i < entries.length; i++) {
+      const entry = entries[i];
+      const fileInfo = this.smbEntryToFileInfo(entry, relativePath);
+      this.webDavFiles.push(fileInfo);
+      if (!entry.isDirectory && this.isAudioFile(entry.name)) {
+        this.webDavSongs.push(this.fileInfoToVideoItem(fileInfo, account));
       }
       }
+    }
+    Logger.info(TAG, `从SMB获取到 ${entries.length} 个文件/文件夹`);
+  }
 
 
-      this.notifyObservers(WebdavManagerStates.LoadFilesInfoSucceed);
-    } catch (error) {
-      this.ErrorMessage = error as BusinessError;
-      this.notifyObservers(WebdavManagerStates.LoadFilesInfoFailed);
+  private async loadNavidromeFiles(account: WebDavAccount, fullPath: string): Promise<void> {
+    const normalized = this.normalizeFullPath(fullPath);
+    this.registerPathLabel('/', '根目录');
+    const segments = normalized.split('/').filter(part => part.length > 0);
+
+    if (normalized === '/' || segments.length === 0) {
+      const artists: NavidromeArtist[] = await this.navidromeApi.getArtists(account);
+      artists.sort((a, b) => a.name.localeCompare(b.name));
+      this.webDavFiles = artists.map(artist => {
+        const info = this.createNavDirectory(artist.name, `/artist/${artist.id}`);
+        this.registerPathLabel(info.href, artist.name);
+        return info;
+      });
+      this.webDavSongs = [];
+      Logger.info(TAG, `从Navidrome获取到 ${artists.length} 位艺术家`);
+      return;
+    }
+
+    if (segments[0] === 'artist' && segments[1]) {
+      const artistId = segments[1];
+      const detail: NavidromeArtistDetail = await this.navidromeApi.getArtist(account, artistId);
+      this.registerPathLabel(`/artist/${artistId}`, detail.name);
+      this.webDavFiles = detail.albums.map(album => {
+        const info = this.createNavDirectory(album.name, `/album/${album.id}`);
+        this.registerPathLabel(info.href, album.name);
+        return info;
+      });
+      this.webDavSongs = [];
+      Logger.info(TAG, `Navidrome 艺术家 ${detail.name} 包含 ${detail.albums.length} 张专辑`);
+      return;
     }
     }
+
+    if (segments[0] === 'album' && segments[1]) {
+      const albumId = segments[1];
+      const detail: NavidromeAlbumDetail = await this.navidromeApi.getAlbum(account, albumId);
+      this.registerPathLabel(`/album/${albumId}`, detail.name);
+      this.webDavFiles = detail.songs.map(song => this.createNavSongFileInfo(song, albumId));
+      this.webDavSongs = detail.songs.map(song => this.buildNavidromeVideoItem(song, account, detail));
+      Logger.info(TAG, `Navidrome 专辑 ${detail.name} 包含 ${detail.songs.length} 首歌曲`);
+      return;
+    }
+
+    throw new Error(`不支持的Navidrome路径: ${fullPath}`);
   }
   }
 
 
   // 将FileInfo转换为VideoItem
   // 将FileInfo转换为VideoItem
   private fileInfoToVideoItem(fileInfo: FileInfo, account: WebDavAccount): VideoItem {
   private fileInfoToVideoItem(fileInfo: FileInfo, account: WebDavAccount): VideoItem {
+    if (account.webType === RemoteDriveType.Smb) {
+      return this.buildSmbVideoItem(fileInfo, account);
+    }
     // 构建安全的WebDAV URL(不包含认证信息)
     // 构建安全的WebDAV URL(不包含认证信息)
     const protocol = account.enableHttps ? 'https' : 'http';
     const protocol = account.enableHttps ? 'https' : 'http';
     const host = account.isUseLocalHost ? account.localHost : account.host;
     const host = account.isUseLocalHost ? account.localHost : account.host;
     const port = account.port;
     const port = account.port;
 
 
     // 构建基础URL,确保路径重新编码(目录/文件名中的中文与空格)
     // 构建基础URL,确保路径重新编码(目录/文件名中的中文与空格)
-    const encodedHref = encodeURI(fileInfo.href); // 仅编码非保留字符,已编码的百分号不重复编码
+    const safeHref = this.normalizeRemoteHref(fileInfo.href);
+    const encodedHref = encodeURI(safeHref); // 仅编码非保留字符,已编码的百分号不重复编码
     const absoluteUrl = `${protocol}://${host}:${port}${encodedHref}`;
     const absoluteUrl = `${protocol}://${host}:${port}${encodedHref}`;
 
 
     // 创建VideoItem对象
     // 创建VideoItem对象
@@ -840,6 +962,160 @@ export class WebdavManager {
     return videoItem;
     return videoItem;
   }
   }
 
 
+  private buildSmbVideoItem(fileInfo: FileInfo, account: WebDavAccount): VideoItem {
+    const relativePath = fileInfo.href && fileInfo.href.length > 0 ? fileInfo.href : `/${fileInfo.fileName}`;
+    const normalizedRelative = relativePath.startsWith('/') ? relativePath : `/${relativePath}`;
+    const sanitizedRelative = this.normalizeSmbRelativePath(normalizedRelative, account.smbShare);
+    const videoItem = new VideoItem(
+      this.getFileNameWithoutExtension(fileInfo.fileName),
+      '',
+      sanitizedRelative,
+      CommonConstants.TYPE_SMB,
+      fileInfo.contentLength,
+      Utility.getFormatDateStr(fileInfo.time, 'yyyy-MM-dd HH:mm'),
+      undefined,
+      undefined,
+      undefined,
+      Constants.UNKNOWN_ARTIST,
+      undefined,
+      fileInfo.fileName
+    );
+    videoItem.size = Utility.formatFSize(fileInfo.contentLength);
+    if (account && account.id) {
+      videoItem.webdav_account_id = account.id.toString();
+    }
+    videoItem.remote_rel_path = sanitizedRelative;
+    const hostForDisplay = account.host && account.host.trim().length > 0
+      ? account.host.trim()
+      : (account.id?.toString() ?? '');
+    const cleanedShare = account.smbShare
+      ? account.smbShare.replace(/^\/+|\/+$/g, '')
+      : '';
+    const relativeForDisplay = sanitizedRelative === '/' ? '' : sanitizedRelative;
+    if (hostForDisplay.length > 0 && cleanedShare.length > 0) {
+      videoItem.filePath = `smb://${hostForDisplay}/${cleanedShare}${relativeForDisplay}`;
+    } else if (hostForDisplay.length > 0) {
+      videoItem.filePath = `smb://${hostForDisplay}${relativeForDisplay}`;
+    } else {
+      videoItem.filePath = `smb://${account.id}${relativeForDisplay}`;
+    }
+    const musicData: MusicInfo = parseMusicFileName(fileInfo.fileName);
+    if (musicData.isValid) {
+      videoItem.artist = musicData.artist;
+      videoItem.name = musicData.title;
+    }
+    return videoItem;
+  }
+
+  private smbEntryToFileInfo(entry: SmbDirectoryEntry, basePath: string): FileInfo {
+    const normalizedPath = this.combineRemotePath(basePath, entry.name, entry.isDirectory);
+    const info = new FileInfo('', entry.name, entry.size, Date.now());
+    info.fileName = entry.name;
+    info.href = normalizedPath;
+    info.contentLength = entry.size ?? 0;
+    info.isDirectory = entry.isDirectory;
+    return info;
+  }
+
+  private combineRemotePath(basePath: string, name: string, isDirectory: boolean): string {
+    const segments: string[] = [];
+    const trimmedBase = basePath ? basePath.replace(/^\/+/, '').replace(/\/+/g, '/') : '';
+    if (trimmedBase.length > 0) {
+      segments.push(trimmedBase);
+    }
+    const cleanedName = name.replace(/^\/+/, '').replace(/\/+/g, '/');
+    if (cleanedName.length > 0) {
+      segments.push(cleanedName);
+    }
+    let fullPath = `/${segments.filter(part => part.length > 0).join('/')}`;
+    if (isDirectory && !fullPath.endsWith('/')) {
+      fullPath = `${fullPath}/`;
+    }
+    if (!isDirectory && fullPath.endsWith('/')) {
+      fullPath = fullPath.slice(0, -1);
+    }
+    return fullPath;
+  }
+
+  private createNavDirectory(name: string, href: string): FileInfo {
+    const info = new FileInfo('', name, 0, Date.now());
+    info.fileName = name;
+    info.href = this.normalizeFullPath(href);
+    info.isDirectory = true;
+    return info;
+  }
+
+  private createNavSongFileInfo(song: NavidromeSong, albumId: string): FileInfo {
+    const displayName = song.title ?? '未知曲目';
+    const info = new FileInfo('', displayName, song.size ?? 0, Date.now());
+    info.fileName = `${displayName}${song.suffix ? '.' + song.suffix : ''}`;
+    info.href = this.normalizeFullPath(`/album/${albumId}/${song.id}`);
+    info.isDirectory = false;
+    info.contentLength = song.size ?? 0;
+    return info;
+  }
+
+  private buildNavidromeVideoItem(song: NavidromeSong, account: WebDavAccount, album?: NavidromeAlbumDetail): VideoItem {
+    const title = song.title ?? '未知曲目';
+    const fileName = `${title}${song.suffix ? '.' + song.suffix : ''}`;
+    const videoItem = new VideoItem(
+      title,
+      song.id,
+      `navidrome://${account.id ?? 0}/${song.id}`,
+      CommonConstants.TYPE_NAVIDROME,
+      song.size ?? 0,
+      Utility.getFormatDateStr(Date.now(), 'yyyy-MM-dd HH:mm'),
+      undefined,
+      Utility.formatFSize(song.size ?? 0),
+      undefined,
+      song.artist ?? album?.artist ?? Constants.UNKNOWN_ARTIST,
+      album?.name ?? song.album,
+      fileName
+    );
+    videoItem.size = Utility.formatFSize(song.size ?? 0);
+    videoItem.webdav_account_id = account.id?.toString();
+    videoItem.remote_rel_path = song.id;
+    videoItem.bit_rate = song.bitRate ? song.bitRate.toString() : undefined;
+    videoItem.mimeType = song.contentType;
+    return videoItem;
+  }
+
+  private registerPathLabel(path: string, label: string): void {
+    if (!path || !label) {
+      return;
+    }
+    const normalized = this.normalizeFullPath(path);
+    this.pathDisplayNames.set(normalized, label);
+  }
+
+  private normalizeSmbRelativePath(path: string, shareName?: string): string {
+    if (!path || path.trim().length === 0) {
+      return '/';
+    }
+    let normalized = path.replace(/\/+/g, '/');
+    if (!normalized.startsWith('/')) {
+      normalized = `/${normalized}`;
+    }
+    if (normalized.length > 1 && normalized.endsWith('/')) {
+      normalized = normalized.slice(0, -1);
+    }
+    if (shareName) {
+      const cleanedShare = shareName.replace(/^\/+|\/+$/g, '');
+      if (cleanedShare.length > 0) {
+        const prefix = `/${cleanedShare}`;
+        if (normalized === prefix) {
+          normalized = '/';
+        } else if (normalized.startsWith(`${prefix}/`)) {
+          normalized = normalized.substring(prefix.length);
+          if (!normalized.startsWith('/')) {
+            normalized = `/${normalized}`;
+          }
+        }
+      }
+    }
+    return normalized || '/';
+  }
+
   // 判断是否为音频文件
   // 判断是否为音频文件
   private isAudioFile(fileName: string): boolean {
   private isAudioFile(fileName: string): boolean {
     const ext = this.getFileExtension(fileName).toLowerCase();
     const ext = this.getFileExtension(fileName).toLowerCase();
@@ -881,11 +1157,12 @@ export class WebdavManager {
     Logger.info(TAG, '目标路径:', folder.href);
     Logger.info(TAG, '目标路径:', folder.href);
 
 
     // 保存当前路径到历史记录
     // 保存当前路径到历史记录
+    this.registerPathLabel(folder.href, folder.fileName);
     this.pathHistory.push(this.currentPath);
     this.pathHistory.push(this.currentPath);
     Logger.info(TAG, '路径历史:', JSON.stringify(this.pathHistory));
     Logger.info(TAG, '路径历史:', JSON.stringify(this.pathHistory));
 
 
     // 加载文件夹内容
     // 加载文件夹内容
-    await this.loadFilesInfoFromWebdav(folder.href);
+    await this.loadFilesInfoFromAccount(this.currentAccount, folder.href);
   }
   }
 
 
   public async enterFolderFromPath(path: string): Promise<void> {
   public async enterFolderFromPath(path: string): Promise<void> {
@@ -896,10 +1173,10 @@ export class WebdavManager {
     this.pathHistory.push(this.currentPath);
     this.pathHistory.push(this.currentPath);
     Logger.info(TAG, '路径历史:', JSON.stringify(this.pathHistory));
     Logger.info(TAG, '路径历史:', JSON.stringify(this.pathHistory));
     // 加载文件夹内容
     // 加载文件夹内容
-    await this.loadFilesInfoFromWebdav(path);
+    await this.loadFilesInfoFromAccount(this.currentAccount, path);
   }
   }
 
 
-  // 在 WebdavManager 类中添加以下方法
+  // 在 RemoteDriveManager 类中添加以下方法
   navigateToBreadcrumb(breadcrumbIndex: number): Promise<string> {
   navigateToBreadcrumb(breadcrumbIndex: number): Promise<string> {
     return new Promise((resolve, reject) => {
     return new Promise((resolve, reject) => {
       try {
       try {
@@ -942,7 +1219,7 @@ export class WebdavManager {
     // 从历史记录中取出上一级路径
     // 从历史记录中取出上一级路径
     const previousPath = this.pathHistory.pop();
     const previousPath = this.pathHistory.pop();
     if (previousPath !== undefined) {
     if (previousPath !== undefined) {
-      await this.loadFilesInfoFromWebdav(previousPath);
+      await this.loadFilesInfoFromAccount(this.currentAccount, previousPath);
     }
     }
   }
   }
 
 
@@ -955,8 +1232,11 @@ export class WebdavManager {
     const parts = this.currentPath.split('/').filter(part => part !== '');
     const parts = this.currentPath.split('/').filter(part => part !== '');
     const breadcrumbs = ['根目录'];
     const breadcrumbs = ['根目录'];
 
 
+    let cumulative = '';
     for (let i = 0; i < parts.length; i++) {
     for (let i = 0; i < parts.length; i++) {
-      breadcrumbs.push(parts[i]);
+      cumulative += `/${parts[i]}`;
+      const label = this.pathDisplayNames.get(cumulative) ?? parts[i];
+      breadcrumbs.push(label);
     }
     }
 
 
     return breadcrumbs;
     return breadcrumbs;
@@ -998,7 +1278,7 @@ export class WebdavManager {
     const task: TransferTask = { song, account };
     const task: TransferTask = { song, account };
     this.downloadQueue.push(task);
     this.downloadQueue.push(task);
     Logger.info(TAG, '添加到下载队列:', song.name);
     Logger.info(TAG, '添加到下载队列:', song.name);
-    this.notifyObservers(WebdavManagerStates.DownloadQueueChanged);
+    this.notifyObservers(RemoteDriveManagerStates.DownloadQueueChanged);
   }
   }
 
 
   // 从下载队列移除
   // 从下载队列移除
@@ -1007,7 +1287,7 @@ export class WebdavManager {
       const task = this.downloadQueue[index];
       const task = this.downloadQueue[index];
       this.downloadQueue.splice(index, 1);
       this.downloadQueue.splice(index, 1);
       Logger.info(TAG, '从下载队列移除:', task.song.name);
       Logger.info(TAG, '从下载队列移除:', task.song.name);
-      this.notifyObservers(WebdavManagerStates.DownloadQueueChanged);
+      this.notifyObservers(RemoteDriveManagerStates.DownloadQueueChanged);
     }
     }
   }
   }
 
 
@@ -1015,7 +1295,7 @@ export class WebdavManager {
   public clearDownloadQueue(): void {
   public clearDownloadQueue(): void {
     this.downloadQueue = [];
     this.downloadQueue = [];
     Logger.info(TAG, '清空下载队列');
     Logger.info(TAG, '清空下载队列');
-    this.notifyObservers(WebdavManagerStates.DownloadQueueChanged);
+    this.notifyObservers(RemoteDriveManagerStates.DownloadQueueChanged);
   }
   }
 
 
   // ==================== 上传队列管理 ====================
   // ==================== 上传队列管理 ====================
@@ -1025,7 +1305,7 @@ export class WebdavManager {
     const task: TransferTask = { song, account };
     const task: TransferTask = { song, account };
     this.uploadQueue.push(task);
     this.uploadQueue.push(task);
     Logger.info(TAG, '添加到上传队列:', song.name);
     Logger.info(TAG, '添加到上传队列:', song.name);
-    this.notifyObservers(WebdavManagerStates.UploadQueueChanged);
+    this.notifyObservers(RemoteDriveManagerStates.UploadQueueChanged);
   }
   }
 
 
   // 从上传队列移除
   // 从上传队列移除
@@ -1034,7 +1314,7 @@ export class WebdavManager {
       const task = this.uploadQueue[index];
       const task = this.uploadQueue[index];
       this.uploadQueue.splice(index, 1);
       this.uploadQueue.splice(index, 1);
       Logger.info(TAG, '从上传队列移除:', task.song.name);
       Logger.info(TAG, '从上传队列移除:', task.song.name);
-      this.notifyObservers(WebdavManagerStates.UploadQueueChanged);
+      this.notifyObservers(RemoteDriveManagerStates.UploadQueueChanged);
     }
     }
   }
   }
 
 
@@ -1042,7 +1322,7 @@ export class WebdavManager {
   public clearUploadQueue(): void {
   public clearUploadQueue(): void {
     this.uploadQueue = [];
     this.uploadQueue = [];
     Logger.info(TAG, '清空上传队列');
     Logger.info(TAG, '清空上传队列');
-    this.notifyObservers(WebdavManagerStates.UploadQueueChanged);
+    this.notifyObservers(RemoteDriveManagerStates.UploadQueueChanged);
   }
   }
 
 
   // ==================== 安全认证方法 ====================
   // ==================== 安全认证方法 ====================
@@ -1134,8 +1414,8 @@ export async function buildHttpHeadersWithWebDav(
       try {
       try {
         if (webDavAuthItem && webDavAuthItem.accountId) {
         if (webDavAuthItem && webDavAuthItem.accountId) {
           Logger.info(`heanup 找到WebDAV认证信息,账户ID: ${webDavAuthItem.accountId}`);
           Logger.info(`heanup 找到WebDAV认证信息,账户ID: ${webDavAuthItem.accountId}`);
-          // 使用WebdavManager获取认证头
-          const webdavManager = WebdavManager.getInstance();
+          // 使用RemoteDriveManager获取认证头
+          const webdavManager = RemoteDriveManager.getInstance();
           const authHeaders = await webdavManager.getWebDavAuthHeaders(webDavAuthItem.accountId.toString());
           const authHeaders = await webdavManager.getWebDavAuthHeaders(webDavAuthItem.accountId.toString());
 
 
           if (authHeaders) {
           if (authHeaders) {
@@ -1171,7 +1451,7 @@ export async function buildHttpHeadersWithWebDav(
     console.log(`heanup ${key}: ${value}`);
     console.log(`heanup ${key}: ${value}`);
     headerEntry = headerIterator.next();
     headerEntry = headerIterator.next();
   }
   }
-  
+
   return headers;
   return headers;
 }
 }
 
 

+ 707 - 0
entry/src/main/ets/dialog/RemoteDriveAccountDialog.ets

@@ -0,0 +1,707 @@
+import { ToastUtil } from '@pura/harmony-utils';
+import { CommonConstants } from '../common/constants/CommonConstants';
+import { RemoteDriveType } from '../common/enums/RemoteDriveType';
+import { WebDavAccount } from '../viewmodel/WebDavAccount';
+import { ImagePickerUtil } from '../common/util/ImagePickerUtil';
+import Logger from '../common/util/Logger';
+import { ConfigurationConstant, Context } from '@kit.AbilityKit';
+
+interface ParsedConnectionParts {
+  protocol: string;
+  username?: string;
+  password?: string;
+  host: string;
+  port?: number;
+  path?: string;
+}
+
+// 工具函数:将 #RRGGBB 字符串和透明度转为 'rgba(r,g,b,a)' 字符串,深色模式下可返回深色变体
+function themeColorWithAlpha(themeColor: string, alpha: number, isDarkMode: boolean = false): string {
+  if (isDarkMode) {
+    // 深色模式下返回更深的灰色或半透明黑色
+    return `rgba(34,34,34,${alpha + 0.2 > 1 ? 1 : alpha + 0.2})`;
+  }
+  const color = themeColor.replace('#', '');
+  const r = parseInt(color.substring(0, 2), 16);
+  const g = parseInt(color.substring(2, 4), 16);
+  const b = parseInt(color.substring(4, 6), 16);
+  return `rgba(${r},${g},${b},${alpha})`;
+}
+
+// 网盘账户对话框
+@Component
+export struct RemoteDriveAccountDialog {
+  @Prop isEditMode: boolean = false;
+  @Prop account: WebDavAccount;
+  onConfirm?: (account: WebDavAccount) => void;
+  onCancel?: () => void;
+  @State accountName: string = '新建WebDAV';
+  @State host: string = '';
+  @State port: number = -1;
+  @State filepath: string = '/';
+  @State username: string = '';
+  @State password: string = '';
+  @State enableHttps: boolean = false;
+  @State driveType: RemoteDriveType = RemoteDriveType.WebDav;
+  @State shareName: string = '';
+  @State domain: string = '';
+  @State navidromeBasePath: string = '/rest';
+  @State coverPath: string = '';
+  @State isDarkMode: boolean = false;
+  @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
+  @StorageProp('currentColorMode') @Watch('onColorModeChange') currentMode: number =
+    ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
+
+  private nameCustomized: boolean = false;
+  private portCustomized: boolean = false;
+  private navBasePathCustomized: boolean = false;
+  private suppressPortChange: boolean = false;
+
+  onColorModeChange() {
+    this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
+  }
+
+  aboutToAppear(): void {
+    // 初始化深色模式状态
+    this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
+
+    if(this.isEditMode){
+      this.accountName = this.account.name;
+      this.host = this.account.host;
+      this.updatePortState(this.account.port, true);
+      this.filepath = this.account.filepath;
+      this.username = this.account.account;
+      this.password = this.account.password;
+      this.enableHttps = this.account.enableHttps;
+      this.driveType = this.account.webType ?? RemoteDriveType.WebDav;
+      this.shareName = this.account.smbShare ?? '';
+      this.domain = this.account.smbDomain ?? '';
+      this.coverPath = this.account.coverPath || '';
+      this.nameCustomized = true;
+      if (this.driveType === RemoteDriveType.Navidrome) {
+        this.navidromeBasePath = this.normalizeNavidromeBasePath(this.account.navidromeBasePath ?? '/rest');
+        this.navBasePathCustomized = true;
+      }
+      Logger.info('heanup RemoteDriveAccountDialog', `编辑模式加载账户: ${this.accountName}, 封面路径: ${this.coverPath}`);
+      if (this.coverPath) {
+        Logger.info('heanup RemoteDriveAccountDialog', `封面文件是否存在: ${ImagePickerUtil.imageExists(this.coverPath)}`);
+      }
+    } else {
+      this.applyTypeDefaults(this.driveType);
+    }
+  }
+
+  build() {
+    Scroll(){
+      this.contentBuilder()
+    }
+    .height('100%')
+    .width('100%')
+    .scrollBar(BarState.Off)
+
+  }
+
+  @Builder
+  contentBuilder() {
+    Column({ space: 16 }) {
+      // 标题
+      Text(this.isEditMode ? '编辑账户' : '添加账户')
+        .fontSize(18)
+        .fontWeight(FontWeight.Bold)
+        .fontColor($r('app.color.index_tab_font_color'));
+
+      // 类型选择 + 封面
+      this.buildTypeSelector();
+
+      // 账户封面选择
+      Column({ space: 8 }) {
+        Text('账户封面(可选)')
+          .fontSize(14)
+          .fontColor(this.isDarkMode ? '#EBEBF5' : '#666666')
+          .fontWeight(FontWeight.Medium)
+          .alignSelf(ItemAlign.Start);
+
+        // 封面选择区域
+        Row({ space: 12 }) {
+          // 封面预览
+          Stack() {
+            if (this.coverPath) {
+              Image(this.coverPath)
+                .width(80)
+                .height(80)
+                .borderRadius(8)
+                .objectFit(ImageFit.Cover);
+            } else {
+              // 默认封面图标
+              Column() {
+                Image($r('app.media.cloudDisk'))
+                  .width(40)
+                  .height(40)
+                  .fillColor(this.isDarkMode ? Color.White : this.themeColor);
+              }
+              .width(80)
+              .height(80)
+              .backgroundColor(this.isDarkMode ? '#2C2C2E' : $r('app.color.input_background'))
+              .borderRadius(8)
+              .justifyContent(FlexAlign.Center);
+            }
+          }
+          .onClick(() => {
+            this.handleSelectCover();
+          });
+
+          // 操作按钮
+          Column({ space: 8 }) {
+            Button('选择封面')
+              .width(120)
+              .height(36)
+              .fontSize(12)
+              .fontColor(this.isDarkMode ? '#FFFFFF' : $r('app.color.text_color'))
+              .backgroundColor(this.isDarkMode ? '#2C2C2E' : $r('app.color.input_background'))
+              .onClick(() => {
+                this.handleSelectCover();
+              });
+
+            if (this.coverPath) {
+              Button('移除封面')
+                .width(120)
+                .height(36)
+                .fontSize(12)
+                .fontColor(this.isDarkMode ? '#FF453A' : Color.Red)
+                .backgroundColor(this.isDarkMode ? 'rgba(255,69,58,0.2)' : '#FFE5E5')
+                .onClick(() => {
+                  this.handleRemoveCover();
+                });
+            }
+          }
+          .alignItems(HorizontalAlign.Start);
+        }
+        .width('100%')
+        .justifyContent(FlexAlign.Start);
+      }
+      .width('100%')
+      .alignItems(HorizontalAlign.Start);
+
+      // 账户名称
+      Row({ space: 8 }) {
+        Text('名称')
+          .fontSize(14)
+          .fontColor($r('app.color.index_tab_font_color'));
+        TextArea({ placeholder: '请输入账户名称', text: this.accountName })
+          .layoutWeight(1)
+          .maxLines(1)
+          .onChange((value: string) => {
+            this.accountName = value;
+            this.nameCustomized = true;
+          });
+      }
+      .width('100%')
+      .alignItems(VerticalAlign.Center);
+
+      // 服务器地址
+      Row({ space: 8 }) {
+        Text(this.getServerLabel())
+          .fontSize(14)
+          .maxLines(2)
+          .fontColor($r('app.color.index_tab_font_color'));
+        TextArea({ placeholder: '服务器地址或连接串', text: this.host })
+          .layoutWeight(1)
+          .onChange((value: string) => {
+            const trimmed = value.trim();
+            if (trimmed.includes('://') && this.tryParseConnectionString(trimmed)) {
+              return;
+            }
+            this.host = trimmed;
+          });
+      }
+      .alignItems(VerticalAlign.Center);
+      this.buildHelperText(this.getServerHint())
+
+      // 端口
+      Row({ space: 8 }) {
+        Text('端口')
+          .fontSize(14)
+          .fontColor($r('app.color.index_tab_font_color'));
+        TextInput({ placeholder: this.getPortPlaceholder(), text: this.port.toString() })
+          .layoutWeight(1)
+          .maxLines(1)
+          .type(InputType.Number)
+          .onChange((value: string) => {
+            if (this.suppressPortChange) {
+              this.suppressPortChange = false;
+              return;
+            }
+            const parsed = parseInt(value);
+            const defaultPort = this.driveType === RemoteDriveType.Smb ? 445 : (this.enableHttps ? 443 : 5005);
+            if (Number.isNaN(parsed) || parsed === -1) {
+              this.updatePortState(defaultPort, false);
+            } else {
+              this.updatePortState(parsed, true);
+            }
+          });
+      }
+      .alignItems(VerticalAlign.Center);
+      this.buildHelperText('若留空将自动使用协议默认端口')
+
+      if (this.driveType === RemoteDriveType.Smb) {
+        // SMB 共享名称
+        Row({ space: 8 }) {
+          Text('共享名称')
+            .fontSize(14)
+            .fontColor($r('app.color.index_tab_font_color'));
+          TextInput({ placeholder: '共享名称', text: this.shareName })
+            .layoutWeight(1)
+            .maxLines(1)
+            .onChange((value: string) => {
+              this.shareName = value;
+            });
+        }
+        .alignItems(VerticalAlign.Center);
+        this.buildHelperText('NAS 上共享根目录的名称,例如 music、share')
+
+        Row({ space: 8 }) {
+          Text('域/工作组')
+            .fontSize(14)
+            .fontColor($r('app.color.index_tab_font_color'));
+          TextInput({ placeholder: '可选项', text: this.domain })
+            .layoutWeight(1)
+            .maxLines(1)
+            .onChange((value: string) => {
+              this.domain = value;
+            });
+        }
+        .alignItems(VerticalAlign.Center);
+        this.buildHelperText('可选,用于需要域/工作组认证的 SMB 服务器')
+      }
+
+      if (this.driveType === RemoteDriveType.Navidrome) {
+        Row({ space: 8 }) {
+          Text('API路径')
+            .fontSize(14)
+            .fontColor($r('app.color.index_tab_font_color'));
+          TextInput({ placeholder: '/rest', text: this.navidromeBasePath })
+            .layoutWeight(1)
+            .maxLines(1)
+            .onChange((value: string) => {
+              this.navidromeBasePath = this.normalizeNavidromeBasePath(value);
+              this.navBasePathCustomized = true;
+            });
+        }
+        .alignItems(VerticalAlign.Center);
+        this.buildHelperText('Navidrome/Subsonic REST 前缀,默认 /rest,可填 /navidrome/rest 等路径')
+      }
+
+      // 文件目录
+      Row({ space: 8 }) {
+        Text('文件目录')
+          .fontSize(14)
+          .fontColor($r('app.color.index_tab_font_color'));
+        TextInput({ placeholder: '远程目录', text: this.filepath })
+          .layoutWeight(1)
+          .maxLines(1)
+          .onChange((value: string) => {
+            this.filepath = value;
+          });
+      }
+      .alignItems(VerticalAlign.Center);
+      this.buildHelperText('从共享根开始的路径,例如 /music 或 /音乐/歌单1')
+
+      // 用户名
+      Row({ space: 8 }) {
+        Text('用户名')
+          .fontSize(14)
+          .fontColor($r('app.color.index_tab_font_color'));
+        TextInput({ placeholder: '请输入用户名', text: this.username })
+          .layoutWeight(1)
+          .maxLines(1)
+          .onChange((value: string) => {
+            this.username = value;
+          });
+      }
+      .alignItems(VerticalAlign.Center);
+
+      // 密码
+      Row({ space: 8 }) {
+        Text('密码')
+          .fontSize(14)
+          .fontColor($r('app.color.index_tab_font_color'));
+        TextInput({ placeholder: '请输入密码', text: this.password })
+          .type(InputType.Password)
+          .layoutWeight(1)
+          .maxLines(2)
+          .onChange((value: string) => {
+            this.password = value;
+          });
+      }
+      .alignItems(VerticalAlign.Center);
+
+      // HTTPS开关
+      if (this.driveType === RemoteDriveType.WebDav) {
+        Row() {
+          Text('启用HTTPS')
+            .fontSize(14)
+            .fontColor($r('app.color.index_tab_font_color'));
+          Blank();
+          Toggle({ type: ToggleType.Switch, isOn: this.enableHttps })
+            .selectedColor(this.themeColor)
+            .onChange((isOn: boolean) => {
+              this.enableHttps = isOn;
+              if (!this.portCustomized && this.driveType === RemoteDriveType.WebDav) {
+                this.updatePortState(isOn ? 443 : 5005, false);
+              }
+            });
+        }
+        .width('100%');
+      }
+
+      // 按钮
+      Row({ space: 12 }) {
+        Button('取消', { type: ButtonType.Capsule })
+          .backgroundColor($r('app.color.cancel_button_background'))
+          .fontColor($r('app.color.cancel_button_text'))
+          .layoutWeight(1)
+          .onClick(() => {
+            this.onCancel?.();
+          });
+
+        Button(this.isEditMode ? '保存' : '添加', { type: ButtonType.Capsule })
+          .backgroundColor(this.themeColor)
+          .layoutWeight(1)
+          .onClick(() => {
+            if (!this.accountName || !this.host) {
+              ToastUtil.showToast('请填写账户名称和服务器地址');
+              return;
+            }
+
+            const updatedAccount = new WebDavAccount();
+            if (this.isEditMode) {
+              updatedAccount.id = this.account.id;
+            }
+            updatedAccount.name = this.accountName;
+            updatedAccount.host = this.host;
+            updatedAccount.port = this.port;
+            updatedAccount.filepath = this.filepath;
+            updatedAccount.account = this.username;
+            updatedAccount.password = this.password;
+            updatedAccount.enableHttps = this.enableHttps;
+            updatedAccount.coverPath = this.coverPath;
+            updatedAccount.isActivate = true;
+            updatedAccount.localHost = '';
+            updatedAccount.isUseLocalHost = false;
+            updatedAccount.lyricFilePath = '';
+            updatedAccount.uploadFilePath = '';
+            updatedAccount.imageFilePath = '';
+            updatedAccount.webType = this.driveType;
+            updatedAccount.smbShare = this.shareName;
+            updatedAccount.smbDomain = this.domain;
+            updatedAccount.navidromeBasePath = this.normalizeNavidromeBasePath(this.navidromeBasePath);
+            this.onConfirm?.(updatedAccount);
+
+          });
+      }
+      .width('100%')
+      .margin({ top: 8 });
+    }
+    .padding(24)
+    .width('90%')
+    .borderRadius(16)
+  }
+
+  @Builder
+  private buildTypeSelector() {
+    Column({ space: 8 }) {
+      Text('协议类型')
+        .fontSize(14)
+        .fontColor($r('app.color.index_tab_font_color'))
+        .fontWeight(FontWeight.Medium)
+        .alignSelf(ItemAlign.Start);
+      Row({ space: 12 }) {
+        this.buildTypeButton('WebDAV', RemoteDriveType.WebDav);
+        this.buildTypeButton('SMB', RemoteDriveType.Smb);
+        this.buildTypeButton('Navidrome', RemoteDriveType.Navidrome);
+      }
+      .width('100%');
+    }
+    .width('100%');
+  }
+
+  @Builder
+  private buildTypeButton(label: string, type: RemoteDriveType) {
+    Button(label)
+      .type(ButtonType.Capsule)
+      .backgroundColor(this.driveType === type ? this.themeColor : (this.isDarkMode ? '#2C2C2E' : $r('app.color.input_background')))
+      .fontColor(this.driveType === type ? Color.White : $r('app.color.index_tab_font_color'))
+      .onClick(() => {
+        this.handleDriveTypeChange(type);
+      });
+  }
+
+  private handleDriveTypeChange(type: RemoteDriveType) {
+    if (this.driveType === type) {
+      return;
+    }
+    this.driveType = type;
+    if (type === RemoteDriveType.Smb) {
+      this.enableHttps = false;
+    }
+    this.applyTypeDefaults(type, true);
+  }
+
+  private applyTypeDefaults(type: RemoteDriveType, forcePort: boolean = false) {
+    if (!this.nameCustomized) {
+      this.accountName = this.getDefaultAccountName(type);
+    }
+    if (type === RemoteDriveType.Navidrome && !this.navBasePathCustomized) {
+      this.navidromeBasePath = '/rest';
+    }
+    this.updatePortForType(type, forcePort);
+  }
+
+  private updatePortForType(type: RemoteDriveType, force: boolean = false) {
+    if (!force && this.portCustomized) {
+      return;
+    }
+    if (type === RemoteDriveType.Smb) {
+      this.updatePortState(445, false);
+      return;
+    }
+    if (type === RemoteDriveType.Navidrome) {
+      this.updatePortState(this.enableHttps ? 443 : 4533, false);
+      return;
+    }
+    this.updatePortState(this.enableHttps ? 443 : 5005, false);
+  }
+
+  private getDefaultAccountName(type: RemoteDriveType): string {
+    switch (type) {
+      case RemoteDriveType.Smb:
+        return '新建SMB';
+      case RemoteDriveType.Navidrome:
+        return '新建Navidrome';
+      default:
+        return '新建WebDAV';
+    }
+  }
+
+  private getServerLabel(): string {
+    return '服务器';
+  }
+
+  private getServerHint(): string {
+    switch (this.driveType) {
+      case RemoteDriveType.Smb:
+        return '支持 smb://user:pass@host/share 输入,自动拆分账户、共享、路径';
+      case RemoteDriveType.Navidrome:
+        return '可粘贴 Navidrome/Subsonic 连接(如 https://user:pass@host:4533/rest),自动填充参数';
+      default:
+        return '支持 https://user:pass@host:port/path WebDAV 连接串,自动填充账户、端口和目录';
+    }
+  }
+
+  private getPortPlaceholder(): string {
+    if (this.driveType === RemoteDriveType.Smb) {
+      return '默认: 445';
+    }
+    if (this.driveType === RemoteDriveType.Navidrome) {
+      return this.enableHttps ? '默认: 443' : '默认: 4533';
+    }
+    return this.enableHttps ? '默认: 443' : '默认: 5005';
+  }
+
+  private updatePortState(value: number, customized: boolean) {
+    this.suppressPortChange = true;
+    this.port = value;
+    this.portCustomized = customized;
+  }
+
+  @Builder
+  private buildHelperText(text: string) {
+    if (text && text.trim().length > 0) {
+      Text(text)
+        .fontSize(12)
+        .fontColor(this.isDarkMode ? '#8E8E93' : '#999999')
+        .opacity(0.9)
+        .alignSelf(ItemAlign.Start)
+    }
+  }
+
+  private tryParseConnectionString(input: string): boolean {
+    const parsed = this.parseConnectionString(input);
+    if (!parsed) {
+      return false;
+    }
+    const protocol = parsed.protocol.toLowerCase();
+    if (protocol === 'smb' || protocol === 'cifs') {
+      if (this.driveType !== RemoteDriveType.Smb) {
+        this.handleDriveTypeChange(RemoteDriveType.Smb);
+      }
+      this.applyParsedSmb(parsed);
+      ToastUtil.showToast('已解析 SMB 连接');
+      return true;
+    }
+    if (protocol === 'http' || protocol === 'https') {
+      const looksNav = this.guessNavidromePath(parsed.path);
+      if (looksNav || this.driveType === RemoteDriveType.Navidrome) {
+        if (this.driveType !== RemoteDriveType.Navidrome) {
+          this.handleDriveTypeChange(RemoteDriveType.Navidrome);
+        }
+        this.applyParsedNavidrome(parsed);
+        ToastUtil.showToast('已解析 Navidrome 连接');
+        return true;
+      }
+      if (this.driveType !== RemoteDriveType.WebDav) {
+        this.handleDriveTypeChange(RemoteDriveType.WebDav);
+      }
+      this.applyParsedWebDav(parsed);
+      ToastUtil.showToast('已解析 WebDAV 连接');
+      return true;
+    }
+    return false;
+  }
+
+  private applyParsedWebDav(parsed: ParsedConnectionParts): void {
+    this.enableHttps = parsed.protocol === 'https';
+    this.host = parsed.host;
+    if (parsed.port) {
+      this.updatePortState(parsed.port, true);
+    } else {
+      this.updatePortForType(RemoteDriveType.WebDav, true);
+    }
+    if (parsed.username) {
+      this.username = parsed.username;
+    }
+    if (parsed.password) {
+      this.password = parsed.password;
+    }
+    this.filepath = parsed.path?.length ? parsed.path : '/';
+  }
+
+  private applyParsedSmb(parsed: ParsedConnectionParts): void {
+    this.host = parsed.host;
+    if (parsed.port) {
+      this.updatePortState(parsed.port, true);
+    } else {
+      this.updatePortForType(RemoteDriveType.Smb, true);
+    }
+    if (parsed.username) {
+      this.username = parsed.username;
+    }
+    if (parsed.password) {
+      this.password = parsed.password;
+    }
+    const segments = parsed.path?.replace(/^\/+/, '').split('/') ?? [];
+    this.shareName = segments.length > 0 && segments[0].length > 0 ? segments[0] : this.shareName;
+    const relative = segments.length > 1 ? segments.slice(1).join('/') : '';
+    this.filepath = relative ? `/${relative}` : '/';
+  }
+
+  private applyParsedNavidrome(parsed: ParsedConnectionParts): void {
+    this.enableHttps = parsed.protocol === 'https';
+    this.host = parsed.host;
+    if (parsed.port) {
+      this.updatePortState(parsed.port, true);
+    } else {
+      this.updatePortForType(RemoteDriveType.Navidrome, true);
+    }
+    if (parsed.username) {
+      this.username = parsed.username;
+    }
+    if (parsed.password) {
+      this.password = parsed.password;
+    }
+    this.navidromeBasePath = this.normalizeNavidromeBasePath(parsed.path ?? this.navidromeBasePath);
+    this.navBasePathCustomized = true;
+    this.filepath = '/';
+  }
+
+  private guessNavidromePath(path?: string): boolean {
+    if (!path) {
+      return false;
+    }
+    const lower = path.toLowerCase();
+    return lower.includes('/rest') || lower.includes('navidrome');
+  }
+
+  private normalizeNavidromeBasePath(value: string): string {
+    if (!value || value.trim().length === 0) {
+      return '/rest';
+    }
+    let normalized = value.trim();
+    if (!normalized.startsWith('/')) {
+      normalized = `/${normalized}`;
+    }
+    if (normalized.length > 1 && normalized.endsWith('/')) {
+      normalized = normalized.slice(0, -1);
+    }
+    return normalized.length === 0 ? '/rest' : normalized;
+  }
+
+  private parseConnectionString(input: string): ParsedConnectionParts | null {
+    const pattern = /^([a-z][a-z0-9+\-.]*):\/\/(?:([^:@\/]+)(?::([^@\/]*))?@)?([^\/:]+)(?::(\d+))?(\/.*)?$/i;
+    const match = input.match(pattern);
+    if (!match) {
+      Logger.warn('heanup RemoteDriveAccountDialog', `连接字符串解析失败: ${input}`);
+      return null;
+    }
+    const protocol = match[1].toLowerCase();
+    const username = match[2] ? this.safeDecode(match[2]) : undefined;
+    const password = match[3] ? this.safeDecode(match[3]) : undefined;
+    const host = match[4];
+    const port = match[5] ? Number(match[5]) : undefined;
+    const path = match[6] && match[6].length > 0 ? match[6] : undefined;
+    return { protocol, username, password, host, port, path };
+  }
+
+  private safeDecode(value: string): string {
+    try {
+      return decodeURIComponent(value);
+    } catch (_error) {
+      return value;
+    }
+  }
+
+  /**
+   * 处理选择封面
+   */
+  private async handleSelectCover() {
+    try {
+      // 需要获取上下文,这里通过全局上下文获取
+      const context = getContext(this) as Context
+      if (!context) {
+        ToastUtil.showToast('获取应用上下文失败')
+        return
+      }
+
+      const selectedPath = await ImagePickerUtil.selectSingleWebDavCover(context)
+      if (selectedPath) {
+        // 如果之前有封面,删除旧封面
+        if (this.coverPath && this.coverPath !== this.account.coverPath) {
+          ImagePickerUtil.deleteImage(this.coverPath)
+        }
+        this.coverPath = selectedPath
+        Logger.info('heanup RemoteDriveAccountDialog', `选择封面成功: ${selectedPath}`)
+      }
+    } catch (error) {
+        Logger.error('heanup RemoteDriveAccountDialog', `选择封面失败: ${(error as Error).message}`)
+        ToastUtil.showToast('选择封面失败')
+    }
+  }
+
+  /**
+   * 处理移除封面
+   */
+  private handleRemoveCover() {
+    if (this.coverPath) {
+      if(!this.account){
+        this.coverPath = ''
+        return
+      }
+      // 只有当封面不是原来的封面时才删除文件
+      if (this.coverPath !== this.account.coverPath) {
+        ImagePickerUtil.deleteImage(this.coverPath)
+      }
+      this.coverPath = ''
+      Logger.info('heanup RemoteDriveAccountDialog', '移除封面成功')
+    }
+  }
+}

+ 0 - 351
entry/src/main/ets/dialog/WebDavAccountDialog.ets

@@ -1,351 +0,0 @@
-import { StrUtil, ToastUtil } from '@pura/harmony-utils';
-import { CommonConstants } from '../common/constants/CommonConstants';
-import { WebDavAccount } from '../viewmodel/WebDavAccount';
-import { ImagePickerUtil } from '../common/util/ImagePickerUtil';
-import Logger from '../common/util/Logger';
-import { ConfigurationConstant, Context } from '@kit.AbilityKit';
-
-// 工具函数:将 #RRGGBB 字符串和透明度转为 'rgba(r,g,b,a)' 字符串,深色模式下可返回深色变体
-function themeColorWithAlpha(themeColor: string, alpha: number, isDarkMode: boolean = false): string {
-  if (isDarkMode) {
-    // 深色模式下返回更深的灰色或半透明黑色
-    return `rgba(34,34,34,${alpha + 0.2 > 1 ? 1 : alpha + 0.2})`;
-  }
-  const color = themeColor.replace('#', '');
-  const r = parseInt(color.substring(0, 2), 16);
-  const g = parseInt(color.substring(2, 4), 16);
-  const b = parseInt(color.substring(4, 6), 16);
-  return `rgba(${r},${g},${b},${alpha})`;
-}
-
-// WebDAV账户对话框
-@Component
-export struct WebDavAccountDialog {
-  @Prop isEditMode: boolean = false;
-  @Prop account: WebDavAccount;
-  onConfirm?: (account: WebDavAccount) => void;
-  onCancel?: () => void;
-  @State accountName: string = 'demo';
-  @State host: string = '';
-  @State port: number = 5005;
-  @State filepath: string = '/';
-  @State username: string = '';
-  @State password: string = '';
-  @State enableHttps: boolean = false;
-  @State coverPath: string = '';
-  @State isDarkMode: boolean = false;
-  @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
-  @StorageProp('currentColorMode') @Watch('onColorModeChange') currentMode: number =
-    ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
-
-  onColorModeChange() {
-    this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
-  }
-
-  aboutToAppear(): void {
-    // 初始化深色模式状态
-    this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
-
-    if(this.isEditMode){
-      this.accountName = this.account.name;
-      this.host = this.account.host;
-      this.port = this.account.port;
-      this.filepath = this.account.filepath;
-      this.username = this.account.account;
-      this.password = this.account.password;
-      this.enableHttps = this.account.enableHttps;
-      this.coverPath = this.account.coverPath || '';
-      Logger.info('heanup WebDavAccountDialog', `编辑模式加载账户: ${this.accountName}, 封面路径: ${this.coverPath}`);
-      if (this.coverPath) {
-        Logger.info('heanup WebDavAccountDialog', `封面文件是否存在: ${ImagePickerUtil.imageExists(this.coverPath)}`);
-      }
-    }
-  }
-
-  build() {
-    Scroll(){
-      this.contentBuilder()
-    }
-    .height('100%')
-    .width('100%')
-    .scrollBar(BarState.Off)
-
-  }
-
-  @Builder
-  contentBuilder() {
-    Column({ space: 16 }) {
-      // 标题
-      Text(this.isEditMode ? '编辑账户' : '添加账户')
-        .fontSize(18)
-        .fontWeight(FontWeight.Bold)
-        .fontColor($r('app.color.index_tab_font_color'));
-
-      // 账户封面选择
-      Column({ space: 8 }) {
-        Text('账户封面(可选)')
-          .fontSize(14)
-          .fontColor(this.isDarkMode ? '#EBEBF5' : '#666666')
-          .fontWeight(FontWeight.Medium)
-          .alignSelf(ItemAlign.Start);
-
-        // 封面选择区域
-        Row({ space: 12 }) {
-          // 封面预览
-          Stack() {
-            if (this.coverPath) {
-              Image(this.coverPath)
-                .width(80)
-                .height(80)
-                .borderRadius(8)
-                .objectFit(ImageFit.Cover);
-            } else {
-              // 默认封面图标
-              Column() {
-                Image($r('app.media.cloudDisk'))
-                  .width(40)
-                  .height(40)
-                  .fillColor(this.isDarkMode ? Color.White : this.themeColor);
-              }
-              .width(80)
-              .height(80)
-              .backgroundColor(this.isDarkMode ? '#2C2C2E' : $r('app.color.input_background'))
-              .borderRadius(8)
-              .justifyContent(FlexAlign.Center);
-            }
-          }
-          .onClick(() => {
-            this.handleSelectCover();
-          });
-
-          // 操作按钮
-          Column({ space: 8 }) {
-            Button('选择封面')
-              .width(120)
-              .height(36)
-              .fontSize(12)
-              .fontColor(this.isDarkMode ? '#FFFFFF' : $r('app.color.text_color'))
-              .backgroundColor(this.isDarkMode ? '#2C2C2E' : $r('app.color.input_background'))
-              .onClick(() => {
-                this.handleSelectCover();
-              });
-
-            if (this.coverPath) {
-              Button('移除封面')
-                .width(120)
-                .height(36)
-                .fontSize(12)
-                .fontColor(this.isDarkMode ? '#FF453A' : Color.Red)
-                .backgroundColor(this.isDarkMode ? 'rgba(255,69,58,0.2)' : '#FFE5E5')
-                .onClick(() => {
-                  this.handleRemoveCover();
-                });
-            }
-          }
-          .alignItems(HorizontalAlign.Start);
-        }
-        .width('100%')
-        .justifyContent(FlexAlign.Start);
-      }
-      .width('100%')
-      .alignItems(HorizontalAlign.Start);
-
-      // 账户名称
-      Row({ space: 8 }) {
-        Text('名称')
-          .fontSize(14)
-          .fontColor($r('app.color.index_tab_font_color'));
-        TextArea({ placeholder: '请输入账户名称', text: this.accountName })
-          .layoutWeight(1)
-          .maxLines(1)
-          .onChange((value: string) => {
-            this.accountName = value;
-          });
-      }
-      .width('100%')
-      .alignItems(VerticalAlign.Center);
-
-      // 服务器地址
-      Row({ space: 8 }) {
-        Text('服务器')
-          .fontSize(14)
-          .maxLines(2)
-          .fontColor($r('app.color.index_tab_font_color'));
-        TextArea({ placeholder: '例如: example.com', text: this.host })
-          .layoutWeight(1)
-          .onChange((value: string) => {
-            this.host = value;
-          });
-      }
-      .alignItems(VerticalAlign.Center);
-
-      // 端口
-      Row({ space: 8 }) {
-        Text('端口')
-          .fontSize(14)
-          .fontColor($r('app.color.index_tab_font_color'));
-        TextInput({ placeholder: '默认: 80', text: this.port.toString() })
-          .layoutWeight(1)
-          .maxLines(1)
-          .type(InputType.Number)
-          .onChange((value: string) => {
-            if(this.enableHttps){
-              this.port = parseInt(value) || 443;
-            }else{
-              this.port = parseInt(value) || 80;
-            }
-          });
-      }
-      .alignItems(VerticalAlign.Center);
-
-      // 文件目录
-      Row({ space: 8 }) {
-        Text('文件目录')
-          .fontSize(14)
-          .fontColor($r('app.color.index_tab_font_color'));
-        TextInput({ placeholder: '例如: /music', text: this.filepath })
-          .layoutWeight(1)
-          .maxLines(1)
-          .onChange((value: string) => {
-            this.filepath = value;
-          });
-      }
-      .alignItems(VerticalAlign.Center);
-
-      // 用户名
-      Row({ space: 8 }) {
-        Text('用户名')
-          .fontSize(14)
-          .fontColor($r('app.color.index_tab_font_color'));
-        TextInput({ placeholder: '请输入用户名', text: this.username })
-          .layoutWeight(1)
-          .maxLines(1)
-          .onChange((value: string) => {
-            this.username = value;
-          });
-      }
-      .alignItems(VerticalAlign.Center);
-
-      // 密码
-      Row({ space: 8 }) {
-        Text('密码')
-          .fontSize(14)
-          .fontColor($r('app.color.index_tab_font_color'));
-        TextInput({ placeholder: '请输入密码', text: this.password })
-          .type(InputType.Password)
-          .layoutWeight(1)
-          .maxLines(2)
-          .onChange((value: string) => {
-            this.password = value;
-          });
-      }
-      .alignItems(VerticalAlign.Center);
-
-      // HTTPS开关
-      Row() {
-        Text('启用HTTPS')
-          .fontSize(14)
-          .fontColor($r('app.color.index_tab_font_color'));
-        Blank();
-        Toggle({ type: ToggleType.Switch, isOn: this.enableHttps })
-          .selectedColor(this.themeColor)
-          .onChange((isOn: boolean) => {
-            this.enableHttps = isOn;
-          });
-      }
-      .width('100%');
-
-      // 按钮
-      Row({ space: 12 }) {
-        Button('取消', { type: ButtonType.Capsule })
-          .backgroundColor($r('app.color.cancel_button_background'))
-          .fontColor($r('app.color.cancel_button_text'))
-          .layoutWeight(1)
-          .onClick(() => {
-            this.onCancel?.();
-          });
-
-        Button(this.isEditMode ? '保存' : '添加', { type: ButtonType.Capsule })
-          .backgroundColor(this.themeColor)
-          .layoutWeight(1)
-          .onClick(() => {
-            if (!this.accountName || !this.host) {
-              ToastUtil.showToast('请填写账户名称和服务器地址');
-              return;
-            }
-
-            const updatedAccount = new WebDavAccount();
-            if (this.isEditMode) {
-              updatedAccount.id = this.account.id;
-            }
-            updatedAccount.name = this.accountName;
-            updatedAccount.host = this.host;
-            updatedAccount.port = this.port;
-            updatedAccount.filepath = this.filepath;
-            updatedAccount.account = this.username;
-            updatedAccount.password = this.password;
-            updatedAccount.enableHttps = this.enableHttps;
-            updatedAccount.coverPath = this.coverPath;
-            updatedAccount.isActivate = true;
-            updatedAccount.localHost = '';
-            updatedAccount.isUseLocalHost = false;
-            updatedAccount.lyricFilePath = '';
-            updatedAccount.uploadFilePath = '';
-            updatedAccount.imageFilePath = '';
-            this.onConfirm?.(updatedAccount);
-
-          });
-      }
-      .width('100%')
-      .margin({ top: 8 });
-    }
-    .padding(24)
-    .width('90%')
-    .borderRadius(16)
-  }
-
-  /**
-   * 处理选择封面
-   */
-  private async handleSelectCover() {
-    try {
-      // 需要获取上下文,这里通过全局上下文获取
-      const context = getContext(this) as Context
-      if (!context) {
-        ToastUtil.showToast('获取应用上下文失败')
-        return
-      }
-
-      const selectedPath = await ImagePickerUtil.selectSingleWebDavCover(context)
-      if (selectedPath) {
-        // 如果之前有封面,删除旧封面
-        if (this.coverPath && this.coverPath !== this.account.coverPath) {
-          ImagePickerUtil.deleteImage(this.coverPath)
-        }
-        this.coverPath = selectedPath
-        Logger.info('heanup WebDavAccountDialog', `选择封面成功: ${selectedPath}`)
-      }
-    } catch (error) {
-        Logger.error('heanup WebDavAccountDialog', `选择封面失败: ${(error as Error).message}`)
-        ToastUtil.showToast('选择封面失败')
-    }
-  }
-
-  /**
-   * 处理移除封面
-   */
-  private handleRemoveCover() {
-    if (this.coverPath) {
-      if(!this.account){
-        this.coverPath = ''
-        return
-      }
-      // 只有当封面不是原来的封面时才删除文件
-      if (this.coverPath !== this.account.coverPath) {
-        ImagePickerUtil.deleteImage(this.coverPath)
-      }
-      this.coverPath = ''
-      Logger.info('heanup WebDavAccountDialog', '移除封面成功')
-    }
-  }
-}

+ 3 - 3
entry/src/main/ets/entryability/EntryAbility.ets

@@ -133,9 +133,9 @@ export default class EntryAbility extends UIAbility {
             // const PreferencesUtil = (await import('../common/util/PreferencesUtil')).default;
             // const PreferencesUtil = (await import('../common/util/PreferencesUtil')).default;
             // PreferencesUtil.getInstance().setContext(this.context);
             // PreferencesUtil.getInstance().setContext(this.context);
 
 
-            // 3. 初始化WebdavManager
-            const WebdavManagerModule = await import('../common/util/WebdavManager');
-            const webdavManager = WebdavManagerModule.WebdavManager.getInstance();
+            // 3. 初始化RemoteDriveManager
+            const RemoteDriveManagerModule = await import('../common/util/RemoteDriveManager');
+            const webdavManager = RemoteDriveManagerModule.RemoteDriveManager.getInstance();
             webdavManager.setContext(this.context);
             webdavManager.setContext(this.context);
 
 
             // 4. 创建WebDAV数据库表
             // 4. 创建WebDAV数据库表

+ 62 - 58
entry/src/main/ets/pages/NewIndex.ets

@@ -46,9 +46,10 @@ import { showCreatePlaylistDialog } from '../dialog/PlaylistDialog';
 import { ImagePickerUtil } from '../common/util/ImagePickerUtil';
 import { ImagePickerUtil } from '../common/util/ImagePickerUtil';
 import { WebDavMainPage } from './WebDavMainPage';
 import { WebDavMainPage } from './WebDavMainPage';
 import { WebDavAccount } from '../viewmodel/WebDavAccount';
 import { WebDavAccount } from '../viewmodel/WebDavAccount';
-import { WebdavManager } from '../common/util/WebdavManager';
-import { WebDavAccountDialog } from '../dialog/WebDavAccountDialog';
+import { RemoteDriveManager } from '../common/util/RemoteDriveManager';
+import { RemoteDriveAccountDialog } from '../dialog/RemoteDriveAccountDialog';
 import ReqPermissionUtil from '../common/util/ReqPermissionUtil';
 import ReqPermissionUtil from '../common/util/ReqPermissionUtil';
+import { getRemoteDriveAccountLabel, getRemoteDriveDisplayLabel, getRemoteDriveProtocolLabel } from '../common/util/RemoteDriveLabel';
 
 
 // import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
 // import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
 
 
@@ -177,10 +178,10 @@ struct NewIndex {
   @State selectedPlaylist: Playlist | null = null
   @State selectedPlaylist: Playlist | null = null
   private playlistTable: PlaylistTable | null = null
   private playlistTable: PlaylistTable | null = null
 
 
-  // WebDAV账户相关状态变量
+  // 网盘账户相关状态变量
   @State webDavAccounts: WebDavAccount[] = []
   @State webDavAccounts: WebDavAccount[] = []
-  private webdavManager: WebdavManager = WebdavManager.getInstance()
-  private _webDavLoading: boolean = false // 防止重复加载WebDAV账户的标志
+  private webdavManager: RemoteDriveManager = RemoteDriveManager.getInstance()
+  private _webDavLoading: boolean = false // 防止重复加载网盘账户的标志
   @State selectedAccount: WebDavAccount = new WebDavAccount()
   @State selectedAccount: WebDavAccount = new WebDavAccount()
   /**
   /**
    * 返回键处理逻辑:
    * 返回键处理逻辑:
@@ -230,10 +231,10 @@ struct NewIndex {
 
 
 
 
   onPageShow() {
   onPageShow() {
-    LogUtil.info('heanup NewIndex', 'onPageShow 被调用,刷新歌单列表和WebDAV账户列表')
+    LogUtil.info('heanup NewIndex', 'onPageShow 被调用,刷新歌单列表和网盘账户列表')
     // 加载歌单列表
     // 加载歌单列表
     this.loadPlaylistList()
     this.loadPlaylistList()
-    // 加载WebDAV账户列表
+    // 加载网盘账户列表
     this.loadWebDavAccounts()
     this.loadWebDavAccounts()
   }
   }
   /**
   /**
@@ -1195,7 +1196,7 @@ struct NewIndex {
    */
    */
   @Builder
   @Builder
   buildCloudStorageTab() {
   buildCloudStorageTab() {
-    // 当前只支持WebDAV账户,将来可以在这里添加其他类型的网盘账户
+    // 当前只支持网盘账户,将来可以在这里添加其他类型的网盘账户
     // 例如:OneDrive, Google Drive, Dropbox等
     // 例如:OneDrive, Google Drive, Dropbox等
     // 添加新账户按钮
     // 添加新账户按钮
     ListItem() {
     ListItem() {
@@ -1208,7 +1209,7 @@ struct NewIndex {
             .alignSelf(ItemAlign.Center)
             .alignSelf(ItemAlign.Center)
             .margin({ left: 25 })
             .margin({ left: 25 })
 
 
-          Text('添加WebDAV账户')
+          Text(`添加${getRemoteDriveAccountLabel()}`)
             .margin({ left: 10, right: 20 })
             .margin({ left: 10, right: 20 })
             .fontSize(14)
             .fontSize(14)
             .fontColor($r('app.color.index_tab_font_color'))
             .fontColor($r('app.color.index_tab_font_color'))
@@ -1228,11 +1229,11 @@ struct NewIndex {
       .backgroundColor(Color.Transparent)
       .backgroundColor(Color.Transparent)
       .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
       .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
       .onClick(() => {
       .onClick(() => {
-        console.info('heanup', '点击添加WebDAV账户')
-        this.showAddWebDavAccountDialog(false)
+        console.info('heanup', '点击添加网盘账户')
+        this.showRemoteDriveAccountDialog(false)
       })
       })
     }
     }
-    // 显示所有WebDAV账户
+    // 显示所有网盘账户
     ForEach(this.webDavAccounts, (account: WebDavAccount,index:number) => {
     ForEach(this.webDavAccounts, (account: WebDavAccount,index:number) => {
       ListItem() {
       ListItem() {
         Button({ type: ButtonType.Capsule, stateEffect: true }) {
         Button({ type: ButtonType.Capsule, stateEffect: true }) {
@@ -1260,7 +1261,7 @@ struct NewIndex {
 
 
               Row() {
               Row() {
                 // 账户类型标签
                 // 账户类型标签
-                Text('WebDAV')
+                Text(getRemoteDriveDisplayLabel(account.webType))
                   .fontSize(10)
                   .fontSize(10)
                   .fontColor(this.selectedAccount==account?this.themeColor:$r('app.color.index_tab_font_color'))
                   .fontColor(this.selectedAccount==account?this.themeColor:$r('app.color.index_tab_font_color'))
                   .opacity(0.8)
                   .opacity(0.8)
@@ -1293,7 +1294,7 @@ struct NewIndex {
         .backgroundColor(Color.Transparent)
         .backgroundColor(Color.Transparent)
         .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 })
         .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 })
         .onClick(() => {
         .onClick(() => {
-          console.info('heanup', '点击WebDAV账户:', account.name)
+          console.info('heanup', '点击网盘账户:', account.name)
           this.selectWebDavAccount(account)
           this.selectWebDavAccount(account)
         })
         })
         .bindContextMenu(this.MenuDavBuilder(account), ResponseType.LongPress,
         .bindContextMenu(this.MenuDavBuilder(account), ResponseType.LongPress,
@@ -1316,7 +1317,7 @@ struct NewIndex {
     // 如果没有账户,显示提示信息
     // 如果没有账户,显示提示信息
     if (this.webDavAccounts.length === 0) {
     if (this.webDavAccounts.length === 0) {
       ListItem() {
       ListItem() {
-        Text('暂无WebDAV账户,点击上方按钮添加')
+        Text(`暂无${getRemoteDriveAccountLabel()},点击上方按钮添加`)
           .fontSize(14)
           .fontSize(14)
           .fontColor($r('app.color.index_tab_font_color'))
           .fontColor($r('app.color.index_tab_font_color'))
           .opacity(0.6)
           .opacity(0.6)
@@ -1360,7 +1361,7 @@ struct NewIndex {
         content: '编辑'
         content: '编辑'
       })
       })
         .onClick(async() => {
         .onClick(async() => {
-         this.showAddWebDavAccountDialog(true,account)
+         this.showRemoteDriveAccountDialog(true,account)
         })
         })
 
 
       MenuItem({
       MenuItem({
@@ -1376,7 +1377,7 @@ struct NewIndex {
 
 
 
 
   /**
   /**
-   * 删除WebDAV账户
+   * 删除网盘账户
    */
    */
   async deleteWebDavAccount(account: WebDavAccount) {
   async deleteWebDavAccount(account: WebDavAccount) {
     try {
     try {
@@ -1394,19 +1395,19 @@ struct NewIndex {
           action: async () => {
           action: async () => {
             try {
             try {
               await this.webdavManager.removeAccount(account)
               await this.webdavManager.removeAccount(account)
-              ToastUtil.showToast('WebDAV账户删除成功')
-              // 重新加载WebDAV账户列表
+              ToastUtil.showToast('网盘账户删除成功')
+              // 重新加载网盘账户列表
               await this.loadWebDavAccounts()
               await this.loadWebDavAccounts()
-              LogUtil.info('heanup NewIndex', 'WebDAV账户删除成功:', account.name)
+              LogUtil.info('heanup NewIndex', '网盘账户删除成功:', account.name)
             } catch (error) {
             } catch (error) {
-              LogUtil.error('heanup NewIndex', `删除WebDAV账户失败: ${(error as Error).message}`)
+              LogUtil.error('heanup NewIndex', `删除网盘账户失败: ${(error as Error).message}`)
               ToastUtil.showToast('删除失败')
               ToastUtil.showToast('删除失败')
             }
             }
           }
           }
         }
         }
       })
       })
     } catch (error) {
     } catch (error) {
-      LogUtil.error('heanup NewIndex', `删除WebDAV账户操作失败: ${(error as Error).message}`)
+      LogUtil.error('heanup NewIndex', `删除网盘账户操作失败: ${(error as Error).message}`)
       ToastUtil.showToast('操作失败')
       ToastUtil.showToast('操作失败')
     }
     }
   }
   }
@@ -1543,7 +1544,7 @@ struct NewIndex {
       // 设置WebDAV管理器的上下文
       // 设置WebDAV管理器的上下文
       this.webdavManager.setContext(this.context)
       this.webdavManager.setContext(this.context)
 
 
-      // 创建WebDAV账户表
+      // 创建网盘账户表
       await this.webdavManager.createWebDavTableInDB()
       await this.webdavManager.createWebDavTableInDB()
 
 
       // 订阅WebDAV管理器事件
       // 订阅WebDAV管理器事件
@@ -1554,7 +1555,7 @@ struct NewIndex {
         }
         }
       })
       })
 
 
-      // 首页EntryAblility去加载WebDAV账户了。所以这边不加载
+      // 首页EntryAblility去加载网盘账户了。所以这边不加载
       // await this.loadWebDavAccounts()
       // await this.loadWebDavAccounts()
 
 
       LogUtil.info('heanup NewIndex', 'WebDAV管理器初始化成功')
       LogUtil.info('heanup NewIndex', 'WebDAV管理器初始化成功')
@@ -1564,28 +1565,28 @@ struct NewIndex {
   }
   }
 
 
   /**
   /**
-   * 加载WebDAV账户列表
+   * 加载网盘账户列表
    */
    */
   async loadWebDavAccounts() {
   async loadWebDavAccounts() {
     try {
     try {
-      LogUtil.info('heanup NewIndex', '开始加载WebDAV账户列表')
+      LogUtil.info('heanup NewIndex', '开始加载网盘账户列表')
       await this.webdavManager.queryWebDavAccountsFromDB()
       await this.webdavManager.queryWebDavAccountsFromDB()
       // 强制触发UI更新
       // 强制触发UI更新
       this.webDavAccounts = this.webdavManager.getAllWebDavAccounts().slice()
       this.webDavAccounts = this.webdavManager.getAllWebDavAccounts().slice()
       this.isRefreshing = false
       this.isRefreshing = false
-      LogUtil.info('heanup NewIndex', `成功加载 ${this.webDavAccounts.length} 个WebDAV账户`)
+      LogUtil.info('heanup NewIndex', `成功加载 ${this.webDavAccounts.length} 个网盘账户`)
     } catch (error) {
     } catch (error) {
       this.isRefreshing = false
       this.isRefreshing = false
-      LogUtil.error('heanup NewIndex', `加载WebDAV账户列表失败: ${(error as Error).message}`)
+      LogUtil.error('heanup NewIndex', `加载网盘账户列表失败: ${(error as Error).message}`)
     }
     }
   }
   }
 
 
   /**
   /**
-   * 选择WebDAV账户
+   * 选择网盘账户
    */
    */
   selectWebDavAccount(account: WebDavAccount) {
   selectWebDavAccount(account: WebDavAccount) {
     try {
     try {
-      LogUtil.info('heanup NewIndex', '选择WebDAV账户:', account.name)
+      LogUtil.info('heanup NewIndex', '选择网盘账户:', account.name)
       // 如果账户未激活,先激活它
       // 如果账户未激活,先激活它
       if (!account.isActivate) {
       if (!account.isActivate) {
         // 先将所有账户设为未激活
         // 先将所有账户设为未激活
@@ -1597,11 +1598,11 @@ struct NewIndex {
         account.isActivate = true
         account.isActivate = true
         // 更新数据库中的激活状态
         // 更新数据库中的激活状态
         this.webdavManager.editAccount(account).then(() => {
         this.webdavManager.editAccount(account).then(() => {
-          LogUtil.info('heanup NewIndex', 'WebDAV账户编辑成功:', account.name)
+          LogUtil.info('heanup NewIndex', '网盘账户编辑成功:', account.name)
 
 
 
 
         }).catch((error: Error) => {
         }).catch((error: Error) => {
-          LogUtil.error('heanup NewIndex', `编辑WebDAV账户失败: ${error.message}`)
+          LogUtil.error('heanup NewIndex', `编辑网盘账户失败: ${error.message}`)
           ToastUtil.showToast('编辑账户失败')
           ToastUtil.showToast('编辑账户失败')
         })
         })
       }
       }
@@ -1610,16 +1611,16 @@ struct NewIndex {
       this.mType = 6
       this.mType = 6
       this.doShowDrawer()
       this.doShowDrawer()
     } catch (error) {
     } catch (error) {
-      LogUtil.error('heanup NewIndex', `选择WebDAV账户失败: ${(error as Error).message}`)
+      LogUtil.error('heanup NewIndex', `选择网盘账户失败: ${(error as Error).message}`)
       ToastUtil.showToast('选择账户失败')
       ToastUtil.showToast('选择账户失败')
     }
     }
   }
   }
 
 
   /**
   /**
-   * 显示添加WebDAV账户对话框
+   * 显示添加网盘账户对话框
    */
    */
   @State addDavDialogId:number = 1
   @State addDavDialogId:number = 1
-  showAddWebDavAccountDialog(isEditMode?: boolean,account?: WebDavAccount) {
+  showRemoteDriveAccountDialog(isEditMode?: boolean,account?: WebDavAccount) {
 
 
     const node: FrameNode | null = this.getUIContext().getFrameNodeById("test_text") || null;
     const node: FrameNode | null = this.getUIContext().getFrameNodeById("test_text") || null;
     this.getUIContext().getPromptAction().openCustomDialog({
     this.getUIContext().getPromptAction().openCustomDialog({
@@ -1637,7 +1638,7 @@ struct NewIndex {
 
 
   @Builder
   @Builder
   webDavAccountBuilder(isEditMode?: boolean,account?: WebDavAccount) {
   webDavAccountBuilder(isEditMode?: boolean,account?: WebDavAccount) {
-     WebDavAccountDialog({
+     RemoteDriveAccountDialog({
        isEditMode: isEditMode,
        isEditMode: isEditMode,
        account: account,
        account: account,
        onCancel: () => {
        onCancel: () => {
@@ -1651,36 +1652,40 @@ struct NewIndex {
            // 编辑模式:更新现有账户
            // 编辑模式:更新现有账户
            this.webdavManager.editAccount(account).then(() => {
            this.webdavManager.editAccount(account).then(() => {
              ToastUtil.showToast('修改成功')
              ToastUtil.showToast('修改成功')
-             // 重新加载WebDAV账户列表,观察着模式去更新了,所以这里不需要更新,先注释掉
+             // 重新加载网盘账户列表,观察着模式去更新了,所以这里不需要更新,先注释掉
              // this.loadWebDavAccounts()
              // this.loadWebDavAccounts()
-             LogUtil.info('heanup NewIndex', 'WebDAV账户修改成功:', account.name)
+             LogUtil.info('heanup NewIndex', '网盘账户修改成功:', account.name)
            }).catch((error: Error) => {
            }).catch((error: Error) => {
-             LogUtil.error('heanup NewIndex', `修改WebDAV账户失败: ${error.message}`)
+             LogUtil.error('heanup NewIndex', `修改网盘账户失败: ${error.message}`)
              ToastUtil.showToast('修改失败')
              ToastUtil.showToast('修改失败')
            })
            })
          } else {
          } else {
            // 添加模式:创建新账户
            // 添加模式:创建新账户
-           this.webdavManager.insertAccount(
-             account.name,
-             account.host,
-             account.localHost,
-             account.isUseLocalHost,
-             account.port,
-             account.filepath,
-             account.lyricFilePath,
-             account.uploadFilePath,
-             account.imageFilePath,
-             account.account,
-             account.password,
-             account.enableHttps,
-             account.coverPath
-           ).then(() => {
+          this.webdavManager.insertAccount(
+            account.name,
+            account.host,
+            account.localHost,
+            account.isUseLocalHost,
+            account.port,
+            account.filepath,
+            account.lyricFilePath,
+            account.uploadFilePath,
+            account.imageFilePath,
+            account.account,
+            account.password,
+            account.enableHttps,
+            account.coverPath,
+            account.webType,
+            account.smbShare,
+            account.smbDomain,
+            account.navidromeBasePath
+          ).then(() => {
              ToastUtil.showToast('添加成功')
              ToastUtil.showToast('添加成功')
-             // 重新加载WebDAV账户列表 观察着模式去更新了,所以这里不需要更新,先注释掉
+             // 重新加载网盘账户列表 观察着模式去更新了,所以这里不需要更新,先注释掉
              // this.loadWebDavAccounts()
              // this.loadWebDavAccounts()
-             LogUtil.info('heanup NewIndex', 'WebDAV账户添加成功:', account.name)
+             LogUtil.info('heanup NewIndex', '网盘账户添加成功:', account.name)
            }).catch((error: Error) => {
            }).catch((error: Error) => {
-             LogUtil.error('heanup NewIndex', `添加WebDAV账户失败: ${error.message}`)
+             LogUtil.error('heanup NewIndex', `添加网盘账户失败: ${error.message}`)
              ToastUtil.showToast('添加失败')
              ToastUtil.showToast('添加失败')
            })
            })
          }
          }
@@ -1713,4 +1718,3 @@ interface HiCarAspectRatio {
   context: Context;
   context: Context;
   playlistId: string;
   playlistId: string;
 }
 }
-

+ 56 - 1
entry/src/main/ets/pages/SettingPage.ets

@@ -1161,6 +1161,61 @@ export struct SettingPage {
           })
           })
           .padding(0)
           .padding(0)
 
 
+          // 网络工具
+          Column() {
+            Row() {
+              Text('网络工具')
+                .margin({ left: 18, right: 20 })
+                .fontSize(16)
+                .fontColor(Color.Gray)
+                .fontWeight(480)
+                .layoutWeight(1)
+            }
+            .height(48)
+
+            Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
+            Button({ type: ButtonType.Normal, stateEffect: true }) {
+              Row() {
+                Image($r('app.media.cloudDisk'))
+                  .width(22)
+                  .height(22)
+                  .alignSelf(ItemAlign.Center)
+                  .margin({ left: 15 })
+                Text('SMB 测试工具')
+                  .margin({ left: 8 })
+                  .fontSize(15)
+                  .fontColor(Color.Gray)
+                  .fontWeight(480)
+                  .layoutWeight(1)
+                Image($r('app.media.arrow_right'))
+                  .width(22)
+                  .height(22)
+                  .margin({ right: 18 })
+              }
+            }
+            .height(55)
+            .backgroundColor(Color.Transparent)
+            .clickEffect({ level: ClickEffectLevel.HEAVY })
+            .onClick(() => {
+              router.pushUrl({
+                url: 'pages/SmbTestPage'
+              });
+            })
+
+            Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
+          }
+          .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
+            .animation({ duration: 500, curve: Curve.Ease, delay: 260 }))
+          .backgroundColor($r('app.color.settings_background_main'))
+          .borderRadius(20)
+          .margin({
+            left: 0,
+            right: 0,
+            top: 0,
+            bottom: 10
+          })
+          .padding(0)
+
 
 
           // 音频设置分组
           // 音频设置分组
           Column() {
           Column() {
@@ -2556,4 +2611,4 @@ function chunkArray<T>(arr: Array<T>, size: number): Array<Array<T>> {
     result.push(row);
     result.push(row);
   }
   }
   return result;
   return result;
-}
+}

+ 563 - 0
entry/src/main/ets/pages/SmbTestPage.ets

@@ -0,0 +1,563 @@
+import { NativeSambaClient, NativeSambaSession as BridgeSession, NativeSambaTree as BridgeTree, NativeDirectoryEntry } from '../common/network/SmbBridge';
+import { router } from '@kit.ArkUI';
+import { ToastUtil } from '@pura/harmony-utils';
+import { CommonConstants } from '../common/constants/CommonConstants';
+import Logger from '../common/util/Logger';
+
+type ErrorPrimitive = string | number | boolean | null | undefined;
+
+type ErrorLike = Error | ErrorPrimitive | object;
+
+interface SambaAuthOptions {
+  domain?: string;
+  username: string;
+  password: string;
+}
+
+interface SambaSession {
+  connectTree: (share: string) => Promise<SambaTree>;
+  disconnect?: () => Promise<void> | void;
+}
+
+interface SambaTree {
+  readDirectory: (path: string) => Promise<SambaDirectoryEntry[]>;
+  close?: () => Promise<void> | void;
+}
+
+interface SambaDirectoryEntry {
+  name?: string;
+  fileName?: string;
+  isDirectory?: boolean;
+  isFile?: boolean;
+  size?: number;
+}
+
+interface ParsedSharePath {
+  host: string;
+  share: string;
+  basePath: string;
+}
+
+interface SambaClientInstance {
+  authenticate: (options: SambaAuthOptions) => Promise<SambaSession>;
+  close?: () => Promise<void> | void;
+}
+
+class NativeSambaTreeAdapter implements SambaTree {
+  private closed: boolean = false;
+  private readonly tree: BridgeTree;
+
+  constructor(tree: BridgeTree) {
+    this.tree = tree;
+  }
+
+  private ensureUsable(): void {
+    if (this.closed) {
+      throw new Error('SMB tree has been closed');
+    }
+  }
+
+  async readDirectory(path: string): Promise<SambaDirectoryEntry[]> {
+    this.ensureUsable();
+    const entries: NativeDirectoryEntry[] = await this.tree.readDirectory(path ?? '');
+    return entries.map((entry: NativeDirectoryEntry, index: number): SambaDirectoryEntry => ({
+      name: entry.name ?? entry.fileName ?? `entry-${index}`,
+      fileName: entry.fileName ?? entry.name,
+      isDirectory: Boolean(entry.isDirectory),
+      isFile: Boolean(entry.isFile),
+      size: entry.size
+    }));
+  }
+
+  async close(): Promise<void> {
+    if (this.closed) {
+      return;
+    }
+    await this.tree.close();
+    this.closed = true;
+  }
+}
+
+class NativeSambaSessionAdapter implements SambaSession {
+  private closed: boolean = false;
+  private readonly session: BridgeSession;
+
+  constructor(session: BridgeSession) {
+    this.session = session;
+  }
+
+  private ensureUsable(): void {
+    if (this.closed) {
+      throw new Error('SMB session has been closed');
+    }
+  }
+
+  async connectTree(share: string): Promise<SambaTree> {
+    this.ensureUsable();
+    const tree = await this.session.connectTree(share);
+    return new NativeSambaTreeAdapter(tree);
+  }
+
+  async disconnect(): Promise<void> {
+    if (this.closed) {
+      return;
+    }
+    await this.session.disconnect();
+    this.closed = true;
+  }
+}
+
+class NativeSambaClientAdapter implements SambaClientInstance {
+  private closed: boolean = false;
+  private readonly client: NativeSambaClient;
+  private readonly host: string;
+
+  constructor(host: string) {
+    this.host = host;
+    this.client = new NativeSambaClient(host);
+  }
+
+  private ensureUsable(): void {
+    if (this.closed) {
+      throw new Error('SMB client has been closed');
+    }
+  }
+
+  async authenticate(options: SambaAuthOptions): Promise<SambaSession> {
+    this.ensureUsable();
+    const session = await this.client.authenticate({
+      host: this.host,
+      share: '',
+      username: options.username,
+      password: options.password,
+      domain: options.domain
+    });
+    return new NativeSambaSessionAdapter(session);
+  }
+
+  async close(): Promise<void> {
+    if (this.closed) {
+      return;
+    }
+    await this.client.close();
+    this.closed = true;
+  }
+}
+
+@Entry
+@Component
+export struct SmbTestPage {
+  @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
+  @State sharePath: string = '\\\\192.168.11.170\\share';
+  @State domain: string = 'WORKGROUP';
+  @State username: string = 'chendeben';
+  @State password: string = 'chen384626WYT';
+  @State targetPath: string = '';
+  @State directoryEntries: string[] = [];
+  @State logMessages: string[] = [];
+  @State isBusy: boolean = false;
+
+  build() {
+    Column() {
+      this.buildTopBar();
+      Scroll() {
+        Column({ space: 16 }) {
+          this.buildDescription();
+          this.buildConnectionForm();
+          this.buildListAction();
+          this.buildDirectoryResult();
+          this.buildLogSection();
+        }
+        .padding({ left: 20, right: 20, bottom: 24 })
+      }
+      .scrollBar(BarState.Off)
+    }
+    .backgroundColor($r('app.color.index_background'))
+    .width('100%')
+    .height('100%')
+  }
+
+  @Builder
+  private buildTopBar() {
+    Row() {
+      Button('返回')
+        .type(ButtonType.Capsule)
+        .backgroundColor(this.themeColor)
+        .fontColor(Color.White)
+        .fontSize(15)
+        .padding({ left: 18, right: 18, top: 8, bottom: 8 })
+        .onClick(() => {
+          router.back();
+        })
+      Text('SMB 测试工具')
+        .fontSize(18)
+        .fontWeight(FontWeight.Medium)
+        .margin({ left: 12 })
+        .fontColor($r('app.color.text_color'))
+      Blank()
+    }
+    .padding({ left: 20, right: 20, top: 24, bottom: 12 })
+    .width('100%')
+  }
+
+  @Builder
+  private buildDescription() {
+    Column({ space: 6 }) {
+      Text('依赖 libsmb2 native 库,通过 SMB2 协议连接 NAS 并列出目录。')
+        .fontSize(14)
+        .fontColor($r('app.color.text_color'))
+      Text('共享路径示例: \\\\192.168.1.100\\music\\albums')
+        .fontSize(12)
+        .fontColor(Color.Gray)
+    }
+  }
+
+  @Builder
+  private buildConnectionForm() {
+    Column({ space: 14 }) {
+      this.buildInputItem('共享路径', '例如:\\\\192.168.1.100\\music', this.sharePath, (value: string) => {
+        this.sharePath = value;
+      })
+      this.buildInputItem('域(可选)', '例如:WORKGROUP', this.domain, (value: string) => {
+        this.domain = value;
+      })
+      this.buildInputItem('用户名', '请输入用户名', this.username, (value: string) => {
+        this.username = value;
+      })
+      this.buildPasswordItem()
+    }
+  }
+
+  @Builder
+  private buildListAction() {
+    Column({ space: 12 }) {
+      this.buildInputItem('目录路径', '留空表示共享根目录', this.targetPath, (value: string) => {
+        this.targetPath = value;
+      })
+      Button('列出目录')
+        .type(ButtonType.Capsule)
+        .backgroundColor(this.themeColor)
+        .fontColor(Color.White)
+        .enabled(!this.isBusy)
+        .onClick(() => {
+          void this.listDirectory();
+        })
+    }
+  }
+
+  @Builder
+  private buildDirectoryResult() {
+    Column({ space: 8 }) {
+      Text('目录结果')
+        .fontSize(15)
+        .fontWeight(FontWeight.Medium)
+        .fontColor($r('app.color.text_color'))
+      if (this.directoryEntries.length === 0) {
+        Text('暂无数据,点击“列出目录”开始测试。')
+          .fontSize(12)
+          .fontColor(Color.Gray)
+      } else {
+        Column({ space: 4 }) {
+          ForEach(this.directoryEntries, (line: string) => {
+            Text(line)
+              .fontSize(13)
+              .fontColor(Color.Gray)
+          })
+        }
+        .backgroundColor($r('app.color.input_background'))
+        .borderRadius(10)
+        .padding({ left: 12, right: 12, top: 10, bottom: 10 })
+      }
+    }
+  }
+
+  @Builder
+  private buildLogSection() {
+    Column({ space: 8 }) {
+      Text('调试输出')
+        .fontSize(15)
+        .fontWeight(FontWeight.Medium)
+        .fontColor($r('app.color.text_color'))
+      Scroll() {
+        Column({ space: 6 }) {
+          if (this.logMessages.length === 0) {
+            Text('暂无日志,执行操作后查看输出。')
+              .fontSize(12)
+              .fontColor(Color.Gray)
+          } else {
+            ForEach(this.logMessages, (line: string) => {
+              Text(line)
+                .fontSize(12)
+                .fontColor(Color.Gray)
+            })
+          }
+        }
+        .padding({ top: 6, bottom: 6 })
+      }
+      .height(220)
+      .backgroundColor($r('app.color.input_background'))
+      .borderRadius(12)
+    }
+  }
+
+  @Builder
+  private buildInputItem(label: string, placeholder: string, value: string, onChange: (value: string) => void) {
+    Column({ space: 6 }) {
+      Text(label)
+        .fontSize(14)
+        .fontColor($r('app.color.text_color'))
+      TextInput({
+        text: value,
+        placeholder: placeholder
+      })
+        .width('100%')
+        .height(36)
+        .fontSize(14)
+        .backgroundColor($r('app.color.input_background'))
+        .borderRadius(8)
+        .padding({ left: 10, right: 10 })
+        .enabled(!this.isBusy)
+        .onChange((val: string) => {
+          onChange(val);
+        })
+    }
+  }
+
+  @Builder
+  private buildPasswordItem() {
+    Column({ space: 6 }) {
+      Text('密码')
+        .fontSize(14)
+        .fontColor($r('app.color.text_color'))
+      TextInput({
+        text: this.password,
+        placeholder: '请输入密码'
+      })
+        .width('100%')
+        .height(36)
+        .type(InputType.Password)
+        .fontSize(14)
+        .backgroundColor($r('app.color.input_background'))
+        .borderRadius(8)
+        .padding({ left: 10, right: 10 })
+        .enabled(!this.isBusy)
+        .onChange((val: string) => {
+          this.password = val;
+        })
+    }
+  }
+
+  private async listDirectory(): Promise<void> {
+    const normalizedPath = this.normalizeRemotePath(this.targetPath);
+    this.appendLog(`准备列出目录,用户输入: ${normalizedPath || '[root]'}`);
+    const parsedShare = this.parseSharePath(this.sharePath);
+    if (!parsedShare) {
+      ToastUtil.showToast('共享路径格式不正确,请检查');
+      return;
+    }
+    await this.runWithConnection(parsedShare, async (tree: SambaTree, basePath: string) => {
+      const shareRelativePath = this.combineShareRelativePath(parsedShare.basePath, normalizedPath);
+      const remotePath = this.combineRemotePath(basePath, normalizedPath);
+      const displayPath = remotePath === '/' ? '[root]' : remotePath;
+      this.appendLog(`开始列出目录: ${displayPath}`);
+      Logger.info('heanup SMB', `开始列出目录: ${displayPath}`);
+      Logger.info('heanup SMB', `readDirectory inputs => base:${parsedShare.basePath || '[root]'} relative:${normalizedPath || '[root]'} effective:${shareRelativePath || '[root]'}`);
+      const entries = await tree.readDirectory(shareRelativePath);
+      Logger.info('heanup SMB', `目录返回 ${entries.length} 项`);
+      const formatted = entries.map((entry: SambaDirectoryEntry, index: number) => {
+        const label = this.describeEntry(entry, index);
+        Logger.info('heanup SMB', `目录项: ${label}`);
+        return label;
+      });
+      this.directoryEntries = formatted;
+      if (formatted.length === 0) {
+        this.appendLog('目录为空');
+      } else {
+        this.appendLog(`目录列出完成,数量: ${formatted.length}`);
+      }
+    });
+  }
+
+  private async runWithConnection(parsedShare: ParsedSharePath, task: (tree: SambaTree, basePath: string) => Promise<void>): Promise<void> {
+    if (!this.validateCredentials()) {
+      return;
+    }
+    let client: SambaClientInstance | undefined;
+    let session: SambaSession | undefined;
+    this.isBusy = true;
+    this.appendLog(`连接 NAS: ${parsedShare.host}/${parsedShare.share}`);
+    try {
+      client = this.createClient(parsedShare.host);
+      const authOptions = this.buildAuthOptions();
+      session = await client.authenticate(authOptions);
+      if (!session) {
+        throw new Error('SMB 认证失败,session 不存在');
+      }
+      const tree = await session.connectTree(parsedShare.share);
+      this.appendLog('连接成功,开始执行 SMB 操作');
+      await task(tree, parsedShare.basePath);
+      await this.safeClose(() => tree.close?.());
+      this.appendLog('操作完成,准备释放连接');
+    } catch (error) {
+      const message = this.errorMessage(error as ErrorLike);
+      this.appendLog(`操作失败: ${message}`);
+      Logger.error('heanup SMB', `操作失败: ${message}`);
+      ToastUtil.showToast(`操作失败: ${message}`);
+    } finally {
+      await this.safeClose((): Promise<void> | void => session?.disconnect?.());
+      await this.safeClose((): Promise<void> | void => client?.close?.());
+      this.isBusy = false;
+      this.appendLog('操作流程结束,恢复可操作状态');
+    }
+  }
+
+  private createClient(host: string): SambaClientInstance {
+    return new NativeSambaClientAdapter(host);
+  }
+
+  private buildAuthOptions(): SambaAuthOptions {
+    const credentials: SambaAuthOptions = {
+      username: this.username.trim(),
+      password: this.password
+    };
+    const domainValue = this.domain.trim();
+    if (domainValue.length > 0) {
+      credentials.domain = domainValue;
+    }
+    return credentials;
+  }
+
+  private parseSharePath(rawValue: string): ParsedSharePath | undefined {
+    const trimmed = rawValue.trim();
+    if (!trimmed) {
+      return undefined;
+    }
+    const cleaned = trimmed.replace(/^\\\\+/, '').replace(/^\/+/, '');
+    const parts = cleaned.split(/\\|\//).filter(part => part.length > 0);
+    if (parts.length < 2) {
+      return undefined;
+    }
+    const host = parts[0];
+    const share = parts[1];
+    const rest = parts.slice(2);
+    return {
+      host,
+      share,
+      basePath: rest.join('/')
+    };
+  }
+
+  private combineRemotePath(basePath: string, relativePath: string): string {
+    const segments: string[] = [];
+    if (basePath) {
+      segments.push(basePath);
+    }
+    if (relativePath) {
+      segments.push(relativePath);
+    }
+    if (segments.length === 0) {
+      return '/';
+    }
+    return `/${segments.join('/')}`;
+  }
+
+  private combineShareRelativePath(basePath: string, relativePath: string): string {
+    const segments: string[] = [];
+    if (basePath) {
+      segments.push(basePath);
+    }
+    if (relativePath) {
+      segments.push(relativePath);
+    }
+    return segments.join('/');
+  }
+
+  private normalizeRemotePath(input: string | undefined): string {
+    if (!input) {
+      return '';
+    }
+    const trimmed = input.trim();
+    if (!trimmed || trimmed === '/' || trimmed === '\\') {
+      return '';
+    }
+    const cleaned = trimmed.replace(/^[\\/]+/, '').replace(/\\/g, '/');
+    return cleaned.replace(/\/{2,}/g, '/');
+  }
+
+  private describeEntry(entry: SambaDirectoryEntry, index: number): string {
+    const name = entry.name ?? entry.fileName ?? `未命名-${index}`;
+    const typeLabel = entry.isDirectory ? '[目录]' : '[文件]';
+    const size = typeof entry.size === 'number' ? ` (${entry.size}B)` : '';
+    return `${typeLabel} ${name}${size}`;
+  }
+
+  private validateCredentials(): boolean {
+    if (!this.username.trim()) {
+      ToastUtil.showToast('请填写用户名');
+      return false;
+    }
+    if (!this.password) {
+      ToastUtil.showToast('请填写密码');
+      return false;
+    }
+    return true;
+  }
+
+  private appendLog(message: string): void {
+    const time = this.formatTime(new Date());
+    const line = `[${time}] ${message}`;
+    const updated = [...this.logMessages, line];
+    const limit = 200;
+    if (updated.length > limit) {
+      updated.splice(0, updated.length - limit);
+    }
+    this.logMessages = updated;
+  }
+
+  private formatTime(date: Date): string {
+    const hours = date.getHours().toString().padStart(2, '0');
+    const minutes = date.getMinutes().toString().padStart(2, '0');
+    const seconds = date.getSeconds().toString().padStart(2, '0');
+    return `${hours}:${minutes}:${seconds}`;
+  }
+
+  private async safeClose(action?: () => Promise<void> | void): Promise<void> {
+    if (!action) {
+      return;
+    }
+    try {
+      const result = action();
+      if (this.isPromise(result)) {
+        await result;
+      }
+    } catch (error) {
+      Logger.warn('heanup SMB', `资源释放时出现异常: ${this.errorMessage(error as ErrorLike)}`);
+    }
+  }
+
+  private isPromise(value: Promise<void> | void): boolean {
+    return typeof value === 'object' && value !== null && typeof (value as Promise<void>).then === 'function';
+  }
+
+  private errorMessage(error: ErrorLike): string {
+    if (error instanceof Error) {
+      return error.message;
+    }
+    if (typeof error === 'string') {
+      return error;
+    }
+    if (typeof error === 'number' || typeof error === 'boolean') {
+      return String(error);
+    }
+    if (error && typeof error === 'object') {
+      try {
+        return JSON.stringify(error);
+      } catch (serializationError) {
+        const message = serializationError instanceof Error ? serializationError.message : '未知错误';
+        Logger.warn('heanup SMB', `序列化错误: ${message}`);
+        return '未知错误';
+      }
+    }
+    return '未知错误';
+  }
+}

+ 22 - 14
entry/src/main/ets/pages/WebDavMainPage.ets

@@ -1,7 +1,7 @@
-import { WebdavManager } from '../common/util/WebdavManager';
+import { RemoteDriveManager } from '../common/util/RemoteDriveManager';
 import { WebDavAccount } from '../viewmodel/WebDavAccount';
 import { WebDavAccount } from '../viewmodel/WebDavAccount';
 import { Song } from '../viewmodel/Song';
 import { Song } from '../viewmodel/Song';
-import { WebdavManagerStates } from '../common/enums/WebdavManagerStates';
+import { RemoteDriveManagerStates } from '../common/enums/RemoteDriveManagerStates';
 import Logger from '../common/util/Logger';
 import Logger from '../common/util/Logger';
 import { promptAction, router, window } from '@kit.ArkUI';
 import { promptAction, router, window } from '@kit.ArkUI';
 import { CommonConstants } from '../common/constants/CommonConstants';
 import { CommonConstants } from '../common/constants/CommonConstants';
@@ -14,6 +14,8 @@ import { EventConstants } from '../common/constants/EventConstants';
 import { LazyDataSource } from '../common/util/LazyDataSource';
 import { LazyDataSource } from '../common/util/LazyDataSource';
 import { PreferencesUtil } from '@pura/harmony-utils';
 import { PreferencesUtil } from '@pura/harmony-utils';
 import { ButtonFancyModifier, ShadowModifier, SymbolGlyphFancyModifier } from '../common/util/AttributeModifierUtil';
 import { ButtonFancyModifier, ShadowModifier, SymbolGlyphFancyModifier } from '../common/util/AttributeModifierUtil';
+import { getRemoteDriveAccountLabel, getRemoteDriveDisplayLabel, getRemoteDrivePlaylistPrefix } from '../common/util/RemoteDriveLabel';
+import { RemoteDriveType } from '../common/enums/RemoteDriveType';
 
 
 /**
 /**
  * 歌单播放事件数据
  * 歌单播放事件数据
@@ -62,7 +64,7 @@ function decodeUrlEncodedString(encodedStr: string): string {
 @Component
 @Component
 export struct WebDavMainPage {
 export struct WebDavMainPage {
   @StorageProp('topSafeHeight') topSafeHeight: number = 0;
   @StorageProp('topSafeHeight') topSafeHeight: number = 0;
-  @State webdavManager: WebdavManager = WebdavManager.getInstance();
+  @State webdavManager: RemoteDriveManager = RemoteDriveManager.getInstance();
   @State accounts: WebDavAccount[] = [];
   @State accounts: WebDavAccount[] = [];
   @Prop @Watch('onSwitchAccount') selectedAccount: WebDavAccount;
   @Prop @Watch('onSwitchAccount') selectedAccount: WebDavAccount;
   @State songs: VideoItem[] = [];
   @State songs: VideoItem[] = [];
@@ -114,6 +116,12 @@ export struct WebDavMainPage {
       }
       }
 
 
       const allFolders = this.webDavFiles.filter(f => f.isDirectory);
       const allFolders = this.webDavFiles.filter(f => f.isDirectory);
+      const isNavAccount = this.selectedAccount?.webType === RemoteDriveType.Navidrome;
+      if (isNavAccount) {
+        const sortedNavFolders = allFolders.sort((a, b) => a.fileName.localeCompare(b.fileName));
+        this.visibleFoldersState = sortedNavFolders;
+        return;
+      }
       const visible: FileInfo[] = [];
       const visible: FileInfo[] = [];
 
 
       for (let i = 0; i < allFolders.length; i++) {
       for (let i = 0; i < allFolders.length; i++) {
@@ -184,7 +192,7 @@ export struct WebDavMainPage {
   // 处理WebDAV事件
   // 处理WebDAV事件
   private handleWebdavEvent(event: string): void {
   private handleWebdavEvent(event: string): void {
     switch (event) {
     switch (event) {
-      case WebdavManagerStates.LoadFilesInfoSucceed:
+      case RemoteDriveManagerStates.LoadFilesInfoSucceed:
         this.songs = this.webdavManager.webDavSongs;
         this.songs = this.webdavManager.webDavSongs;
         this.updateListData(this.songs)
         this.updateListData(this.songs)
         // 直接引用webdavManager的数组,避免@Observed序列化问题
         // 直接引用webdavManager的数组,避免@Observed序列化问题
@@ -198,13 +206,13 @@ export struct WebDavMainPage {
         //   message: '加载成功: ' + this.webDavFiles.filter(f => f.isDirectory).length + '个文件夹, ' + this.songs.length + '首歌曲'
         //   message: '加载成功: ' + this.webDavFiles.filter(f => f.isDirectory).length + '个文件夹, ' + this.songs.length + '首歌曲'
         // });
         // });
         break;
         break;
-      case WebdavManagerStates.LoadFilesInfoFailed:
+      case RemoteDriveManagerStates.LoadFilesInfoFailed:
         this.isLoading = false;
         this.isLoading = false;
         this.getUIContext().getPromptAction().showToast({ message: '加载失败' });
         this.getUIContext().getPromptAction().showToast({ message: '加载失败' });
         break;
         break;
-      case WebdavManagerStates.InsertAccountSucceed:
-      case WebdavManagerStates.EditAccountSucceed:
-      case WebdavManagerStates.RemoveAccountSucceed:
+      case RemoteDriveManagerStates.InsertAccountSucceed:
+      case RemoteDriveManagerStates.EditAccountSucceed:
+      case RemoteDriveManagerStates.RemoveAccountSucceed:
         this.loadAccounts();
         this.loadAccounts();
         break;
         break;
     }
     }
@@ -322,8 +330,8 @@ export struct WebDavMainPage {
       const eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY };
       const eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY };
 
 
       const playlistData: PlaylistEventData = {
       const playlistData: PlaylistEventData = {
-        playlistId: 'webdav-playlist', // 使用特殊的ID标识WebDAV播放列表
-        playlistName: 'WebDAV - ' + (this.selectedAccount?.name || '未知账户'),
+        playlistId: 'webdav-playlist', // 使用特殊的ID标识网盘播放列表
+        playlistName: `${getRemoteDriveDisplayLabel(this.selectedAccount?.webType)} - ${this.selectedAccount?.name || '未知账户'}`,
         songCount: this.songs.length,
         songCount: this.songs.length,
         startIndex: index,
         startIndex: index,
         songFilePaths: songFilePaths
         songFilePaths: songFilePaths
@@ -358,7 +366,7 @@ export struct WebDavMainPage {
     try {
     try {
       Logger.info(TAG, 'heanup 一键创建歌单开始');
       Logger.info(TAG, 'heanup 一键创建歌单开始');
       if (!this.selectedAccount || !this.selectedAccount.id) {
       if (!this.selectedAccount || !this.selectedAccount.id) {
-        this.getUIContext().getPromptAction().showToast({ message: '请先选择WebDAV账户' });
+        this.getUIContext().getPromptAction().showToast({ message: `请先选择${getRemoteDriveAccountLabel()}` });
         return;
         return;
       }
       }
       if (!this.songs || this.songs.length === 0) {
       if (!this.songs || this.songs.length === 0) {
@@ -382,7 +390,7 @@ export struct WebDavMainPage {
       // 构建歌单名称:账户名 + 当前路径(简化)
       // 构建歌单名称:账户名 + 当前路径(简化)
       const rawPath = this.webdavManager.currentPath || '/';
       const rawPath = this.webdavManager.currentPath || '/';
       const shortPath = rawPath === '/' ? '根目录' : rawPath.replace(/\/$/, '').split('/').slice(-1)[0];
       const shortPath = rawPath === '/' ? '根目录' : rawPath.replace(/\/$/, '').split('/').slice(-1)[0];
-      const playlistName = `WebDAV-${this.selectedAccount.name}-${shortPath}`;
+      const playlistName = `${getRemoteDrivePlaylistPrefix(this.selectedAccount.webType)}-${this.selectedAccount.name}-${shortPath}`;
 
 
       // 创建歌单
       // 创建歌单
       // 安全获取HostContext
       // 安全获取HostContext
@@ -436,7 +444,7 @@ export struct WebDavMainPage {
         return;
         return;
       }
       }
       Logger.info(TAG, `heanup 准备创建歌单: ${playlistName}`);
       Logger.info(TAG, `heanup 准备创建歌单: ${playlistName}`);
-      const created = await playlistTable.createPlaylist(playlistName, `来自WebDAV账户: ${this.selectedAccount.name} 路径: ${rawPath}`);
+      const created = await playlistTable.createPlaylist(playlistName, `来自${getRemoteDriveDisplayLabel(this.selectedAccount.webType)}: ${this.selectedAccount.name} 路径: ${rawPath}`);
       if (!created) {
       if (!created) {
         this.getUIContext().getPromptAction().showToast({ message: '歌单创建失败' });
         this.getUIContext().getPromptAction().showToast({ message: '歌单创建失败' });
         return;
         return;
@@ -558,7 +566,7 @@ export struct WebDavMainPage {
         .height(120)
         .height(120)
         .opacity(0.3)
         .opacity(0.3)
 
 
-      Text('暂无WebDAV账户')
+      Text(`暂无${getRemoteDriveAccountLabel()}`)
         .fontSize(16)
         .fontSize(16)
         .fontColor($r('app.color.index_tab_font_color'))
         .fontColor($r('app.color.index_tab_font_color'))
         .opacity(0.6)
         .opacity(0.6)

+ 446 - 56
entry/src/main/ets/view/LocalMusic.ets

@@ -79,6 +79,9 @@ import { ringtone } from '@kit.RingtoneKit';
 import { BreakpointType, BreakpointTypeEnum } from '../common/util/BreakpointSystem';
 import { BreakpointType, BreakpointTypeEnum } from '../common/util/BreakpointSystem';
 import { resourceManager } from '@kit.LocalizationKit';
 import { resourceManager } from '@kit.LocalizationKit';
 import { deviceInfo } from '@kit.BasicServicesKit';
 import { deviceInfo } from '@kit.BasicServicesKit';
+import { ensureSmbFileCached } from '../common/network/SmbFileCache';
+import { findWebDavCacheIfExists, triggerWebDavCacheDownload } from '../common/network/WebDavFileCache';
+import { navidromeApi } from '../common/network/NavidromeApi';
 
 
 import { HSBColorPicker, HSBColorPickerLayout } from '@keke/color-picker'
 import { HSBColorPicker, HSBColorPickerLayout } from '@keke/color-picker'
 import app from '@system.app';
 import app from '@system.app';
@@ -98,9 +101,9 @@ import { convertPlaylistSongsToVideoItems, emptyView,updateAllSongsSortOrder } f
 import { Playlist, PlaylistSong } from '../viewmodel/Playlist';
 import { Playlist, PlaylistSong } from '../viewmodel/Playlist';
 import { Song } from '../viewmodel/Song';
 import { Song } from '../viewmodel/Song';
 import { SongType } from '../common/enums/SongType';
 import { SongType } from '../common/enums/SongType';
-import { WebdavManager, WebDavAuthInfo as WebDavManagerAuthInfo,
+import { RemoteDriveManager, WebDavAuthInfo as WebDavManagerAuthInfo,
   buildHttpHeadersWithWebDav,
   buildHttpHeadersWithWebDav,
-  WebDavAuthItem} from '../common/util/WebdavManager';
+  WebDavAuthItem} from '../common/util/RemoteDriveManager';
 import PermissionUtil from '../common/util/PermissionUtil'
 import PermissionUtil from '../common/util/PermissionUtil'
 import { EditAudio, setRingTone } from './EditAudio';
 import { EditAudio, setRingTone } from './EditAudio';
 import { ExtractAccompaniment } from './ExtractAccompaniment';
 import { ExtractAccompaniment } from './ExtractAccompaniment';
@@ -147,11 +150,216 @@ function getTypeOrder(type: number) {
       return 2; // Middle
       return 2; // Middle
     case CommonConstants.TYPE_LOCAL:
     case CommonConstants.TYPE_LOCAL:
       return 3; // Last
       return 3; // Last
+    case CommonConstants.TYPE_WEBDAV:
+    case CommonConstants.TYPE_SMB:
+    case CommonConstants.TYPE_NAVIDROME:
+      return 3;
     default:
     default:
       return 4; // Unknown types, if any, go last
       return 4; // Unknown types, if any, go last
   }
   }
 }
 }
 
 
+function isWebDavType(type: number): boolean {
+  return type === CommonConstants.TYPE_WEBDAV;
+}
+
+function isSmbType(type: number): boolean {
+  return type === CommonConstants.TYPE_SMB;
+}
+
+function isNavidromeType(type: number): boolean {
+  return type === CommonConstants.TYPE_NAVIDROME;
+}
+
+function isRemoteCloudType(type: number): boolean {
+  return isWebDavType(type) || isSmbType(type) || isNavidromeType(type);
+}
+
+function getShareNameFromFilePath(filePath?: string): string | undefined {
+  if (!filePath) {
+    return undefined;
+  }
+  const match = filePath.match(/^smb:\/\/[^/]+\/([^/]+)/i);
+  return match && match[1] ? match[1] : undefined;
+}
+
+function removeSharePrefixFromPath(path: string, primaryShare?: string, secondaryShare?: string): string {
+  const uniqueShares: string[] = [];
+  [primaryShare, secondaryShare].forEach((name?: string) => {
+    const cleaned = name ? name.replace(/^\/+|\/+$/g, '') : '';
+    if (cleaned.length > 0 && !uniqueShares.some(item => item.toLowerCase() === cleaned.toLowerCase())) {
+      uniqueShares.push(cleaned);
+    }
+  });
+  if (uniqueShares.length === 0) {
+    return path;
+  }
+  let result = path;
+  for (let i = 0; i < uniqueShares.length; i++) {
+    const share = uniqueShares[i];
+    const lowerPath = result.toLowerCase();
+    const lowerShare = share.toLowerCase();
+    if (lowerPath === lowerShare) {
+      result = '';
+      break;
+    }
+    const prefix = `${lowerShare}/`;
+    if (lowerPath.startsWith(prefix)) {
+      result = result.substring(share.length + 1);
+      break;
+    }
+  }
+  return result;
+}
+
+function extractSmbRelativePath(song: VideoItem, shareNameOverride?: string): string {
+  const shareFromFilePath = getShareNameFromFilePath(song.filePath);
+  const shareName = shareNameOverride ?? shareFromFilePath;
+  if (song.remote_rel_path && song.remote_rel_path.length > 0) {
+    const cleaned = song.remote_rel_path.replace(/^\/+/, '');
+    return removeSharePrefixFromPath(cleaned, shareName, shareFromFilePath);
+  }
+  if (!song.filePath) {
+    return '';
+  }
+  const match = song.filePath.match(/^smb:\/\/[^/]+\/([^/]+)(\/.*)?$/i);
+  if (!match) {
+    return song.filePath.replace(/^\/+/, '');
+  }
+  const remainder = match[2] ? match[2].replace(/^\/+/, '') : '';
+  if (remainder.length === 0) {
+    return '';
+  }
+  return remainder;
+}
+
+const workerInstance = new worker.ThreadWorker("entry/ets/workers/Worker.ets");
+
+interface MetadataExtractionOptions {
+  context?: common.Context;
+  autoParseMusicName?: boolean;
+}
+
+interface WorkerMetadataPayload {
+  originFilePath: string;
+  originId?: string;
+  bit_rate?: string;
+  duration?: string;
+  sampleRate?: string;
+  mimeType?: string;
+  md5Str?: string;
+  videoSize?: number;
+  size?: string;
+  pixelMapPath?: string;
+  artist?: string;
+  album?: string;
+  name?: string;
+  error?: string;
+}
+
+const metadataExtractionInFlight: Set<string> = new Set();
+
+function scheduleMetadataExtractionFromCache(
+  song: VideoItem,
+  cachePath: string,
+  options?: MetadataExtractionOptions
+): void {
+  if (!song || !cachePath || !song.filePath || !options || !options.context) {
+    return;
+  }
+  if (!isRemoteCloudType(song.type ?? -1)) {
+    return;
+  }
+  if (metadataExtractionInFlight.has(song.filePath)) {
+    return;
+  }
+  if (song.bit_rate && song.sampleRate && song.duration) {
+    return;
+  }
+  metadataExtractionInFlight.add(song.filePath);
+  workerInstance.postMessage({
+    code: 5,
+    data: options.context,
+    data2: cachePath,
+    data3: song.type,
+    data4: {
+      originFilePath: song.filePath,
+      originId: song.id,
+      autoParseMusicName: options.autoParseMusicName ?? false
+    }
+  });
+}
+
+function sanitizePlaybackUrl(rawUrl: string | null | undefined): string {
+  if (!rawUrl) {
+    return '';
+  }
+  try {
+    const decoded = decodeURI(rawUrl);
+    const encoded = encodeURI(decoded);
+    // encodeURI 不会处理 #,手动转义避免播放器把剩余内容当作片段
+    return encoded.replace(/#/g, '%23');
+  } catch (error) {
+    const err = error as Error;
+    Logger.warn(TAG, `URL 编码失败,使用降级方案: ${err?.message}`);
+    return rawUrl.replace(/ /g, '%20').replace(/#/g, '%23');
+  }
+}
+
+function cloneVideoItem(item: VideoItem): VideoItem {
+  const copy = new VideoItem(
+    item.name,
+    item.id,
+    item.filePath,
+    item.type,
+    item.videoSize,
+    item.cTime,
+    item.pixelMap,
+    item.size,
+    item.pixelMapPath,
+    item.artist,
+    item.album,
+    item.fileName,
+    item.lastPlayed
+  );
+  copy.parentPath = item.parentPath;
+  copy.isFav = item.isFav;
+  copy.pixelMap = item.pixelMap;
+  copy.size = item.size;
+  copy.pixelMapPath = item.pixelMapPath;
+  copy.artist = item.artist;
+  copy.album = item.album;
+  copy.duration = item.duration;
+  copy.mimeType = item.mimeType;
+  copy.sampleRate = item.sampleRate;
+  copy.trackCount = item.trackCount;
+  copy.lastPlayedStr = item.lastPlayedStr;
+  copy.playCount = item.playCount;
+  copy.lyricContent = item.lyricContent;
+  copy.md5Str = item.md5Str;
+  copy.extra_json = item.extra_json;
+  copy.pyStr = item.pyStr;
+  copy.bit_rate = item.bit_rate;
+  copy.probe_score = item.probe_score;
+  copy.year = item.year;
+  copy.nb_streams = item.nb_streams;
+  copy.nb_programs = item.nb_programs;
+  copy.genre = item.genre;
+  copy.track = item.track;
+  copy.bits_per_raw_sample = item.bits_per_raw_sample;
+  copy.channels = item.channels;
+  copy.channel_layout = item.channel_layout;
+  copy.start_time = item.start_time;
+  copy.ALBUMARTIST = item.ALBUMARTIST;
+  copy.COMPOSER = item.COMPOSER;
+  copy.LYRICIST = item.LYRICIST;
+  copy.COMMENT = item.COMMENT;
+  copy.disc = item.disc;
+  copy.webdav_account_id = item.webdav_account_id;
+  copy.remote_rel_path = item.remote_rel_path;
+  return copy;
+}
+
 /**
 /**
  * 异步设置videoUrl的辅助方法,处理WebDAV URL的构建
  * 异步设置videoUrl的辅助方法,处理WebDAV URL的构建
  * @param song 歌曲对象
  * @param song 歌曲对象
@@ -163,38 +371,108 @@ function getTypeOrder(type: number) {
  * @param song 歌曲对象
  * @param song 歌曲对象
  * @returns Promise<string> 完整的URL
  * @returns Promise<string> 完整的URL
  */
  */
-async function setVideoUrlForSong(song: VideoItem): Promise<string> {
-  if (song.type === CommonConstants.TYPE_WEBDAV && song.webdav_account_id) {
+async function setVideoUrlForSong(song: VideoItem, options?: MetadataExtractionOptions): Promise<string> {
+  const metadataOptions = options ?? {};
+  if (isWebDavType(song.type) && song.webdav_account_id) {
     try {
     try {
-      Logger.info(TAG, `WebDAV完整URL构建成功remote_rel_path: ${song.remote_rel_path}`);
-      Logger.info(TAG, `WebDAV完整URL构建成功song.filePath: ${song.filePath}`);
-      // 优先使用远端相对路径,其次回退到 filePath
+      Logger.info(TAG, `WebDAV 路径信息 remote_rel_path: ${song.remote_rel_path}`);
+      Logger.info(TAG, `WebDAV 路径信息 filePath: ${song.filePath}`);
       const relativePath = song.remote_rel_path || song.filePath;
       const relativePath = song.remote_rel_path || song.filePath;
-      const fullUrl = await WebDavUrlUtil.buildFullUrlByAccountId(song.webdav_account_id, relativePath);
-      if (fullUrl) {
-        Logger.info(TAG, `WebDAV完整URL构建成功: ${fullUrl}`);
-        return fullUrl.replace(/ /g, '%20');
-      } else {
-        Logger.error(TAG, `WebDAV URL构建失败,使用原始路径: ${relativePath}`);
-        return relativePath.replace(/ /g, '%20');
+      const manager = RemoteDriveManager.getInstance();
+      const account = await manager.getWebDavAccountById(song.webdav_account_id);
+
+      if (account) {
+        const cachePath = await findWebDavCacheIfExists(account, relativePath);
+        if (cachePath) {
+          Logger.info(TAG, `WebDAV 缓存命中,直接播放: ${cachePath}`);
+          scheduleMetadataExtractionFromCache(song, cachePath, metadataOptions);
+          return cachePath;
+        }
+
+        const fullUrlFromAccount = WebDavUrlUtil.buildFullUrl(account, relativePath);
+        if (fullUrlFromAccount) {
+          const sanitizedUrl = sanitizePlaybackUrl(fullUrlFromAccount);
+          const downloadPromise = triggerWebDavCacheDownload(account, relativePath, sanitizedUrl);
+          if (downloadPromise) {
+            downloadPromise.then((completedPath?: string | null) => {
+              if (completedPath) {
+                scheduleMetadataExtractionFromCache(song, completedPath, metadataOptions);
+              }
+            }).catch((error: Error) => {
+              Logger.error(TAG, `WebDAV 缓存后台下载失败: ${error.message}`);
+            });
+          }
+          Logger.info(TAG, `WebDAV 启动异步缓存: ${relativePath}`);
+          return sanitizedUrl;
+        }
+      }
+
+      const fallbackUrl = await WebDavUrlUtil.buildFullUrlByAccountId(song.webdav_account_id, relativePath);
+      if (fallbackUrl) {
+        Logger.warn(TAG, `WebDAV 使用全局查询到的URL: ${fallbackUrl}`);
+        return sanitizePlaybackUrl(fallbackUrl);
       }
       }
+
+      Logger.error(TAG, `WebDAV URL构建失败,使用原始路径: ${relativePath}`);
+      return sanitizePlaybackUrl(relativePath);
     } catch (error) {
     } catch (error) {
       Logger.error(TAG, `WebDAV URL构建出错: ${(error as Error).message}`);
       Logger.error(TAG, `WebDAV URL构建出错: ${(error as Error).message}`);
       const fallbackPath = song.remote_rel_path || song.filePath;
       const fallbackPath = song.remote_rel_path || song.filePath;
-      return fallbackPath.replace(/ /g, '%20');
+      return sanitizePlaybackUrl(fallbackPath);
     }
     }
-  } else {
-    // 非WebDAV文件或缺少webdav_account_id,直接使用原始路径
-    if (song.type === CommonConstants.TYPE_WEBDAV) {
-      Logger.warn(TAG, `WebDAV歌曲缺少webdav_account_id,使用原始路径: ${song.filePath}`);
+  }
+
+  if (isNavidromeType(song.type) && song.webdav_account_id) {
+    try {
+      const manager = RemoteDriveManager.getInstance();
+      const account = await manager.getWebDavAccountById(song.webdav_account_id);
+      if (!account) {
+        throw new Error('Navidrome账号不可用');
+      }
+      const navSongId = song.remote_rel_path || song.id || song.filePath;
+      const streamUrl = await navidromeApi.buildStreamUrl(account, navSongId);
+      Logger.info(TAG, `Navidrome 流地址构建成功: ${streamUrl}`);
+      return sanitizePlaybackUrl(streamUrl);
+    } catch (error) {
+      const err = error as Error;
+      Logger.error(TAG, `Navidrome URL构建失败: ${err.message}`);
+      throw new Error(err.message);
+    }
+  }
+
+  if (isSmbType(song.type) && song.webdav_account_id) {
+    try {
+      const manager = RemoteDriveManager.getInstance();
+      const account = await manager.getWebDavAccountById(song.webdav_account_id);
+      if (!account) {
+        throw new Error('SMB账号不可用');
+      }
+      const relativePath = extractSmbRelativePath(song, account.smbShare);
+      const cachedPath = await ensureSmbFileCached(account, relativePath);
+      Logger.info(TAG, `SMB 缓存路径: ${cachedPath}`);
+      scheduleMetadataExtractionFromCache(song, cachedPath, metadataOptions);
+      return cachedPath;
+    } catch (error) {
+      const err = error as Error;
+      Logger.error(TAG, `SMB 缓存失败: ${err.message}`);
+      throw new Error(err.message);
     }
     }
-    return song.type === CommonConstants.TYPE_WEBDAV ?
-      song.filePath.replace(/ /g, '%20') :
-      song.filePath;
   }
   }
+
+  if (isWebDavType(song.type)) {
+    Logger.warn(TAG, `WebDAV歌曲缺少webdav_account_id,使用原始路径: ${song.filePath}`);
+    return sanitizePlaybackUrl(song.filePath);
+  }
+  if (isSmbType(song.type)) {
+    Logger.warn(TAG, `SMB歌曲缺少webdav_account_id,使用原始路径: ${song.filePath}`);
+    return song.filePath;
+  }
+  if (isNavidromeType(song.type)) {
+    throw new Error('Navidrome歌曲缺少webdav_account_id,无法构建播放链接');
+  }
+  return song.filePath;
 }
 }
 
 
-const workerInstance = new worker.ThreadWorker("entry/ets/workers/Worker.ets");
 // 定义接口
 // 定义接口
 interface HiCarAspectRatio {
 interface HiCarAspectRatio {
   ratio: number;
   ratio: number;
@@ -1085,31 +1363,111 @@ export struct LocalMusic {
             .slice(0, 60);
             .slice(0, 60);
           PreferencesUtil.putSync('albumMap',  JSON.stringify(mapArrayAlbum));
           PreferencesUtil.putSync('albumMap',  JSON.stringify(mapArrayAlbum));
           break;
           break;
+        case 105: // 缓存完成后返回的元数据
+          this.handleWorkerMetadataPayload(e.data.data as WorkerMetadataPayload);
+          break;
 
 
       }
       }
     };
     };
 
 
     // 在调用terminate后,执行onexit
     // 在调用terminate后,执行onexit
     workerInstance.onexit = (code) => {
     workerInstance.onexit = (code) => {
-      console.log("main thread terminate");
+      metadataExtractionInFlight.clear();
+      Logger.info(TAG, 'workerInstance 线程已退出,code: ' + code);
     }
     }
-
-    //若Worker处于已销毁或正在销毁等非运行状态时,调用其功能接口,会抛出相应的错误。
-    // 使用Worker模块时,需要在主线程中注册onerror接口,否则当worker线程出现异常时会发生jscrash问题。
     workerInstance.onerror = (err: ErrorEvent) => {
     workerInstance.onerror = (err: ErrorEvent) => {
-
-      console.log("onerror" + err.message);
-
+      metadataExtractionInFlight.clear();
+      console.error('workerInstance 线程执行发生异常: ' + err.message)
     }
     }
+  }
 
 
-    // let context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
-    //碰一碰分享的监听
-    this.knockController = KnockController.getInstance(this.context);
-    this.knockController?.immersiveListening();
-
+  private handleWorkerMetadataPayload(payload: WorkerMetadataPayload | undefined): void {
+    if (!payload || !payload.originFilePath) {
+      return;
+    }
+    metadataExtractionInFlight.delete(payload.originFilePath);
+    if (payload.error) {
+      Logger.warn(TAG, `缓存文件元数据解析失败: ${payload.error}`);
+      return;
+    }
+    this.applyMetadataToCollections(payload.originFilePath, payload);
   }
   }
 
 
+  private applyMetadataToCollections(filePath: string, payload: WorkerMetadataPayload): void {
+    const targets: Array<VideoItem> = [];
+    if (this.currentSong && this.currentSong.filePath === filePath) {
+      targets.push(this.currentSong);
+    }
+    const collectFromList = (list?: Array<VideoItem>) => {
+      if (!list || list.length === 0) {
+        return;
+      }
+      const found = Utility.getItemByFilePath(list, filePath);
+      if (found) {
+        targets.push(found);
+      }
+    };
+    collectFromList(this.songList);
+    collectFromList(this.videoLocalList);
+    collectFromList(this.historyList);
+    collectFromList(this.favList);
+
+    const updated = new Set<VideoItem>();
+    for (let i = 0; i < targets.length; i++) {
+      const target = targets[i];
+      if (!target || updated.has(target)) {
+        continue;
+      }
+      updated.add(target);
+      if (payload.name) {
+        target.name = payload.name;
+      }
+      if (payload.artist) {
+        target.artist = payload.artist;
+      }
+      if (payload.album) {
+        target.album = payload.album;
+      }
+      if (payload.bit_rate) {
+        target.bit_rate = payload.bit_rate;
+      }
+      if (payload.duration) {
+        target.duration = payload.duration;
+      }
+      if (payload.sampleRate) {
+        target.sampleRate = payload.sampleRate;
+      }
+      if (payload.mimeType) {
+        target.mimeType = payload.mimeType;
+      }
+      if (payload.md5Str) {
+        target.md5Str = payload.md5Str;
+      }
+      if (payload.videoSize !== undefined) {
+        target.videoSize = payload.videoSize;
+      }
+      if (payload.size) {
+        target.size = payload.size;
+      }
+      if (payload.pixelMapPath) {
+        target.pixelMapPath = payload.pixelMapPath;
+      }
+    }
 
 
+    if (this.currentSong && this.currentSong.filePath === filePath) {
+      if (payload.name) {
+        this.name = payload.name;
+      }
+      if (payload.artist) {
+        this.artist = payload.artist;
+      }
+      if (payload.pixelMapPath) {
+        this.cover = payload.pixelMapPath;
+      }
+      this.currentSong = cloneVideoItem(this.currentSong);
+      AppStorage.setOrCreate('currentSong', this.currentSong);
+    }
+  }
 
 
   onPageHide(): void {
   onPageHide(): void {
     this.knockController?.immersiveDisableListening();
     this.knockController?.immersiveDisableListening();
@@ -1164,7 +1522,10 @@ export struct LocalMusic {
           this.currentSong = this.songList[0]
           this.currentSong = this.songList[0]
         }
         }
         // 使用辅助方法设置videoUrl,等待WebDAV URL构建完成
         // 使用辅助方法设置videoUrl,等待WebDAV URL构建完成
-        this.videoUrl = await setVideoUrlForSong(this.currentSong);
+        this.videoUrl = await setVideoUrlForSong(this.currentSong, {
+          context: this.context,
+          autoParseMusicName: this.autoParseMusicName
+        });
         Logger.info(TAG, `WebDAV URL同步构建完成: ${this.videoUrl}`);
         Logger.info(TAG, `WebDAV URL同步构建完成: ${this.videoUrl}`);
         this.name = this.currentSong.name
         this.name = this.currentSong.name
         this.cover = this.currentSong.pixelMapPath
         this.cover = this.currentSong.pixelMapPath
@@ -5986,8 +6347,10 @@ export struct LocalMusic {
         break;
         break;
 
 
       case CommonConstants.TYPE_WEBDAV:
       case CommonConstants.TYPE_WEBDAV:
-        // 处理网络音频播放(WebDAV)
-        Logger.info(`heanup 处理网络音频播放: ${item.name}, URL: ${item.filePath}`)
+      case CommonConstants.TYPE_SMB:
+      case CommonConstants.TYPE_NAVIDROME:
+        // 处理网络音频播放(WebDAV/SMB)
+        Logger.info(`heanup 处理云端音频播放: ${item.name}, URL: ${item.filePath}`)
 
 
         if (this.CONTROL_PlayStatus !== PlayStatus.INIT) {
         if (this.CONTROL_PlayStatus !== PlayStatus.INIT) {
           this.stop();
           this.stop();
@@ -6011,7 +6374,7 @@ export struct LocalMusic {
           Logger.info(`heanup 网络音频 isFromSonPlayList分支 - 当前播放列表长度: ${this.songList.length}, 当前索引: ${this.curIndex}`)
           Logger.info(`heanup 网络音频 isFromSonPlayList分支 - 当前播放列表长度: ${this.songList.length}, 当前索引: ${this.curIndex}`)
         } else {
         } else {
           // 从全局网络音频列表中查找
           // 从全局网络音频列表中查找
-          let globalVideoList = this.videoLocalList.filter(video => video.type === CommonConstants.TYPE_WEBDAV) as VideoItem[];
+          let globalVideoList = this.videoLocalList.filter(video => isRemoteCloudType(video.type)) as VideoItem[];
           this.curIndex = globalVideoList.findIndex(video => video.filePath === item.filePath);
           this.curIndex = globalVideoList.findIndex(video => video.filePath === item.filePath);
           if (this.curIndex === -1 && index !== undefined) {
           if (this.curIndex === -1 && index !== undefined) {
             this.curIndex = index;
             this.curIndex = index;
@@ -6025,7 +6388,10 @@ export struct LocalMusic {
         AppStorage.setOrCreate('currentSong', this.currentSong);
         AppStorage.setOrCreate('currentSong', this.currentSong);
 
 
         // 使用辅助方法设置videoUrl,等待WebDAV URL构建完成
         // 使用辅助方法设置videoUrl,等待WebDAV URL构建完成
-        this.videoUrl = await setVideoUrlForSong(this.currentSong);
+        this.videoUrl = await setVideoUrlForSong(this.currentSong, {
+          context: this.context,
+          autoParseMusicName: this.autoParseMusicName
+        });
         Logger.info(TAG, `WebDAV URL同步构建完成(分支2): ${this.videoUrl}`);
         Logger.info(TAG, `WebDAV URL同步构建完成(分支2): ${this.videoUrl}`);
 
 
         this.name = this.currentSong.name
         this.name = this.currentSong.name
@@ -6077,7 +6443,7 @@ export struct LocalMusic {
       onCancel:()=>{
       onCancel:()=>{
         this.getUIContext().getPromptAction().closeCustomDialog(this.cueComponentId)
         this.getUIContext().getPromptAction().closeCustomDialog(this.cueComponentId)
       },
       },
-      onDoItemClick:(item: CueTrack, index: number)=>{
+      onDoItemClick: async (item: CueTrack, index: number)=>{
         //获取cueinfo对应的filePath
         //获取cueinfo对应的filePath
         let globalVideoList = Utility.getGlobalList(this.videoLocalList, CommonConstants.TYPE_LOCAL) as VideoItem[];
         let globalVideoList = Utility.getGlobalList(this.videoLocalList, CommonConstants.TYPE_LOCAL) as VideoItem[];
         this.curIndex = Utility.getCurIndexFromGlobalList(globalVideoList, cueinfo.filePath)
         this.curIndex = Utility.getCurIndexFromGlobalList(globalVideoList, cueinfo.filePath)
@@ -6090,10 +6456,16 @@ export struct LocalMusic {
           }
           }
           this.songList = globalVideoList
           this.songList = globalVideoList
           this.sonDataSource.pushArrayData(this.songList)
           this.sonDataSource.pushArrayData(this.songList)
-          if(this.currentSong.type==CommonConstants.TYPE_WEBDAV){
-            this.videoUrl = this.currentSong.filePath.replace(/ /g, '%20');
-          }else{
-            this.videoUrl =  this.currentSong.filePath
+          if (this.currentSong) {
+            try {
+              this.videoUrl = await setVideoUrlForSong(this.currentSong, {
+                context: this.context,
+                autoParseMusicName: this.autoParseMusicName
+              });
+            } catch (error) {
+              Logger.error(TAG, `CUE 点播构建播放地址失败: ${(error as Error).message}`);
+              this.videoUrl = this.currentSong.filePath;
+            }
           }
           }
           this.name = item.title||this.currentSong.name
           this.name = item.title||this.currentSong.name
           this.cover = this.currentSong.pixelMapPath
           this.cover = this.currentSong.pixelMapPath
@@ -12221,7 +12593,7 @@ export struct LocalMusic {
 
 
   updateLastPlayTimeStr(filePath: string) {
   updateLastPlayTimeStr(filePath: string) {
     // 检查是否为网络音频(WebDAV),如果是则不更新本地数据库
     // 检查是否为网络音频(WebDAV),如果是则不更新本地数据库
-    if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_WEBDAV) {
+    if (this.currentSong && isRemoteCloudType(this.currentSong.type)) {
       Logger.info(`heanup 检测到网络音频播放,跳过数据库更新: ${filePath}`)
       Logger.info(`heanup 检测到网络音频播放,跳过数据库更新: ${filePath}`)
       return;
       return;
     }
     }
@@ -12429,11 +12801,11 @@ export struct LocalMusic {
     }
     }
 
 
     // 如果是WebDAV网络音频,使用webdav_account_id获取认证信息(等待完成后再设置一次性头部)
     // 如果是WebDAV网络音频,使用webdav_account_id获取认证信息(等待完成后再设置一次性头部)
-    if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_WEBDAV) {
+    if (this.currentSong && isRemoteCloudType(this.currentSong.type)) {
       Logger.info(`heanup WebDAV歌曲认证 - 歌曲名: ${this.currentSong.name}, webdav_account_id: ${this.currentSong.webdav_account_id || '未设置'}`);
       Logger.info(`heanup WebDAV歌曲认证 - 歌曲名: ${this.currentSong.name}, webdav_account_id: ${this.currentSong.webdav_account_id || '未设置'}`);
       if (this.currentSong.webdav_account_id) {
       if (this.currentSong.webdav_account_id) {
         try {
         try {
-          const webdavManager = WebdavManager.getInstance();
+          const webdavManager = RemoteDriveManager.getInstance();
           const webDavHeaders = await webdavManager.buildHttpHeadersWithAccountId(this.currentSong, this.videoUrl);
           const webDavHeaders = await webdavManager.buildHttpHeadersWithAccountId(this.currentSong, this.videoUrl);
           if (webDavHeaders && webDavHeaders.size > 0) {
           if (webDavHeaders && webDavHeaders.size > 0) {
             webDavHeaders.forEach((value, key) => headers.set(key, value));
             webDavHeaders.forEach((value, key) => headers.set(key, value));
@@ -12451,7 +12823,7 @@ export struct LocalMusic {
     }
     }
     // 统一设置带认证的请求头
     // 统一设置带认证的请求头
     this.mIjkMediaPlayer.setDataSourceHeader(headers);
     this.mIjkMediaPlayer.setDataSourceHeader(headers);
-    if (this.currentSong&&this.currentSong.type === CommonConstants.TYPE_WEBDAV) {
+    if (this.currentSong && isRemoteCloudType(this.currentSong.type)) {
       console.log(`heanup 为WebDAV播放设置IjkPlayer选项`);
       console.log(`heanup 为WebDAV播放设置IjkPlayer选项`);
       // 网络超时设置(单位:微秒,文档显示应该是字符串格式)
       // 网络超时设置(单位:微秒,文档显示应该是字符串格式)
       this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", "30000000"); // 30秒
       this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", "30000000"); // 30秒
@@ -12774,7 +13146,7 @@ export struct LocalMusic {
         LogUtils.getInstance().LOGI('heanup 播放错误,歌曲详情:' + JSON.stringify(this.currentSong))
         LogUtils.getInstance().LOGI('heanup 播放错误,歌曲详情:' + JSON.stringify(this.currentSong))
         // 检查是否为WebDAV播放错误
         // 检查是否为WebDAV播放错误
         let isWebDavError = false;
         let isWebDavError = false;
-        if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_WEBDAV && this.currentSong.filePath) {
+        if (this.currentSong && isRemoteCloudType(this.currentSong.type) && this.currentSong.filePath) {
           try {
           try {
             const globalContext = GlobalContext.getContext();
             const globalContext = GlobalContext.getContext();
             const webDavAuthInfo = globalContext.getObject('webDavAuthInfo') as WebDavAuthItem;
             const webDavAuthInfo = globalContext.getObject('webDavAuthInfo') as WebDavAuthItem;
@@ -13474,7 +13846,10 @@ export struct LocalMusic {
       Logger.info('heanup playNext', `直接使用歌曲列表中的信息: ${this.currentSong.name}, type: ${this.currentSong.type}`);
       Logger.info('heanup playNext', `直接使用歌曲列表中的信息: ${this.currentSong.name}, type: ${this.currentSong.type}`);
 
 
       // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
       // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
-      this.videoUrl = await setVideoUrlForSong(this.currentSong);
+      this.videoUrl = await setVideoUrlForSong(this.currentSong, {
+        context: this.context,
+        autoParseMusicName: this.autoParseMusicName
+      });
 
 
       this.artist = this.songList[this.curIndex].artist
       this.artist = this.songList[this.curIndex].artist
       this.name = this.songList[this.curIndex].name
       this.name = this.songList[this.curIndex].name
@@ -13565,7 +13940,10 @@ export struct LocalMusic {
       this.currentSong = this.songList[this.curIndex];
       this.currentSong = this.songList[this.curIndex];
 
 
       // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
       // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
-      this.videoUrl = await setVideoUrlForSong(this.currentSong);
+      this.videoUrl = await setVideoUrlForSong(this.currentSong, {
+        context: this.context,
+        autoParseMusicName: this.autoParseMusicName
+      });
       this.name = this.songList[this.curIndex].name;
       this.name = this.songList[this.curIndex].name;
       this.artist = this.songList[this.curIndex].artist
       this.artist = this.songList[this.curIndex].artist
       // this.cover = this.songList[this.curIndex].pixelMapPath
       // this.cover = this.songList[this.curIndex].pixelMapPath
@@ -13585,7 +13963,10 @@ export struct LocalMusic {
       this.currentSong = this.songList[index];
       this.currentSong = this.songList[index];
 
 
       // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
       // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
-      this.videoUrl = await setVideoUrlForSong(this.currentSong);
+      this.videoUrl = await setVideoUrlForSong(this.currentSong, {
+        context: this.context,
+        autoParseMusicName: this.autoParseMusicName
+      });
 
 
       this.name = this.currentSong.name
       this.name = this.currentSong.name
       this.artist = this.currentSong.artist
       this.artist = this.currentSong.artist
@@ -13618,7 +13999,10 @@ export struct LocalMusic {
     Logger.info('heanup playPrevious', `直接使用歌曲列表中的信息: ${this.currentSong.name}, type: ${this.currentSong.type}`);
     Logger.info('heanup playPrevious', `直接使用歌曲列表中的信息: ${this.currentSong.name}, type: ${this.currentSong.type}`);
 
 
     // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
     // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
-    this.videoUrl = await setVideoUrlForSong(this.currentSong);
+    this.videoUrl = await setVideoUrlForSong(this.currentSong, {
+      context: this.context,
+      autoParseMusicName: this.autoParseMusicName
+    });
     this.name = this.songList[this.curIndex].name
     this.name = this.songList[this.curIndex].name
     this.artist = this.songList[this.curIndex].artist
     this.artist = this.songList[this.curIndex].artist
     this.changeImageAnimation()
     this.changeImageAnimation()
@@ -13638,7 +14022,10 @@ export struct LocalMusic {
       Logger.info('heanup randomModePlayFromHistory', `直接使用历史歌曲信息: ${prevSong.name}, type: ${prevSong.type}`);
       Logger.info('heanup randomModePlayFromHistory', `直接使用历史歌曲信息: ${prevSong.name}, type: ${prevSong.type}`);
 
 
       // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
       // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
-      this.videoUrl = await setVideoUrlForSong(this.currentSong);
+      this.videoUrl = await setVideoUrlForSong(this.currentSong, {
+        context: this.context,
+        autoParseMusicName: this.autoParseMusicName
+      });
       this.cover = prevSong.pixelMapPath
       this.cover = prevSong.pixelMapPath
       this.artist = prevSong.artist
       this.artist = prevSong.artist
       this.name = prevSong.name;
       this.name = prevSong.name;
@@ -13659,7 +14046,10 @@ export struct LocalMusic {
       Logger.info('heanup randomModePlayFromHistory', `直接使用歌曲列表中的信息: ${this.currentSong.name}, type: ${this.currentSong.type}`);
       Logger.info('heanup randomModePlayFromHistory', `直接使用歌曲列表中的信息: ${this.currentSong.name}, type: ${this.currentSong.type}`);
 
 
       // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
       // 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
-      this.videoUrl = await setVideoUrlForSong(this.currentSong);
+      this.videoUrl = await setVideoUrlForSong(this.currentSong, {
+        context: this.context,
+        autoParseMusicName: this.autoParseMusicName
+      });
       this.artist = this.songList[this.curIndex].artist
       this.artist = this.songList[this.curIndex].artist
       this.name = this.songList[this.curIndex].name;
       this.name = this.songList[this.curIndex].name;
       this.changeImageAnimation()
       this.changeImageAnimation()

+ 4 - 4
entry/src/main/ets/viewmodel/Song.ets

@@ -8,8 +8,8 @@ export class Song{
   // 歌曲索引
   // 歌曲索引
   public id: number = -1
   public id: number = -1
   // 云盘歌曲还是本地歌曲
   // 云盘歌曲还是本地歌曲
-  public type: SongType = SongType.WebDav
-  public songType: SongType = SongType.WebDav
+  public type: SongType = SongType.Local
+  public songType: SongType = SongType.Local
   // WebDav所属账号
   // WebDav所属账号
   public WebDavAccountId: number = -1
   public WebDavAccountId: number = -1
   public webDavAccountId: number = -1
   public webDavAccountId: number = -1
@@ -61,7 +61,7 @@ export class Song{
   public static  equal(song1: Song,song2: Song):boolean{
   public static  equal(song1: Song,song2: Song):boolean{
     if(song1.type === song2.type && song1.type === SongType.Local){
     if(song1.type === song2.type && song1.type === SongType.Local){
       return song1.localFilePath === song2.localFilePath && song1.name === song2.name
       return song1.localFilePath === song2.localFilePath && song1.name === song2.name
-    }else if(song1.type === song2.type && song2.type === SongType.WebDav){
+    }else if(song1.type === song2.type && (song2.type === SongType.WebDav || song2.type === SongType.Smb)){
       return song1.WebDavAccountId === song2.WebDavAccountId && song1.name === song2.name && song1.webFilePath === song2.webFilePath
       return song1.WebDavAccountId === song2.WebDavAccountId && song1.name === song2.name && song1.webFilePath === song2.webFilePath
     }else{
     }else{
       return false
       return false
@@ -90,4 +90,4 @@ export class Song{
     return clonedSong;
     return clonedSong;
   }
   }
 
 
-}
+}

+ 6 - 2
entry/src/main/ets/viewmodel/WebDavAccount.ets

@@ -1,4 +1,5 @@
 import { Constants } from "../Constants";
 import { Constants } from "../Constants";
+import { RemoteDriveType } from "../common/enums/RemoteDriveType";
 import { FileInfo } from "./FileInfo";
 import { FileInfo } from "./FileInfo";
 
 
 @Observed
 @Observed
@@ -23,9 +24,12 @@ export class WebDavAccount{
   public imageFilePaths: string[] = []
   public imageFilePaths: string[] = []
   // 自定义封面路径
   // 自定义封面路径
   public coverPath?: string
   public coverPath?: string
-  public webType : number = 0
+  public webType : number = RemoteDriveType.WebDav
+  public smbShare: string = ''
+  public smbDomain: string = ''
+  public navidromeBasePath: string = '/rest'
 
 
   public setIsUseLocalHost(isuse: boolean){
   public setIsUseLocalHost(isuse: boolean){
     this.isUseLocalHost = isuse
     this.isUseLocalHost = isuse
   }
   }
-}
+}

+ 61 - 1
entry/src/main/ets/workers/Worker.ets

@@ -8,6 +8,12 @@ import { VideoItem } from '../viewmodel/VideoItem';
 
 
 const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
 const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
 
 
+interface MetadataRequestPayload {
+  originFilePath?: string;
+  originId?: string;
+  autoParseMusicName?: boolean;
+}
+
 /**
 /**
  * Defines the event handler to be called when the worker thread receives a message sent by the host thread.
  * Defines the event handler to be called when the worker thread receives a message sent by the host thread.
  * The event handler is executed in the worker thread.
  * The event handler is executed in the worker thread.
@@ -35,6 +41,14 @@ workerPort.onmessage = async (e: MessageEvents) => {
     case 4://查询专辑列表
     case 4://查询专辑列表
       queryAlbumTask(e.data.data)
       queryAlbumTask(e.data.data)
       break;
       break;
+    case 5://解析缓存音频元数据
+      await analyzeCachedMediaMetadata(
+        e.data.data,
+        e.data.data2,
+        e.data.data3,
+        e.data.data4 as MetadataRequestPayload
+      );
+      break;
 
 
   }
   }
 
 
@@ -261,6 +275,53 @@ async function queryAlbumTask(context:Context) {
   }
   }
 }
 }
 
 
+async function analyzeCachedMediaMetadata(
+  context: Context,
+  cachePath: string,
+  songType: number,
+  extra?: MetadataRequestPayload
+): Promise<void> {
+  if (!context || !cachePath) {
+    return;
+  }
+  try {
+    const videoItem = await Utility.uriGetMusicAssetsFromFile(
+      context,
+      cachePath,
+      songType ?? CommonConstants.TYPE_LOCAL,
+      extra?.autoParseMusicName ?? false
+    );
+    workerPort.postMessage({
+      code: 105,
+      data: {
+        originFilePath: extra?.originFilePath ?? cachePath,
+        originId: extra?.originId,
+        bit_rate: videoItem.bit_rate,
+        duration: videoItem.duration,
+        sampleRate: videoItem.sampleRate,
+        mimeType: videoItem.mimeType,
+        md5Str: videoItem.md5Str,
+        videoSize: videoItem.videoSize,
+        size: videoItem.size,
+        pixelMapPath: videoItem.pixelMapPath,
+        artist: videoItem.artist,
+        album: videoItem.album,
+        name: videoItem.name
+      }
+    });
+  } catch (error) {
+    const err = error as Error;
+    workerPort.postMessage({
+      code: 105,
+      data: {
+        originFilePath: extra?.originFilePath ?? cachePath,
+        originId: extra?.originId,
+        error: err.message
+      }
+    });
+  }
+}
+
 
 
 /**
 /**
  * Defines the event handler to be called when the worker receives a message that cannot be deserialized.
  * Defines the event handler to be called when the worker receives a message that cannot be deserialized.
@@ -279,4 +340,3 @@ workerPort.onmessageerror = (event: MessageEvents) => {
  */
  */
 workerPort.onerror = (event: ErrorEvent) => {
 workerPort.onerror = (event: ErrorEvent) => {
 };
 };
-

+ 3 - 2
entry/src/main/resources/base/profile/main_pages.json

@@ -7,6 +7,7 @@
     "pages/VerifyPage",
     "pages/VerifyPage",
     "pages/VipPage",
     "pages/VipPage",
     "pages/Demo",
     "pages/Demo",
-    "pages/PlaylistDetailPage"
+    "pages/PlaylistDetailPage",
+    "pages/SmbTestPage"
   ]
   ]
-}
+}