From 8413c67ec6195da9a27c5e8e3ccdc94da0b43823 Mon Sep 17 00:00:00 2001 From: Mike Nolan Date: Wed, 27 May 2026 03:02:16 -0500 Subject: [PATCH] Overhaul cmake configuration, add console api, fix http code that caused issues with cgi-bin --- .clang-format | 321 +++ .gitea/workflows/tag.yaml | 19 +- CMakeLists.txt | 354 +-- Config.cmake.in | 18 +- LICENSE.md | 688 ++++- OTHERS_CODE.md | 8 +- Packaging/Tools/build.sh | 15 +- Packaging/WinToolsIntel/build.sh | 13 + README.md | 38 +- apps/printtorrent.cpp | 13 +- apps/tanonydrop.cpp | 123 +- apps/tbin2h.cpp | 18 +- apps/tclearwebseed.cpp | 18 +- apps/tdoc2json.cpp | 53 +- apps/tfetch.cpp | 25 +- apps/tfileserver.cpp | 201 +- apps/tjson2doc.cpp | 52 +- apps/tjsonpretty.cpp | 52 +- apps/tjsonunpretty.cpp | 54 +- apps/trng.cpp | 26 +- apps/tshell.cpp | 138 +- apps/ttime.cpp | 77 +- apps/ttorrentcreate.cpp | 58 +- apps/tuuid.cpp | 9 +- apps/twatch.cpp | 10 +- changelog.md | 3 + cmake/apps.cmake | 67 + cmake/examples.cmake | 43 + cmake/findmbed.cmake | 27 + cmake/helpers.cmake | 78 + cmake/install-dev.cmake | 29 + cmake/options.cmake | 16 + cmake/sharedlib.cmake | 21 + cmake/sources.cmake | 1 + cmake/staticlib.cmake | 14 + cmake/version.cmake | 3 + examples/console-list.cpp | 31 + examples/console-raw.cpp | 20 + examples/console-test.cpp | 20 + examples/copyfile.cpp | 8 +- examples/echo-my-name.cpp | 9 + examples/echo-platform.cpp | 9 + examples/mountabletest.cpp | 51 +- examples/pathtest.cpp | 9 +- examples/printjsondecodedemoji.cpp | 6 +- examples/safesubpath.cpp | 8 +- examples/tests.cpp | 12 +- examples/timer.cpp | 9 +- examples/webserverex.cpp | 269 +- examples/wrappertest.c | 21 - examples/wsecho.cpp | 63 +- include/TessesFramework/Args.hpp | 38 +- .../BitTorrent/TorrentFile.hpp | 146 +- .../BitTorrent/TorrentStream.hpp | 98 +- include/TessesFramework/Common.hpp | 179 +- include/TessesFramework/Console.hpp | 79 + .../Crypto/ClientTLSStream.hpp | 53 +- include/TessesFramework/Crypto/Crypto.hpp | 139 +- include/TessesFramework/Date/Date.hpp | 250 +- .../TessesFramework/Filesystem/FSHelpers.hpp | 85 +- .../TessesFramework/Filesystem/LocalFS.hpp | 82 +- .../Filesystem/MountableFilesystem.hpp | 112 +- .../Filesystem/NullFilesystem.hpp | 41 +- .../Filesystem/RelativeFilesystem.hpp | 114 +- .../Filesystem/SubdirFilesystem.hpp | 106 +- include/TessesFramework/Filesystem/TempFS.hpp | 99 +- include/TessesFramework/Filesystem/VFS.hpp | 635 +++-- include/TessesFramework/Filesystem/VFSFix.hpp | 18 + include/TessesFramework/HiddenField.hpp | 57 +- .../TessesFramework/Http/BasicAuthServer.hpp | 47 +- include/TessesFramework/Http/CGIServer.hpp | 53 +- .../TessesFramework/Http/CallbackServer.hpp | 44 +- .../TessesFramework/Http/ChangeableServer.hpp | 39 +- .../Http/ContentDisposition.hpp | 39 +- include/TessesFramework/Http/FileServer.hpp | 60 +- include/TessesFramework/Http/HttpClient.hpp | 196 +- include/TessesFramework/Http/HttpServer.hpp | 278 +- include/TessesFramework/Http/HttpStream.hpp | 72 +- include/TessesFramework/Http/HttpUtils.hpp | 335 +-- .../TessesFramework/Http/MountableServer.hpp | 61 +- include/TessesFramework/Http/RouteServer.hpp | 88 +- include/TessesFramework/Http/WebSocket.hpp | 99 +- include/TessesFramework/Lazy.hpp | 101 +- include/TessesFramework/Mail/Smtp.hpp | 89 +- .../TessesFramework/Platform/Environment.hpp | 98 +- include/TessesFramework/Platform/Process.hpp | 85 +- include/TessesFramework/Random.hpp | 41 +- .../TessesFramework/Serialization/Bencode.hpp | 124 +- .../Serialization/BitConverter.hpp | 135 +- .../TessesFramework/Serialization/Json.hpp | 165 +- .../TessesFramework/Serialization/SQLite.hpp | 52 +- .../Streams/BufferedStream.hpp | 59 +- .../TessesFramework/Streams/ByteReader.hpp | 82 +- .../TessesFramework/Streams/ByteWriter.hpp | 77 +- .../TessesFramework/Streams/FileStream.hpp | 68 +- .../TessesFramework/Streams/MemoryStream.hpp | 58 +- .../TessesFramework/Streams/NetworkStream.hpp | 141 +- include/TessesFramework/Streams/PtyStream.hpp | 73 +- include/TessesFramework/Streams/Stream.hpp | 72 +- include/TessesFramework/TessesFramework.hpp | 91 +- .../TessesFramework/Text/HeaderGenerator.hpp | 35 +- .../TessesFramework/Text/StringConverter.hpp | 58 +- .../TextStreams/StdIOReader.hpp | 37 +- .../TextStreams/StdIOWriter.hpp | 39 +- .../TextStreams/StreamReader.hpp | 51 +- .../TextStreams/StreamWriter.hpp | 44 +- .../TextStreams/StringReader.hpp | 43 +- .../TextStreams/StringWriter.hpp | 42 +- .../TextStreams/TextReader.hpp | 55 +- .../TextStreams/TextWriter.hpp | 150 +- include/TessesFramework/Threading/Mutex.hpp | 42 +- include/TessesFramework/Threading/Thread.hpp | 42 +- .../TessesFramework/Threading/ThreadPool.hpp | 57 +- include/TessesFramework/Uuid.hpp | 77 +- pkgconfig/CMakeLists.txt | 10 +- pkgconfig/tessesframework.pc.in | 4 +- pkgconfig/tessesframework_static.pc.in | 13 - src/Args.cpp | 118 +- src/BitTorrent/TorrentFile.cpp | 675 +++-- src/BitTorrent/TorrentStream.cpp | 242 +- src/Console.cpp | 869 ++++++ src/Crypto/MbedTLS/ClientTLSStream.cpp | 411 ++- src/Crypto/MbedTLS/Crypto.cpp | 730 ++--- src/Date/Date.cpp | 1608 +++++------ src/Filesystem/FSHelpers.cpp | 327 +-- src/Filesystem/LocalFS.cpp | 852 +++--- src/Filesystem/MountableFilesystem.cpp | 931 +++---- src/Filesystem/NullFilesystem.cpp | 42 +- src/Filesystem/RelativeFilesystem.cpp | 333 +-- src/Filesystem/SubdirFilesystem.cpp | 303 +-- src/Filesystem/TempFS.cpp | 356 ++- src/Filesystem/VFS.cpp | 1298 ++++----- src/HiddenField.cpp | 52 +- src/Http/BasicAuthServer.cpp | 106 +- src/Http/CGIServer.cpp | 256 +- src/Http/CallbackServer.cpp | 49 +- src/Http/ChangeableServer.cpp | 39 +- src/Http/ContentDisposition.cpp | 134 +- src/Http/FileServer.cpp | 246 +- src/Http/HttpClient.cpp | 1243 +++++---- src/Http/HttpServer.cpp | 2356 ++++++++--------- src/Http/HttpStream.cpp | 335 ++- src/Http/HttpUtils.cpp | 1895 +++++++------ src/Http/MountableServer.cpp | 95 +- src/Http/RouteServer.cpp | 183 +- src/Http/WebSocket.cpp | 136 +- src/Mail/Smtp.cpp | 274 +- src/Platform/Environment.cpp | 588 ++-- src/Platform/Process.cpp | 1212 +++++---- src/Random.cpp | 57 +- src/Serialization/Bencode.cpp | 459 ++-- src/Serialization/BitConverter.cpp | 573 ++-- src/Serialization/Json.cpp | 1509 +++++------ src/Serialization/SQLite.cpp | 168 +- src/Streams/BufferedStream.cpp | 123 +- src/Streams/ByteReader.cpp | 318 ++- src/Streams/ByteWriter.cpp | 266 +- src/Streams/FileStream.cpp | 225 +- src/Streams/MemoryStream.cpp | 132 +- src/Streams/NetworkStream.cpp | 1614 ++++++----- src/Streams/PtyStream.cpp | 280 +- src/Streams/Stream.cpp | 252 +- src/TF_Init.cpp | 1825 ++++++------- src/Text/HeaderGenerator.cpp | 135 +- src/Text/StringConverter.cpp | 474 ++-- src/TextStreams/StdIOReader.cpp | 62 +- src/TextStreams/StdIOWriter.cpp | 71 +- src/TextStreams/StreamReader.cpp | 80 +- src/TextStreams/StreamWriter.cpp | 55 +- src/TextStreams/StringReader.cpp | 73 +- src/TextStreams/StringWriter.cpp | 45 +- src/TextStreams/TextReader.cpp | 215 +- src/TextStreams/TextWriter.cpp | 201 +- src/Threading/Mutex.cpp | 181 +- src/Threading/Thread.cpp | 389 ++- src/Threading/ThreadPool.cpp | 163 +- src/Uuid.cpp | 397 ++- 177 files changed, 20088 insertions(+), 17948 deletions(-) create mode 100644 .clang-format create mode 100644 Packaging/WinToolsIntel/build.sh create mode 100644 cmake/apps.cmake create mode 100644 cmake/examples.cmake create mode 100644 cmake/findmbed.cmake create mode 100644 cmake/helpers.cmake create mode 100644 cmake/install-dev.cmake create mode 100644 cmake/options.cmake create mode 100644 cmake/sharedlib.cmake create mode 100644 cmake/staticlib.cmake create mode 100644 cmake/version.cmake create mode 100644 examples/console-list.cpp create mode 100644 examples/console-raw.cpp create mode 100644 examples/console-test.cpp create mode 100644 examples/echo-my-name.cpp create mode 100644 examples/echo-platform.cpp delete mode 100644 examples/wrappertest.c create mode 100644 include/TessesFramework/Console.hpp delete mode 100644 pkgconfig/tessesframework_static.pc.in create mode 100644 src/Console.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f924aed --- /dev/null +++ b/.clang-format @@ -0,0 +1,321 @@ +--- +Language: Cpp +AlignAfterOpenBracket: true +AccessModifierOffset: -2 +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: true + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + AlignPPAndNotPP: true + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowBreakBeforeQtProperty: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AllowShortNamespacesOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AttributeMacros: + - __capability +BinPackArguments: true +BinPackLongBracedList: true +BinPackParameters: BinPack +BitFieldColonSpacing: Both +BracedInitializerIndentWidth: -1 +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterOpenBracketBracedList: false +BreakAfterOpenBracketFunction: false +BreakAfterOpenBracketIf: false +BreakAfterOpenBracketLoop: false +BreakAfterOpenBracketSwitch: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeCloseBracketBracedList: false +BreakBeforeCloseBracketFunction: false +BreakBeforeCloseBracketIf: false +BreakBeforeCloseBracketLoop: false +BreakBeforeCloseBracketSwitch: false +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTemplateCloser: false +BreakBeforeTernaryOperators: true +BreakBinaryOperations: Never +BreakConstructorInitializers: BeforeColon +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +BreakTemplateDeclarations: MultiLine +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: AlignFirstComment +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +EnumTrailingComma: Leave +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExportBlock: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigitsInsert: 0 + BinaryMaxDigitsRemove: 0 + Decimal: 0 + DecimalMinDigitsInsert: 0 + DecimalMaxDigitsRemove: 0 + Hex: 0 + HexMinDigitsInsert: 0 + HexMaxDigitsRemove: 0 + BinaryMinDigits: 0 + DecimalMinDigits: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLines: + AtEndOfFile: false + AtStartOfBlock: true + AtStartOfFile: true +KeepFormFeed: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MainIncludeChar: Quote +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +NumericLiteralCase: + ExponentLetter: Leave + HexDigit: Leave + Prefix: Leave + Suffix: Leave +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +OneLineFormatOffRegex: '' +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakBeforeMemberAccess: 150 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: Always +RemoveBracesLLVM: false +RemoveEmptyLinesInUnwrappedLines: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: + Enabled: true + IgnoreCase: false + IgnoreExtension: false +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterOperatorKeyword: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterNot: false + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBraces: Never +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TableGenBreakInsideDAGArg: DontBreak +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +WrapNamespaceBodyWithEmptyLines: Leave +... + diff --git a/.gitea/workflows/tag.yaml b/.gitea/workflows/tag.yaml index 5de2747..3c08250 100644 --- a/.gitea/workflows/tag.yaml +++ b/.gitea/workflows/tag.yaml @@ -10,6 +10,7 @@ env: jobs: build-arch: + name: "🔨 Build for Arch Linux/Static Builds" runs-on: arch-builder steps: - uses: actions/checkout@v4 @@ -30,10 +31,22 @@ jobs: files: |- artifacts/** - update-tap: - runs-on: ubuntu-latest + update-tap-and-build-win32: + name: "🔨 Build win32 and update the tap 🍺" + runs-on: global-containers-mingw steps: - uses: actions/checkout@v4 + - run: | + env -C Builds/WinToolsIntel bash build.sh + + - uses: akkuman/gitea-release-action@v1 + env: + NODE_OPTIONS: '--experimental-fetch' # if nodejs < 18 + with: + prerelease: true + files: |- + artifacts/** + - uses: actions/checkout@v4 with: ref: "master" @@ -46,5 +59,5 @@ jobs: git config user.name "Tesses Gitea Bot" git config user.email "noreply@tesses.net" git add . - git commit -m "Push tesses-framework=${{ env.VERSION }}" + git commit -m "Push tessesframework=${{ env.VERSION }}" git push diff --git a/CMakeLists.txt b/CMakeLists.txt index 20cc302..3c89f49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,25 +1,15 @@ cmake_minimum_required(VERSION 3.16) -set(TESSESFRAMEWORK_MAJOR_VERSION 0) -set(TESSESFRAMEWORK_MINOR_VERSION 0) -set(TESSESFRAMEWORK_PATCH_VERSION 2) - +include(cmake/version.cmake) project(TessesFramework VERSION ${TESSESFRAMEWORK_MAJOR_VERSION}.${TESSESFRAMEWORK_MINOR_VERSION}.${TESSESFRAMEWORK_PATCH_VERSION}) set(CMAKE_CXX_STANDARD 17) +include(CheckLibraryExists) +include(FetchContent) include(cmake/sources.cmake) +include(cmake/options.cmake) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) - -option(TESSESFRAMEWORK_EMBED_CERT_BUNDLE "Embed the certificate chain bundle" ON) -option(TESSESFRAMEWORK_ENABLE_MBED "Enable Tesses Framework mbedtls" ON) -option(TESSESFRAMEWORK_ENABLE_NETWORKING "Enable Networking" ON) -option(TESSESFRAMEWORK_ENABLE_THREADING "Enable Threading" ON) -option(TESSESFRAMEWORK_ENABLE_SQLITE "Enable sqlite (embedded in source)" ON) -option(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS "Enable sago platformfolders (embedded in source)" ON) -option(TESSESFRAMEWORK_ENABLE_PROCESS "Enable process api" ON) -option(TESSESFRAMEWORK_ENABLE_RPATH "Enable RPATH" ON) - if(TESSESFRAMEWORK_ENABLE_SQLITE) list(APPEND TESSESFRAMEWORK_SOURCE src/Serialization/sqlite/sqlite3-mod.c @@ -31,343 +21,45 @@ list(APPEND TESSESFRAMEWORK_SOURCE src/Platform/sago/platform_folders.cpp ) endif() -option(TESSESFRAMEWORK_ENABLE_EXAMPLES "Enable Tesses Framework examples" ON) -option(TESSESFRAMEWORK_ENABLE_APPS "Enable Tesses Framework cli apps" ON) -option(TESSESFRAMEWORK_INSTALL_DEVELOPMENT "Enable Installing Tesses Framework Development Packages" ON) -option(TESSESFRAMEWORK_ENABLE_STATIC "Enable Tesses Framework Static Libraries" ON) -option(TESSESFRAMEWORK_ENABLE_SHARED "Enable Tesses Framework Shared Libraries" ON) -option(TESSESFRAMEWORK_ENABLE_SETDATE "Enable setting date to file" ON) -option(TESSESFRAMEWORK_LOGTOFILE "TessesFramework Log to file" OFF) -option(TESSESFRAMEWORK_FETCHCONTENT "TessesFramework fetchcontent" ON) -option(TESSESFRAMEWORK_VENDERCERTCHAIN "Use the ca-certificates.crt in project rather than system" ON) -if(TESSESFRAMEWORK_FETCHCONTENT) -set(TESSESFRAMEWORK_CERT_BUNDLE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ca-certificates.crt" CACHE FILEPATH "Path to ca-chain") -include(FetchContent) -else() + +include(cmake/findmbed.cmake) + + + + if(TESSESFRAMEWORK_VENDERCERTCHAIN) set(TESSESFRAMEWORK_CERT_BUNDLE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ca-certificates.crt" CACHE FILEPATH "Path to ca-chain") else() set(TESSESFRAMEWORK_CERT_BUNDLE_FILE "/etc/ssl/certs/ca-certificates.crt" CACHE FILEPATH "Path to ca-chain") endif() -endif() file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include) include(cmake/bin2h.cmake) - - -# Making sure libraries don't generate symlinks - if(TESSESFRAMEWORK_ENABLE_MBED) if(TESSESFRAMEWORK_EMBED_CERT_BUNDLE) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/CertificateChain.h" "#pragma once\n") -#target_compile_definitions(TessesFramework PUBLIC TESSESFRAMEWORK_EMBED_CERT_BUNDLE) bin2h(SOURCE_FILE "${TESSESFRAMEWORK_CERT_BUNDLE_FILE}" HEADER_FILE "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/CertificateChain.h" VARIABLE_NAME CertificateChain APPEND NULL_TERMINATE) file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/CertificateChain.h" "\n") + +endif() +endif() + +include(cmake/sources.cmake) + +if(TESSESFRAMEWORK_STATIC) +include(cmake/staticlib.cmake) else() -target_compile_definitions(TessesFramework PUBLIC TESSESFRAMEWORK_CERT_BUNDLE_FILE=${TESSESFRAMEWORK_CERT_BUNDLE_FILE}) - -endif() -if(TESSESFRAMEWORK_FETCHCONTENT) -set(MBEDTLS_FATAL_WARNINGS OFF CACHE INTERNAL "For TessesFramework" FORCE) -set(ENABLE_TESTING OFF CACHE INTERNAL "For TessesFramework" FORCE) -set(ENABLE_PROGRAMS OFF CACHE INTERNAL "For TessesFramework" FORCE) -if(NOT TESSESFRAMEWORK_ENABLE_STATIC) - -set(USE_STATIC_MBEDTLS_LIBRARY OFF CACHE INTERNAL "For TessesFramework" FORCE) -endif() -if(TESSESFRAMEWORK_ENABLE_SHARED) -set(USE_SHARED_MBEDTLS_LIBRARY ON CACHE INTERNAL "For TessesFramework" FORCE) -endif() -FetchContent_Declare( - mbedtls - URL https://downloads.tesses.net/cache/libraries/source/mbedtls-3.6.4.tar.bz2 - -) - -FetchContent_MakeAvailable(mbedtls) -else() -find_package(MbedTLS REQUIRED) +include(cmake/sharedlib.cmake) endif() -endif() -function(TESSESFRAMEWORK_LINKDEPS TessesFramework_TARGET) -if(TESSESFRAMEWORK_ENABLE_SQLITE) -target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_ENABLE_SQLITE) -endif() -if(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) -target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) -endif() -if(TESSESFRAMEWORK_ENABLE_PROCESS) -target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_ENABLE_PROCESS) +include(cmake/install-dev.cmake) -endif() -target_include_directories(${TessesFramework_TARGET} - PUBLIC - "$" - "$" -) -target_include_directories(${TessesFramework_TARGET} - PUBLIC - "$" - "$" -) -if(WIN32) -target_link_libraries(${TessesFramework_TARGET} PUBLIC iphlpapi) -endif() -if(TESSESFRAMEWORK_LOGTOFILE) -target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_LOGTOFILE) -endif() -if(TESSESFRAMEWORK_ENABLE_THREADING) -target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_ENABLE_THREADING) -endif() -if(TESSESFRAMEWORK_ENABLE_NETWORKING) -target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_ENABLE_NETWORKING) -endif() -if(TESSESFRAMEWORK_FETCHCONTENT) -target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_FETCHCONTENT) -endif() -if(TESSESFRAMEWORK_ENABLE_MBED) -target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_ENABLE_MBED) -if(TESSESFRAMEWORK_EMBED_CERT_BUNDLE) -target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_EMBED_CERT_BUNDLE) -else() -target_compile_definitions(TessesFramework PUBLIC TESSESFRAMEWORK_CERT_BUNDLE_FILE=${TESSESFRAMEWORK_CERT_BUNDLE_FILE}) -endif() -if(NOT TESSESFRAMEWORK_FETCHCONTENT) -target_link_libraries(${TessesFramework_TARGET} PUBLIC mbedtls mbedx509 mbedcrypto) -endif() -endif() +include(cmake/examples.cmake) +include(cmake/apps.cmake) -if("${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube") -target_link_libraries(${TessesFramework_TARGET} PUBLIC fat) - -endif() -if("${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoSwitch" AND TESSESFRAMEWORK_ENABLE_THREADING) -target_link_libraries(${TessesFramework_TARGET} PUBLIC -lpthread) -endif() -if("${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii") -target_link_libraries(${TessesFramework_TARGET} PUBLIC wiisocket) -target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_USE_WII_SOCKET) -endif() -if("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") - -target_link_libraries(${TessesFramework_TARGET} PUBLIC ws2_32) - -endif() - -if(NOT("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows" OR PLATFORM_PS2 OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube")) - -if(TESSESFRAMEWORK_ENABLE_THREADING) -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) -target_link_libraries(${TessesFramework_TARGET} PRIVATE Threads::Threads) -endif() -endif() -if(TESSESFRAMEWORK_ENABLE_SETDATE) -target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_ENABLE_SETDATE) -endif() -target_compile_definitions(${TessesFramework_TARGET} PUBLIC _FILE_OFFSET_BITS=64) - -if(PLATFORM_PS2) -target_link_libraries(${TessesFramework_TARGET} PUBLIC -ldebug) -endif() -endfunction() - -include(GNUInstallDirs) - -if(TESSESFRAMEWORK_ENABLE_STATIC) - -add_library(tessesframework STATIC ${TESSESFRAMEWORK_SOURCE}) -TESSESFRAMEWORK_LINKDEPS(tessesframework) - -if(TESSESFRAMEWORK_FETCHCONTENT AND TESSESFRAMEWORK_ENABLE_MBED) -if(TESSESFRAMEWORK_ENABLE_SHARED) -target_link_libraries(tessesframework PUBLIC mbedtls_static mbedx509_static mbedcrypto_static) -else() - -target_link_libraries(tessesframework PUBLIC mbedtls mbedx509 mbedcrypto everest p256m) -endif() -endif() -list(APPEND TessesFrameworkLibs tessesframework) -endif() - - -if(TESSESFRAMEWORK_ENABLE_SHARED) -if(TESSESFRAMEWORK_ENABLE_RPATH) -set(CMAKE_MACOSX_RPATH 1) -set(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE) - -if (APPLE) - set(CMAKE_INSTALL_RPATH "@executable_path/../${CMAKE_INSTALL_LIBDIR}") -else() - set(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}") -endif() -endif() -add_library(tessesframework_shared SHARED ${TESSESFRAMEWORK_SOURCE}) -TESSESFRAMEWORK_LINKDEPS(tessesframework_shared) - -if(TESSESFRAMEWORK_ENABLE_MBED) -if(TESSESFRAMEWORK_FETCHCONTENT) -target_link_libraries(tessesframework_shared PUBLIC mbedtls mbedx509 mbedcrypto everest p256m) - -else() -target_link_libraries(tessesframework_shared PUBLIC mbedtls mbedx509 mbedcrypto) - -endif() -endif() -list(APPEND TessesFrameworkLibs tessesframework_shared) - - - - -endif() - -if((TESSESFRAMEWORK_ENABLE_STATIC OR TESSESFRAMEWORK_ENABLE_SHARED) AND TESSESFRAMEWORK_FETCHCONTENT AND TESSESFRAMEWORK_ENABLE_MBED) -list(APPEND TessesFrameworkLibs mbedtls mbedx509 mbedcrypto everest p256m) -if(TESSESFRAMEWORK_ENABLE_STATIC AND TESSESFRAMEWORK_ENABLE_SHARED) -list(APPEND TessesFrameworkLibs mbedtls_static mbedx509_static mbedcrypto_static) -endif() -endif() - - -if(TESSESFRAMEWORK_INSTALL_DEVELOPMENT) -install(TARGETS ${TessesFrameworkLibs} - EXPORT TessesFrameworkTargets - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) - -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/TessesFramework DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -install(EXPORT TessesFrameworkTargets - FILE TessesFrameworkTargets.cmake - NAMESPACE TessesFramework:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TessesFramework -) - - -include(CMakePackageConfigHelpers) -configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/TessesFrameworkConfig.cmake" -INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TessesFramework) -configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/TessesFrameworkFeatures.h.in "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/TessesFrameworkFeatures.h" -INSTALL_DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/TessesFramework/TessesFrameworkFeatures.h) - -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/TessesFrameworkConfig.cmake" -DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TessesFramework) -endif() - -if(TESSESFRAMEWORK_ENABLE_EXAMPLES) - add_executable(timer examples/timer.cpp) - target_link_libraries(timer PUBLIC tessesframework) - - add_executable(copyfile examples/copyfile.cpp) - target_link_libraries(copyfile PUBLIC tessesframework) - - add_executable(webserverex examples/webserverex.cpp) - target_link_libraries(webserverex PUBLIC tessesframework) - - add_executable(safesubpath examples/safesubpath.cpp) - target_link_libraries(safesubpath PUBLIC tessesframework) - - add_executable(pathtest examples/pathtest.cpp) - target_link_libraries(pathtest PUBLIC tessesframework) - - add_executable(mountabletest examples/mountabletest.cpp) - target_link_libraries(mountabletest PUBLIC tessesframework) - - add_executable(printjsondecodedemoji examples/printjsondecodedemoji.cpp) - target_link_libraries(printjsondecodedemoji PUBLIC tessesframework) - - add_executable(wsecho examples/wsecho.cpp) - target_link_libraries(wsecho PUBLIC tessesframework) - - - add_executable(tests examples/tests.cpp) - target_link_libraries(tests PUBLIC tessesframework) -endif() - -if(TESSESFRAMEWORK_ENABLE_APPS) -add_executable(tbin2h apps/tbin2h.cpp) -target_link_libraries(tbin2h PUBLIC tessesframework) -install(TARGETS tbin2h DESTINATION "${CMAKE_INSTALL_BINDIR}") -add_executable(tanonydrop apps/tanonydrop.cpp) -target_link_libraries(tanonydrop PUBLIC tessesframework) -install(TARGETS tanonydrop DESTINATION "${CMAKE_INSTALL_BINDIR}") -add_executable(tfetch apps/tfetch.cpp) -target_link_libraries(tfetch PUBLIC tessesframework) -install(TARGETS tfetch DESTINATION "${CMAKE_INSTALL_BINDIR}") -add_executable(tfileserver apps/tfileserver.cpp) -target_link_libraries(tfileserver PUBLIC tessesframework) -install(TARGETS tfileserver DESTINATION "${CMAKE_INSTALL_BINDIR}") - -add_executable(tdoc2json apps/tdoc2json.cpp) -target_link_libraries(tdoc2json PUBLIC tessesframework) -install(TARGETS tdoc2json DESTINATION "${CMAKE_INSTALL_BINDIR}") - -add_executable(tjson2doc apps/tjson2doc.cpp) -target_link_libraries(tjson2doc PUBLIC tessesframework) -install(TARGETS tjson2doc DESTINATION "${CMAKE_INSTALL_BINDIR}") - -add_executable(tjsonpretty apps/tjsonpretty.cpp) -target_link_libraries(tjsonpretty PUBLIC tessesframework) -install(TARGETS tjsonpretty DESTINATION "${CMAKE_INSTALL_BINDIR}") - -add_executable(tjsonunpretty apps/tjsonunpretty.cpp) -target_link_libraries(tjsonunpretty PUBLIC tessesframework) -install(TARGETS tjsonunpretty DESTINATION "${CMAKE_INSTALL_BINDIR}") - -add_executable(ttime apps/ttime.cpp) -target_link_libraries(ttime PUBLIC tessesframework) -install(TARGETS ttime DESTINATION "${CMAKE_INSTALL_BINDIR}") -add_executable(tshell apps/tshell.cpp) -target_link_libraries(tshell PUBLIC tessesframework) -install(TARGETS tshell DESTINATION "${CMAKE_INSTALL_BINDIR}") - -add_executable(tprinttorrent apps/printtorrent.cpp) - -target_link_libraries(tprinttorrent PUBLIC tessesframework) -install(TARGETS tprinttorrent DESTINATION "${CMAKE_INSTALL_BINDIR}") - -add_executable(ttorrentcreate apps/ttorrentcreate.cpp) - -target_link_libraries(ttorrentcreate PUBLIC tessesframework) -install(TARGETS ttorrentcreate DESTINATION "${CMAKE_INSTALL_BINDIR}") - -add_executable(tclearwebseed apps/tclearwebseed.cpp) - -target_link_libraries(tclearwebseed PUBLIC tessesframework) -install(TARGETS tclearwebseed DESTINATION "${CMAKE_INSTALL_BINDIR}") - -add_executable(trng apps/trng.cpp) - -target_link_libraries(trng PUBLIC tessesframework) -install(TARGETS trng DESTINATION "${CMAKE_INSTALL_BINDIR}") - -add_executable(twatch apps/twatch.cpp) - -target_link_libraries(twatch PUBLIC tessesframework) -install(TARGETS twatch DESTINATION "${CMAKE_INSTALL_BINDIR}") - -add_executable(tuuid apps/tuuid.cpp) - -target_link_libraries(tuuid PUBLIC tessesframework) -install(TARGETS tuuid DESTINATION "${CMAKE_INSTALL_BINDIR}") -endif() - -include(InstallRequiredSystemLibraries) -set(CPACK_PACKAGE_CONTACT "Mike Nolan ") -set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.md") -set(CPACK_PACKAGE_VERSION_MAJOR "${TessesFramework_VERSION_MAJOR}") -set(CPACK_PACKAGE_VERSION_MINOR "${TessesFramework_VERSION_MINOR}") -set(CPACK_DEBIAN_PACKAGE_DEPENDS "libmbedtls-dev (>= 2.28.8)") - -include(CPack) if(TESSESFRAMEWORK_INSTALL_DEVELOPMENT) add_subdirectory(pkgconfig) -endif() +endif() \ No newline at end of file diff --git a/Config.cmake.in b/Config.cmake.in index 149735a..6b2ac3f 100644 --- a/Config.cmake.in +++ b/Config.cmake.in @@ -3,15 +3,15 @@ include("${CMAKE_CURRENT_LIST_DIR}/TessesFrameworkTargets.cmake") check_required_components(TessesFramework) - -if(NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube")) +include(GNUInstallDirs) +if(NOT WIN32) if(@TESSESFRAMEWORK_ENABLE_THREADING@) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) endif() endif() - +set(TESSESFRAMEWORK_ENABLE_MBED @TESSESFRAMEWORK_ENABLE_MBED@) set(TESSESFRAMEWORK_ENABLE_SQLITE @TESSESFRAMEWORK_ENABLE_SQLITE@) set(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS @TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS@) @@ -19,3 +19,15 @@ set(TESSESFRAMEWORK_ENABLE_NETWORKING @TESSESFRAMEWORK_ENABLE_NETWORKING@) set(TESSESFRAMEWORK_ENABLE_THREADING @TESSESFRAMEWORK_ENABLE_THREADING@) set(TESSESFRAMEWORK_ENABLE_PROCESS @TESSESFRAMEWORK_ENABLE_PROCESS@) set(TESSESFRAMEWORK_ENABLE_RPATH @TESSESFRAMEWORK_ENABLE_RPATH@) +set(TESSESFRAMEWORK_STATIC @TESSESFRAMEWORK_STATIC@) + +if(@TESSESFRAMEWORK_ENABLE_RPATH@ AND NOT @TESSESFRAMEWORK_STATIC@) +set(CMAKE_MACOSX_RPATH 1) +set(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE) + +if (APPLE) + set(CMAKE_INSTALL_RPATH "@executable_path/../${CMAKE_INSTALL_LIBDIR}") +else() + set(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}") +endif() +endif() \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 04b87d5..46ff6af 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,21 +1,675 @@ -MIT License +# GNU GENERAL PUBLIC LICENSE -Copyright (c) 2024 Mike Nolan +Version 3, 29 June 2007 -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: +Copyright (C) 2007 Free Software Foundation, Inc. + -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. -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. \ No newline at end of file +## Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom +to share and change all versions of a program--to make sure it remains +free software for all its users. We, the Free Software Foundation, use +the GNU General Public License for most of our software; it applies +also to any other work released this way by its authors. You can apply +it to your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you +have certain responsibilities if you distribute copies of the +software, or if you modify it: responsibilities to respect the freedom +of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the +manufacturer can do so. This is fundamentally incompatible with the +aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for +individuals to use, which is precisely where it is most unacceptable. +Therefore, we have designed this version of the GPL to prohibit the +practice for those products. If such problems arise substantially in +other domains, we stand ready to extend this provision to those +domains in future versions of the GPL, as needed to protect the +freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish +to avoid the special danger that patents applied to a free program +could make it effectively proprietary. To prevent this, the GPL +assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +## TERMS AND CONDITIONS + +### 0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU General Public +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that numbered version or +of any later version published by the Free Software Foundation. If the +Program does not specify a version number of the GNU General Public +License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU General Public License can be used, that proxy's public +statement of acceptance of a version permanently authorizes you to +choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +## How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper +mail. + +If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands \`show w' and \`show c' should show the +appropriate parts of the General Public License. Of course, your +program's commands might be different; for a GUI interface, you would +use an "about box". + +You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see . + +The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read . \ No newline at end of file diff --git a/OTHERS_CODE.md b/OTHERS_CODE.md index 60384ec..74add21 100644 --- a/OTHERS_CODE.md +++ b/OTHERS_CODE.md @@ -1,8 +1,10 @@ Other peoples code ================== -- cmake/bin2h.cmake, from: https://github.com/sivachandran/cmake-bin2h (MIT) -- Cppified the multipart/form-data code from: https://github.com/dajuric/simple-http (MIT) +- cmake/bin2h.cmake, from: [https://github.com/sivachandran/cmake-bin2h](https://github.com/sivachandran/cmake-bin2h) (MIT) +- Cppified the multipart/form-data code from: [https://github.com/dajuric/simple-http](https://github.com/dajuric/simple-http) (MIT) - mbedtls ssl code from mbedtls examples (Apache 2.0) -- [Sqlite](https://www.sqlite.org/) (in folder src/sqlite also uses demo VFS (at least on Wii)) +- [MbedTLS](https://github.com/Mbed-TLS/mbedtls) (external dependency) (Apache 2.0/GPLv2 or later, either are compatible with TessesFramework now) +- [Sqlite](https://www.sqlite.org/) (in folder src/sqlite also uses demo VFS (at least on Wii)) (Public Domain, licensed by hopes and prayers, praise God!) - [Sago's platform_folders](https://github.com/sago007/PlatformFolders) (in folder src/sago) +- [HowardHinnart's date](https://github.com/HowardHinnant/date) (in folder src/HowardHinnant_date) (MIT) \ No newline at end of file diff --git a/Packaging/Tools/build.sh b/Packaging/Tools/build.sh index ccf35a8..85f1e2a 100644 --- a/Packaging/Tools/build.sh +++ b/Packaging/Tools/build.sh @@ -7,27 +7,26 @@ git clone --depth 1 https://git.tesses.org/tesses50/zig-cross builds/zig-cross for tripple in x86_64-linux-musl x86-linux-musl aarch64-linux-musl arm-linux-musleabi riscv64-linux-musl powerpc-linux-musleabihf; do export BUILDDIR=builds/$tripple mkdir -p $BUILDDIR - cmake -S ../.. -B $BUILDDIR --toolchain $PWD/builds/zig-cross/$tripple\.cmake -DCMAKE_BUILD_TYPE=Release -DTESSESFRAMEWORK_INSTALL_DEVELOPMENT=OFF -DTESSESFRAMEWORK_ENABLE_EXAMPLES=OFF -DTESSESFRAMEWORK_ENABLE_SHARED=OFF -DTESSESFRAMEWORK_ENABLE_STATIC=ON -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++ -static -Wl,--strip-all" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -GNinja + cmake -S ../.. -B $BUILDDIR --toolchain $PWD/builds/zig-cross/$tripple\.cmake -DCMAKE_BUILD_TYPE=Release -DTESSESFRAMEWORK_INSTALL_DEVELOPMENT=OFF -DTESSESFRAMEWORK_ENABLE_EXAMPLES=OFF -DTESSESFRAMEWORK_STATIC=ON -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++ -static -Wl,--strip-all" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -GNinja cmake --build $BUILDDIR || exit 1 cmake --install $BUILDDIR --prefix $BUILDDIR/out tar cvzf ../../artifacts/tessesframework-tools-$tripple\.tar.gz -C $BUILDDIR/out/ bin done - -for tripple in x86_64-windows-gnu x86-windows-gnu aarch64-windows-gnu; do - export BUILDDIR=builds/$tripple +#x86_64-windows-gnu x86-windows-gnu + export BUILDDIR=builds/aarch64-windows-gnu mkdir -p $BUILDDIR - cmake -S ../.. -B $BUILDDIR --toolchain $PWD/builds/zig-cross/$tripple\.cmake -DCMAKE_BUILD_TYPE=Release -DTESSESFRAMEWORK_INSTALL_DEVELOPMENT=OFF -DTESSESFRAMEWORK_ENABLE_EXAMPLES=OFF -DTESSESFRAMEWORK_ENABLE_SHARED=OFF -DTESSESFRAMEWORK_ENABLE_STATIC=ON -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++ -static -Wl,--strip-all" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -GNinja + cmake -S ../.. -B $BUILDDIR --toolchain $PWD/builds/zig-cross/aarch64-windows-gnu.cmake -DCMAKE_BUILD_TYPE=Release -DTESSESFRAMEWORK_INSTALL_DEVELOPMENT=OFF -DTESSESFRAMEWORK_ENABLE_EXAMPLES=OFF -DTESSESFRAMEWORK_STATIC=ON -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++ -static -Wl,--strip-all" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -GNinja cmake --build $BUILDDIR || exit 1 cmake --install $BUILDDIR --prefix $BUILDDIR/out - env -C $BUILDDIR/out zip -r ../../../../../artifacts/tessesframework-tools-$tripple\.zip bin -done + env -C $BUILDDIR/out zip -r ../../../../../artifacts/tessesframework-tools-aarch64-windows-gnu.zip bin + for tripple in x86_64-macos-none aarch64-macos-none; do export BUILDDIR=builds/$tripple mkdir -p $BUILDDIR - cmake -S ../.. -B $BUILDDIR --toolchain $PWD/builds/zig-cross/$tripple\.cmake -DCMAKE_BUILD_TYPE=Release -DTESSESFRAMEWORK_INSTALL_DEVELOPMENT=OFF -DTESSESFRAMEWORK_ENABLE_EXAMPLES=OFF -DTESSESFRAMEWORK_ENABLE_SHARED=OFF -DTESSESFRAMEWORK_ENABLE_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -GNinja -DCMAKE_EXE_LINKER_FLAGS="-Wl,--strip-all" + cmake -S ../.. -B $BUILDDIR --toolchain $PWD/builds/zig-cross/$tripple\.cmake -DCMAKE_BUILD_TYPE=Release -DTESSESFRAMEWORK_INSTALL_DEVELOPMENT=OFF -DTESSESFRAMEWORK_ENABLE_EXAMPLES=OFF -DTESSESFRAMEWORK_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -GNinja -DCMAKE_EXE_LINKER_FLAGS="-Wl,--strip-all" cmake --build $BUILDDIR || exit 1 cmake --install $BUILDDIR --prefix $BUILDDIR/out tar cvzf ../../artifacts/tessesframework-tools-$tripple\.tar.gz -C $BUILDDIR/out/ bin diff --git a/Packaging/WinToolsIntel/build.sh b/Packaging/WinToolsIntel/build.sh new file mode 100644 index 0000000..55862f2 --- /dev/null +++ b/Packaging/WinToolsIntel/build.sh @@ -0,0 +1,13 @@ +export BUILDDIR=builds/x86-windows-gnu +mkdir -p $BUILDDIR +cmake -S ../.. -B $BUILDDIR --toolchain $PWD/x86.cmake -DCMAKE_BUILD_TYPE=Release -DTESSESFRAMEWORK_INSTALL_DEVELOPMENT=OFF -DTESSESFRAMEWORK_ENABLE_EXAMPLES=OFF -DTESSESFRAMEWORK_STATIC=ON -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++ -static -Wl,--strip-all" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -GNinja +cmake --build $BUILDDIR || exit 1 +cmake --install $BUILDDIR --prefix $BUILDDIR/out +env -C $BUILDDIR/out zip -r ../../../../../artifacts/tessesframework-tools-x86-windows-gnu.zip bin + +export BUILDDIR=builds/x86_64-windows-gnu +mkdir -p $BUILDDIR +cmake -S ../.. -B $BUILDDIR --toolchain $PWD/x86_64.cmake -DCMAKE_BUILD_TYPE=Release -DTESSESFRAMEWORK_INSTALL_DEVELOPMENT=OFF -DTESSESFRAMEWORK_ENABLE_EXAMPLES=OFF -DTESSESFRAMEWORK_STATIC=ON -DCMAKE_EXE_LINKER_FLAGS="-static-libgcc -static-libstdc++ -static -Wl,--strip-all" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -GNinja +cmake --build $BUILDDIR || exit 1 +cmake --install $BUILDDIR --prefix $BUILDDIR/out +env -C $BUILDDIR/out zip -r ../../../../../artifacts/tessesframework-tools-x86_64-windows-gnu.zip bin \ No newline at end of file diff --git a/README.md b/README.md index 2907c7e..e84ac24 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,48 @@ Tesses Framework ================ +## Features +- DateTime, TimeSpan +- VFS (polymorphic filesystems) (Mountable is kind of broken) +- BitTorrent file generation, bencoding +- Http Client/Server +- Json +- Simple SMTP client +- Environment variables (cross platform), user special folders (also cross platform, using sago/platform-folders) +- Sqlite3 (embedded into this library) +- Process API (for windows/mac/linux/probably bsd) +- Polymorphic byte streams (for files/network/memory) +- C/C++ header generator for embedding files +- A console api which works on windows/mac/linux (even before windows 10, tested on 8.1 and works), with password entry, readline, partial vt100 emulation for win32, colors, raw/cooked modes and terminal list (for somewhat primitive interactive tuis, it does scroll though, pages) +- Thread pool and threading +- Basic arg parser +- Uuids +- Lazy initializable type +- Unicode support, (I need to do better validation, I know, only really used for windows and parsing json) +- EventLoop +- Basic crytography using mbedtls +- a LCG Random number generator (do not use for crypto, use it for non security things like number guessing games and stuff, where rand would be ok) + +## Supported Platforms +- Linux +- Windows (with mingw and static linked for sure) +- MacOS +- FreeBSD (including GhostBSD) +- NetBSD +- Wii (using libogc2, may be broken) +- Switch (using libnx, may be broken) +- Android (kinda, uses legacy and hardcoded /sdcard api unless you use TF_AllowPortable(std::string)) + +> NOTE: if platform folders is disabled tessesframework uses the path /TF_User for special folders (except for android which it uses /sdcard) + ## To Install - Install [mbedtls](https://github.com/Mbed-TLS/mbedtls) (use sudo apt install libmbedtls-dev on debian) for TessesFramework - Follow the commands bellow ## Run these commands to install tesses-framework ```bash -git clone https://onedev.site.tesses.net/tesses-framework -cd tesses-framework +git clone https://git.tesses.org/tesses50/tessesframework +cd tessesframework mkdir build cd build cmake -S .. -B . diff --git a/apps/printtorrent.cpp b/apps/printtorrent.cpp index 73054a1..a5d26e2 100644 --- a/apps/printtorrent.cpp +++ b/apps/printtorrent.cpp @@ -6,18 +6,15 @@ using namespace Tesses::Framework::Filesystem; using namespace Tesses::Framework::Serialization::Bencode; using namespace Tesses::Framework::TextStreams; using namespace Tesses::Framework::BitTorrent; -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { TF_Init(); - if(argc < 2) - { - printf("USAGE: %s /path/to/torrent/file\n",argv[0]); + if (argc < 2) { + printf("USAGE: %s /path/to/torrent/file\n", argv[0]); return 1; } - auto strm = LocalFS->OpenFile((std::string)argv[1],"rb"); + auto strm = LocalFS->OpenFile((std::string)argv[1], "rb"); auto bencode = Bencode::Load(strm); - if(std::holds_alternative(bencode)) - { + if (std::holds_alternative(bencode)) { TorrentFile file(std::get(bencode)); file.Print(std::make_shared()); diff --git a/apps/tanonydrop.cpp b/apps/tanonydrop.cpp index 84e9b20..97abf2f 100644 --- a/apps/tanonydrop.cpp +++ b/apps/tanonydrop.cpp @@ -7,83 +7,84 @@ using namespace Tesses::Framework::Filesystem; std::shared_ptr vfs; -int main(int argc, char** argv) -{ - +int main(int argc, char **argv) { TF_InitWithConsole(); - vfs = std::make_shared(LocalFS,Tesses::Framework::Filesystem::VFSPath::GetAbsoluteCurrentDirectory()); + vfs = std::make_shared( + LocalFS, + Tesses::Framework::Filesystem::VFSPath::GetAbsoluteCurrentDirectory()); - std::shared_ptr cb = std::make_shared([](ServerContext& ctx)->bool{ - if(ctx.path == "/") - { + std::shared_ptr cb = std::make_shared< + CallbackServer>([](ServerContext &ctx) -> bool { + if (ctx.path == "/") { ctx.WithMimeType("text/html") - .SendText( - "" - "" - "AnonyDrop" - "" - "

AnonyDrop

" - "Files" - "
" - "" - "" - "
" - "" - "" - ); - - return true; - } - else if(ctx.path == "/upload") - { - if(ctx.NeedToParseFormData()) - { - ctx.ParseFormData([](std::string mime, std::string filename,std::string name)->std::shared_ptr { - if(name != "file") return nullptr; - VFSPath path("/"+filename); - auto strm = vfs->OpenFile(path,"wb"); - - return strm; - }); - ctx.WithMimeType("text/html") .SendText( "" "" - "AnonyDrop - Uploaded successfully" + "AnonyDrop" "" - "

Uploaded successfully

" - "Back" - - "" - "" - ); - return true; - } - else { - ctx.statusCode= Tesses::Framework::Http::BadRequest; - ctx.WithMimeType("text/html") - .SendText( - "" - "" - "AnonyDump - Error: Must contain multipart and POST" - "" - "

Error: Must contain multipart and POST

" - "Back" - + "

AnonyDrop

" + "Files" + "
" + "" + "" "
" "" - "" - ); + ""); + + return true; + } else if (ctx.path == "/upload") { + if (ctx.NeedToParseFormData()) { + ctx.ParseFormData( + [](std::string mime, std::string filename, + std::string name) -> std::shared_ptr { + if (name != "file") + return nullptr; + VFSPath path("/" + filename); + auto strm = vfs->OpenFile(path, "wb"); + + return strm; + }); + ctx.WithMimeType("text/html") + .SendText("" + "" + "AnonyDrop - " + "Uploaded successfully" + "" + "

Uploaded successfully

" + "Back" + + "" + ""); + return true; + } else { + ctx.statusCode = Tesses::Framework::Http::BadRequest; + ctx.WithMimeType("text/html") + .SendText("" + "" + "AnonyDump - Error: Must contain " + "multipart and POST" + "" + "

Error: Must contain multipart and POST

" + "Back" + + "" + "" + ""); } } return false; }); - auto mountable = std::make_shared(cb); - mountable->Mount("/files/",std::make_shared(vfs,true,false)); + auto mountable = + std::make_shared(cb); + mountable->Mount("/files/", std::make_shared(vfs, true, false)); - HttpServer srv(4985,mountable); + HttpServer srv(4985, mountable); srv.StartAccepting(); TF_RunEventLoop(); diff --git a/apps/tbin2h.cpp b/apps/tbin2h.cpp index 6cecafc..8b6efa2 100644 --- a/apps/tbin2h.cpp +++ b/apps/tbin2h.cpp @@ -1,15 +1,17 @@ #include "TessesFramework/TessesFramework.hpp" -int main(int argc, char** argv) -{ - if(argc < 4) - { - std::cout << "USAGE: " << argv[0] << " BINARYFILE VARNAME HEADERFILE" << std::endl; +int main(int argc, char **argv) { + if (argc < 4) { + std::cout << "USAGE: " << argv[0] << " BINARYFILE VARNAME HEADERFILE" + << std::endl; return 0; } - auto fs=std::make_shared(argv[1],"rb"); - if(!fs->CanRead()) return 1; - auto writer = std::make_shared(argv[3]); + auto fs = + std::make_shared(argv[1], "rb"); + if (!fs->CanRead()) + return 1; + auto writer = + std::make_shared(argv[3]); Tesses::Framework::Text::GenerateCHeaderFile(fs, argv[2], writer); return 0; } \ No newline at end of file diff --git a/apps/tclearwebseed.cpp b/apps/tclearwebseed.cpp index be4ac5e..643b97b 100644 --- a/apps/tclearwebseed.cpp +++ b/apps/tclearwebseed.cpp @@ -3,20 +3,18 @@ using namespace Tesses::Framework; using namespace Tesses::Framework::BitTorrent; using namespace Tesses::Framework::Serialization::Bencode; using namespace Tesses::Framework::Filesystem; -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { TF_Init(); - if(argc < 2) - { - printf("USAGE: %s torrent_file\n",argv[0]); + if (argc < 2) { + printf("USAGE: %s torrent_file\n", argv[0]); return 1; } - auto strm = LocalFS->OpenFile((std::string)argv[1],"rb"); + auto strm = LocalFS->OpenFile((std::string)argv[1], "rb"); auto data = Bencode::Load(strm); - if(std::holds_alternative(data)){ - std::get(data).SetValue("url-list",BeUndefined()); + if (std::holds_alternative(data)) { + std::get(data).SetValue("url-list", BeUndefined()); } - strm = LocalFS->OpenFile((std::string)argv[1],"wb"); - Bencode::Save(strm,data); + strm = LocalFS->OpenFile((std::string)argv[1], "wb"); + Bencode::Save(strm, data); return 0; } \ No newline at end of file diff --git a/apps/tdoc2json.cpp b/apps/tdoc2json.cpp index ed371f9..4a28da6 100644 --- a/apps/tdoc2json.cpp +++ b/apps/tdoc2json.cpp @@ -2,44 +2,34 @@ using namespace Tesses::Framework::Streams; using namespace Tesses::Framework::Serialization::Json; using namespace Tesses::Framework::TextStreams; -std::shared_ptr OpenWrite(std::string dest) -{ - if(dest == "-") - { - return std::make_shared(stdout,false,"w"); - } - else - { - auto strm = std::make_shared(dest,"w"); - if(!strm->CanWrite()) - { +std::shared_ptr OpenWrite(std::string dest) { + if (dest == "-") { + return std::make_shared(stdout, false, "w"); + } else { + auto strm = std::make_shared(dest, "w"); + if (!strm->CanWrite()) { return nullptr; } return strm; } } -std::shared_ptr OpenRead(std::string src) -{ - if(src == "-") - { - return std::make_shared(stdin,false,"r"); - } - else - { - auto strm = std::make_shared(src,"r"); - if(!strm->CanRead()) - { +std::shared_ptr OpenRead(std::string src) { + if (src == "-") { + return std::make_shared(stdin, false, "r"); + } else { + auto strm = std::make_shared(src, "r"); + if (!strm->CanRead()) { return nullptr; } return strm; } } -int main(int argc, char** argv) -{ - if(argc < 3) - { +int main(int argc, char **argv) { + if (argc < 3) { std::cout << "USAGE: " << argv[0] << " SRC DEST" << std::endl; - std::cout << "SRC: doc json file or - for stdin to convert to pretty json" << std::endl; + std::cout + << "SRC: doc json file or - for stdin to convert to pretty json" + << std::endl; std::cout << "DEST: prettied file or - for stdout" << std::endl; return 0; } @@ -47,14 +37,12 @@ int main(int argc, char** argv) auto dest = OpenWrite(argv[2]); - if(src == nullptr) - { - + if (src == nullptr) { + std::cerr << "ERROR: Input could not be read" << std::endl; return 1; } - if(dest == nullptr) - { + if (dest == nullptr) { std::cerr << "ERROR: Output could not be read" << std::endl; return 1; } @@ -65,6 +53,5 @@ int main(int argc, char** argv) auto str = reader.ReadToEnd(); writer.WriteLine(Json::Encode(Json::DocDecode(str))); - return 0; } \ No newline at end of file diff --git a/apps/tfetch.cpp b/apps/tfetch.cpp index 9605a8e..6f790d7 100644 --- a/apps/tfetch.cpp +++ b/apps/tfetch.cpp @@ -3,25 +3,22 @@ using namespace Tesses::Framework; using namespace Tesses::Framework::Streams; using namespace Tesses::Framework::Http; -int main(int argc, char** argv) -{ - if(argc < 3) - { - printf("USAGE: %s \n",argv[0]); - printf("USAGE: %s \n",argv[0]); +int main(int argc, char **argv) { + if (argc < 3) { + printf("USAGE: %s \n", argv[0]); + printf("USAGE: %s \n", argv[0]); return 1; } - if(argc >= 4) - { + if (argc >= 4) { std::string unixSocket = argv[1]; std::string url = argv[2]; - std::string path = argv[3]; - Tesses::Framework::Http::DownloadUnixSocketToFileSimple(unixSocket,url,path); - } - else { + std::string path = argv[3]; + Tesses::Framework::Http::DownloadUnixSocketToFileSimple(unixSocket, url, + path); + } else { std::string path = argv[2]; - Tesses::Framework::Http::DownloadToFileSimple(argv[1],path); + Tesses::Framework::Http::DownloadToFileSimple(argv[1], path); } - + return 0; } \ No newline at end of file diff --git a/apps/tfileserver.cpp b/apps/tfileserver.cpp index 1116d67..ab1df29 100644 --- a/apps/tfileserver.cpp +++ b/apps/tfileserver.cpp @@ -6,121 +6,106 @@ using namespace Tesses::Framework::Streams; using namespace Tesses::Framework::TextStreams; using namespace Tesses::Framework::Threading; -void print_help(const char* name) -{ - printf("Tesses FileServer\nUSAGE: %s [OPTIONS] \n",name); - printf("OPTIONS:\n-p PORT, --port PORT: Change port from 9852\n-l, --listing: Enable listing\n-s, --spa: Enable SPA mode (send \"/\" body instead of not found)\n-c, --cgi-bin: Enable cgi (common gateway interface) support (specify a folder like /cgi-bin)\n-a, --cgi-admin: cgi admin email\n-w, --cgi-working: working directory for cgi scripts\n-h, --help: This Screen\n"); +void print_help(const char *name) { + printf("Tesses FileServer\nUSAGE: %s [OPTIONS] \n", name); + printf("OPTIONS:\n-p PORT, --port PORT: Change port from 9852\n-l, " + "--listing: Enable listing\n-s, --spa: Enable SPA mode (send \"/\" " + "body instead of not found)\n-c, --cgi-bin: Enable cgi (common " + "gateway interface) support (specify a folder like /cgi-bin)\n-a, " + "--cgi-admin: cgi admin email\n-w, --cgi-working: working directory " + "for cgi scripts\n-h, --help: This Screen\n"); exit(1); } -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { try { - TF_InitWithConsole(); - TF_LOG("INIT"); - const char* directory = "wwwroot"; - bool spa=false; - bool allowListing = false; - std::optional cgi_bin; - std::optional cgi_admin; - std::optional cgi_workdir; - uint16_t port = 9852L; - - for(int i = 1; i < argc; i++) - { - if(strcmp(argv[i],"--help") == 0 || strcmp(argv[i],"-h") == 0) - { - print_help(argv[0]); - } - else if(strcmp(argv[i], "-c") == 0 || strcmp(argv[i],"--cgi-bin") == 0) - { - if(i+1>=argc) - { - printf("ERROR: Not enough arguments for cgi-bin\n"); - print_help(argv[0]); - } - else { - printf("CGI is enabled\n"); - cgi_bin = argv[++i]; - } - } - else if(strcmp(argv[i], "-a") == 0 || strcmp(argv[i],"--cgi-admin") == 0) - { - if(i+1>=argc) - { - printf("ERROR: Not enough arguments for cgi-admin\n"); - print_help(argv[0]); - } - else { - cgi_admin = argv[++i]; - } - } - else if(strcmp(argv[i], "-w") == 0 || strcmp(argv[i],"--cgi-working") == 0) - { - if(i+1>=argc) - { - printf("ERROR: Not enough arguments for cgi-working\n"); - print_help(argv[0]); - } - else { - cgi_workdir = (Tesses::Framework::Filesystem::VFSPath)argv[++i]; - } - } - - else if(strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--listing") == 0) - { - allowListing = true; - } - else if(strcmp(argv[i],"-s") == 0 || strcmp(argv[i],"--spa") == 0) - { - spa=true; - } - else if(strcmp(argv[i],"-p") == 0 || strcmp(argv[i],"--port") == 0) - { - if(i+1>=argc) - { - printf("ERROR: Not enough arguments for PORT\n"); - print_help(argv[0]); - } - else - { + TF_InitWithConsole(); + TF_LOG("INIT"); + const char *directory = "wwwroot"; + bool spa = false; + bool allowListing = false; + std::optional cgi_bin; + std::optional cgi_admin; + std::optional cgi_workdir; + uint16_t port = 9852L; - port = (uint16_t)std::stoul(argv[++i]); + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + print_help(argv[0]); + } else if (strcmp(argv[i], "-c") == 0 || + strcmp(argv[i], "--cgi-bin") == 0) { + if (i + 1 >= argc) { + printf("ERROR: Not enough arguments for cgi-bin\n"); + print_help(argv[0]); + } else { + printf("CGI is enabled\n"); + cgi_bin = argv[++i]; + } + } else if (strcmp(argv[i], "-a") == 0 || + strcmp(argv[i], "--cgi-admin") == 0) { + if (i + 1 >= argc) { + printf("ERROR: Not enough arguments for cgi-admin\n"); + print_help(argv[0]); + } else { + cgi_admin = argv[++i]; + } + } else if (strcmp(argv[i], "-w") == 0 || + strcmp(argv[i], "--cgi-working") == 0) { + if (i + 1 >= argc) { + printf("ERROR: Not enough arguments for cgi-working\n"); + print_help(argv[0]); + } else { + cgi_workdir = + (Tesses::Framework::Filesystem::VFSPath)argv[++i]; + } + } + + else if (strcmp(argv[i], "-l") == 0 || + strcmp(argv[i], "--listing") == 0) { + allowListing = true; + } else if (strcmp(argv[i], "-s") == 0 || + strcmp(argv[i], "--spa") == 0) { + spa = true; + } else if (strcmp(argv[i], "-p") == 0 || + strcmp(argv[i], "--port") == 0) { + if (i + 1 >= argc) { + printf("ERROR: Not enough arguments for PORT\n"); + print_help(argv[0]); + } else { + + port = (uint16_t)std::stoul(argv[++i]); + } + } else { + directory = argv[i]; } } - else - { - directory = argv[i]; - } - } - - std::cout << "In folder: " << std::filesystem::absolute(directory).string() << std::endl; - std::shared_ptr http; - - auto fs = std::make_shared(directory,allowListing, spa); - if(cgi_bin) - { - Tesses::Framework::Filesystem::VFSPath dir = *cgi_bin; - dir.relative = true; - auto svr = std::make_shared(fs); - auto cgi = std::make_shared(std::filesystem::absolute(directory).string() / dir); - cgi->adminEmail = cgi_admin; - cgi->workingDirectory = cgi_workdir; - svr->Mount(*cgi_bin, cgi); - http = svr; - } - else { - http = fs; - } - HttpServer server(port,http); - server.StartAccepting(); - TF_RunEventLoop(); - std::cout << "Closing server" << std::endl; - TF_Quit(); -}catch(std::exception& ex) -{ - TF_LOG(ex.what()); -} + std::cout << "In folder: " + << std::filesystem::absolute(directory).string() << std::endl; + std::shared_ptr http; + + auto fs = std::make_shared(directory, allowListing, spa); + if (cgi_bin) { + Tesses::Framework::Filesystem::VFSPath dir = *cgi_bin; + dir.relative = true; + auto svr = std::make_shared(fs); + auto cgi = std::make_shared( + std::filesystem::absolute(directory).string() / dir); + cgi->adminEmail = cgi_admin; + cgi->workingDirectory = cgi_workdir; + svr->Mount(*cgi_bin, cgi); + http = svr; + } else { + http = fs; + } + + HttpServer server(port, http); + server.StartAccepting(); + TF_RunEventLoop(); + std::cout << "Closing server" << std::endl; + TF_Quit(); + } catch (std::exception &ex) { + TF_LOG(ex.what()); + } return 0; } diff --git a/apps/tjson2doc.cpp b/apps/tjson2doc.cpp index 4ee366e..486b315 100644 --- a/apps/tjson2doc.cpp +++ b/apps/tjson2doc.cpp @@ -2,44 +2,33 @@ using namespace Tesses::Framework::Streams; using namespace Tesses::Framework::Serialization::Json; using namespace Tesses::Framework::TextStreams; -std::shared_ptr OpenWrite(std::string dest) -{ - if(dest == "-") - { - return std::make_shared(stdout,false,"w"); - } - else - { - auto strm = std::make_shared(dest,"w"); - if(!strm->CanWrite()) - { +std::shared_ptr OpenWrite(std::string dest) { + if (dest == "-") { + return std::make_shared(stdout, false, "w"); + } else { + auto strm = std::make_shared(dest, "w"); + if (!strm->CanWrite()) { return nullptr; } return strm; } } -std::shared_ptr OpenRead(std::string src) -{ - if(src == "-") - { - return std::make_shared(stdin,false,"r"); - } - else - { - auto strm = std::make_shared(src,"r"); - if(!strm->CanRead()) - { +std::shared_ptr OpenRead(std::string src) { + if (src == "-") { + return std::make_shared(stdin, false, "r"); + } else { + auto strm = std::make_shared(src, "r"); + if (!strm->CanRead()) { return nullptr; } return strm; } } -int main(int argc, char** argv) -{ - if(argc < 3) - { +int main(int argc, char **argv) { + if (argc < 3) { std::cout << "USAGE: " << argv[0] << " SRC DEST" << std::endl; - std::cout << "SRC: json file or - for stdin to convert to json doc" << std::endl; + std::cout << "SRC: json file or - for stdin to convert to json doc" + << std::endl; std::cout << "DEST: json doc file or - for stdout" << std::endl; return 0; } @@ -47,14 +36,12 @@ int main(int argc, char** argv) auto dest = OpenWrite(argv[2]); - if(src == nullptr) - { - + if (src == nullptr) { + std::cerr << "ERROR: Input could not be read" << std::endl; return 1; } - if(dest == nullptr) - { + if (dest == nullptr) { std::cerr << "ERROR: Output could not be read" << std::endl; return 1; } @@ -68,6 +55,5 @@ int main(int argc, char** argv) TryGetJToken(doc, docLs); writer.WriteLine(Json::DocEncode(docLs)); - return 0; } \ No newline at end of file diff --git a/apps/tjsonpretty.cpp b/apps/tjsonpretty.cpp index 545451f..6e321b1 100644 --- a/apps/tjsonpretty.cpp +++ b/apps/tjsonpretty.cpp @@ -2,44 +2,33 @@ using namespace Tesses::Framework::Streams; using namespace Tesses::Framework::Serialization::Json; using namespace Tesses::Framework::TextStreams; -std::shared_ptr OpenWrite(std::string dest) -{ - if(dest == "-") - { - return std::make_shared(stdout,false,"w"); - } - else - { - auto strm = std::make_shared(dest,"w"); - if(!strm->CanWrite()) - { +std::shared_ptr OpenWrite(std::string dest) { + if (dest == "-") { + return std::make_shared(stdout, false, "w"); + } else { + auto strm = std::make_shared(dest, "w"); + if (!strm->CanWrite()) { return nullptr; } return strm; } } -std::shared_ptr OpenRead(std::string src) -{ - if(src == "-") - { - return std::make_shared(stdin,false,"r"); - } - else - { - auto strm = std::make_shared(src,"r"); - if(!strm->CanRead()) - { +std::shared_ptr OpenRead(std::string src) { + if (src == "-") { + return std::make_shared(stdin, false, "r"); + } else { + auto strm = std::make_shared(src, "r"); + if (!strm->CanRead()) { return nullptr; } return strm; } } -int main(int argc, char** argv) -{ - if(argc < 3) - { +int main(int argc, char **argv) { + if (argc < 3) { std::cout << "USAGE: " << argv[0] << " SRC DEST" << std::endl; - std::cout << "SRC: json file or - for stdin to make pretty" << std::endl; + std::cout << "SRC: json file or - for stdin to make pretty" + << std::endl; std::cout << "DEST: prettied file or - for stdout" << std::endl; return 0; } @@ -47,14 +36,12 @@ int main(int argc, char** argv) auto dest = OpenWrite(argv[2]); - if(src == nullptr) - { - + if (src == nullptr) { + std::cerr << "ERROR: Input could not be read" << std::endl; return 1; } - if(dest == nullptr) - { + if (dest == nullptr) { std::cerr << "ERROR: Output could not be read" << std::endl; return 1; } @@ -65,6 +52,5 @@ int main(int argc, char** argv) auto str = reader.ReadToEnd(); writer.WriteLine(Json::Encode(Json::Decode(str))); - return 0; } \ No newline at end of file diff --git a/apps/tjsonunpretty.cpp b/apps/tjsonunpretty.cpp index d9c5394..d236979 100644 --- a/apps/tjsonunpretty.cpp +++ b/apps/tjsonunpretty.cpp @@ -2,44 +2,33 @@ using namespace Tesses::Framework::Streams; using namespace Tesses::Framework::Serialization::Json; using namespace Tesses::Framework::TextStreams; -std::shared_ptr OpenWrite(std::string dest) -{ - if(dest == "-") - { - return std::make_shared(stdout,false,"w"); - } - else - { - auto strm = std::make_shared(dest,"w"); - if(!strm->CanWrite()) - { +std::shared_ptr OpenWrite(std::string dest) { + if (dest == "-") { + return std::make_shared(stdout, false, "w"); + } else { + auto strm = std::make_shared(dest, "w"); + if (!strm->CanWrite()) { return nullptr; } return strm; } } -std::shared_ptr OpenRead(std::string src) -{ - if(src == "-") - { - return std::make_shared(stdin,false,"r"); - } - else - { - auto strm = std::make_shared(src,"r"); - if(!strm->CanRead()) - { +std::shared_ptr OpenRead(std::string src) { + if (src == "-") { + return std::make_shared(stdin, false, "r"); + } else { + auto strm = std::make_shared(src, "r"); + if (!strm->CanRead()) { return nullptr; } return strm; } } -int main(int argc, char** argv) -{ - if(argc < 3) - { +int main(int argc, char **argv) { + if (argc < 3) { std::cout << "USAGE: " << argv[0] << " SRC DEST" << std::endl; - std::cout << "SRC: json file or - for stdin to make unpretty" << std::endl; + std::cout << "SRC: json file or - for stdin to make unpretty" + << std::endl; std::cout << "DEST: unprettied file or - for stdout" << std::endl; return 0; } @@ -47,14 +36,12 @@ int main(int argc, char** argv) auto dest = OpenWrite(argv[2]); - if(src == nullptr) - { + if (src == nullptr) { std::cerr << "ERROR: Input could not be read" << std::endl; return 1; } - if(dest == nullptr) - { - + if (dest == nullptr) { + std::cerr << "ERROR: Output could not be read" << std::endl; return 1; } @@ -63,8 +50,7 @@ int main(int argc, char** argv) StreamWriter writer(dest); auto str = reader.ReadToEnd(); - writer.WriteLine(Json::Encode(Json::Decode(str),false)); - + writer.WriteLine(Json::Encode(Json::Decode(str), false)); return 0; } \ No newline at end of file diff --git a/apps/trng.cpp b/apps/trng.cpp index c3c3c55..f6b19ab 100644 --- a/apps/trng.cpp +++ b/apps/trng.cpp @@ -1,9 +1,7 @@ #include "TessesFramework/TessesFramework.hpp" -int main(int argc, char** argv) -{ - if(argc < 2) - { +int main(int argc, char **argv) { + if (argc < 2) { std::cout << "USAGE: " << argv[0] << " times" << std::endl; std::cout << "USAGE: " << argv[0] << " times max" << std::endl; @@ -11,25 +9,19 @@ int main(int argc, char** argv) return 0; } Tesses::Framework::Random random; - int times=std::atoi(argv[1]); - if(argc > 2) - { + int times = std::atoi(argv[1]); + if (argc > 2) { int32_t min = 0; int32_t max = std::stoi(argv[2]); - if(argc > 3) - { + if (argc > 3) { min = max; max = std::stoi(argv[3]); - } - for(int i = 0; i < times; i++) - { - std::cout << random.Next(min,max) << std::endl; + for (int i = 0; i < times; i++) { + std::cout << random.Next(min, max) << std::endl; } - } - else { - for(int i = 0; i < times; i++) - { + } else { + for (int i = 0; i < times; i++) { std::cout << random.Next() << std::endl; } } diff --git a/apps/tshell.cpp b/apps/tshell.cpp index 1919e4c..773f33c 100644 --- a/apps/tshell.cpp +++ b/apps/tshell.cpp @@ -1,124 +1,110 @@ -#include "TessesFramework/TessesFramework.hpp" #include "TessesFramework/Platform/Process.hpp" +#include "TessesFramework/TessesFramework.hpp" using namespace Tesses::Framework; using namespace Tesses::Framework::Platform; using namespace Tesses::Framework::TextStreams; using namespace Tesses::Framework::Filesystem; -void split_command(std::string cmd, std::vector& args) -{ - bool inStr=false; - std::string cur={}; - auto flush=[&]()->void { - if(cur.empty()) return; +void split_command(std::string cmd, std::vector &args) { + bool inStr = false; + std::string cur = {}; + auto flush = [&]() -> void { + if (cur.empty()) + return; args.push_back(cur); - cur={}; + cur = {}; }; - for(size_t i = 0; i < cmd.size(); i++) - { - if(inStr) - { - if(cmd[i] == '\"') inStr=false; - else if(cmd[i] == '\\') - { + for (size_t i = 0; i < cmd.size(); i++) { + if (inStr) { + if (cmd[i] == '\"') + inStr = false; + else if (cmd[i] == '\\') { i++; - if(i < cmd.size()) - { + if (i < cmd.size()) { cmd.push_back(cmd[i]); } - } - else cur.push_back(cmd[i]); - } - else - { - if(cmd[i] == ' ') flush(); - else if(cmd[i] == '\"') { - inStr=true; - } - else { + } else + cur.push_back(cmd[i]); + } else { + if (cmd[i] == ' ') + flush(); + else if (cmd[i] == '\"') { + inStr = true; + } else { cur.push_back(cmd[i]); } } - } flush(); } -int main(int argc,char** argv) -{ +int main(int argc, char **argv) { TF_Init(); - - - while(true) - { - std::cout << VFSPath::GetAbsoluteCurrentDirectory().ToString() << "$ "; - + while (true) { + std::cout << VFSPath::GetAbsoluteCurrentDirectory().ToString() << "$ "; std::string text; StdIn().ReadLine(text); std::vector args; - split_command(text,args); - if(args.empty()) continue; - if(args[0] == "exit") break; - else if(args[0] == "echo") { if(args.size() > 1) std::cout << args[1] << std::endl;} - else if(args[0] == "cd") { - if(args.size() < 2) - { - VFSPath::SetAbsoluteCurrentDirectory(Environment::SpecialFolders::GetHomeFolder()); - } - else { + split_command(text, args); + if (args.empty()) + continue; + if (args[0] == "exit") + break; + else if (args[0] == "echo") { + if (args.size() > 1) + std::cout << args[1] << std::endl; + } else if (args[0] == "cd") { + if (args.size() < 2) { + VFSPath::SetAbsoluteCurrentDirectory( + Environment::SpecialFolders::GetHomeFolder()); + } else { VFSPath::SetAbsoluteCurrentDirectory(args[1]); } - } - else if(args[0] == "printargs") - { - for(size_t i = 1; i < args.size(); i++) - { - std::cout << "\"" << Http::HttpUtils::Replace(args[i],"\"","\\\"") << "\"" << std::endl; + } else if (args[0] == "printargs") { + for (size_t i = 1; i < args.size(); i++) { + std::cout << "\"" + << Http::HttpUtils::Replace(args[i], "\"", "\\\"") + << "\"" << std::endl; } - } - else if (args[0] == "sigtermtest") - { - if (args.size() < 2) continue; + } else if (args[0] == "sigtermtest") { + if (args.size() < 2) + continue; std::vector args2(args.begin() + 1, args.end()); auto path = Environment::GetRealExecutablePath(args2[0]); Platform::Process p(path.ToString(), args2); - if (p.Start()) - { + if (p.Start()) { std::cout << "Press enter to sigterm" << std::endl; StdIn().ReadLine(); p.Kill(SIGTERM); } - } - else if(args[0] == "rstdi") - { - if(args.size()<3) continue; - std::vector args2(args.begin()+2,args.end()); - auto f = LocalFS->OpenFile(args[1],"rb"); - if(f != nullptr) - { + } else if (args[0] == "rstdi") { + if (args.size() < 3) + continue; + std::vector args2(args.begin() + 2, args.end()); + auto f = LocalFS->OpenFile(args[1], "rb"); + if (f != nullptr) { auto path = Environment::GetRealExecutablePath(args2[0]); - Platform::Process p(path.ToString(),args2); - p.redirectStdIn=true; - if(p.Start()) - { + Platform::Process p(path.ToString(), args2); + p.redirectStdIn = true; + if (p.Start()) { auto strm = p.GetStdinStream(); f->CopyTo(strm); - + p.CloseStdInNow(); p.WaitForExit(); } } - } - else { + } else { auto path = Environment::GetRealExecutablePath(args[0]); - Platform::Process p(path.ToString(),args); + Platform::Process p(path.ToString(), args); if (p.Start()) - p.WaitForExit(); + p.WaitForExit(); else - std::cout << "Failed To Run Process: " << path.ToString() << std::endl; + std::cout << "Failed To Run Process: " << path.ToString() + << std::endl; } } diff --git a/apps/ttime.cpp b/apps/ttime.cpp index 33b3406..4b01aec 100644 --- a/apps/ttime.cpp +++ b/apps/ttime.cpp @@ -3,15 +3,12 @@ using namespace Tesses::Framework; using namespace Tesses::Framework::Date; -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { TF_Init(); - DateTime dt=DateTime::Now(); - //dt = dt.ToLocal(); - if(argc > 1) - { - if(strcmp(argv[1],"--help") == 0) - { + DateTime dt = DateTime::Now(); + // dt = dt.ToLocal(); + if (argc > 1) { + if (strcmp(argv[1], "--help") == 0) { /* case 'r': { @@ -34,15 +31,15 @@ int main(int argc, char** argv) break; case 'T': { - + text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0')); text.push_back(':'); text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0')); text.push_back(':'); text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0')); - + } - + break; case 'u': { @@ -53,7 +50,7 @@ int main(int argc, char** argv) break; case 'w': { - + text.append(std::to_string(weekday)); } break; @@ -72,14 +69,13 @@ int main(int argc, char** argv) text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0')); text.push_back(' '); text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0')); - - + + } break; case 'C': - text.append(Http::HttpUtils::LeftPad(std::to_string(year / 100),2,'0')); - break; - case 'Y': + text.append(Http::HttpUtils::LeftPad(std::to_string(year + / 100),2,'0')); break; case 'Y': text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0')); break; case 'p': @@ -90,7 +86,7 @@ int main(int argc, char** argv) auto hours = hour % 12; if(hours == 0) hours=12; text.append(Http::HttpUtils::LeftPad(std::to_string(hours),2,'0')); - } + } break; case 'h': case 'b': @@ -110,24 +106,37 @@ int main(int argc, char** argv) break; */ std::cout << argv[0] << " " << std::endl; - std::cout << "The fmt is for DateTime::ToString(fmt) just like strftime but just these are supported:" << std::endl; - std::cout << "%a: weekday short eg " << dt.ToString("%a") << std::endl; - std::cout << "%A: weekday long eg " << dt.ToString("%A") << std::endl; - std::cout << "%S: seconds with leading zeros eg " << dt.ToString("%S") << std::endl; - std::cout << "%m: month with leading zeros eg " << dt.ToString("%m") << std::endl; - std::cout << "%d: day with leading zeros eg " << dt.ToString("%d") << std::endl; - std::cout << "%e: day with leading spaces eg " << dt.ToString("%e") << std::endl; - std::cout << "%M: minute with leading zeros eg " << dt.ToString("%M") << std::endl; - std::cout << "%H: 24 hour with leading zeros eg " << dt.ToString("%H") << std::endl; - std::cout << "%F: %Y-%m-%d like this " << dt.ToString("%F") << std::endl; - std::cout << "%D: %m/%d/%y like this " << dt.ToString("%D") << std::endl; - std::cout << "%y: year two digits eg " << dt.ToString("%y") << std::endl; - std::cout << "%Y: year four digits eg " << dt.ToString("%Y") << std::endl; + std::cout << "The fmt is for DateTime::ToString(fmt) just like " + "strftime but just these are supported:" + << std::endl; + std::cout << "%a: weekday short eg " << dt.ToString("%a") + << std::endl; + std::cout << "%A: weekday long eg " << dt.ToString("%A") + << std::endl; + std::cout << "%S: seconds with leading zeros eg " + << dt.ToString("%S") << std::endl; + std::cout << "%m: month with leading zeros eg " << dt.ToString("%m") + << std::endl; + std::cout << "%d: day with leading zeros eg " << dt.ToString("%d") + << std::endl; + std::cout << "%e: day with leading spaces eg " << dt.ToString("%e") + << std::endl; + std::cout << "%M: minute with leading zeros eg " + << dt.ToString("%M") << std::endl; + std::cout << "%H: 24 hour with leading zeros eg " + << dt.ToString("%H") << std::endl; + std::cout << "%F: %Y-%m-%d like this " << dt.ToString("%F") + << std::endl; + std::cout << "%D: %m/%d/%y like this " << dt.ToString("%D") + << std::endl; + std::cout << "%y: year two digits eg " << dt.ToString("%y") + << std::endl; + std::cout << "%Y: year four digits eg " << dt.ToString("%Y") + << std::endl; return 1; } std::cout << dt.ToString(argv[1]) << std::endl; - } - else - std::cout << dt.ToString() << std::endl; + } else + std::cout << dt.ToString() << std::endl; return 0; } diff --git a/apps/ttorrentcreate.cpp b/apps/ttorrentcreate.cpp index e7aade4..9bb7d5b 100644 --- a/apps/ttorrentcreate.cpp +++ b/apps/ttorrentcreate.cpp @@ -6,61 +6,67 @@ using namespace Tesses::Framework::Filesystem; using namespace Tesses::Framework::Serialization::Bencode; using namespace Tesses::Framework::TextStreams; using namespace Tesses::Framework::BitTorrent; -void usage(Args& args) -{ - std::cout << "USAGE: " << args.filename << " [options] torrent_contents torrent_file.torrent" << std::endl << std::endl; +void usage(Args &args) { + std::cout << "USAGE: " << args.filename + << " [options] torrent_contents torrent_file.torrent" << std::endl + << std::endl; std::cout << "OPTIONS:" << std::endl; - std::cout << "tracker: torrent tracker url (require at least one)" << std::endl; + std::cout << "tracker: torrent tracker url (require at least one)" + << std::endl; std::cout << "webseed: url for web seeding" << std::endl; std::cout << "comment: set the comment" << std::endl; - std::cout << "created_by: set the created by field, defaults to TessesFrameworkTorrent" << std::endl; - std::cout << "piece_length: set the piece length, defaults to " + std::to_string(DEFAULT_PIECE_LENGTH) << std::endl << std::endl; + std::cout << "created_by: set the created by field, defaults to " + "TessesFrameworkTorrent" + << std::endl; + std::cout << "piece_length: set the piece length, defaults to " + + std::to_string(DEFAULT_PIECE_LENGTH) + << std::endl + << std::endl; std::cout << "FLAGS:" << std::endl; std::cout << "help: bring this help message up" << std::endl; std::cout << "private: enable private tracker flag" << std::endl; exit(1); } -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { TF_Init(); std::vector webseeds; std::vector trackers; - bool isPrivate=false; + bool isPrivate = false; int64_t pieceLength = DEFAULT_PIECE_LENGTH; - std::string comment=""; + std::string comment = ""; std::string created_by = "TessesFrameworkTorrent"; Args args(argc, argv); - if(args.positional.size() < 2) + if (args.positional.size() < 2) usage(args); - for(auto& opt : args.options) - { - if(opt.first == "tracker") + for (auto &opt : args.options) { + if (opt.first == "tracker") trackers.push_back(opt.second); - else if(opt.first == "webseed") + else if (opt.first == "webseed") webseeds.push_back(opt.second); - else if(opt.first == "comment") + else if (opt.first == "comment") comment = opt.second; - else if(opt.first == "created_by") + else if (opt.first == "created_by") created_by = opt.second; - else if(opt.first == "piece_length") + else if (opt.first == "piece_length") pieceLength = std::stoll(opt.second); } - for(auto& flag : args.flags) - { - if(flag == "help") + for (auto &flag : args.flags) { + if (flag == "help") usage(args); - if(flag == "private") + if (flag == "private") isPrivate = true; } - - if(trackers.empty()) + + if (trackers.empty()) usage(args); { - auto torrent_file_stream = LocalFS->OpenFile(args.positional[1],"wb"); - TorrentFile::CreateTorrent(torrent_file_stream,trackers,webseeds,LocalFS, args.positional[0], isPrivate, pieceLength, comment, created_by); + auto torrent_file_stream = LocalFS->OpenFile(args.positional[1], "wb"); + TorrentFile::CreateTorrent(torrent_file_stream, trackers, webseeds, + LocalFS, args.positional[0], isPrivate, + pieceLength, comment, created_by); } return 0; } \ No newline at end of file diff --git a/apps/tuuid.cpp b/apps/tuuid.cpp index e6b9218..3d6facb 100644 --- a/apps/tuuid.cpp +++ b/apps/tuuid.cpp @@ -1,9 +1,8 @@ #include "TessesFramework/Uuid.hpp" #include -int main(int argc, char** argv) -{ - //e794499c +int main(int argc, char **argv) { + // e794499c using namespace Tesses::Framework; Uuid uuid = Uuid::Generate(); @@ -20,5 +19,7 @@ int main(int argc, char** argv) uuid.node[4] = 0xb4; uuid.node[5] = 0xac;*/ - std::cout << uuid.ToString(Tesses::Framework::UuidStringifyConfig::LowercaseNoCurly) << std::endl; + std::cout << uuid.ToString( + Tesses::Framework::UuidStringifyConfig::LowercaseNoCurly) + << std::endl; } \ No newline at end of file diff --git a/apps/twatch.cpp b/apps/twatch.cpp index e23835d..d1c0351 100644 --- a/apps/twatch.cpp +++ b/apps/twatch.cpp @@ -4,18 +4,16 @@ using namespace Tesses::Framework; using namespace Tesses::Framework::Filesystem; using namespace Tesses::Framework::Filesystem::Literals; -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { TF_Init(); - if(argc<2) - { + if (argc < 2) { std::cout << "USAGE " << argv[0] << " " << std::endl; return 1; } - auto watcher=FSWatcher::Create(LocalFS,VFSPath{argv[1]}); + auto watcher = FSWatcher::Create(LocalFS, VFSPath{argv[1]}); watcher->events = FSWatcherEventType::All; - watcher->event = [](FSWatcherEvent& evt)->void{ + watcher->event = [](FSWatcherEvent &evt) -> void { std::cout << evt.ToString() << std::endl; }; watcher->SetEnabled(true); diff --git a/changelog.md b/changelog.md index ec76a3a..4215de1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,8 @@ # Changelog +## 0.0.4 +Overhaul cmake configuration, add console api, fix http code that caused issues with cgi-bin + ## 0.0.3 Add server sent events, change vfs structure, dark mode error pages and dark mode anonydrop diff --git a/cmake/apps.cmake b/cmake/apps.cmake new file mode 100644 index 0000000..5cde485 --- /dev/null +++ b/cmake/apps.cmake @@ -0,0 +1,67 @@ +if(TESSESFRAMEWORK_ENABLE_APPS) +add_executable(tbin2h apps/tbin2h.cpp) +target_link_libraries(tbin2h PUBLIC tessesframework) +install(TARGETS tbin2h DESTINATION "${CMAKE_INSTALL_BINDIR}") +add_executable(tanonydrop apps/tanonydrop.cpp) +target_link_libraries(tanonydrop PUBLIC tessesframework) +install(TARGETS tanonydrop DESTINATION "${CMAKE_INSTALL_BINDIR}") +add_executable(tfetch apps/tfetch.cpp) +target_link_libraries(tfetch PUBLIC tessesframework) +install(TARGETS tfetch DESTINATION "${CMAKE_INSTALL_BINDIR}") +add_executable(tfileserver apps/tfileserver.cpp) +target_link_libraries(tfileserver PUBLIC tessesframework) +install(TARGETS tfileserver DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(tdoc2json apps/tdoc2json.cpp) +target_link_libraries(tdoc2json PUBLIC tessesframework) +install(TARGETS tdoc2json DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(tjson2doc apps/tjson2doc.cpp) +target_link_libraries(tjson2doc PUBLIC tessesframework) +install(TARGETS tjson2doc DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(tjsonpretty apps/tjsonpretty.cpp) +target_link_libraries(tjsonpretty PUBLIC tessesframework) +install(TARGETS tjsonpretty DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(tjsonunpretty apps/tjsonunpretty.cpp) +target_link_libraries(tjsonunpretty PUBLIC tessesframework) +install(TARGETS tjsonunpretty DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(ttime apps/ttime.cpp) +target_link_libraries(ttime PUBLIC tessesframework) +install(TARGETS ttime DESTINATION "${CMAKE_INSTALL_BINDIR}") +add_executable(tshell apps/tshell.cpp) +target_link_libraries(tshell PUBLIC tessesframework) +install(TARGETS tshell DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(tprinttorrent apps/printtorrent.cpp) + +target_link_libraries(tprinttorrent PUBLIC tessesframework) +install(TARGETS tprinttorrent DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(ttorrentcreate apps/ttorrentcreate.cpp) + +target_link_libraries(ttorrentcreate PUBLIC tessesframework) +install(TARGETS ttorrentcreate DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(tclearwebseed apps/tclearwebseed.cpp) + +target_link_libraries(tclearwebseed PUBLIC tessesframework) +install(TARGETS tclearwebseed DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(trng apps/trng.cpp) + +target_link_libraries(trng PUBLIC tessesframework) +install(TARGETS trng DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(twatch apps/twatch.cpp) + +target_link_libraries(twatch PUBLIC tessesframework) +install(TARGETS twatch DESTINATION "${CMAKE_INSTALL_BINDIR}") + +add_executable(tuuid apps/tuuid.cpp) + +target_link_libraries(tuuid PUBLIC tessesframework) +install(TARGETS tuuid DESTINATION "${CMAKE_INSTALL_BINDIR}") +endif() \ No newline at end of file diff --git a/cmake/examples.cmake b/cmake/examples.cmake new file mode 100644 index 0000000..0d7886c --- /dev/null +++ b/cmake/examples.cmake @@ -0,0 +1,43 @@ +if(TESSESFRAMEWORK_ENABLE_EXAMPLES) + add_executable(echo-my-name examples/echo-my-name.cpp) + target_link_libraries(echo-my-name PUBLIC tessesframework) + + add_executable(echo-platform examples/echo-platform.cpp) + target_link_libraries(echo-platform PUBLIC tessesframework) + + add_executable(console-list examples/console-list.cpp) + target_link_libraries(console-list PUBLIC tessesframework) + + add_executable(console-test examples/console-test.cpp) + target_link_libraries(console-test PUBLIC tessesframework) + add_executable(console-raw examples/console-raw.cpp) + target_link_libraries(console-raw PUBLIC tessesframework) + + add_executable(timer examples/timer.cpp) + target_link_libraries(timer PUBLIC tessesframework) + + add_executable(copyfile examples/copyfile.cpp) + target_link_libraries(copyfile PUBLIC tessesframework) + + add_executable(webserverex examples/webserverex.cpp) + target_link_libraries(webserverex PUBLIC tessesframework) + + add_executable(safesubpath examples/safesubpath.cpp) + target_link_libraries(safesubpath PUBLIC tessesframework) + + add_executable(pathtest examples/pathtest.cpp) + target_link_libraries(pathtest PUBLIC tessesframework) + + add_executable(mountabletest examples/mountabletest.cpp) + target_link_libraries(mountabletest PUBLIC tessesframework) + + add_executable(printjsondecodedemoji examples/printjsondecodedemoji.cpp) + target_link_libraries(printjsondecodedemoji PUBLIC tessesframework) + + add_executable(wsecho examples/wsecho.cpp) + target_link_libraries(wsecho PUBLIC tessesframework) + + + add_executable(tests examples/tests.cpp) + target_link_libraries(tests PUBLIC tessesframework) +endif() \ No newline at end of file diff --git a/cmake/findmbed.cmake b/cmake/findmbed.cmake new file mode 100644 index 0000000..991e974 --- /dev/null +++ b/cmake/findmbed.cmake @@ -0,0 +1,27 @@ +if(TESSESFRAMEWORK_FETCHCONTENT) +set(MBEDTLS_FATAL_WARNINGS OFF CACHE INTERNAL "For TessesFramework" FORCE) +set(ENABLE_TESTING OFF CACHE INTERNAL "For TessesFramework" FORCE) +set(ENABLE_PROGRAMS OFF CACHE INTERNAL "For TessesFramework" FORCE) + +FetchContent_Declare( + mbedtls + URL https://downloads.tesses.net/cache/libraries/source/mbedtls-3.6.4.tar.bz2 + +) +if(TESSESFRAMEWORK_ENABLE_MBED AND TESSESFRAMEWORK_FETCHCONTENT) +set(MBEDTLS_FATAL_WARNINGS OFF CACHE INTERNAL "For TessesFramework" FORCE) +set(ENABLE_TESTING OFF CACHE INTERNAL "For TessesFramework" FORCE) +set(ENABLE_PROGRAMS OFF CACHE INTERNAL "For TessesFramework" FORCE) +if(TESSESFRAMEWORK_STATIC) +set(USE_STATIC_MBEDTLS_LIBRARY ON CACHE INTERNAL "For TessesFramework" FORCE) +set(USE_SHARED_MBEDTLS_LIBRARY OFF CACHE INTERNAL "For TessesFramework" FORCE) +else() +set(USE_STATIC_MBEDTLS_LIBRARY OFF CACHE INTERNAL "For TessesFramework" FORCE) +set(USE_SHARED_MBEDTLS_LIBRARY ON CACHE INTERNAL "For TessesFramework" FORCE) +endif() + +endif() +FetchContent_MakeAvailable(mbedtls) +else() +find_package(MbedTLS REQUIRED) +endif() \ No newline at end of file diff --git a/cmake/helpers.cmake b/cmake/helpers.cmake new file mode 100644 index 0000000..6706c5f --- /dev/null +++ b/cmake/helpers.cmake @@ -0,0 +1,78 @@ +if(TESSESFRAMEWORK_ENABLE_SQLITE) +target_compile_definitions(tessesframework PRIVATE TESSESFRAMEWORK_ENABLE_SQLITE) +endif() +if(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) +target_compile_definitions(tessesframework PRIVATE TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) +endif() +if(TESSESFRAMEWORK_ENABLE_PROCESS) +target_compile_definitions(tessesframework PRIVATE TESSESFRAMEWORK_ENABLE_PROCESS) + +endif() +target_include_directories(tessesframework + PUBLIC + "$" + "$" +) +target_include_directories(tessesframework + PUBLIC + "$" + "$" +) +if(WIN32) +target_link_libraries(tessesframework PRIVATE iphlpapi) +target_link_libraries(tessesframework PRIVATE ws2_32) + +endif() + +if(TESSESFRAMEWORK_LOGTOFILE) +target_compile_definitions(tessesframework PRIVATE TESSESFRAMEWORK_LOGTOFILE) +endif() +if(TESSESFRAMEWORK_ENABLE_THREADING) +target_compile_definitions(tessesframework PRIVATE TESSESFRAMEWORK_ENABLE_THREADING) +endif() +if(TESSESFRAMEWORK_ENABLE_NETWORKING) +target_compile_definitions(tessesframework PRIVATE TESSESFRAMEWORK_ENABLE_NETWORKING) +endif() +if(TESSESFRAMEWORK_FETCHCONTENT) +target_compile_definitions(tessesframework PRIVATE TESSESFRAMEWORK_FETCHCONTENT) +endif() +if(TESSESFRAMEWORK_ENABLE_MBED) +target_compile_definitions(tessesframework PRIVATE TESSESFRAMEWORK_ENABLE_MBED) +if(TESSESFRAMEWORK_EMBED_CERT_BUNDLE) +target_compile_definitions(tessesframework PRIVATE TESSESFRAMEWORK_EMBED_CERT_BUNDLE) +else() +target_compile_definitions(tessesframework PRIVATE TESSESFRAMEWORK_CERT_BUNDLE_FILE=${TESSESFRAMEWORK_CERT_BUNDLE_FILE}) +endif() + +if(TESSESFRAMEWORK_FETCHCONTENT) +target_link_libraries(tessesframework PRIVATE mbedtls mbedx509 mbedcrypto everest p256m) +list(APPEND TessesFrameworkLibs mbedtls mbedx509 mbedcrypto everest p256m) +else() +target_link_libraries(tessesframework PRIVATE mbedtls mbedx509 mbedcrypto) + +endif() +endif() + + +if(NOT(WIN32 OR PLATFORM_PS2)) + +if(TESSESFRAMEWORK_ENABLE_THREADING) +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) +target_link_libraries(tessesframework PRIVATE Threads::Threads) +endif() +endif() + +if(NOT(WIN32 OR PLATFORM_PS2 OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube") OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoSwitch") +check_library_exists(util forkpty "" HAVE_LIBUTIL) +if(HAVE_LIBUTIL) + target_link_libraries(tessesframework PRIVATE util) +endif() +endif() + +if(NOT(WIN32 OR PLATFORM_PS2 OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube") OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoSwitch") +check_library_exists(dl dlopen "" HAVE_LIBDL) +if(HAVE_LIBDL) + target_link_libraries(tessesframework PRIVATE dl) +endif() +endif() \ No newline at end of file diff --git a/cmake/install-dev.cmake b/cmake/install-dev.cmake new file mode 100644 index 0000000..d15f3a2 --- /dev/null +++ b/cmake/install-dev.cmake @@ -0,0 +1,29 @@ +if(TESSESFRAMEWORK_INSTALL_DEVELOPMENT) +install(TARGETS ${TessesFrameworkLibs} + EXPORT TessesFrameworkTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/TessesFramework DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +install(EXPORT TessesFrameworkTargets + FILE TessesFrameworkTargets.cmake + NAMESPACE TessesFramework:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TessesFramework +) + + +include(CMakePackageConfigHelpers) +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/TessesFrameworkConfig.cmake" +INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TessesFramework) +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/TessesFrameworkFeatures.h.in "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/TessesFrameworkFeatures.h" +INSTALL_DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/TessesFramework/TessesFrameworkFeatures.h) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/TessesFrameworkConfig.cmake" +DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TessesFramework) + +endif() \ No newline at end of file diff --git a/cmake/options.cmake b/cmake/options.cmake new file mode 100644 index 0000000..ce33247 --- /dev/null +++ b/cmake/options.cmake @@ -0,0 +1,16 @@ +option(TESSESFRAMEWORK_EMBED_CERT_BUNDLE "Embed the certificate chain bundle" ON) +option(TESSESFRAMEWORK_ENABLE_MBED "Enable Tesses Framework mbedtls" ON) +option(TESSESFRAMEWORK_ENABLE_NETWORKING "Enable Networking" ON) +option(TESSESFRAMEWORK_ENABLE_THREADING "Enable Threading" ON) +option(TESSESFRAMEWORK_ENABLE_SQLITE "Enable sqlite (embedded in source)" ON) +option(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS "Enable sago platformfolders (embedded in source)" ON) +option(TESSESFRAMEWORK_ENABLE_PROCESS "Enable process api" ON) +option(TESSESFRAMEWORK_ENABLE_RPATH "Enable RPATH" ON) +option(TESSESFRAMEWORK_ENABLE_EXAMPLES "Enable Tesses Framework examples" ON) +option(TESSESFRAMEWORK_ENABLE_APPS "Enable Tesses Framework cli apps" ON) +option(TESSESFRAMEWORK_INSTALL_DEVELOPMENT "Enable Installing Tesses Framework Development Packages" ON) +option(TESSESFRAMEWORK_STATIC "Build with static libraries instead of shared" OFF) +option(TESSESFRAMEWORK_ENABLE_SETDATE "Enable setting date to file" ON) +option(TESSESFRAMEWORK_LOGTOFILE "TessesFramework Log to file" OFF) +option(TESSESFRAMEWORK_FETCHCONTENT "TessesFramework fetchcontent" ON) +option(TESSESFRAMEWORK_VENDERCERTCHAIN "Use the ca-certificates.crt in project rather than system" ON) diff --git a/cmake/sharedlib.cmake b/cmake/sharedlib.cmake new file mode 100644 index 0000000..3d2c53e --- /dev/null +++ b/cmake/sharedlib.cmake @@ -0,0 +1,21 @@ +if(TESSESFRAMEWORK_ENABLE_RPATH) +set(CMAKE_MACOSX_RPATH 1) +set(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE) + +if (APPLE) + set(CMAKE_INSTALL_RPATH "@executable_path/../${CMAKE_INSTALL_LIBDIR}") +else() + set(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}") +endif() +endif() +add_library(tessesframework SHARED ${TESSESFRAMEWORK_SOURCE}) +set_target_properties(tessesframework PROPERTIES + VERSION ${TESSESFRAMEWORK_MAJOR_VERSION}.${TESSESFRAMEWORK_MINOR_VERSION}.${TESSESFRAMEWORK_PATCH_VERSION} + SOVERSION ${TESSESFRAMEWORK_MAJOR_VERSION} +) +include(${CMAKE_CURRENT_LIST_DIR}/helpers.cmake) + +install(TARGETS tessesframework DESTINATION) + + +list(APPEND TessesFrameworkLibs tessesframework) \ No newline at end of file diff --git a/cmake/sources.cmake b/cmake/sources.cmake index 6588531..2587293 100644 --- a/cmake/sources.cmake +++ b/cmake/sources.cmake @@ -1,4 +1,5 @@ list(APPEND TESSESFRAMEWORK_SOURCE +src/Console.cpp src/Random.cpp src/Date/Date.cpp src/Http/FileServer.cpp diff --git a/cmake/staticlib.cmake b/cmake/staticlib.cmake new file mode 100644 index 0000000..13a6969 --- /dev/null +++ b/cmake/staticlib.cmake @@ -0,0 +1,14 @@ +add_library(tessesframework STATIC ${TESSESFRAMEWORK_SOURCE}) + +include(${CMAKE_CURRENT_LIST_DIR}/helpers.cmake) + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube") +target_link_libraries(tessesframework PUBLIC fat) + +endif() +if("${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii") +target_link_libraries(tessesframework PUBLIC wiisocket) +target_compile_definitions(tessesframework PUBLIC TESSESFRAMEWORK_USE_WII_SOCKET) +endif() + +list(APPEND TessesFrameworkLibs tessesframework) \ No newline at end of file diff --git a/cmake/version.cmake b/cmake/version.cmake new file mode 100644 index 0000000..478244a --- /dev/null +++ b/cmake/version.cmake @@ -0,0 +1,3 @@ +set(TESSESFRAMEWORK_MAJOR_VERSION 0) +set(TESSESFRAMEWORK_MINOR_VERSION 0) +set(TESSESFRAMEWORK_PATCH_VERSION 4) \ No newline at end of file diff --git a/examples/console-list.cpp b/examples/console-list.cpp new file mode 100644 index 0000000..31f1c94 --- /dev/null +++ b/examples/console-list.cpp @@ -0,0 +1,31 @@ +#include "TessesFramework/TessesFramework.hpp" + +int main(int argc, char** argv) +{ + using namespace Tesses::Framework; + using namespace Tesses::Framework::TextStreams; + TF_Init(); + + std::vector myList = { + "Demi Lovato", + "The Gremlins", + "Al Gore", + "Steve Ballmer", + "CrossLang", + "Tom Scott", + "Louis Rossmann", + "Mike Nolan", + "Tim Cook" + }; + + auto res = Console::List(myList); + + if(res > myList.size()) + { + Console::WriteLine("You must select one"); + } + else { + StdOut() << "You selected " << myList[res] << "." << NewLine(); + } + return 0; +} \ No newline at end of file diff --git a/examples/console-raw.cpp b/examples/console-raw.cpp new file mode 100644 index 0000000..6410f74 --- /dev/null +++ b/examples/console-raw.cpp @@ -0,0 +1,20 @@ +#include "TessesFramework/TessesFramework.hpp" + +int main(int argc, char** argv) +{ + using namespace Tesses::Framework; + TF_Init(); + Console::WriteLine("Press q to quit"); + Console::SetEcho(false); + Console::SetCanonical(false); + while(int r = Console::Read()) + { + if(r == 'q') + { + break; + } + Console::WriteLine(std::to_string(r)); + } + Console::SetCanonical(true); + Console::SetEcho(true); +} \ No newline at end of file diff --git a/examples/console-test.cpp b/examples/console-test.cpp new file mode 100644 index 0000000..f59ce56 --- /dev/null +++ b/examples/console-test.cpp @@ -0,0 +1,20 @@ +#include + +using namespace Tesses::Framework; + +int main(int argc, char** argv) +{ + TF_Init(); + Console::SetPosition(0,0); + Console::WriteLine("Demi Lovato Is Cute Right"); + Console::WriteLine("Right"); + Console::WriteLine("Al Gore"); + Console::WriteLine("Demi Lovato"); + Console::WriteLine("Hello \x1B[33myellow\x1B[37m world"); + Console::ReadLine(); + + Console::SetPosition(2,1); + + Console::ClearRetainPosition(ClearBehaviour::CB_CURSORANDBELOW); + Console::ReadLine(); +} \ No newline at end of file diff --git a/examples/copyfile.cpp b/examples/copyfile.cpp index ad884e6..e9ff748 100644 --- a/examples/copyfile.cpp +++ b/examples/copyfile.cpp @@ -2,13 +2,11 @@ using namespace Tesses::Framework::Filesystem; -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { LocalFilesystem fs; VFSPath src = fs.SystemToVFSPath(argv[1]); VFSPath dest = fs.SystemToVFSPath(argv[2]); - auto srcs = fs.OpenFile(src,"rb"); - auto dests = fs.OpenFile(dest,"wb"); + auto srcs = fs.OpenFile(src, "rb"); + auto dests = fs.OpenFile(dest, "wb"); srcs->CopyTo(dests); - } \ No newline at end of file diff --git a/examples/echo-my-name.cpp b/examples/echo-my-name.cpp new file mode 100644 index 0000000..27d4dc5 --- /dev/null +++ b/examples/echo-my-name.cpp @@ -0,0 +1,9 @@ +#include "TessesFramework/TessesFramework.hpp" + +int main(int argc, char** argv) +{ + using namespace Tesses::Framework; + TF_Init(); + Console::WriteLine("\"" + TF_GetExecutableName() + "\""); + return 0; +} \ No newline at end of file diff --git a/examples/echo-platform.cpp b/examples/echo-platform.cpp new file mode 100644 index 0000000..3655ab5 --- /dev/null +++ b/examples/echo-platform.cpp @@ -0,0 +1,9 @@ +#include "TessesFramework/TessesFramework.hpp" + +int main(int argc, char** argv) +{ + using namespace Tesses::Framework; + TF_Init(); + Console::WriteLine(Tesses::Framework::Platform::Environment::GetPlatform()); + return 0; +} \ No newline at end of file diff --git a/examples/mountabletest.cpp b/examples/mountabletest.cpp index 729cced..95b939f 100644 --- a/examples/mountabletest.cpp +++ b/examples/mountabletest.cpp @@ -5,57 +5,50 @@ using namespace Tesses::Framework; using namespace Tesses::Framework::Filesystem; using namespace Tesses::Framework::Streams; -int main(int argc, char** argv) -{ - if(argc < 2) - { - printf("USAGE: %s \n",argv[0]); +int main(int argc, char **argv) { + if (argc < 2) { + printf("USAGE: %s \n", argv[0]); return 1; } - std::string root = "./root"; std::string mountDemi = "./demi"; std::string mountJoelSlashJim = "./joelslashjim"; - std::shared_ptr rootdir = std::make_shared(LocalFS,root); + std::shared_ptr rootdir = + std::make_shared(LocalFS, root); - std::shared_ptr mountDemidir = std::make_shared(LocalFS,mountDemi); + std::shared_ptr mountDemidir = + std::make_shared(LocalFS, mountDemi); + std::shared_ptr mountjohnslashjim = + std::make_shared(LocalFS, mountJoelSlashJim); - std::shared_ptr mountjohnslashjim = std::make_shared(LocalFS,mountJoelSlashJim); - - std::shared_ptr fs = std::make_shared(rootdir); + std::shared_ptr fs = + std::make_shared(rootdir); fs->Mount(std::string("/demi"), mountDemidir); fs->Mount(std::string("/joel/jim"), mountjohnslashjim); std::string command = argv[1]; - - if(command == "ls") - { + if (command == "ls") { std::string dir = "/"; - if(argc > 2) dir = argv[2]; - - for(auto item : fs->EnumeratePaths(dir)) - { + if (argc > 2) + dir = argv[2]; + + for (auto item : fs->EnumeratePaths(dir)) { std::cout << item.GetFileName() << std::endl; } - } - else if(command == "cat") - { - std::shared_ptr strm = std::make_shared(stdout, false,"wb",false); - for(int a = 2; a < argc; a++) - { + } else if (command == "cat") { + std::shared_ptr strm = + std::make_shared(stdout, false, "wb", false); + for (int a = 2; a < argc; a++) { std::string path = argv[a]; - auto f = fs->OpenFile(path,"rb"); - if(f != nullptr) - { + auto f = fs->OpenFile(path, "rb"); + if (f != nullptr) { f->CopyTo(strm); - } } } - } \ No newline at end of file diff --git a/examples/pathtest.cpp b/examples/pathtest.cpp index 4b569fc..971fca5 100644 --- a/examples/pathtest.cpp +++ b/examples/pathtest.cpp @@ -3,14 +3,15 @@ #include using namespace Tesses::Framework::Filesystem; - -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { VFSPath path("C:/home/user/file"); VFSPath path2("D:/home/user/file"); VFSPath path3("C:/home/user"); std::cout << path.MakeRelative(path2).ToString() << std::endl; - std::cout << (path3 / path.MakeRelative(path2) / "John").CollapseRelativeParents().ToString() << std::endl; + std::cout << (path3 / path.MakeRelative(path2) / "John") + .CollapseRelativeParents() + .ToString() + << std::endl; return 0; } \ No newline at end of file diff --git a/examples/printjsondecodedemoji.cpp b/examples/printjsondecodedemoji.cpp index ab17533..cddcaf4 100644 --- a/examples/printjsondecodedemoji.cpp +++ b/examples/printjsondecodedemoji.cpp @@ -2,12 +2,10 @@ using namespace Tesses::Framework::Serialization::Json; -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { auto json = Json::Decode("\"\\uD83D\\uDE44\""); std::string str; - if(TryGetJToken(json,str)) - { + if (TryGetJToken(json, str)) { std::cout << str << std::endl; } } \ No newline at end of file diff --git a/examples/safesubpath.cpp b/examples/safesubpath.cpp index 9b74be2..df7ef72 100644 --- a/examples/safesubpath.cpp +++ b/examples/safesubpath.cpp @@ -3,17 +3,15 @@ using namespace Tesses::Framework::Filesystem; -int main(int argc, char** argv) -{ - if(argc < 3) - { +int main(int argc, char **argv) { + if (argc < 3) { std::cout << "USAGE: " << argv[0] << " \n"; return 1; } VFSPath parent(argv[1]); VFSPath subpath(argv[2]); - VFSPath newPath(parent,subpath.CollapseRelativeParents()); + VFSPath newPath(parent, subpath.CollapseRelativeParents()); std::cout << newPath.ToString() << "\n"; } \ No newline at end of file diff --git a/examples/tests.cpp b/examples/tests.cpp index d36d72e..1e93ac1 100644 --- a/examples/tests.cpp +++ b/examples/tests.cpp @@ -1,15 +1,15 @@ #include "TessesFramework/TessesFramework.hpp" -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { Tesses::Framework::TF_Init(); std::cout << "Basic auth test" << std::endl; - std::string theEncoded="dXNlcjpwYXNz"; + std::string theEncoded = "dXNlcjpwYXNz"; Tesses::Framework::Http::ServerContext ctx(nullptr); - ctx.requestHeaders.SetValue("Authorization","Basic " + theEncoded); + ctx.requestHeaders.SetValue("Authorization", "Basic " + theEncoded); std::string user; std::string pass; - std::cout << Tesses::Framework::Http::BasicAuthServer::GetCreds(ctx,user,pass) << std::endl; + std::cout << Tesses::Framework::Http::BasicAuthServer::GetCreds(ctx, user, + pass) + << std::endl; std::cout << user << std::endl; std::cout << pass << std::endl; - } \ No newline at end of file diff --git a/examples/timer.cpp b/examples/timer.cpp index 50a7665..bd34242 100644 --- a/examples/timer.cpp +++ b/examples/timer.cpp @@ -1,15 +1,12 @@ #include "TessesFramework/Common.hpp" #include "TessesFramework/TessesFramework.hpp" - -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { using namespace Tesses::Framework; TF_Init(); - auto timer = TF_Timer([]()->void { - std::cout << "Hi, I am a timer" << std::endl; - }); + auto timer = TF_Timer( + []() -> void { std::cout << "Hi, I am a timer" << std::endl; }); TF_RunEventLoop(); diff --git a/examples/webserverex.cpp b/examples/webserverex.cpp index 387d20d..d8d2e42 100644 --- a/examples/webserverex.cpp +++ b/examples/webserverex.cpp @@ -11,206 +11,171 @@ using namespace Tesses::Framework::Threading; std::shared_ptr sse = std::make_shared(); -class Johnny : public ServerContextData -{ - public: - Johnny() - { - text = "Steve Ballmer"; - } - std::string text; - ~Johnny() - { - std::cout << "Destroying" << std::endl; - } +class Johnny : public ServerContextData { + public: + Johnny() { text = "Steve Ballmer"; } + std::string text; + ~Johnny() { std::cout << "Destroying" << std::endl; } }; class MyWebServer : public IHttpServer { - public: - bool Handle(ServerContext& ctx) - { - std::cout << ctx.path << std::endl; - if(ctx.path == "/") - { - std::shared_ptr fs = std::make_shared("index.html","rb"); + public: + bool Handle(ServerContext &ctx) { + std::cout << ctx.path << std::endl; + if (ctx.path == "/") { + std::shared_ptr fs = + std::make_shared("index.html", "rb"); - ctx - .WithMimeType("text/html") - .SendStream(fs); - return true; - } - else if(ctx.path == "/mypath.html") - { - std::string txt = "

Root: " + HttpUtils::HtmlEncode(ctx.GetServerRoot()) + "

"; + ctx.WithMimeType("text/html").SendStream(fs); + return true; + } else if (ctx.path == "/mypath.html") { + std::string txt = + "

Root: " + HttpUtils::HtmlEncode(ctx.GetServerRoot()) + + "

"; + ctx.WithMimeType("text/html").SendText(txt); + return true; + } else if (ctx.path == "/getabsolute.html") { + std::string path; + if (ctx.queryParams.TryGetFirst("path", path)) { + std::string txt = + "

Path: " + + HttpUtils::HtmlEncode(ctx.MakeAbsolute(path)) + "

"; ctx.WithMimeType("text/html").SendText(txt); return true; } - else if(ctx.path == "/getabsolute.html") - { - std::string path; - if(ctx.queryParams.TryGetFirst("path",path)) - { - std::string txt = "

Path: " + HttpUtils::HtmlEncode(ctx.MakeAbsolute(path)) + "

"; - ctx.WithMimeType("text/html").SendText(txt); - return true; - } + } else if (ctx.path == "/streaming.html") { + StreamWriter writer(ctx.OpenResponseStream()); + writer.WriteLine(""); + writer.WriteLine("Streaming"); + writer.WriteLine(""); + + writer.WriteLine("

Streaming

"); + + writer.WriteLine("
    "); + + for (size_t i = 0; i < 10000; i++) { + writer.WriteLine("
  • " + std::to_string(i) + "
  • "); + TF_Sleep(10); } - else if(ctx.path == "/streaming.html") - { - StreamWriter writer(ctx.OpenResponseStream()); - writer.WriteLine(""); - writer.WriteLine("Streaming"); - writer.WriteLine(""); - writer.WriteLine("

    Streaming

    "); + writer.WriteLine("
"); - writer.WriteLine("
    "); + writer.WriteLine(""); + writer.WriteLine(""); + return true; + } else if (ctx.path == "/ssetest.html") { - for(size_t i=0;i<10000; i++) - { - writer.WriteLine("
  • " + std::to_string(i) + "
  • "); - TF_Sleep(10); - } + } else if (ctx.path == "/sse") { + ctx.SendServerSentEvents(sse); + return true; + } else if (ctx.path == "/main.js") { - writer.WriteLine("
"); + std::shared_ptr fs = + std::make_shared("main.js", "rb"); - writer.WriteLine(""); - writer.WriteLine(""); - return true; - } - else if(ctx.path == "/ssetest.html") - { - - } - else if(ctx.path == "/sse") - { - ctx.SendServerSentEvents(sse); - return true; - } - else if(ctx.path == "/main.js") - { - - std::shared_ptr fs = std::make_shared("main.js","rb"); - - ctx - .WithMimeType("text/js") - .SendStream(fs); - return true; - } - else if(ctx.path == "/upload") - { - ctx.ParseFormData([](std::string mime, std::string filename, std::string name)->std::shared_ptr{ - return std::make_shared(filename,"wb"); + ctx.WithMimeType("text/js").SendStream(fs); + return true; + } else if (ctx.path == "/upload") { + ctx.ParseFormData( + [](std::string mime, std::string filename, std::string name) + -> std::shared_ptr { + return std::make_shared(filename, "wb"); }); - } - else if(ctx.path == "/steve") - { - Johnny* data = ctx.GetServerContentData("mytag"); - data->text = "Demi Lovato"; - } - - return false; + } else if (ctx.path == "/steve") { + Johnny *data = ctx.GetServerContentData("mytag"); + data->text = "Demi Lovato"; } + return false; + } }; -class MyOtherWebServer : public IHttpServer -{ - public: - bool Handle(ServerContext& ctx) - { - TF_LOG("IN HANDLE"); - if(ctx.path == "/") - { +class MyOtherWebServer : public IHttpServer { + public: + bool Handle(ServerContext &ctx) { + TF_LOG("IN HANDLE"); + if (ctx.path == "/") { std::string name; - if(ctx.queryParams.TryGetFirst("name",name)) - { + if (ctx.queryParams.TryGetFirst("name", name)) { std::cout << name << std::endl; TF_LOG(name); - ctx - .WithMimeType("text/plain") - .WithContentDisposition(HttpUtils::Sanitise(name) + ".txt",false) - .SendText(name + " is cool."); - //do something with q + ctx.WithMimeType("text/plain") + .WithContentDisposition(HttpUtils::Sanitise(name) + ".txt", + false) + .SendText(name + " is cool."); + // do something with q return true; } - } - else if(ctx.path == "/status") - { - int64_t num; - if(ctx.queryParams.TryGetFirstInt("code", num)) - { - ctx.statusCode = (StatusCode)num; - } - - ctx.SendErrorPage(true); - return true; - } - else if(ctx.path == "/error") - { - throw std::runtime_error("This is a error"); - } - else if(ctx.path == "/error-debug") - { - ctx.WithDebug(true); - throw std::runtime_error("Platform is " + Tesses::Framework::Platform::Environment::GetPlatform()); - } - else if(ctx.path == "/mymount/steve") - { - Johnny* data = ctx.GetServerContentData("mytag"); - ctx.SendText(data->text); - return true; - } - else if(ctx.path == "/mypath.html") - { - std::string txt = "

Root: " + HttpUtils::HtmlEncode(ctx.GetServerRoot()) + "

"; - ctx.WithMimeType("text/html").SendText(txt); - return true; + } else if (ctx.path == "/status") { + int64_t num; + if (ctx.queryParams.TryGetFirstInt("code", num)) { + ctx.statusCode = (StatusCode)num; } - return false; + ctx.SendErrorPage(true); + return true; + } else if (ctx.path == "/error") { + throw std::runtime_error("This is a error"); + } else if (ctx.path == "/error-debug") { + ctx.WithDebug(true); + throw std::runtime_error( + "Platform is " + + Tesses::Framework::Platform::Environment::GetPlatform()); + } else if (ctx.path == "/mymount/steve") { + Johnny *data = ctx.GetServerContentData("mytag"); + ctx.SendText(data->text); + return true; + } else if (ctx.path == "/mypath.html") { + std::string txt = + "

Root: " + HttpUtils::HtmlEncode(ctx.GetServerRoot()) + + "

"; + ctx.WithMimeType("text/html").SendText(txt); + return true; } + + return false; + } }; -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { TF_InitWithConsole(); int64_t timer = 0; - auto timerHDL= TF_Timer([&timer]()->void{ + auto timerHDL = TF_Timer([&timer]() -> void { timer++; - sse->SendData("Timer has ticked " + std::to_string(timer) + " times now"); + sse->SendData("Timer has ticked " + std::to_string(timer) + + " times now"); }); std::shared_ptr routeSvr = std::make_shared(); - routeSvr->Get("/name/{name}/greeting",[](ServerContext& ctx)->bool{ + routeSvr->Get("/name/{name}/greeting", [](ServerContext &ctx) -> bool { std::string name; - if(ctx.pathArguments.TryGetFirst("name",name)) - { + if (ctx.pathArguments.TryGetFirst("name", name)) { ctx.WithMimeType("text/plain").SendText("Hello " + name); - } - else { + } else { ctx.WithMimeType("text/plain").SendText("Please provide a name"); } return true; }); - routeSvr->Get("/name/{name}/length",[](ServerContext& ctx)->bool{ + routeSvr->Get("/name/{name}/length", [](ServerContext &ctx) -> bool { std::string name; - if(ctx.pathArguments.TryGetFirst("name",name)) - { - ctx.WithMimeType("text/plain").SendText("The length of the name is " + std::to_string(name.size())); - } - else { + if (ctx.pathArguments.TryGetFirst("name", name)) { + ctx.WithMimeType("text/plain") + .SendText("The length of the name is " + + std::to_string(name.size())); + } else { ctx.WithMimeType("text/plain").SendText("Please provide a name"); } return true; }); - std::shared_ptr myo = std::make_shared(); + std::shared_ptr myo = + std::make_shared(); std::shared_ptr mws = std::make_shared(); - std::shared_ptr mountable = std::make_shared(myo); - mountable->Mount("/mymount/",mws); + std::shared_ptr mountable = + std::make_shared(myo); + mountable->Mount("/mymount/", mws); mountable->Mount("/routeSvr/", routeSvr); - HttpServer server(10001,mountable); + HttpServer server(10001, mountable); server.StartAccepting(); TF_RunEventLoop(); std::cout << "Closing server" << std::endl; diff --git a/examples/wrappertest.c b/examples/wrappertest.c deleted file mode 100644 index 8c7037d..0000000 --- a/examples/wrappertest.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "TessesFramework.h" - -int main(int argc, char** argv) -{ - tf_vfs_t* local = tf_vfs_create_local(); - - tf_vfs_dir_t* dir = tf_vfs_opendir(local, "/home/mike"); - - string_t* str; - while(str = tf_vfs_readdir(dir)) - { - string_println(str); - } - tf_vfs_closedir(dir); - - tf_stream_t* strm = tf_vfs_open(local,"/home/mike/myPhoto.png", "wb"); - - - - tf_vfs_close(local); -} \ No newline at end of file diff --git a/examples/wsecho.cpp b/examples/wsecho.cpp index 8a5056c..a745464 100644 --- a/examples/wsecho.cpp +++ b/examples/wsecho.cpp @@ -3,46 +3,35 @@ #include using namespace Tesses::Framework::Http; -class WebSocketConn : public WebSocketConnection -{ - - public: - void OnOpen(std::function sendMessage, std::function ping, std::function close) - { - while(true) - { - std::cout << "> "; - std::string req; - std::getline(std::cin, req); - if(req == "exit") - break; - else - SendWebSocketMessage(sendMessage,req); - } - close(); - - } - void OnReceive(WebSocketMessage& message) - { - std::cout << "Message: " << message.ToString() << std::endl; - - - } - void OnClose(bool clean) - { - std::cout << (clean ? "Closed cleanly" : "Closed unclean") << std::endl; - } - -}; +class WebSocketConn : public WebSocketConnection { -int main(int argc, char** argv) -{ + public: + void OnOpen(std::function sendMessage, + std::function ping, std::function close) { + while (true) { + std::cout << "> "; + std::string req; + std::getline(std::cin, req); + if (req == "exit") + break; + else + SendWebSocketMessage(sendMessage, req); + } + close(); + } + void OnReceive(WebSocketMessage &message) { + std::cout << "Message: " << message.ToString() << std::endl; + } + void OnClose(bool clean) { + std::cout << (clean ? "Closed cleanly" : "Closed unclean") << std::endl; + } +}; + +int main(int argc, char **argv) { Tesses::Framework::TF_Init(); HttpDictionary reqHeaders; std::shared_ptr conn = std::make_shared(); - WebSocketClient("ws://echo.websocket.org/",reqHeaders,conn); - + WebSocketClient("ws://echo.websocket.org/", reqHeaders, conn); + return 0; } - - diff --git a/include/TessesFramework/Args.hpp b/include/TessesFramework/Args.hpp index 03aead2..a5d9b8b 100644 --- a/include/TessesFramework/Args.hpp +++ b/include/TessesFramework/Args.hpp @@ -1,13 +1,31 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "Common.hpp" namespace Tesses::Framework { - class Args { - public: - Args(std::vector args); - Args(int argc, char** argv); - std::string filename; - std::vector positional; - std::vector flags; - std::vector> options; - }; -} \ No newline at end of file +class Args { + public: + Args(std::vector args); + Args(int argc, char **argv); + std::string filename; + std::vector positional; + std::vector flags; + std::vector> options; +}; +} // namespace Tesses::Framework \ No newline at end of file diff --git a/include/TessesFramework/BitTorrent/TorrentFile.hpp b/include/TessesFramework/BitTorrent/TorrentFile.hpp index eae5b16..b022660 100644 --- a/include/TessesFramework/BitTorrent/TorrentFile.hpp +++ b/include/TessesFramework/BitTorrent/TorrentFile.hpp @@ -1,70 +1,98 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Serialization/Bencode.hpp" #include "TorrentStream.hpp" -namespace Tesses::Framework::BitTorrent -{ - constexpr int DEFAULT_PIECE_LENGTH = 524288; - - class TorrentFileInfo { - - Serialization::Bencode::BeDictionary info; - public: - TorrentFileInfo(); - TorrentFileInfo(const Serialization::Bencode::BeDictionary& tkn); - - - int64_t piece_length=DEFAULT_PIECE_LENGTH; - Serialization::Bencode::BeString pieces; - int64_t isPrivate=0; - Serialization::Bencode::BeString name; - std::variant> fileListOrLength; - int64_t GetTorrentFileSize(); - /// @brief Generate the info - /// @return the dictionary containing the info - Serialization::Bencode::BeDictionary& GenerateInfoDictionary(); - /// @brief Get the info hash for info (if you are filling out the fields, you need to call GenerateInfoDictionary first) - /// @return the 20 byte info hash - Serialization::Bencode::BeString GetInfoHash(); - /// @brief Get the info (does not regenerate info from the fields) - /// @return the dictionary containing the info - Serialization::Bencode::BeDictionary& GetInfoDictionary(); +namespace Tesses::Framework::BitTorrent { +constexpr int DEFAULT_PIECE_LENGTH = 524288; - /// @brief Used to load a dictionary in, will overwrite all the fields - /// @param dict the dictionary to load - void Load(const Serialization::Bencode::BeDictionary& dict); - /// @brief Gets a read write based on this info object - /// @param vfs the vfs object - /// @param path file or directory inside vfs - /// @return The read write at object - std::shared_ptr GetStreamFromFilesystem(std::shared_ptr vfs, const Tesses::Framework::Filesystem::VFSPath& path); +class TorrentFileInfo { - /// @brief Create info from file/directory - /// @param vfs the vfs object - /// @param path file or directory inside vfs - /// @param isPrivate whether you use private trackers - /// @param pieceLength length of pieces - void CreateFromFilesystem(std::shared_ptr vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate=false, int64_t pieceLength=DEFAULT_PIECE_LENGTH); - }; - class TorrentFile { - public: - TorrentFile(); - TorrentFile(const Serialization::Bencode::BeDictionary& tkn); + Serialization::Bencode::BeDictionary info; + public: + TorrentFileInfo(); + TorrentFileInfo(const Serialization::Bencode::BeDictionary &tkn); - Serialization::Bencode::BeString announce; - std::vector announce_list; - std::vector url_list; - Serialization::Bencode::BeString comment; - Serialization::Bencode::BeString created_by; - int64_t creation_date=0; - TorrentFileInfo info; + int64_t piece_length = DEFAULT_PIECE_LENGTH; + Serialization::Bencode::BeString pieces; + int64_t isPrivate = 0; + Serialization::Bencode::BeString name; + std::variant> fileListOrLength; + int64_t GetTorrentFileSize(); + /// @brief Generate the info + /// @return the dictionary containing the info + Serialization::Bencode::BeDictionary &GenerateInfoDictionary(); + /// @brief Get the info hash for info (if you are filling out the fields, + /// you need to call GenerateInfoDictionary first) + /// @return the 20 byte info hash + Serialization::Bencode::BeString GetInfoHash(); + /// @brief Get the info (does not regenerate info from the fields) + /// @return the dictionary containing the info + Serialization::Bencode::BeDictionary &GetInfoDictionary(); + /// @brief Used to load a dictionary in, will overwrite all the fields + /// @param dict the dictionary to load + void Load(const Serialization::Bencode::BeDictionary &dict); + /// @brief Gets a read write based on this info object + /// @param vfs the vfs object + /// @param path file or directory inside vfs + /// @return The read write at object + std::shared_ptr GetStreamFromFilesystem( + std::shared_ptr vfs, + const Tesses::Framework::Filesystem::VFSPath &path); - Serialization::Bencode::BeDictionary ToDictionary(); + /// @brief Create info from file/directory + /// @param vfs the vfs object + /// @param path file or directory inside vfs + /// @param isPrivate whether you use private trackers + /// @param pieceLength length of pieces + void CreateFromFilesystem( + std::shared_ptr vfs, + const Tesses::Framework::Filesystem::VFSPath &path, + bool isPrivate = false, int64_t pieceLength = DEFAULT_PIECE_LENGTH); +}; +class TorrentFile { + public: + TorrentFile(); + TorrentFile(const Serialization::Bencode::BeDictionary &tkn); - void Print(std::shared_ptr writer); + Serialization::Bencode::BeString announce; + std::vector announce_list; + std::vector url_list; + Serialization::Bencode::BeString comment; + Serialization::Bencode::BeString created_by; + int64_t creation_date = 0; + TorrentFileInfo info; - static void CreateTorrent(std::shared_ptr torrent_file_stream,const std::vector& trackers,const std::vector& webseeds, std::shared_ptr vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate=false, int64_t pieceLength=DEFAULT_PIECE_LENGTH, Serialization::Bencode::BeString comment="", Serialization::Bencode::BeString created_by = "TessesFrameworkTorrent"); - - }; -} \ No newline at end of file + Serialization::Bencode::BeDictionary ToDictionary(); + + void Print(std::shared_ptr writer); + + static void CreateTorrent( + std::shared_ptr torrent_file_stream, + const std::vector &trackers, + const std::vector &webseeds, + std::shared_ptr vfs, + const Tesses::Framework::Filesystem::VFSPath &path, + bool isPrivate = false, int64_t pieceLength = DEFAULT_PIECE_LENGTH, + Serialization::Bencode::BeString comment = "", + Serialization::Bencode::BeString created_by = "TessesFrameworkTorrent"); +}; +} // namespace Tesses::Framework::BitTorrent \ No newline at end of file diff --git a/include/TessesFramework/BitTorrent/TorrentStream.hpp b/include/TessesFramework/BitTorrent/TorrentStream.hpp index 921c2d6..37bac89 100644 --- a/include/TessesFramework/BitTorrent/TorrentStream.hpp +++ b/include/TessesFramework/BitTorrent/TorrentStream.hpp @@ -1,46 +1,68 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "TessesFramework/Http/HttpClient.hpp" #include "TessesFramework/Streams/NetworkStream.hpp" -namespace Tesses::Framework::BitTorrent -{ - class TorrentFileEntry { - public: - TorrentFileEntry(); - TorrentFileEntry(Tesses::Framework::Filesystem::VFSPath path, int64_t length); - Tesses::Framework::Filesystem::VFSPath path; - int64_t length=0; - }; - class ReadWriteAt { - public: - virtual bool ReadBlockAt(uint64_t offset, uint8_t* data, size_t len)=0; - virtual void WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len)=0; - virtual ~ReadWriteAt(); +namespace Tesses::Framework::BitTorrent { +class TorrentFileEntry { + public: + TorrentFileEntry(); + TorrentFileEntry(Tesses::Framework::Filesystem::VFSPath path, + int64_t length); + Tesses::Framework::Filesystem::VFSPath path; + int64_t length = 0; +}; +class ReadWriteAt { + public: + virtual bool ReadBlockAt(uint64_t offset, uint8_t *data, size_t len) = 0; + virtual void WriteBlockAt(uint64_t offset, const uint8_t *data, + size_t len) = 0; + virtual ~ReadWriteAt(); +}; - }; +class TorrentFileStream : public ReadWriteAt { + Tesses::Framework::Threading::Mutex mtx; - class TorrentFileStream : public ReadWriteAt - { - Tesses::Framework::Threading::Mutex mtx; - public: - std::shared_ptr vfs; - Tesses::Framework::Filesystem::VFSPath path; - uint64_t length; - TorrentFileStream(std::shared_ptr vfs, Tesses::Framework::Filesystem::VFSPath path, uint64_t length); - bool ReadBlockAt(uint64_t offset, uint8_t* data, size_t len); - void WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len); + public: + std::shared_ptr vfs; + Tesses::Framework::Filesystem::VFSPath path; + uint64_t length; + TorrentFileStream(std::shared_ptr vfs, + Tesses::Framework::Filesystem::VFSPath path, + uint64_t length); + bool ReadBlockAt(uint64_t offset, uint8_t *data, size_t len); + void WriteBlockAt(uint64_t offset, const uint8_t *data, size_t len); +}; +class TorrentDirectoryStream : public ReadWriteAt { - }; - class TorrentDirectoryStream : public ReadWriteAt - { + std::vector mtxs; - std::vector mtxs; - public: - std::shared_ptr vfs; - Tesses::Framework::Filesystem::VFSPath path; - std::vector entries; - TorrentDirectoryStream(std::shared_ptr vfs, Tesses::Framework::Filesystem::VFSPath path,std::vector entries); - bool ReadBlockAt(uint64_t offset, uint8_t* data, size_t len); - void WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len); - }; -} \ No newline at end of file + public: + std::shared_ptr vfs; + Tesses::Framework::Filesystem::VFSPath path; + std::vector entries; + TorrentDirectoryStream( + std::shared_ptr vfs, + Tesses::Framework::Filesystem::VFSPath path, + std::vector entries); + bool ReadBlockAt(uint64_t offset, uint8_t *data, size_t len); + void WriteBlockAt(uint64_t offset, const uint8_t *data, size_t len); +}; +} // namespace Tesses::Framework::BitTorrent \ No newline at end of file diff --git a/include/TessesFramework/Common.hpp b/include/TessesFramework/Common.hpp index 26dafb7..fbb62d6 100644 --- a/include/TessesFramework/Common.hpp +++ b/include/TessesFramework/Common.hpp @@ -1,98 +1,119 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once +#include "Threading/Mutex.hpp" +#include #include -#include -#include #include +#include +#include +#include +#include +#include #include +#include #include #include -#include -#include #include -#include -#include "Threading/Mutex.hpp" -#include -#include - -namespace Tesses::Framework -{ - - - std::optional TF_GetCommandName(); - class TF_Timer_Handle; - //Used for internal purposes, don't use unless you want to run timer events on another thread - - class TF_Timer_Handler { - private: - std::vector> handles; - public: - void Update(); - - static std::shared_ptr Make(std::shared_ptr handler); - friend class TF_Timer_Handle; - }; +namespace Tesses::Framework { - class TF_Timer_Handle { - - private: - std::shared_ptr timerHandler; - std::function cb; - std::chrono::milliseconds interval; - std::chrono::time_point last; - bool enabled; - TF_Timer_Handle() = delete; +class TF_Timer_Handle; +// Used for internal purposes, don't use unless you want to run timer events on +// another thread - TF_Timer_Handle(std::shared_ptr timerHandler); +class TF_Timer_Handler { + private: + std::vector> handles; - public: - - void SetCallback(std::function cb); + public: + void Update(); - int64_t GetIntervalMilliseconds(); - std::chrono::duration GetIntervalDuration(); + static std::shared_ptr + Make(std::shared_ptr handler); + friend class TF_Timer_Handle; +}; - void SetIntervalFromMilliseconds(int64_t ms); - - void SetIntervalFromDuration(std::chrono::milliseconds dur); - +class TF_Timer_Handle { - void SetEnabled(bool enabled); - bool GetEnabled(); - friend class TF_Timer_Handler; - }; + private: + std::shared_ptr timerHandler; + std::function cb; + std::chrono::milliseconds interval; + std::chrono::time_point + last; + bool enabled; + TF_Timer_Handle() = delete; + TF_Timer_Handle(std::shared_ptr timerHandler); - std::shared_ptr TF_Timer(); - std::shared_ptr TF_Timer(std::function cb, int64_t interval=1000, bool enabled=true); - std::shared_ptr TF_Timer(std::function cb, std::chrono::milliseconds interval, bool enabled=true); - - void TF_Init(); - void TF_InitWithConsole(); - void TF_AllowPortable(std::string argv0); - void TF_ConnectToSelf(uint16_t port); - void TF_InitEventLoop(); - void TF_RunEventLoop(); - void TF_RunEventLoopItteration(); - bool TF_IsRunning(); - void TF_Sleep(uint32_t sleepMS); - void TF_SetIsRunning(bool _isRunning); - void TF_Quit(); - bool TF_GetConsoleEventsEnabled(); - void TF_SetConsoleEventsEnabled(bool flag); - void TF_InitConsole(); - void TF_Invoke(std::function cb); - std::string TF_FileSize(uint64_t bytes, bool usesBin=true); - - #if defined(TESSESFRAMEWORK_LOGTOFILE) - void TF_Log(std::string dataToLog); - #endif + public: + void SetCallback(std::function cb); + int64_t GetIntervalMilliseconds(); + std::chrono::duration GetIntervalDuration(); - #if defined(TESSESFRAMEWORK_LOGTOFILE) - #define TF_LOG(log) Tesses::Framework::TF_Log(log) - #else - #define TF_LOG(log) - #endif -} \ No newline at end of file + void SetIntervalFromMilliseconds(int64_t ms); + + void SetIntervalFromDuration(std::chrono::milliseconds dur); + + void SetEnabled(bool enabled); + bool GetEnabled(); + friend class TF_Timer_Handler; +}; + +std::shared_ptr TF_Timer(); +std::shared_ptr TF_Timer(std::function cb, + int64_t interval = 1000, + bool enabled = true); +std::shared_ptr TF_Timer(std::function cb, + std::chrono::milliseconds interval, + bool enabled = true); + +void TF_Init(); +void TF_InitWithConsole(); +std::string TF_GetExecutableName(); +void TF_AllowPortable(); +void TF_AllowPortable(std::string argv0); +void TF_ConnectToSelf(uint16_t port); +void TF_InitEventLoop(); +void TF_RunEventLoop(); +void TF_RunEventLoopItteration(); +bool TF_IsRunning(); +void TF_Sleep(uint32_t sleepMS); +void TF_SetIsRunning(bool _isRunning); +void TF_Quit(); +bool TF_GetConsoleEventsEnabled(); +void TF_SetConsoleEventsEnabled(bool flag); +void TF_InitConsole(); +void TF_Invoke(std::function cb); +std::string TF_FileSize(uint64_t bytes, bool usesBin = true); + +#if defined(TESSESFRAMEWORK_LOGTOFILE) +void TF_Log(std::string dataToLog); +#endif + +#if defined(TESSESFRAMEWORK_LOGTOFILE) +#define TF_LOG(log) Tesses::Framework::TF_Log(log) +#else +#define TF_LOG(log) +#endif +} // namespace Tesses::Framework \ No newline at end of file diff --git a/include/TessesFramework/Console.hpp b/include/TessesFramework/Console.hpp new file mode 100644 index 0000000..60cdb35 --- /dev/null +++ b/include/TessesFramework/Console.hpp @@ -0,0 +1,79 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "Common.hpp" +#include + +namespace Tesses::Framework { + enum class ConsoleColor { + CC_BLACK, + CC_RED, + CC_GREEN, + CC_YELLOW, + CC_BLUE, + CC_MAGENTA, + CC_CYAN, + CC_WHITE + }; + enum class ClearBehaviour { + CB_CURSORANDBELOW, + CB_CURSORANDABOVE, + CB_ENTIRESCREEN + }; + struct Console { + static bool IsTTY(); + static std::pair GetSize(); + static void ProgressBar(double); + static void ProgressBar(int); + static void Write(std::string text); + static void WriteLine(std::string text); + static void WriteView(std::string_view text); + static void WriteLineView(std::string_view text); + static void Error(std::string text); + static void ErrorLine(std::string text); + static void ErrorView(std::string_view text); + static void ErrorLineView(std::string_view text); + static int Read(); + static std::string ReadLine(); + + static bool GetEcho(); + static bool SetEcho(bool echo); + static bool GetCanonical(); + static bool SetCanonical(bool can); + static bool GetSignals(); + static bool SetSignals(bool sig); + + static void SetPosition(int x, int y); + + static void SetBackgroundColor(ConsoleColor c, bool alt); + static void SetForegroundColor(ConsoleColor c, bool alt); + static void Clear(); + static void MoveToHome(); + static void ClearRetainPosition(ClearBehaviour clearBehaviour); + static void Flush(); + + //returns std::string::npos if no tty + static size_t List(std::vector& strs); + + + static std::string ReadPassword(); + + private: + static void WriteToStream(std::string_view text, bool isStderr); + }; +} \ No newline at end of file diff --git a/include/TessesFramework/Crypto/ClientTLSStream.hpp b/include/TessesFramework/Crypto/ClientTLSStream.hpp index b469501..ff4fc5e 100644 --- a/include/TessesFramework/Crypto/ClientTLSStream.hpp +++ b/include/TessesFramework/Crypto/ClientTLSStream.hpp @@ -1,22 +1,43 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Streams/Stream.hpp" +namespace Tesses::Framework::Crypto { +class ClientTLSStream : public Tesses::Framework::Streams::Stream { + void *privateData; -namespace Tesses::Framework::Crypto -{ - class ClientTLSStream : public Tesses::Framework::Streams::Stream { - void* privateData; - public: - static std::string GetCertChain(); + public: + static std::string GetCertChain(); - ClientTLSStream(std::shared_ptr innerStream, bool verify, std::string domain); - ClientTLSStream(std::shared_ptr innerStream, bool verify, std::string domain, std::string cert); - size_t Read(uint8_t* buff, size_t sz); - size_t Write(const uint8_t* buff, size_t sz); - bool CanRead(); - bool CanWrite(); - bool EndOfStream(); - ~ClientTLSStream(); - }; + ClientTLSStream( + std::shared_ptr innerStream, + bool verify, std::string domain); + ClientTLSStream( + std::shared_ptr innerStream, + bool verify, std::string domain, std::string cert); + size_t Read(uint8_t *buff, size_t sz); + size_t Write(const uint8_t *buff, size_t sz); + bool CanRead(); + bool CanWrite(); + bool EndOfStream(); + ~ClientTLSStream(); +}; -} \ No newline at end of file +} // namespace Tesses::Framework::Crypto \ No newline at end of file diff --git a/include/TessesFramework/Crypto/Crypto.hpp b/include/TessesFramework/Crypto/Crypto.hpp index c860cff..045a43d 100644 --- a/include/TessesFramework/Crypto/Crypto.hpp +++ b/include/TessesFramework/Crypto/Crypto.hpp @@ -1,60 +1,87 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Streams/Stream.hpp" -namespace Tesses::Framework::Crypto -{ - bool HaveCrypto(); - std::string Base64_Encode(std::vector data); - std::vector Base64_Decode(std::string str); - class Sha1 { - void* inner; - public: - Sha1(); - bool Start(); - bool Update(const uint8_t* buffer, size_t sz); - bool Update(std::shared_ptr strm); - std::vector Finish(); - ~Sha1(); - static std::vector ComputeHash(const uint8_t* buffer, size_t len); - static std::vector ComputeHash(std::shared_ptr strm); - }; - class Sha256 { - void* inner; - bool is224; - public: - Sha256(); - bool Start(bool is224=false); - bool Is224(); - bool Update(const uint8_t* buffer, size_t sz); - bool Update(std::shared_ptr strm); - std::vector Finish(); - ~Sha256(); - static std::vector ComputeHash(const uint8_t* buffer, size_t len,bool is224=false); - static std::vector ComputeHash(std::shared_ptr strm,bool is224=false); - - }; - class Sha512 { - void* inner; - bool is384; - public: - Sha512(); - bool Start(bool is384=false); - bool Is384(); - bool Update(const uint8_t* buffer, size_t sz); - bool Update(std::shared_ptr strm); - std::vector Finish(); - ~Sha512(); +namespace Tesses::Framework::Crypto { +bool HaveCrypto(); +std::string Base64_Encode(std::vector data); +std::vector Base64_Decode(std::string str); +class Sha1 { + void *inner; - static std::vector ComputeHash(const uint8_t* buffer, size_t len,bool is384=false); - static std::vector ComputeHash(std::shared_ptr strm,bool is384=false); - }; - typedef enum { - VERSION_SHA1=1, - VERSION_SHA224=224, - VERSION_SHA256=256, - VERSION_SHA384=384, - VERSION_SHA512=512 - } ShaVersion; - bool PBKDF2(std::vector& output,std::string pass, std::vector& salt, long itterations, ShaVersion version); + public: + Sha1(); + bool Start(); + bool Update(const uint8_t *buffer, size_t sz); + bool Update(std::shared_ptr strm); + std::vector Finish(); + ~Sha1(); + static std::vector ComputeHash(const uint8_t *buffer, size_t len); + static std::vector + ComputeHash(std::shared_ptr strm); +}; +class Sha256 { + void *inner; + bool is224; - bool RandomBytes(std::vector& output, std::string personal_str); -} \ No newline at end of file + public: + Sha256(); + bool Start(bool is224 = false); + bool Is224(); + bool Update(const uint8_t *buffer, size_t sz); + bool Update(std::shared_ptr strm); + std::vector Finish(); + ~Sha256(); + static std::vector ComputeHash(const uint8_t *buffer, size_t len, + bool is224 = false); + static std::vector + ComputeHash(std::shared_ptr strm, + bool is224 = false); +}; +class Sha512 { + void *inner; + bool is384; + + public: + Sha512(); + bool Start(bool is384 = false); + bool Is384(); + bool Update(const uint8_t *buffer, size_t sz); + bool Update(std::shared_ptr strm); + std::vector Finish(); + ~Sha512(); + + static std::vector ComputeHash(const uint8_t *buffer, size_t len, + bool is384 = false); + static std::vector + ComputeHash(std::shared_ptr strm, + bool is384 = false); +}; +typedef enum { + VERSION_SHA1 = 1, + VERSION_SHA224 = 224, + VERSION_SHA256 = 256, + VERSION_SHA384 = 384, + VERSION_SHA512 = 512 +} ShaVersion; +bool PBKDF2(std::vector &output, std::string pass, + std::vector &salt, long itterations, ShaVersion version); + +bool RandomBytes(std::vector &output, std::string personal_str); +} // namespace Tesses::Framework::Crypto \ No newline at end of file diff --git a/include/TessesFramework/Date/Date.hpp b/include/TessesFramework/Date/Date.hpp index 43b2064..b55fa79 100644 --- a/include/TessesFramework/Date/Date.hpp +++ b/include/TessesFramework/Date/Date.hpp @@ -1,142 +1,146 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once -#include #include #include +#include -namespace Tesses::Framework::Date -{ - int GetTimeZone(); - bool TimeZoneSupportDST(); - - class DateTime { - int year=1970; - int month=1; - int day=1; - int hour=0; - int minute=0; - int second=0; - bool isLocal=false; - int64_t ToEpochNoConvert() const; - void FromEpochNoConvert(int64_t gmt); - public: - DateTime(); - DateTime(int year, int month, int day, int hour, int minute, int seconds, bool isLocal=true); - DateTime(int64_t epoch); - int Year() const; - int Month() const; - int Day() const; - int Hour() const; - int Minute() const; - int Second() const; - int DayOfWeek() const; - bool IsLocal() const; - int64_t ToEpoch() const; - DateTime ToLocal() const; - DateTime ToUTC() const; - void SetToLocal(); - void SetToUTC(); - void SetYear(int y); - void SetMonth(int m); - void SetDay(int d); - void SetHour(int h); - void SetMinute(int m); - void SetSecond(int s); - void SetLocal(bool local); - void Set(int64_t epoch); - void Set(int year, int month, int day, int hour, int minute, int seconds, bool isLocal=true); +namespace Tesses::Framework::Date { +int GetTimeZone(); +bool TimeZoneSupportDST(); - +class DateTime { + int year = 1970; + int month = 1; + int day = 1; + int hour = 0; + int minute = 0; + int second = 0; + bool isLocal = false; + int64_t ToEpochNoConvert() const; + void FromEpochNoConvert(int64_t gmt); - void SetToNow(); - void SetToNowUTC(); - static DateTime Now(); - static DateTime NowUTC(); + public: + DateTime(); + DateTime(int year, int month, int day, int hour, int minute, int seconds, + bool isLocal = true); + DateTime(int64_t epoch); + int Year() const; + int Month() const; + int Day() const; + int Hour() const; + int Minute() const; + int Second() const; + int DayOfWeek() const; + bool IsLocal() const; + int64_t ToEpoch() const; + DateTime ToLocal() const; + DateTime ToUTC() const; + void SetToLocal(); + void SetToUTC(); + void SetYear(int y); + void SetMonth(int m); + void SetDay(int d); + void SetHour(int h); + void SetMinute(int m); + void SetSecond(int s); + void SetLocal(bool local); + void Set(int64_t epoch); + void Set(int year, int month, int day, int hour, int minute, int seconds, + bool isLocal = true); - std::string ToString() const; - std::string ToString(std::string fmt) const; + void SetToNow(); + void SetToNowUTC(); + static DateTime Now(); + static DateTime NowUTC(); + std::string ToString() const; + std::string ToString(std::string fmt) const; - std::string ToHttpDate() const; - static bool TryParseHttpDate(std::string txt, DateTime& dt); + std::string ToHttpDate() const; + static bool TryParseHttpDate(std::string txt, DateTime &dt); +}; +class TimeSpan { + int64_t totalSeconds; - }; - class TimeSpan { - int64_t totalSeconds; - public: - TimeSpan(); - TimeSpan(int64_t totalSeconds); - TimeSpan(int hours, int minutes, int seconds); - TimeSpan(int days,int hours, int minutes, int seconds); - - - void Set(int days, int hours, int minutes, int seconds); - void Set(int hours, int minutes, int seconds); + public: + TimeSpan(); + TimeSpan(int64_t totalSeconds); + TimeSpan(int hours, int minutes, int seconds); + TimeSpan(int days, int hours, int minutes, int seconds); - void SetDays(int d); - void SetHours(int h); - void SetMinutes(int m); - void SetSeconds(int s); - - - int Days() const; - int Hours() const; - int Minutes() const; - int Seconds() const; + void Set(int days, int hours, int minutes, int seconds); + void Set(int hours, int minutes, int seconds); + void SetDays(int d); + void SetHours(int h); + void SetMinutes(int m); + void SetSeconds(int s); - int64_t TotalSeconds() const; - int64_t TotalMinutes() const; - int64_t TotalHours() const; + int Days() const; + int Hours() const; + int Minutes() const; + int Seconds() const; + int64_t TotalSeconds() const; + int64_t TotalMinutes() const; + int64_t TotalHours() const; - void SetTotalSeconds(int64_t totalSeconds); - void SetTotalMinutes(int64_t totalMinutes); - void SetTotalHours(int64_t totalHours); - - void AddSeconds(int64_t seconds); - void AddMinutes(int64_t minutes); - void AddHours(int64_t hours); - void AddDays(int64_t days); + void SetTotalSeconds(int64_t totalSeconds); + void SetTotalMinutes(int64_t totalMinutes); + void SetTotalHours(int64_t totalHours); - std::string ToString(bool slim=true) const; + void AddSeconds(int64_t seconds); + void AddMinutes(int64_t minutes); + void AddHours(int64_t hours); + void AddDays(int64_t days); - static bool TryParse(std::string text, TimeSpan& span); + std::string ToString(bool slim = true) const; - - - }; + static bool TryParse(std::string text, TimeSpan &span); +}; - inline DateTime operator+(const DateTime& dt, const TimeSpan& ts) - { - DateTime dt2(dt.ToEpoch() + ts.TotalSeconds()); - if(dt.IsLocal()) - dt2.SetToLocal(); - return dt2; - } - inline DateTime operator+(const TimeSpan& ts,const DateTime& dt) - { - DateTime dt2(dt.ToEpoch() + ts.TotalSeconds()); - if(dt.IsLocal()) - dt2.SetToLocal(); - return dt2; - } - inline TimeSpan operator+(const TimeSpan& ts, const TimeSpan& ts2) - { - return ts.TotalSeconds() + ts2.TotalSeconds(); - } - inline DateTime operator-(const DateTime& dt, const TimeSpan& ts) - { - DateTime dt2(dt.ToEpoch() - ts.TotalSeconds()); - if(dt.IsLocal()) - dt2.SetToLocal(); - return dt2; - } - inline TimeSpan operator-(const DateTime& dt, const DateTime& dt2) - { - return dt.ToEpoch() - dt2.ToEpoch(); - } - inline TimeSpan operator-(const TimeSpan& ts, const TimeSpan& ts2) - { - return ts.TotalSeconds() - ts2.TotalSeconds(); - } -}; \ No newline at end of file +inline DateTime operator+(const DateTime &dt, const TimeSpan &ts) { + DateTime dt2(dt.ToEpoch() + ts.TotalSeconds()); + if (dt.IsLocal()) + dt2.SetToLocal(); + return dt2; +} +inline DateTime operator+(const TimeSpan &ts, const DateTime &dt) { + DateTime dt2(dt.ToEpoch() + ts.TotalSeconds()); + if (dt.IsLocal()) + dt2.SetToLocal(); + return dt2; +} +inline TimeSpan operator+(const TimeSpan &ts, const TimeSpan &ts2) { + return ts.TotalSeconds() + ts2.TotalSeconds(); +} +inline DateTime operator-(const DateTime &dt, const TimeSpan &ts) { + DateTime dt2(dt.ToEpoch() - ts.TotalSeconds()); + if (dt.IsLocal()) + dt2.SetToLocal(); + return dt2; +} +inline TimeSpan operator-(const DateTime &dt, const DateTime &dt2) { + return dt.ToEpoch() - dt2.ToEpoch(); +} +inline TimeSpan operator-(const TimeSpan &ts, const TimeSpan &ts2) { + return ts.TotalSeconds() - ts2.TotalSeconds(); +} +}; // namespace Tesses::Framework::Date \ No newline at end of file diff --git a/include/TessesFramework/Filesystem/FSHelpers.hpp b/include/TessesFramework/Filesystem/FSHelpers.hpp index 4b0e0f7..670c0cd 100644 --- a/include/TessesFramework/Filesystem/FSHelpers.hpp +++ b/include/TessesFramework/Filesystem/FSHelpers.hpp @@ -1,33 +1,68 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "TempFS.hpp" #include "VFSFix.hpp" -namespace Tesses::Framework::Filesystem::Helpers -{ - void ReadAllText(std::shared_ptr vfs, VFSPath path, std::string& text); - void ReadAllLines(std::shared_ptr vfs, VFSPath path, std::vector& lines); - void ReadAllBytes(std::shared_ptr vfs, VFSPath path, std::vector& array); - std::string ReadAllText(std::shared_ptr vfs, VFSPath path); - std::vector ReadAllLines(std::shared_ptr vfs, VFSPath path); - std::vector ReadAllBytes(std::shared_ptr vfs, VFSPath path); - void WriteAllText(std::shared_ptr vfs, VFSPath path, const std::string& text); - void WriteAllLines(std::shared_ptr vfs, VFSPath path, const std::vector& parts); - void WriteAllBytes(std::shared_ptr vfs, VFSPath path, const std::vector& bytes); +namespace Tesses::Framework::Filesystem::Helpers { +void ReadAllText(std::shared_ptr vfs, VFSPath path, std::string &text); +void ReadAllLines(std::shared_ptr vfs, VFSPath path, + std::vector &lines); +void ReadAllBytes(std::shared_ptr vfs, VFSPath path, + std::vector &array); +std::string ReadAllText(std::shared_ptr vfs, VFSPath path); +std::vector ReadAllLines(std::shared_ptr vfs, VFSPath path); +std::vector ReadAllBytes(std::shared_ptr vfs, VFSPath path); +void WriteAllText(std::shared_ptr vfs, VFSPath path, + const std::string &text); +void WriteAllLines(std::shared_ptr vfs, VFSPath path, + const std::vector &parts); +void WriteAllBytes(std::shared_ptr vfs, VFSPath path, + const std::vector &bytes); - void CopyStreamProgress(std::shared_ptr src,std::shared_ptr dest, std::function progress); +void CopyStreamProgress( + std::shared_ptr src, std::shared_ptr dest, + std::function progress); - void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, bool overwrite=true); - void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, std::function progress, bool overwrite=true); - void CopyDirectory(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, bool overwrite=true); - void CopyDirectory(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, std::function progress, bool overwrite=true); +void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, + std::shared_ptr vfsDest, VFSPath pathDest, + bool overwrite = true); +void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, + std::shared_ptr vfsDest, VFSPath pathDest, + std::function progress, + bool overwrite = true); +void CopyDirectory(std::shared_ptr vfsSrc, VFSPath pathSrc, + std::shared_ptr vfsDest, VFSPath pathDest, + bool overwrite = true); +void CopyDirectory( + std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, + VFSPath pathDest, + std::function + progress, + bool overwrite = true); - inline std::shared_ptr CreateTempFS(bool deleteOnDestroy=true) - { - return std::make_shared(deleteOnDestroy); - } - inline std::shared_ptr CreateTempFS(std::shared_ptr vfs,bool deleteOnDestroy=true) - { - return std::make_shared(vfs,deleteOnDestroy); - } +inline std::shared_ptr CreateTempFS(bool deleteOnDestroy = true) { + return std::make_shared(deleteOnDestroy); +} +inline std::shared_ptr CreateTempFS(std::shared_ptr vfs, + bool deleteOnDestroy = true) { + return std::make_shared(vfs, deleteOnDestroy); +} -} \ No newline at end of file +} // namespace Tesses::Framework::Filesystem::Helpers \ No newline at end of file diff --git a/include/TessesFramework/Filesystem/LocalFS.hpp b/include/TessesFramework/Filesystem/LocalFS.hpp index b9cc6ab..359ae93 100644 --- a/include/TessesFramework/Filesystem/LocalFS.hpp +++ b/include/TessesFramework/Filesystem/LocalFS.hpp @@ -1,46 +1,62 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "VFS.hpp" #include "VFSFix.hpp" -namespace Tesses::Framework::Filesystem -{ - class LocalFilesystem : public VFS - { - public: +namespace Tesses::Framework::Filesystem { +class LocalFilesystem : public VFS { + public: + std::shared_ptr + OpenFile(VFSPath path, std::string mode); - std::shared_ptr OpenFile(VFSPath path, std::string mode); + void CreateDirectory(VFSPath path); + void DeleteDirectory(VFSPath path); - void CreateDirectory(VFSPath path); - void DeleteDirectory(VFSPath path); + void DeleteFile(VFSPath path); + void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); + VFSPathEnumerator EnumeratePaths(VFSPath path); + void CreateHardlink(VFSPath existingFile, VFSPath newName); + void MoveFile(VFSPath src, VFSPath dest); - void DeleteFile(VFSPath path); - void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); - VFSPathEnumerator EnumeratePaths(VFSPath path); - void CreateHardlink(VFSPath existingFile, VFSPath newName); + void MoveDirectory(VFSPath src, VFSPath dest); + VFSPath ReadLink(VFSPath path); + std::string VFSPathToSystem(VFSPath path); + VFSPath SystemToVFSPath(std::string path); - void MoveFile(VFSPath src, VFSPath dest); + void SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess); + bool Stat(VFSPath path, StatData &stat); + bool StatVFS(VFSPath path, StatVFSData &vfsData); - void MoveDirectory(VFSPath src, VFSPath dest); - VFSPath ReadLink(VFSPath path); - std::string VFSPathToSystem(VFSPath path); - VFSPath SystemToVFSPath(std::string path); + void Chmod(VFSPath path, uint32_t mode); + void Chown(VFSPath path, uint32_t uid, uint32_t gid); - void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess); - bool Stat(VFSPath path, StatData& stat); - bool StatVFS(VFSPath path, StatVFSData& vfsData); + void Lock(VFSPath path); + void Unlock(VFSPath path); - void Chmod(VFSPath path, uint32_t mode); - void Chown(VFSPath path, uint32_t uid, uint32_t gid); + FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mod); - void Lock(VFSPath path); - void Unlock(VFSPath path); - - FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mod); - - protected: - std::shared_ptr CreateWatcher(std::shared_ptr vfs, VFSPath path); - - }; - extern std::shared_ptr LocalFS; -} + protected: + std::shared_ptr CreateWatcher(std::shared_ptr vfs, + VFSPath path); +}; +extern std::shared_ptr LocalFS; +} // namespace Tesses::Framework::Filesystem diff --git a/include/TessesFramework/Filesystem/MountableFilesystem.hpp b/include/TessesFramework/Filesystem/MountableFilesystem.hpp index 31ee049..550aa38 100644 --- a/include/TessesFramework/Filesystem/MountableFilesystem.hpp +++ b/include/TessesFramework/Filesystem/MountableFilesystem.hpp @@ -1,59 +1,75 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "VFS.hpp" #include "VFSFix.hpp" -namespace Tesses::Framework::Filesystem -{ - class MountableDirectory { - public: - std::string name; - std::shared_ptr vfs; - bool owns; - std::vector dirs; - void GetFS(VFSPath srcPath, VFSPath curDir, VFSPath& destRoot, VFSPath& destPath, std::shared_ptr& vfs); - ~MountableDirectory(); - }; +namespace Tesses::Framework::Filesystem { +class MountableDirectory { + public: + std::string name; + std::shared_ptr vfs; + bool owns; + std::vector dirs; + void GetFS(VFSPath srcPath, VFSPath curDir, VFSPath &destRoot, + VFSPath &destPath, std::shared_ptr &vfs); + ~MountableDirectory(); +}; - class MountableFilesystem : public VFS - { - std::shared_ptr root; +class MountableFilesystem : public VFS { + std::shared_ptr root; - std::vector directories; + std::vector directories; - void GetFS(VFSPath srcPath, VFSPath& destRoot, VFSPath& destPath, std::shared_ptr& vfs); + void GetFS(VFSPath srcPath, VFSPath &destRoot, VFSPath &destPath, + std::shared_ptr &vfs); + public: + MountableFilesystem(); + MountableFilesystem(std::shared_ptr root); + void Mount(VFSPath path, std::shared_ptr vfs); + void Unmount(VFSPath path); + std::shared_ptr + OpenFile(VFSPath path, std::string mode); + void CreateDirectory(VFSPath path); + void DeleteDirectory(VFSPath path); + bool Stat(VFSPath path, StatData &data); + void DeleteFile(VFSPath path); + void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); + VFSPathEnumerator EnumeratePaths(VFSPath path); + void CreateHardlink(VFSPath existingFile, VFSPath newName); + void MoveFile(VFSPath src, VFSPath dest); + void MoveDirectory(VFSPath src, VFSPath dest); + VFSPath ReadLink(VFSPath path); + std::string VFSPathToSystem(VFSPath path); + VFSPath SystemToVFSPath(std::string path); + ~MountableFilesystem(); + void SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess); - public: - MountableFilesystem(); - MountableFilesystem(std::shared_ptr root); - void Mount(VFSPath path, std::shared_ptr vfs); - void Unmount(VFSPath path); - std::shared_ptr OpenFile(VFSPath path, std::string mode); - void CreateDirectory(VFSPath path); - void DeleteDirectory(VFSPath path); - bool Stat(VFSPath path, StatData& data); - void DeleteFile(VFSPath path); - void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); - VFSPathEnumerator EnumeratePaths(VFSPath path); - void CreateHardlink(VFSPath existingFile, VFSPath newName); - void MoveFile(VFSPath src, VFSPath dest); - void MoveDirectory(VFSPath src, VFSPath dest); - VFSPath ReadLink(VFSPath path); - std::string VFSPathToSystem(VFSPath path); - VFSPath SystemToVFSPath(std::string path); - ~MountableFilesystem(); - void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess); + bool StatVFS(VFSPath path, StatVFSData &vfsData); - bool StatVFS(VFSPath path, StatVFSData& vfsData); + void Chmod(VFSPath path, uint32_t mode); + void Chown(VFSPath path, uint32_t uid, uint32_t gid); + FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mode); - void Chmod(VFSPath path, uint32_t mode); - void Chown(VFSPath path, uint32_t uid, uint32_t gid); - FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mode); - - - void Lock(VFSPath path); - void Unlock(VFSPath path); - - - }; -} + void Lock(VFSPath path); + void Unlock(VFSPath path); +}; +} // namespace Tesses::Framework::Filesystem diff --git a/include/TessesFramework/Filesystem/NullFilesystem.hpp b/include/TessesFramework/Filesystem/NullFilesystem.hpp index ab30c88..77d9f00 100644 --- a/include/TessesFramework/Filesystem/NullFilesystem.hpp +++ b/include/TessesFramework/Filesystem/NullFilesystem.hpp @@ -1,16 +1,33 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "VFS.hpp" #include "VFSFix.hpp" -namespace Tesses::Framework::Filesystem -{ - class NullFilesystem : public VFS - { - public: - std::shared_ptr OpenFile(VFSPath path, std::string mode); - VFSPathEnumerator EnumeratePaths(VFSPath path); - bool Stat(VFSPath path, StatData& data); - std::string VFSPathToSystem(VFSPath path); - VFSPath SystemToVFSPath(std::string path); - }; -} +namespace Tesses::Framework::Filesystem { +class NullFilesystem : public VFS { + public: + std::shared_ptr + OpenFile(VFSPath path, std::string mode); + VFSPathEnumerator EnumeratePaths(VFSPath path); + bool Stat(VFSPath path, StatData &data); + std::string VFSPathToSystem(VFSPath path); + VFSPath SystemToVFSPath(std::string path); +}; +} // namespace Tesses::Framework::Filesystem diff --git a/include/TessesFramework/Filesystem/RelativeFilesystem.hpp b/include/TessesFramework/Filesystem/RelativeFilesystem.hpp index c82c95e..7eeb70d 100644 --- a/include/TessesFramework/Filesystem/RelativeFilesystem.hpp +++ b/include/TessesFramework/Filesystem/RelativeFilesystem.hpp @@ -1,54 +1,78 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "VFS.hpp" #include "VFSFix.hpp" -namespace Tesses::Framework::Filesystem -{ - class RelativeFilesystem : public VFS { - Tesses::Framework::Threading::Mutex mtx; - VFSPath working; - std::shared_ptr vfs; - private: - VFSPath ToParent(VFSPath path); - public: - RelativeFilesystem(std::shared_ptr vfs, VFSPath working); - VFSPath GetWorking(); - void SetWorking(VFSPath path); - std::shared_ptr GetVFS(); +namespace Tesses::Framework::Filesystem { +class RelativeFilesystem : public VFS { + Tesses::Framework::Threading::Mutex mtx; + VFSPath working; + std::shared_ptr vfs; - std::shared_ptr OpenFile(VFSPath path, std::string mode); - void CreateDirectory(VFSPath path); - void DeleteDirectory(VFSPath path); - void DeleteFile(VFSPath path); - void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); - VFSPathEnumerator EnumeratePaths(VFSPath path); - void CreateHardlink(VFSPath existingFile, VFSPath newName); - void MoveFile(VFSPath src, VFSPath dest); - void MoveDirectory(VFSPath src, VFSPath dest); - void DeleteDirectoryRecurse(VFSPath path); - VFSPath ReadLink(VFSPath path); - std::string VFSPathToSystem(VFSPath path); - VFSPath SystemToVFSPath(std::string path); - void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess); - bool StatVFS(VFSPath path, StatVFSData& vfsData); - bool Stat(VFSPath path, StatData& data); + private: + VFSPath ToParent(VFSPath path); - void Chown(VFSPath path, uint32_t uid, uint32_t gid); - void Chmod(VFSPath path, uint32_t mode); - FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mode); - void Lock(VFSPath path); - void Unlock(VFSPath path); - protected: - std::shared_ptr CreateWatcher(std::shared_ptr vfs, VFSPath path); + public: + RelativeFilesystem(std::shared_ptr vfs, VFSPath working); + VFSPath GetWorking(); + void SetWorking(VFSPath path); + std::shared_ptr GetVFS(); + std::shared_ptr + OpenFile(VFSPath path, std::string mode); + void CreateDirectory(VFSPath path); + void DeleteDirectory(VFSPath path); + void DeleteFile(VFSPath path); + void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); + VFSPathEnumerator EnumeratePaths(VFSPath path); + void CreateHardlink(VFSPath existingFile, VFSPath newName); + void MoveFile(VFSPath src, VFSPath dest); + void MoveDirectory(VFSPath src, VFSPath dest); + void DeleteDirectoryRecurse(VFSPath path); + VFSPath ReadLink(VFSPath path); + std::string VFSPathToSystem(VFSPath path); + VFSPath SystemToVFSPath(std::string path); + void SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess); + bool StatVFS(VFSPath path, StatVFSData &vfsData); + bool Stat(VFSPath path, StatData &data); - class Watcher : public FSWatcher { - std::shared_ptr watcher; - protected: - void SetEnabledImpl(bool enabled); - public: - Watcher(std::shared_ptr vfs, VFSPath path); - ~Watcher(); - }; + void Chown(VFSPath path, uint32_t uid, uint32_t gid); + void Chmod(VFSPath path, uint32_t mode); + FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mode); + void Lock(VFSPath path); + void Unlock(VFSPath path); + + protected: + std::shared_ptr CreateWatcher(std::shared_ptr vfs, + VFSPath path); + + class Watcher : public FSWatcher { + std::shared_ptr watcher; + + protected: + void SetEnabledImpl(bool enabled); + + public: + Watcher(std::shared_ptr vfs, VFSPath path); + ~Watcher(); }; -} \ No newline at end of file +}; +} // namespace Tesses::Framework::Filesystem \ No newline at end of file diff --git a/include/TessesFramework/Filesystem/SubdirFilesystem.hpp b/include/TessesFramework/Filesystem/SubdirFilesystem.hpp index d0e341a..7e4cb0b 100644 --- a/include/TessesFramework/Filesystem/SubdirFilesystem.hpp +++ b/include/TessesFramework/Filesystem/SubdirFilesystem.hpp @@ -1,53 +1,73 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "VFS.hpp" #include "VFSFix.hpp" -namespace Tesses::Framework::Filesystem -{ - class SubdirFilesystem : public VFS - { - std::shared_ptr parent; - VFSPath path; - VFSPath ToParent(VFSPath path); - VFSPath FromParent(VFSPath path); - public: - SubdirFilesystem(std::shared_ptr parent, VFSPath path); - std::shared_ptr OpenFile(VFSPath path, std::string mode); - void CreateDirectory(VFSPath path); - void DeleteDirectory(VFSPath path); - void DeleteFile(VFSPath path); - void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); - VFSPathEnumerator EnumeratePaths(VFSPath path); - void CreateHardlink(VFSPath existingFile, VFSPath newName); - void MoveFile(VFSPath src, VFSPath dest); - void MoveDirectory(VFSPath src, VFSPath dest); - void DeleteDirectoryRecurse(VFSPath path); - VFSPath ReadLink(VFSPath path); - std::string VFSPathToSystem(VFSPath path); - VFSPath SystemToVFSPath(std::string path); - ~SubdirFilesystem(); - void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess); - bool StatVFS(VFSPath path, StatVFSData& vfsData); - bool Stat(VFSPath path, StatData& data); +namespace Tesses::Framework::Filesystem { +class SubdirFilesystem : public VFS { + std::shared_ptr parent; + VFSPath path; + VFSPath ToParent(VFSPath path); + VFSPath FromParent(VFSPath path); - void Chown(VFSPath path, uint32_t uid, uint32_t gid); - void Chmod(VFSPath path, uint32_t mode); - FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mode); - void Lock(VFSPath path); - void Unlock(VFSPath path); + public: + SubdirFilesystem(std::shared_ptr parent, VFSPath path); + std::shared_ptr + OpenFile(VFSPath path, std::string mode); + void CreateDirectory(VFSPath path); + void DeleteDirectory(VFSPath path); + void DeleteFile(VFSPath path); + void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); + VFSPathEnumerator EnumeratePaths(VFSPath path); + void CreateHardlink(VFSPath existingFile, VFSPath newName); + void MoveFile(VFSPath src, VFSPath dest); + void MoveDirectory(VFSPath src, VFSPath dest); + void DeleteDirectoryRecurse(VFSPath path); + VFSPath ReadLink(VFSPath path); + std::string VFSPathToSystem(VFSPath path); + VFSPath SystemToVFSPath(std::string path); + ~SubdirFilesystem(); + void SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess); + bool StatVFS(VFSPath path, StatVFSData &vfsData); + bool Stat(VFSPath path, StatData &data); + void Chown(VFSPath path, uint32_t uid, uint32_t gid); + void Chmod(VFSPath path, uint32_t mode); + FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mode); + void Lock(VFSPath path); + void Unlock(VFSPath path); - protected: - std::shared_ptr CreateWatcher(std::shared_ptr vfs, VFSPath path); + protected: + std::shared_ptr CreateWatcher(std::shared_ptr vfs, + VFSPath path); + class Watcher : public FSWatcher { + std::shared_ptr watcher; - class Watcher : public FSWatcher { - std::shared_ptr watcher; - protected: - void SetEnabledImpl(bool enabled); - public: - Watcher(std::shared_ptr vfs, VFSPath path); - ~Watcher(); - }; + protected: + void SetEnabledImpl(bool enabled); + + public: + Watcher(std::shared_ptr vfs, VFSPath path); + ~Watcher(); }; -} +}; +} // namespace Tesses::Framework::Filesystem diff --git a/include/TessesFramework/Filesystem/TempFS.hpp b/include/TessesFramework/Filesystem/TempFS.hpp index 4676446..09e2359 100644 --- a/include/TessesFramework/Filesystem/TempFS.hpp +++ b/include/TessesFramework/Filesystem/TempFS.hpp @@ -1,52 +1,71 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Common.hpp" #include "VFS.hpp" #include "VFSFix.hpp" -namespace Tesses::Framework::Filesystem -{ - void UniqueString(std::string& text); +namespace Tesses::Framework::Filesystem { +void UniqueString(std::string &text); - class TempFS : public VFS - { - std::shared_ptr vfs; - bool deleteOnDestroy; - std::shared_ptr parent; - std::string tmp_str; - public: - std::string TempDirectoryName(); - TempFS(bool deleteOnDestroy=true); - TempFS(std::shared_ptr vfs,bool deleteOnDestroy=true); +class TempFS : public VFS { + std::shared_ptr vfs; + bool deleteOnDestroy; + std::shared_ptr parent; + std::string tmp_str; - std::shared_ptr OpenFile(VFSPath path, std::string mode); - void CreateDirectory(VFSPath path); - void DeleteDirectory(VFSPath path); - void DeleteFile(VFSPath path); - void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); - VFSPathEnumerator EnumeratePaths(VFSPath path); - void CreateHardlink(VFSPath existingFile, VFSPath newName); - void MoveFile(VFSPath src, VFSPath dest); - void MoveDirectory(VFSPath src, VFSPath dest); - void DeleteDirectoryRecurse(VFSPath path); - VFSPath ReadLink(VFSPath path); - std::string VFSPathToSystem(VFSPath path); - VFSPath SystemToVFSPath(std::string path); + public: + std::string TempDirectoryName(); + TempFS(bool deleteOnDestroy = true); + TempFS(std::shared_ptr vfs, bool deleteOnDestroy = true); - void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess); - bool StatVFS(VFSPath path, StatVFSData& vfsData); - bool Stat(VFSPath path, StatData& data); + std::shared_ptr + OpenFile(VFSPath path, std::string mode); + void CreateDirectory(VFSPath path); + void DeleteDirectory(VFSPath path); + void DeleteFile(VFSPath path); + void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); + VFSPathEnumerator EnumeratePaths(VFSPath path); + void CreateHardlink(VFSPath existingFile, VFSPath newName); + void MoveFile(VFSPath src, VFSPath dest); + void MoveDirectory(VFSPath src, VFSPath dest); + void DeleteDirectoryRecurse(VFSPath path); + VFSPath ReadLink(VFSPath path); + std::string VFSPathToSystem(VFSPath path); + VFSPath SystemToVFSPath(std::string path); - void Chmod(VFSPath path, uint32_t mode); + void SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess); + bool StatVFS(VFSPath path, StatVFSData &vfsData); + bool Stat(VFSPath path, StatData &data); - void Chown(VFSPath path, uint32_t uid, uint32_t gid); - FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mode); - void Close(); + void Chmod(VFSPath path, uint32_t mode); - void Lock(VFSPath path); - void Unlock(VFSPath path); - ~TempFS(); + void Chown(VFSPath path, uint32_t uid, uint32_t gid); + FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mode); + void Close(); - protected: - std::shared_ptr CreateWatcher(std::shared_ptr vfs, VFSPath path); + void Lock(VFSPath path); + void Unlock(VFSPath path); + ~TempFS(); - }; -} + protected: + std::shared_ptr CreateWatcher(std::shared_ptr vfs, + VFSPath path); +}; +} // namespace Tesses::Framework::Filesystem diff --git a/include/TessesFramework/Filesystem/VFS.hpp b/include/TessesFramework/Filesystem/VFS.hpp index c7b4aff..0cfcf5b 100644 --- a/include/TessesFramework/Filesystem/VFS.hpp +++ b/include/TessesFramework/Filesystem/VFS.hpp @@ -1,343 +1,336 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Common.hpp" +#include "../Date/Date.hpp" #include "../Streams/Stream.hpp" +#include "VFSFix.hpp" #include #include #include #include -#include "../Date/Date.hpp" -#include "VFSFix.hpp" -namespace Tesses::Framework::Filesystem -{ +namespace Tesses::Framework::Filesystem { - struct StatVFSData { +struct StatVFSData { - uint64_t BlockSize; - uint64_t FragmentSize; - uint64_t Blocks; - uint64_t BlocksFree; - uint64_t BlocksAvailable; - uint64_t TotalInodes; - uint64_t FreeInodes; - uint64_t AvailableInodes; - uint64_t Id; - uint64_t Flags; - uint64_t MaxNameLength; - }; + uint64_t BlockSize; + uint64_t FragmentSize; + uint64_t Blocks; + uint64_t BlocksFree; + uint64_t BlocksAvailable; + uint64_t TotalInodes; + uint64_t FreeInodes; + uint64_t AvailableInodes; + uint64_t Id; + uint64_t Flags; + uint64_t MaxNameLength; +}; - constexpr uint32_t MODE_USER_READ = 0400; - constexpr uint32_t MODE_USER_WRITE = 0200; - constexpr uint32_t MODE_USER_EXEC = 0100; +constexpr uint32_t MODE_USER_READ = 0400; +constexpr uint32_t MODE_USER_WRITE = 0200; +constexpr uint32_t MODE_USER_EXEC = 0100; - constexpr uint32_t MODE_GROUP_READ = (MODE_USER_READ >> 3); - constexpr uint32_t MODE_GROUP_WRITE = (MODE_USER_WRITE >> 3); - constexpr uint32_t MODE_GROUP_EXEC = (MODE_USER_EXEC >> 3); +constexpr uint32_t MODE_GROUP_READ = (MODE_USER_READ >> 3); +constexpr uint32_t MODE_GROUP_WRITE = (MODE_USER_WRITE >> 3); +constexpr uint32_t MODE_GROUP_EXEC = (MODE_USER_EXEC >> 3); - constexpr uint32_t MODE_OTHER_READ = (MODE_GROUP_READ >> 3); - constexpr uint32_t MODE_OTHER_WRITE = (MODE_GROUP_WRITE >> 3); - constexpr uint32_t MODE_OTHER_EXEC = (MODE_GROUP_EXEC >> 3); +constexpr uint32_t MODE_OTHER_READ = (MODE_GROUP_READ >> 3); +constexpr uint32_t MODE_OTHER_WRITE = (MODE_GROUP_WRITE >> 3); +constexpr uint32_t MODE_OTHER_EXEC = (MODE_GROUP_EXEC >> 3); - constexpr uint32_t MODE_REGULAR = 0100000; - constexpr uint32_t MODE_DIRECTORY = 0040000; - constexpr uint32_t MODE_CHAR_DEVICE = 0020000; - constexpr uint32_t MODE_BLOCK_DEVICE = 0060000; - constexpr uint32_t MODE_SOCKET = 0140000; - constexpr uint32_t MODE_FIFO = 0010000; - constexpr uint32_t MODE_SYMLINK = 0120000; +constexpr uint32_t MODE_REGULAR = 0100000; +constexpr uint32_t MODE_DIRECTORY = 0040000; +constexpr uint32_t MODE_CHAR_DEVICE = 0020000; +constexpr uint32_t MODE_BLOCK_DEVICE = 0060000; +constexpr uint32_t MODE_SOCKET = 0140000; +constexpr uint32_t MODE_FIFO = 0010000; +constexpr uint32_t MODE_SYMLINK = 0120000; +struct StatData { + uint64_t Device; + uint64_t Inode; + uint32_t Mode; + uint64_t HardLinks; + uint32_t UserId; + uint32_t GroupId; + uint64_t DeviceId; + uint64_t Size; + uint64_t BlockSize; + uint64_t BlockCount; + Date::DateTime LastAccess; + Date::DateTime LastModified; + Date::DateTime LastStatus; - struct StatData { - uint64_t Device; - uint64_t Inode; - uint32_t Mode; - uint64_t HardLinks; - uint32_t UserId; - uint32_t GroupId; - uint64_t DeviceId; - uint64_t Size; - uint64_t BlockSize; - uint64_t BlockCount; - Date::DateTime LastAccess; - Date::DateTime LastModified; - Date::DateTime LastStatus; + bool IsRegularFile() { return (Mode & MODE_REGULAR) > 0; } + bool IsDirectory() { return (Mode & MODE_DIRECTORY) > 0; } - bool IsRegularFile() - { - return (Mode & MODE_REGULAR) > 0; - } - bool IsDirectory() - { - return (Mode & MODE_DIRECTORY) > 0; - } + bool IsCharDevice() { return (Mode & MODE_CHAR_DEVICE) > 0; } - bool IsCharDevice() - { - return (Mode & MODE_CHAR_DEVICE) > 0; - } + bool IsBlockDevice() { return (Mode & MODE_BLOCK_DEVICE) > 0; } - bool IsBlockDevice() - { - return (Mode & MODE_BLOCK_DEVICE) > 0; - } + bool IsSocket() { return (Mode & MODE_SOCKET) > 0; } + bool IsFIFO() { return (Mode & MODE_FIFO) > 0; } - bool IsSocket() - { - return (Mode & MODE_SOCKET) > 0; - } - bool IsFIFO() - { - return (Mode & MODE_FIFO) > 0; - } + bool IsSymlink() { return (Mode & MODE_SYMLINK) > 0; } - bool IsSymlink() - { - return (Mode & MODE_SYMLINK) > 0; - } - - bool IsSpecial() - { - if(IsRegularFile()) return false; - if(IsDirectory()) return false; - return true; - } - - std::string ToSizeString(bool usesBin=true) - { - return TF_FileSize(this->Size, usesBin); - } - }; - class VFSPath { - public: - static VFSPath CurrentDirectoryAsRelative(); - bool relative; - static std::vector SplitPath(std::string path); - std::vector path; - VFSPath(); - explicit VFSPath(const char* path) : VFSPath(std::string(path)) - {} - VFSPath(std::vector path); - VFSPath(std::string path); - VFSPath(VFSPath p, std::string subent); - VFSPath(VFSPath p, VFSPath p2); - - //does not check for ? - static VFSPath ParseUriPath(std::string path); - - VFSPath GetParent() const; - VFSPath CollapseRelativeParents() const; - std::string GetFileName() const; - bool HasExtension() const; - std::string GetExtension() const; - void ChangeExtension(std::string ext); - void RemoveExtension(); - std::string ToString() const; - - static VFSPath GetAbsoluteCurrentDirectory(); - static void SetAbsoluteCurrentDirectory(VFSPath path); - VFSPath MakeAbsolute() const; - - VFSPath MakeAbsolute(VFSPath curDir) const; - VFSPath MakeRelative() const; - VFSPath MakeRelative(VFSPath toMakeRelativeTo) const; - }; - VFSPath operator/(VFSPath p, VFSPath p2); - VFSPath operator/(VFSPath p, std::string p2); - VFSPath operator/(std::string p, VFSPath p2); - VFSPath operator+(VFSPath p, VFSPath p2); - VFSPath operator+(VFSPath p, std::string p2); - VFSPath operator+(std::string p, VFSPath p2); - bool operator==(VFSPath p,VFSPath p2); - bool operator!=(VFSPath p,VFSPath p2); - bool operator==(std::string p,VFSPath p2); - bool operator!=(std::string p,VFSPath p2); - bool operator==(VFSPath p,std::string p2); - bool operator!=(VFSPath p,std::string p2); - class VFSPathEnumeratorData { - public: - VFSPathEnumeratorData(std::function moveNext, std::function destroy) - { - this->eof=false; - this->moveNext=moveNext; - this->destroy=destroy; - } - bool eof; - std::function moveNext; - std::function destroy; - ~VFSPathEnumeratorData() - { - this->destroy(); - } - }; - - class VFSPathEnumerator; - - class VFSPathEnumeratorItterator - { - VFSPath e; - VFSPathEnumerator* enumerator; - public: - VFSPathEnumeratorItterator(); - VFSPathEnumeratorItterator(VFSPathEnumerator* enumerator); - - VFSPathEnumeratorItterator& operator++(); - VFSPathEnumeratorItterator& operator++(int); - - VFSPath& operator*(); - VFSPath* operator->(); - - bool operator!=(VFSPathEnumeratorItterator right); - bool operator==(VFSPathEnumeratorItterator right); - }; - - class VFSPathEnumerator { - std::shared_ptr data; - public: - VFSPathEnumerator(); - VFSPathEnumerator* MakePointer(); - VFSPathEnumerator(std::function moveNext, std::function destroy); - VFSPath Current; - bool MoveNext(); - bool IsDone(); - - VFSPathEnumeratorItterator begin(); - - VFSPathEnumeratorItterator end(); - }; - enum class FSWatcherEventType { - - None = 0, - //IN_ACCESS - Accessed=1, - //IN_ATTRIB - AttributeChanged =2, - //IN_CLOSE_WRITE - Writen = 4, - //IN_CLOSE_NOWRITE - Read = 8, - //IN_CREATE - Created = 16, - //IN_DELETE - Deleted = 32, - //IN_DELETE_SELF - WatchEntryDeleted = 64, - //IN_MODIFY - Modified = 128, - //IN_MOVE_SELF - WatchEntryMoved = 256, - //IN_MOVED_FROM - MoveOld = 512, - //IN_MOVED_TO - MoveNew = 1024, - //IN_OPEN - Opened = 2048, - //IN_CLOSE - Closed = Writen | Read, - //IN_MOVE - Moved = MoveOld | MoveNew, - //IN_ALL_EVENTS - All = Accessed | AttributeChanged | Created | Deleted | WatchEntryDeleted | Modified | WatchEntryMoved | Opened | Closed | Moved - }; - struct FSWatcherEvent { - //the file or source on move - VFSPath src; - //the dest when moving - VFSPath dest; - FSWatcherEventType type; - bool isDir; - - bool IsEvent(FSWatcherEventType e); - - std::string ToString(); - }; - class VFS; - - class FSWatcher { - private: - std::shared_ptr vfs; - VFSPath path; - protected: - - std::atomic enabled=false; - virtual void SetEnabledImpl(bool enabled); - public: - FSWatcher(std::shared_ptr vfs, VFSPath path); - std::function event; - FSWatcherEventType events = FSWatcherEventType::All; - bool GetEnabled(); - void SetEnabled(bool val); - std::shared_ptr GetFilesystem(); - const VFSPath& GetPath(); - virtual ~FSWatcher() = default; - - static std::shared_ptr Create(std::shared_ptr vfs, VFSPath path); - }; - - enum class FIFOCreationResult { - Success=0, - Exists = 1, - ReadOnlyFS = 2, - Denied = 3, - OutOfInodes = 4, - UnknownError = 5, - Unsupported = 255 - }; - - class VFS { - public: - - virtual std::shared_ptr OpenFile(VFSPath path, std::string mode)=0; - virtual void CreateDirectory(VFSPath path); - virtual void DeleteDirectory(VFSPath path); - bool DirectoryExists(VFSPath path); - bool RegularFileExists(VFSPath path); - bool SymlinkExists(VFSPath path); - bool CharacterDeviceExists(VFSPath path); - bool BlockDeviceExists(VFSPath path); - bool SocketFileExists(VFSPath path); - bool FIFOFileExists(VFSPath path); - bool FileExists(VFSPath path); - bool SpecialFileExists(VFSPath path); - virtual void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); - virtual void CreateHardlink(VFSPath existingFile, VFSPath newName); - virtual void DeleteFile(VFSPath path); - virtual void DeleteDirectoryRecurse(VFSPath path); - virtual VFSPathEnumerator EnumeratePaths(VFSPath path) = 0; - virtual void MoveFile(VFSPath src, VFSPath dest); - virtual void MoveDirectory(VFSPath src, VFSPath dest); - virtual VFSPath ReadLink(VFSPath path); - virtual std::string VFSPathToSystem(VFSPath path)=0; - virtual VFSPath SystemToVFSPath(std::string path)=0; - - - void GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess); - virtual void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess); - - virtual bool Stat(VFSPath path, StatData& data) = 0; - virtual bool StatVFS(VFSPath path, StatVFSData& data); - - virtual void Chmod(VFSPath path, uint32_t mode); - virtual void Chown(VFSPath path, uint32_t userId, uint32_t groupId); - - virtual void Lock(VFSPath path); - virtual void Unlock(VFSPath path); - virtual FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mode); - - virtual ~VFS(); - - virtual void Close(); - - protected: - virtual std::shared_ptr CreateWatcher(std::shared_ptr vfs, VFSPath path); - friend class FSWatcher; - }; - - - namespace Literals - { - inline VFSPath operator""_tpath(const char* path) - { - return VFSPath(path); - } + bool IsSpecial() { + if (IsRegularFile()) + return false; + if (IsDirectory()) + return false; + return true; } -} + + std::string ToSizeString(bool usesBin = true) { + return TF_FileSize(this->Size, usesBin); + } +}; +class VFSPath { + public: + static VFSPath CurrentDirectoryAsRelative(); + bool relative; + static std::vector SplitPath(std::string path); + std::vector path; + VFSPath(); + explicit VFSPath(const char *path) : VFSPath(std::string(path)) {} + VFSPath(std::vector path); + VFSPath(std::string path); + VFSPath(VFSPath p, std::string subent); + VFSPath(VFSPath p, VFSPath p2); + + // does not check for ? + static VFSPath ParseUriPath(std::string path); + + VFSPath GetParent() const; + VFSPath CollapseRelativeParents() const; + std::string GetFileName() const; + bool HasExtension() const; + std::string GetExtension() const; + void ChangeExtension(std::string ext); + void RemoveExtension(); + std::string ToString() const; + + static VFSPath GetAbsoluteCurrentDirectory(); + static void SetAbsoluteCurrentDirectory(VFSPath path); + VFSPath MakeAbsolute() const; + + VFSPath MakeAbsolute(VFSPath curDir) const; + VFSPath MakeRelative() const; + VFSPath MakeRelative(VFSPath toMakeRelativeTo) const; +}; +VFSPath operator/(VFSPath p, VFSPath p2); +VFSPath operator/(VFSPath p, std::string p2); +VFSPath operator/(std::string p, VFSPath p2); +VFSPath operator+(VFSPath p, VFSPath p2); +VFSPath operator+(VFSPath p, std::string p2); +VFSPath operator+(std::string p, VFSPath p2); +bool operator==(VFSPath p, VFSPath p2); +bool operator!=(VFSPath p, VFSPath p2); +bool operator==(std::string p, VFSPath p2); +bool operator!=(std::string p, VFSPath p2); +bool operator==(VFSPath p, std::string p2); +bool operator!=(VFSPath p, std::string p2); +class VFSPathEnumeratorData { + public: + VFSPathEnumeratorData(std::function moveNext, + std::function destroy) { + this->eof = false; + this->moveNext = moveNext; + this->destroy = destroy; + } + bool eof; + std::function moveNext; + std::function destroy; + ~VFSPathEnumeratorData() { this->destroy(); } +}; + +class VFSPathEnumerator; + +class VFSPathEnumeratorItterator { + VFSPath e; + VFSPathEnumerator *enumerator; + + public: + VFSPathEnumeratorItterator(); + VFSPathEnumeratorItterator(VFSPathEnumerator *enumerator); + + VFSPathEnumeratorItterator &operator++(); + VFSPathEnumeratorItterator &operator++(int); + + VFSPath &operator*(); + VFSPath *operator->(); + + bool operator!=(VFSPathEnumeratorItterator right); + bool operator==(VFSPathEnumeratorItterator right); +}; + +class VFSPathEnumerator { + std::shared_ptr data; + + public: + VFSPathEnumerator(); + VFSPathEnumerator *MakePointer(); + VFSPathEnumerator(std::function moveNext, + std::function destroy); + VFSPath Current; + bool MoveNext(); + bool IsDone(); + + VFSPathEnumeratorItterator begin(); + + VFSPathEnumeratorItterator end(); +}; +enum class FSWatcherEventType { + + None = 0, + // IN_ACCESS + Accessed = 1, + // IN_ATTRIB + AttributeChanged = 2, + // IN_CLOSE_WRITE + Writen = 4, + // IN_CLOSE_NOWRITE + Read = 8, + // IN_CREATE + Created = 16, + // IN_DELETE + Deleted = 32, + // IN_DELETE_SELF + WatchEntryDeleted = 64, + // IN_MODIFY + Modified = 128, + // IN_MOVE_SELF + WatchEntryMoved = 256, + // IN_MOVED_FROM + MoveOld = 512, + // IN_MOVED_TO + MoveNew = 1024, + // IN_OPEN + Opened = 2048, + // IN_CLOSE + Closed = Writen | Read, + // IN_MOVE + Moved = MoveOld | MoveNew, + // IN_ALL_EVENTS + All = Accessed | AttributeChanged | Created | Deleted | WatchEntryDeleted | + Modified | WatchEntryMoved | Opened | Closed | Moved +}; +struct FSWatcherEvent { + // the file or source on move + VFSPath src; + // the dest when moving + VFSPath dest; + FSWatcherEventType type; + bool isDir; + + bool IsEvent(FSWatcherEventType e); + + std::string ToString(); +}; +class VFS; + +class FSWatcher { + private: + std::shared_ptr vfs; + VFSPath path; + + protected: + std::atomic enabled = false; + virtual void SetEnabledImpl(bool enabled); + + public: + FSWatcher(std::shared_ptr vfs, VFSPath path); + std::function event; + FSWatcherEventType events = FSWatcherEventType::All; + bool GetEnabled(); + void SetEnabled(bool val); + std::shared_ptr GetFilesystem(); + const VFSPath &GetPath(); + virtual ~FSWatcher() = default; + + static std::shared_ptr Create(std::shared_ptr vfs, + VFSPath path); +}; + +enum class FIFOCreationResult { + Success = 0, + Exists = 1, + ReadOnlyFS = 2, + Denied = 3, + OutOfInodes = 4, + UnknownError = 5, + Unsupported = 255 +}; + +class VFS { + public: + virtual std::shared_ptr + OpenFile(VFSPath path, std::string mode) = 0; + virtual void CreateDirectory(VFSPath path); + virtual void DeleteDirectory(VFSPath path); + bool DirectoryExists(VFSPath path); + bool RegularFileExists(VFSPath path); + bool SymlinkExists(VFSPath path); + bool CharacterDeviceExists(VFSPath path); + bool BlockDeviceExists(VFSPath path); + bool SocketFileExists(VFSPath path); + bool FIFOFileExists(VFSPath path); + bool FileExists(VFSPath path); + bool SpecialFileExists(VFSPath path); + virtual void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile); + virtual void CreateHardlink(VFSPath existingFile, VFSPath newName); + virtual void DeleteFile(VFSPath path); + virtual void DeleteDirectoryRecurse(VFSPath path); + virtual VFSPathEnumerator EnumeratePaths(VFSPath path) = 0; + virtual void MoveFile(VFSPath src, VFSPath dest); + virtual void MoveDirectory(VFSPath src, VFSPath dest); + virtual VFSPath ReadLink(VFSPath path); + virtual std::string VFSPathToSystem(VFSPath path) = 0; + virtual VFSPath SystemToVFSPath(std::string path) = 0; + + void GetDate(VFSPath path, Date::DateTime &lastWrite, + Date::DateTime &lastAccess); + virtual void SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess); + + virtual bool Stat(VFSPath path, StatData &data) = 0; + virtual bool StatVFS(VFSPath path, StatVFSData &data); + + virtual void Chmod(VFSPath path, uint32_t mode); + virtual void Chown(VFSPath path, uint32_t userId, uint32_t groupId); + + virtual void Lock(VFSPath path); + virtual void Unlock(VFSPath path); + virtual FIFOCreationResult CreateFIFO(VFSPath path, uint32_t mode); + + virtual ~VFS(); + + virtual void Close(); + + protected: + virtual std::shared_ptr CreateWatcher(std::shared_ptr vfs, + VFSPath path); + friend class FSWatcher; +}; + +namespace Literals { +inline VFSPath operator""_tpath(const char *path) { return VFSPath(path); } +} // namespace Literals +} // namespace Tesses::Framework::Filesystem diff --git a/include/TessesFramework/Filesystem/VFSFix.hpp b/include/TessesFramework/Filesystem/VFSFix.hpp index 3c6f742..4d4d9a7 100644 --- a/include/TessesFramework/Filesystem/VFSFix.hpp +++ b/include/TessesFramework/Filesystem/VFSFix.hpp @@ -1,3 +1,21 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #undef CreateDirectory #undef DeleteDirectory #undef DeleteFile diff --git a/include/TessesFramework/HiddenField.hpp b/include/TessesFramework/HiddenField.hpp index 9a7894b..7859993 100644 --- a/include/TessesFramework/HiddenField.hpp +++ b/include/TessesFramework/HiddenField.hpp @@ -1,32 +1,45 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include namespace Tesses::Framework { class HiddenFieldData { - public: - virtual ~HiddenFieldData(); + public: + virtual ~HiddenFieldData(); }; class HiddenField { - private: - HiddenFieldData* ptr; - public: - HiddenField(); - HiddenField(HiddenFieldData* data); - void SetField(HiddenFieldData* data); - template - T GetField() - { - return dynamic_cast(ptr); - } - template - T* AllocField() - { - auto v = new T(); - SetField(v); - return v; - } - ~HiddenField(); + private: + HiddenFieldData *ptr; + + public: + HiddenField(); + HiddenField(HiddenFieldData *data); + void SetField(HiddenFieldData *data); + template T GetField() { return dynamic_cast(ptr); } + template T *AllocField() { + auto v = new T(); + SetField(v); + return v; + } + ~HiddenField(); }; -} \ No newline at end of file +} // namespace Tesses::Framework \ No newline at end of file diff --git a/include/TessesFramework/Http/BasicAuthServer.hpp b/include/TessesFramework/Http/BasicAuthServer.hpp index ed475c9..9dcc685 100644 --- a/include/TessesFramework/Http/BasicAuthServer.hpp +++ b/include/TessesFramework/Http/BasicAuthServer.hpp @@ -1,18 +1,39 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "HttpServer.hpp" namespace Tesses::Framework::Http { - class BasicAuthServer : public Tesses::Framework::Http::IHttpServer - { - public: - std::shared_ptr server; - std::function authorization; - std::string realm; +class BasicAuthServer : public Tesses::Framework::Http::IHttpServer { + public: + std::shared_ptr server; + std::function + authorization; + std::string realm; - BasicAuthServer(); - BasicAuthServer(std::shared_ptr server, std::function auth,std::string realm="Protected Content"); - bool Handle(ServerContext& ctx); + BasicAuthServer(); + BasicAuthServer( + std::shared_ptr server, + std::function auth, + std::string realm = "Protected Content"); + bool Handle(ServerContext &ctx); - - static bool GetCreds(ServerContext& ctx, std::string& user, std::string& pass); - }; -} \ No newline at end of file + static bool GetCreds(ServerContext &ctx, std::string &user, + std::string &pass); +}; +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Http/CGIServer.hpp b/include/TessesFramework/Http/CGIServer.hpp index 18e4b9d..6b6509d 100644 --- a/include/TessesFramework/Http/CGIServer.hpp +++ b/include/TessesFramework/Http/CGIServer.hpp @@ -1,23 +1,42 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Filesystem/VFS.hpp" #include "../Filesystem/VFSFix.hpp" #include "HttpServer.hpp" #include namespace Tesses::Framework::Http { - struct CGIParams { - std::optional document_root; - Tesses::Framework::Filesystem::VFSPath program; - std::optional adminEmail; - std::optional workingDirectory; - }; - class CGIServer : public Tesses::Framework::Http::IHttpServer { - Tesses::Framework::Filesystem::VFSPath dir; - public: - std::optional document_root; - std::optional adminEmail; - std::optional workingDirectory; - CGIServer(Tesses::Framework::Filesystem::VFSPath dir); - bool Handle(ServerContext& ctx); - static bool ServeCGIRequest(ServerContext& ctx, CGIParams& params); - }; -} \ No newline at end of file +struct CGIParams { + std::optional document_root; + Tesses::Framework::Filesystem::VFSPath program; + std::optional adminEmail; + std::optional workingDirectory; +}; +class CGIServer : public Tesses::Framework::Http::IHttpServer { + Tesses::Framework::Filesystem::VFSPath dir; + + public: + std::optional document_root; + std::optional adminEmail; + std::optional workingDirectory; + CGIServer(Tesses::Framework::Filesystem::VFSPath dir); + bool Handle(ServerContext &ctx); + static bool ServeCGIRequest(ServerContext &ctx, CGIParams ¶ms); +}; +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Http/CallbackServer.hpp b/include/TessesFramework/Http/CallbackServer.hpp index 291bdf6..e862920 100644 --- a/include/TessesFramework/Http/CallbackServer.hpp +++ b/include/TessesFramework/Http/CallbackServer.hpp @@ -1,16 +1,34 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "HttpServer.hpp" -namespace Tesses::Framework::Http -{ - class CallbackServer : public IHttpServer - { - std::function cb; - std::function destroy; - public: - CallbackServer(std::function cb); - CallbackServer(std::function cb,std::function destroy); - bool Handle(ServerContext& ctx); - ~CallbackServer(); - }; -} \ No newline at end of file +namespace Tesses::Framework::Http { +class CallbackServer : public IHttpServer { + std::function cb; + std::function destroy; + + public: + CallbackServer(std::function cb); + CallbackServer(std::function cb, + std::function destroy); + bool Handle(ServerContext &ctx); + ~CallbackServer(); +}; +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Http/ChangeableServer.hpp b/include/TessesFramework/Http/ChangeableServer.hpp index d6d6439..7583e7f 100644 --- a/include/TessesFramework/Http/ChangeableServer.hpp +++ b/include/TessesFramework/Http/ChangeableServer.hpp @@ -1,14 +1,31 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "HttpServer.hpp" -namespace Tesses::Framework::Http -{ - class ChangeableServer { - public: - ChangeableServer(); - ChangeableServer(std::shared_ptr original); - std::shared_ptr server; - bool Handle(ServerContext& ctx); - ~ChangeableServer(); - }; -} \ No newline at end of file +namespace Tesses::Framework::Http { +class ChangeableServer { + public: + ChangeableServer(); + ChangeableServer(std::shared_ptr original); + std::shared_ptr server; + bool Handle(ServerContext &ctx); + ~ChangeableServer(); +}; +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Http/ContentDisposition.hpp b/include/TessesFramework/Http/ContentDisposition.hpp index 638c71d..5e1907b 100644 --- a/include/TessesFramework/Http/ContentDisposition.hpp +++ b/include/TessesFramework/Http/ContentDisposition.hpp @@ -1,13 +1,30 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Common.hpp" -namespace Tesses::Framework::Http -{ - class ContentDisposition { - public: - std::string filename; - std::string type; - std::string fieldName; - static bool TryParse(std::string str, ContentDisposition& cd); - std::string ToString(); - }; -} \ No newline at end of file +namespace Tesses::Framework::Http { +class ContentDisposition { + public: + std::string filename; + std::string type; + std::string fieldName; + static bool TryParse(std::string str, ContentDisposition &cd); + std::string ToString(); +}; +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Http/FileServer.hpp b/include/TessesFramework/Http/FileServer.hpp index e77c656..1c18a2b 100644 --- a/include/TessesFramework/Http/FileServer.hpp +++ b/include/TessesFramework/Http/FileServer.hpp @@ -1,25 +1,45 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Filesystem/VFS.hpp" #include "HttpServer.hpp" -namespace Tesses::Framework::Http -{ - class FileServer : public IHttpServer - { - std::shared_ptr vfs; - - +namespace Tesses::Framework::Http { +class FileServer : public IHttpServer { + std::shared_ptr vfs; - bool SendFile(ServerContext& ctx,Tesses::Framework::Filesystem::VFSPath path); - public: - bool allowListing; - bool spa; - std::vector defaultNames; - FileServer(std::filesystem::path path,bool allowListing,bool spa); - FileServer(std::filesystem::path path,bool allowListing, bool spa, std::vector defaultNames); - FileServer(std::shared_ptr fs, bool allowListing, bool spa); - FileServer(std::shared_ptr fs, bool allowListing, bool spa, std::vector defaultNames); - bool Handle(ServerContext& ctx); - ~FileServer(); - }; -} \ No newline at end of file + bool SendFile(ServerContext &ctx, + Tesses::Framework::Filesystem::VFSPath path); + + public: + bool allowListing; + bool spa; + std::vector defaultNames; + FileServer(std::filesystem::path path, bool allowListing, bool spa); + FileServer(std::filesystem::path path, bool allowListing, bool spa, + std::vector defaultNames); + FileServer(std::shared_ptr fs, + bool allowListing, bool spa); + FileServer(std::shared_ptr fs, + bool allowListing, bool spa, + std::vector defaultNames); + bool Handle(ServerContext &ctx); + ~FileServer(); +}; +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Http/HttpClient.hpp b/include/TessesFramework/Http/HttpClient.hpp index 6132e6a..efa726b 100644 --- a/include/TessesFramework/Http/HttpClient.hpp +++ b/include/TessesFramework/Http/HttpClient.hpp @@ -1,95 +1,139 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Streams/Stream.hpp" #include "HttpUtils.hpp" -#include "WebSocket.hpp" #include "TessesFramework/Filesystem/LocalFS.hpp" #include "TessesFramework/Filesystem/VFSFix.hpp" -namespace Tesses::Framework::Http -{ - - class HttpRequestBody { - public: - virtual void HandleHeaders(HttpDictionary& dict); - virtual void Write(std::shared_ptr strm)=0; - virtual ~HttpRequestBody(); - }; +#include "WebSocket.hpp" +namespace Tesses::Framework::Http { - class StreamHttpRequestBody : public HttpRequestBody { - std::shared_ptr strm; - - std::string mimeType; - public: - StreamHttpRequestBody(std::shared_ptr strm, std::string mimeType); - void HandleHeaders(HttpDictionary& dict); - void Write(std::shared_ptr strm); - ~StreamHttpRequestBody(); - }; +class HttpRequestBody { + public: + virtual void HandleHeaders(HttpDictionary &dict); + virtual void + Write(std::shared_ptr strm) = 0; + virtual ~HttpRequestBody(); +}; - class TextHttpRequestBody : public HttpRequestBody { - std::string text; - std::string mimeType; - public: - TextHttpRequestBody(std::string text, std::string mimeType); - void HandleHeaders(HttpDictionary& dict); - void Write(std::shared_ptr strm); - ~TextHttpRequestBody(); - }; +class StreamHttpRequestBody : public HttpRequestBody { + std::shared_ptr strm; + std::string mimeType; - class HttpRequest { - public: - HttpRequest(); - std::string trusted_root_cert_bundle; - bool ignoreSSLErrors; - bool followRedirects; + public: + StreamHttpRequestBody( + std::shared_ptr strm, + std::string mimeType); + void HandleHeaders(HttpDictionary &dict); + void Write(std::shared_ptr strm); + ~StreamHttpRequestBody(); +}; - std::string method; - std::string url; - std::string unixSocket; - HttpDictionary requestHeaders; - HttpRequestBody* body; +class TextHttpRequestBody : public HttpRequestBody { + std::string text; + std::string mimeType; - static std::shared_ptr EstablishConnection(Uri uri,bool ignoreSSLErrors,std::string trusted_root_cert_bundle); - static std::shared_ptr EstablishUnixPathConnection(std::string unixPath, Uri uri, bool ignoreSSLErrors, std::string trusted_root_cert_bundle); + public: + TextHttpRequestBody(std::string text, std::string mimeType); + void HandleHeaders(HttpDictionary &dict); + void Write(std::shared_ptr strm); + ~TextHttpRequestBody(); +}; - void SendRequest(std::shared_ptr strm); - }; +class HttpRequest { + public: + HttpRequest(); + std::string trusted_root_cert_bundle; + bool ignoreSSLErrors; + bool followRedirects; - class HttpResponse { - private: - bool owns; - std::shared_ptr handleStrm; - public: - HttpResponse(std::shared_ptr strm); - HttpResponse(HttpRequest& request); - std::string version; - StatusCode statusCode; - HttpDictionary responseHeaders; - std::string ReadAsString(); - std::shared_ptr ReadAsStream(); - void CopyToStream(std::shared_ptr strm); - std::shared_ptr GetInternalStream(); - ~HttpResponse(); - }; + std::string method; + std::string url; + std::string unixSocket; + HttpDictionary requestHeaders; + HttpRequestBody *body; - void DownloadToStreamSimple(std::string url, std::shared_ptr strm); + static std::shared_ptr + EstablishConnection(Uri uri, bool ignoreSSLErrors, + std::string trusted_root_cert_bundle); + static std::shared_ptr + EstablishUnixPathConnection(std::string unixPath, Uri uri, + bool ignoreSSLErrors, + std::string trusted_root_cert_bundle); - void DownloadToFileSimple(std::string url, std::shared_ptr vfs, Tesses::Framework::Filesystem::VFSPath path); + void SendRequest(std::shared_ptr strm); +}; - void DownloadToFileSimple(std::string url, Tesses::Framework::Filesystem::VFSPath path); - std::string DownloadToStringSimple(std::string url); - - bool WebSocketClientSuccessDefault(HttpDictionary& dict,bool v); - void WebSocketClient(std::string url, HttpDictionary& requestHeaders, std::shared_ptr wsc, std::function cb=WebSocketClientSuccessDefault); - - - void DownloadUnixSocketToStreamSimple(std::string unixSocket,std::string url, std::shared_ptr strm); +class HttpResponse { + private: + bool owns; + std::shared_ptr handleStrm; - void DownloadUnixSocketToFileSimple(std::string unixSocket,std::string url, std::shared_ptr vfs, Tesses::Framework::Filesystem::VFSPath path); - - void DownloadUnixSocketToFileSimple(std::string unixSocket,std::string url, Tesses::Framework::Filesystem::VFSPath path); - std::string DownloadUnixSocketToStringSimple(std::string unixSocket,std::string url); + public: + HttpResponse(std::shared_ptr strm); + HttpResponse(HttpRequest &request); + std::string version; + StatusCode statusCode; + HttpDictionary responseHeaders; + std::string ReadAsString(); + std::shared_ptr ReadAsStream(); + void CopyToStream(std::shared_ptr strm); + std::shared_ptr GetInternalStream(); + ~HttpResponse(); +}; +void DownloadToStreamSimple( + std::string url, std::shared_ptr strm); - void WebSocketUnixSocketClient(std::string unixSocket,std::string url, HttpDictionary& requestHeaders, std::shared_ptr wsc, std::function cb=WebSocketClientSuccessDefault); -} \ No newline at end of file +void DownloadToFileSimple( + std::string url, std::shared_ptr vfs, + Tesses::Framework::Filesystem::VFSPath path); + +void DownloadToFileSimple(std::string url, + Tesses::Framework::Filesystem::VFSPath path); +std::string DownloadToStringSimple(std::string url); + +bool WebSocketClientSuccessDefault(HttpDictionary &dict, bool v); +void WebSocketClient(std::string url, HttpDictionary &requestHeaders, + std::shared_ptr wsc, + std::function cb = + WebSocketClientSuccessDefault); + +void DownloadUnixSocketToStreamSimple( + std::string unixSocket, std::string url, + std::shared_ptr strm); + +void DownloadUnixSocketToFileSimple( + std::string unixSocket, std::string url, + std::shared_ptr vfs, + Tesses::Framework::Filesystem::VFSPath path); + +void DownloadUnixSocketToFileSimple( + std::string unixSocket, std::string url, + Tesses::Framework::Filesystem::VFSPath path); +std::string DownloadUnixSocketToStringSimple(std::string unixSocket, + std::string url); + +void WebSocketUnixSocketClient(std::string unixSocket, std::string url, + HttpDictionary &requestHeaders, + std::shared_ptr wsc, + std::function cb = + WebSocketClientSuccessDefault); +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Http/HttpServer.hpp b/include/TessesFramework/Http/HttpServer.hpp index 9c4c60a..49f5d05 100644 --- a/include/TessesFramework/Http/HttpServer.hpp +++ b/include/TessesFramework/Http/HttpServer.hpp @@ -1,138 +1,170 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Streams/NetworkStream.hpp" -#include "HttpUtils.hpp" -#include "../Threading/Thread.hpp" #include "../Date/Date.hpp" -#include +#include "../Threading/Thread.hpp" +#include "HttpUtils.hpp" #include "WebSocket.hpp" #include -namespace Tesses::Framework::Http -{ - class ServerContextData { - public: - virtual ~ServerContextData(); - }; - class ServerContext; - class ServerSentEvents { - std::vector> strms; - Tesses::Framework::Threading::Mutex mtx; - private: - void SendEventRaw(const std::string& evt); - public: - void SendRetry(uint32_t ms); - void SendRetry(std::chrono::milliseconds ms); - void SendRetry(Tesses::Framework::Date::TimeSpan ts); - void SendData(const std::string& message); - void SendData(const std::string& message, const std::string& dataType); - void SendId(const std::string& idVal); - void SendCustomEvent(const std::string& type, const std::string& value); - void SendComment(const std::string& comment); - friend class ServerContext; - }; +#include +namespace Tesses::Framework::Http { +class ServerContextData { + public: + virtual ~ServerContextData(); +}; +class ServerContext; +class ServerSentEvents { + std::vector> strms; + Tesses::Framework::Threading::Mutex mtx; + private: + void SendEventRaw(const std::string &evt); - class ServerContext { - - bool sent; - bool debug; - std::vector> sse; - std::shared_ptr strm; - std::map data; - std::queue> headerhandlers; - public: - HttpDictionary requestHeaders; - HttpDictionary responseHeaders; - HttpDictionary queryParams; - //used by path - HttpDictionary pathArguments; - std::string path; - std::string originalPath; - std::string method; - StatusCode statusCode; - std::string ip; - uint16_t port; - uint16_t serverPort; - std::string version; - bool encrypted; - ServerContext(std::shared_ptr strm, bool debug=false); - ~ServerContext(); - std::shared_ptr GetStream(); - std::string GetOriginalPathWithQuery(); - std::string GetUrlWithQuery(); - bool Sent(); - bool NeedToParseFormData(); - void ParseFormData(std::function(std::string mime, std::string filename, std::string name)> cb); - void ReadStream(std::shared_ptr strm); - std::string ReadString(); - void SendBytes(std::vector buffer); - void SendText(std::string text); - void SendStream(std::shared_ptr strm); - void SendErrorPage(bool showPath); - void SendNotFound(); - void SendBadRequest(); - void SendException(std::exception& ex); - void SendServerSentEvents(std::shared_ptr sse); - std::shared_ptr OpenResponseStream(); - std::shared_ptr OpenRequestStream(); - ServerContext& WithStatusCode(StatusCode code); - ServerContext& WithLastModified(Date::DateTime time); - ServerContext& WithHeader(std::string key, std::string value); - ServerContext& WithSingleHeader(std::string key, std::string value); - ServerContext& WithMimeType(std::string mime); - ServerContext& WithContentDisposition(std::string filename, bool isInline); - ServerContext& WriteHeaders(); - ServerContext& WithLocationHeader(std::string url); - ServerContext& WithLocationHeader(std::string url,StatusCode sc); - ServerContext& WithHeaderIntercepter(std::function cb); - ServerContext& WithDebug(bool debug=true); - bool Debug(); - void StartWebSocketSession(std::function,std::function,std::function)> onOpen, std::function onReceive, std::function onClose); - void StartWebSocketSession(std::shared_ptr connection); - std::string GetServerRoot(); - std::string MakeAbsolute(std::string path); - void SendRedirect(std::string url); - void SendRedirect(std::string url, StatusCode sc); + public: + void SendRetry(uint32_t ms); + void SendRetry(std::chrono::milliseconds ms); + void SendRetry(Tesses::Framework::Date::TimeSpan ts); + void SendData(const std::string &message); + void SendData(const std::string &message, const std::string &dataType); + void SendId(const std::string &idVal); + void SendCustomEvent(const std::string &type, const std::string &value); + void SendComment(const std::string &comment); + friend class ServerContext; +}; +class ServerContext { - template - T* GetServerContentData(std::string tag) - { - std::string name = typeid(T).name(); - name.push_back(' '); - name.append(tag); - if(data.count(name) > 0) return dynamic_cast(data[name]); - T* item = new T(); - data[name] = item; - return item; - } - friend class ServerSentEvents; + bool sent; + bool debug; + std::vector> sse; + std::shared_ptr strm; + std::map data; + std::queue> headerhandlers; - - }; + public: + HttpDictionary requestHeaders; + HttpDictionary responseHeaders; + HttpDictionary queryParams; + // used by path + HttpDictionary pathArguments; + std::string path; + std::string originalPath; + std::string method; + StatusCode statusCode; + std::string ip; + uint16_t port; + uint16_t serverPort; + std::string version; + bool encrypted; + ServerContext(std::shared_ptr strm, + bool debug = false); + ~ServerContext(); + std::shared_ptr GetStream(); + std::string GetOriginalPathWithQuery(); + std::string GetUrlWithQuery(); + bool Sent(); + bool NeedToParseFormData(); + void ParseFormData( + std::function( + std::string mime, std::string filename, std::string name)> + cb); + void ReadStream(std::shared_ptr strm); + std::string ReadString(); + void SendBytes(std::vector buffer); + void SendText(std::string text); + void SendStream(std::shared_ptr strm); + void SendErrorPage(bool showPath); + void SendNotFound(); + void SendBadRequest(); + void SendException(std::exception &ex); + void SendServerSentEvents(std::shared_ptr sse); + std::shared_ptr OpenResponseStream(); + std::shared_ptr OpenRequestStream(); + ServerContext &WithStatusCode(StatusCode code); + ServerContext &WithLastModified(Date::DateTime time); + ServerContext &WithHeader(std::string key, std::string value); + ServerContext &WithSingleHeader(std::string key, std::string value); + ServerContext &WithMimeType(std::string mime); + ServerContext &WithContentDisposition(std::string filename, bool isInline); + ServerContext &WriteHeaders(); + ServerContext &WithLocationHeader(std::string url); + ServerContext &WithLocationHeader(std::string url, StatusCode sc); + ServerContext & + WithHeaderIntercepter(std::function cb); + ServerContext &WithDebug(bool debug = true); + bool Debug(); + void StartWebSocketSession( + std::function, + std::function, std::function)> + onOpen, + std::function onReceive, + std::function onClose); + void StartWebSocketSession(std::shared_ptr connection); + std::string GetServerRoot(); + std::string MakeAbsolute(std::string path); + void SendRedirect(std::string url); + void SendRedirect(std::string url, StatusCode sc); - class IHttpServer { - public: - virtual bool Handle(ServerContext& ctx)=0; - virtual ~IHttpServer(); - }; + template T *GetServerContentData(std::string tag) { + std::string name = typeid(T).name(); + name.push_back(' '); + name.append(tag); + if (data.count(name) > 0) + return dynamic_cast(data[name]); + T *item = new T(); + data[name] = item; + return item; + } + friend class ServerSentEvents; +}; - class HttpServer { - std::shared_ptr server; - std::shared_ptr http; - Tesses::Framework::Threading::Thread* thrd; +class IHttpServer { + public: + virtual bool Handle(ServerContext &ctx) = 0; + virtual ~IHttpServer(); +}; - bool showIPs; - bool showARTL; - bool debug; +class HttpServer { + std::shared_ptr server; + std::shared_ptr http; + Tesses::Framework::Threading::Thread *thrd; - public: - HttpServer(std::shared_ptr tcpServer, std::shared_ptr http, bool showIPs=true, bool debug=false); - HttpServer(uint16_t port, std::shared_ptr http, bool showIPs=true, bool debug=false); - HttpServer(std::string unixPath, std::shared_ptr http, bool debug=false); - uint16_t GetPort(); - void StartAccepting(); - static void Process(std::shared_ptr strm, std::shared_ptr server, std::string ip, uint16_t port,uint16_t serverPort, bool encrypted, bool debug=false); - ~HttpServer(); - }; -} + bool showIPs; + bool showARTL; + bool debug; + + public: + HttpServer(std::shared_ptr tcpServer, + std::shared_ptr http, bool showIPs = true, + bool debug = false); + HttpServer(uint16_t port, std::shared_ptr http, + bool showIPs = true, bool debug = false); + HttpServer(std::string unixPath, std::shared_ptr http, + bool debug = false); + uint16_t GetPort(); + void StartAccepting(); + static void + Process(std::shared_ptr strm, + std::shared_ptr server, std::string ip, uint16_t port, + uint16_t serverPort, bool encrypted, bool debug = false); + ~HttpServer(); +}; +} // namespace Tesses::Framework::Http diff --git a/include/TessesFramework/Http/HttpStream.hpp b/include/TessesFramework/Http/HttpStream.hpp index 4388802..40b2678 100644 --- a/include/TessesFramework/Http/HttpStream.hpp +++ b/include/TessesFramework/Http/HttpStream.hpp @@ -1,31 +1,49 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Streams/Stream.hpp" -namespace Tesses::Framework::Http -{ - class HttpStream : public Tesses::Framework::Streams::Stream { - std::shared_ptr strm; - - size_t offset; - size_t read; - int64_t length; - int64_t position; - - bool recv; - bool http1_1; - - bool done; +namespace Tesses::Framework::Http { +class HttpStream : public Tesses::Framework::Streams::Stream { + std::shared_ptr strm; - public: - HttpStream(std::shared_ptr strm, int64_t length, bool recv, bool http1_1); - bool CanRead(); - bool CanWrite(); - bool EndOfStream(); - int64_t GetLength(); - int64_t GetPosition(); - size_t Read(uint8_t* buffer, size_t len); - size_t Write(const uint8_t* buffer, size_t len); - void Close(); - ~HttpStream(); - }; -} \ No newline at end of file + size_t offset; + size_t read; + int64_t length; + int64_t position; + + bool recv; + bool http1_1; + + bool done; + + public: + HttpStream(std::shared_ptr strm, + int64_t length, bool recv, bool http1_1); + bool CanRead(); + bool CanWrite(); + bool EndOfStream(); + int64_t GetLength(); + int64_t GetPosition(); + size_t Read(uint8_t *buffer, size_t len); + size_t Write(const uint8_t *buffer, size_t len); + void Close(); + ~HttpStream(); +}; +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Http/HttpUtils.hpp b/include/TessesFramework/Http/HttpUtils.hpp index 3d17102..067db8f 100644 --- a/include/TessesFramework/Http/HttpUtils.hpp +++ b/include/TessesFramework/Http/HttpUtils.hpp @@ -1,181 +1,196 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Common.hpp" #include "../Date/Date.hpp" #include -namespace Tesses::Framework::Http -{ - typedef enum StatusCode { - Continue=100, - SwitchingProtocols=101, - Processing=102, - EarlyHints=103, - OK=200, - Created=201, - Accepted=202, - NonAuthoritativeInformation=203, - NoContent=204, - ResetContent=205, - PartialContent=206, - MultiStatus=207, - AlreadyReported=208, - IMUsed=226, - MultipleChoices=300, - MovedPermanently=301, - Found=302, - SeeOther=303, - NotModified=304, - UseProxy=305, - TemporaryRedirect=307, - PermanentRedirect=308, - BadRequest=400, - Unauthorized=401, - PaymentRequired=402, - Forbidden=403, - NotFound=404, - MethodNotAllowed=405, - NotAcceptable=406, - ProxyAuthenticationRequired=407, - RequestTimeout=408, - Conflict=409, - Gone=410, - LengthRequired=411, - PreconditionFailed=412, - PayloadTooLarge=413, - URITooLong=414, - UnsupportedMediaType=415, - RangeNotSatisfiable=416, - ExpectationFailed=417, - ImATeapot=418, - MisdirectedRequest=421, - UnprocessableContent=422, - Locked=423, - FailedDependency=424, - TooEarly=425, - UpgradeRequired=426, - PreconditionRequired=428, - TooManyRequests=429, - RequestHeaderFieldsTooLarge=431, - UnavailableForLegalReasons=451, - InternalServerError=500, - NotImplemented=501, - BadGateway=502, - ServiceUnavailable=503, - GatewayTimeout=504, - HTTPVersionNotSupported=505, - VariantAlsoNegotiates=506, - InsufficientStorage=507, - LoopDetected=508, - NotExtended=510, - NetworkAuthenticationRequired=511 +namespace Tesses::Framework::Http { +typedef enum StatusCode { + Continue = 100, + SwitchingProtocols = 101, + Processing = 102, + EarlyHints = 103, + OK = 200, + Created = 201, + Accepted = 202, + NonAuthoritativeInformation = 203, + NoContent = 204, + ResetContent = 205, + PartialContent = 206, + MultiStatus = 207, + AlreadyReported = 208, + IMUsed = 226, + MultipleChoices = 300, + MovedPermanently = 301, + Found = 302, + SeeOther = 303, + NotModified = 304, + UseProxy = 305, + TemporaryRedirect = 307, + PermanentRedirect = 308, + BadRequest = 400, + Unauthorized = 401, + PaymentRequired = 402, + Forbidden = 403, + NotFound = 404, + MethodNotAllowed = 405, + NotAcceptable = 406, + ProxyAuthenticationRequired = 407, + RequestTimeout = 408, + Conflict = 409, + Gone = 410, + LengthRequired = 411, + PreconditionFailed = 412, + PayloadTooLarge = 413, + URITooLong = 414, + UnsupportedMediaType = 415, + RangeNotSatisfiable = 416, + ExpectationFailed = 417, + ImATeapot = 418, + MisdirectedRequest = 421, + UnprocessableContent = 422, + Locked = 423, + FailedDependency = 424, + TooEarly = 425, + UpgradeRequired = 426, + PreconditionRequired = 428, + TooManyRequests = 429, + RequestHeaderFieldsTooLarge = 431, + UnavailableForLegalReasons = 451, + InternalServerError = 500, + NotImplemented = 501, + BadGateway = 502, + ServiceUnavailable = 503, + GatewayTimeout = 504, + HTTPVersionNotSupported = 505, + VariantAlsoNegotiates = 506, + InsufficientStorage = 507, + LoopDetected = 508, + NotExtended = 510, + NetworkAuthenticationRequired = 511 } StatusCode; struct CaseInsensitiveLess { - CaseInsensitiveLess(const CaseInsensitiveLess& str); + CaseInsensitiveLess(const CaseInsensitiveLess &str); CaseInsensitiveLess(); - CaseInsensitiveLess* offset; + CaseInsensitiveLess *offset; bool caseSensitive; - bool operator() (const std::string& s1, const std::string& s2) const; + bool operator()(const std::string &s1, const std::string &s2) const; }; - - class HttpDictionary { - public: - HttpDictionary(); - HttpDictionary(bool caseSensitive); - std::map,CaseInsensitiveLess> kvp; - void SetCaseSensitive(bool isCaseSensitive); - void Clear(); - void Clear(std::string key, bool kvpExistsAfter); - void SetValue(std::string key, std::string value); - void SetValue(std::string key, int64_t v); - void SetValue(std::string key, double v); - void SetValue(std::string key, Date::DateTime v); - void SetValue(std::string key, std::vector value); - template - void SetValue(std::string key, Itterator begin, Itterator end) - { - Clear(key,true); - AddValue(key, begin, end); - } - void AddValue(std::string key, std::string value); - void AddValue(std::string key, int64_t v); - void AddValue(std::string key, double v); - void AddValue(std::string key, Date::DateTime v); - void AddValue(std::string key, std::vector value); - - template - void AddValue(std::string key, Itterator begin, Itterator end) - { - auto& ls = kvp[key]; - ls.insert(ls.end(), begin, end); - } - bool TryGetFirst(std::string key, std::string& value); +class HttpDictionary { + public: + HttpDictionary(); + HttpDictionary(bool caseSensitive); + std::map, CaseInsensitiveLess> kvp; + void SetCaseSensitive(bool isCaseSensitive); + void Clear(); + void Clear(std::string key, bool kvpExistsAfter); + void SetValue(std::string key, std::string value); + void SetValue(std::string key, int64_t v); + void SetValue(std::string key, double v); + void SetValue(std::string key, Date::DateTime v); + void SetValue(std::string key, std::vector value); + template + void SetValue(std::string key, Itterator begin, Itterator end) { + Clear(key, true); + AddValue(key, begin, end); + } + void AddValue(std::string key, std::string value); + void AddValue(std::string key, int64_t v); + void AddValue(std::string key, double v); + void AddValue(std::string key, Date::DateTime v); + void AddValue(std::string key, std::vector value); - bool TryGetFirstInt(std::string key, int64_t& value); + template + void AddValue(std::string key, Itterator begin, Itterator end) { + auto &ls = kvp[key]; + ls.insert(ls.end(), begin, end); + } - bool TryGetFirstDouble(std::string key, double& value); - bool TryGetFirstDate(std::string key, Date::DateTime& value); + bool TryGetFirst(std::string key, std::string &value); - bool GetFirstBoolean(std::string key); + bool TryGetFirstInt(std::string key, int64_t &value); - bool AnyEquals(std::string key, std::string value); - }; + bool TryGetFirstDouble(std::string key, double &value); + bool TryGetFirstDate(std::string key, Date::DateTime &value); - class Uri { - public: - Uri(); - std::string GetQuery(); - std::string GetPathAndQuery(); - uint16_t GetPort(); - std::string HostPort(); - bool Relative(std::string url, Uri& uri); - std::string ToString(); - static bool TryParse(std::string url, Uri& uri); - std::string scheme; - std::string host; - uint16_t port; - std::string path; - HttpDictionary query; - std::string hash; - }; + bool GetFirstBoolean(std::string key); - class HttpUtils - { - public: - static char NibbleToHex(uint8_t b, bool isUppercase); - static char NibbleToHex(uint8_t nibble); - static uint8_t HexToNibble(char c); - static std::string BytesToHex(const std::vector& data); - static void BytesToHex(std::string& text,const std::vector& data); + bool AnyEquals(std::string key, std::string value); +}; - static std::string BytesToHex(const std::vector& data,bool isUppercase); - static void BytesToHex(std::string& text,const std::vector& data, bool isUppercase); - static std::vector HexToBytes(const std::string& text); - static void HexToBytes(std::vector& data,const std::string& text); - static std::string MimeType(std::filesystem::path p); - static bool Invalid(char c); - static std::string Sanitise(std::string text); - static void QueryParamsDecode(HttpDictionary& dict,std::string query); - static std::string Join(std::string joinStr, std::vector ents); - static std::string QueryParamsEncode(HttpDictionary& dict); - static std::string UrlDecode(std::string v); - static std::string UrlEncode(std::string v); - static std::string UrlPathDecode(std::string v); - static std::string UrlPathEncode(std::string v, bool ignoreSpace=false); - static std::string HtmlEncode(std::string v); - static std::string HtmlP(std::string text); - static std::string HtmlDecodeOnlyEntityNumber(std::string v); - static std::vector SplitString(std::string text, std::string delimiter,std::size_t maxCnt = std::string::npos); - static std::string Replace(std::string str, std::string find, std::string replace); - static std::string StatusCodeString(StatusCode code); - static std::string ToLower(std::string str); - static std::string ToUpper(std::string str); - static std::string LeftPad(std::string text, int count, char c); - }; +class Uri { + public: + Uri(); + std::string GetQuery(); + std::string GetPathAndQuery(); + uint16_t GetPort(); + std::string HostPort(); + bool Relative(std::string url, Uri &uri); + std::string ToString(); + static bool TryParse(std::string url, Uri &uri); + std::string scheme; + std::string host; + uint16_t port; + std::string path; + HttpDictionary query; + std::string hash; +}; - +class HttpUtils { + public: + static char NibbleToHex(uint8_t b, bool isUppercase); + static char NibbleToHex(uint8_t nibble); + static uint8_t HexToNibble(char c); + static std::string BytesToHex(const std::vector &data); + static void BytesToHex(std::string &text, const std::vector &data); - - -} \ No newline at end of file + static std::string BytesToHex(const std::vector &data, + bool isUppercase); + static void BytesToHex(std::string &text, const std::vector &data, + bool isUppercase); + static std::vector HexToBytes(const std::string &text); + static void HexToBytes(std::vector &data, const std::string &text); + static std::string MimeType(std::filesystem::path p); + static bool Invalid(char c); + static std::string Sanitise(std::string text); + static void QueryParamsDecode(HttpDictionary &dict, std::string query); + static std::string Join(std::string joinStr, std::vector ents); + static std::string QueryParamsEncode(HttpDictionary &dict); + static std::string UrlDecode(std::string v); + static std::string UrlEncode(std::string v); + static std::string UrlPathDecode(std::string v); + static std::string UrlPathEncode(std::string v, bool ignoreSpace = false); + static std::string HtmlEncode(std::string v); + static std::string HtmlP(std::string text); + static std::string HtmlDecodeOnlyEntityNumber(std::string v); + static std::vector + SplitString(std::string text, std::string delimiter, + std::size_t maxCnt = std::string::npos); + static std::string Replace(std::string str, std::string find, + std::string replace); + static std::string StatusCodeString(StatusCode code); + static std::string ToLower(std::string str); + static std::string ToUpper(std::string str); + static std::string LeftPad(std::string text, int count, char c); +}; + +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Http/MountableServer.hpp b/include/TessesFramework/Http/MountableServer.hpp index 38ae107..2242b9d 100644 --- a/include/TessesFramework/Http/MountableServer.hpp +++ b/include/TessesFramework/Http/MountableServer.hpp @@ -1,22 +1,41 @@ -#pragma once -#include "HttpServer.hpp" -#include "../Filesystem/VFSFix.hpp" -#include "../Filesystem/VFS.hpp" +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan -namespace Tesses::Framework::Http -{ - class MountableServer : public IHttpServer - { - std::shared_ptr root; - std::vector>> servers; - std::string Subpath(Filesystem::VFSPath fullPath, Filesystem::VFSPath offsetPath); - bool StartsWith(Filesystem::VFSPath fullPath, Filesystem::VFSPath offsetPath); - public: - MountableServer(); - MountableServer(std::shared_ptr root); - void Mount(std::string path, std::shared_ptr server); - void Unmount(std::string path); - bool Handle(ServerContext& ctx); - ~MountableServer(); - }; -} \ No newline at end of file + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once +#include "../Filesystem/VFS.hpp" +#include "../Filesystem/VFSFix.hpp" +#include "HttpServer.hpp" + +namespace Tesses::Framework::Http { +class MountableServer : public IHttpServer { + std::shared_ptr root; + std::vector>> servers; + std::string Subpath(Filesystem::VFSPath fullPath, + Filesystem::VFSPath offsetPath); + bool StartsWith(Filesystem::VFSPath fullPath, + Filesystem::VFSPath offsetPath); + + public: + MountableServer(); + MountableServer(std::shared_ptr root); + void Mount(std::string path, std::shared_ptr server); + void Unmount(std::string path); + bool Handle(ServerContext &ctx); + ~MountableServer(); +}; +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Http/RouteServer.hpp b/include/TessesFramework/Http/RouteServer.hpp index c1f2925..55f70ff 100644 --- a/include/TessesFramework/Http/RouteServer.hpp +++ b/include/TessesFramework/Http/RouteServer.hpp @@ -1,41 +1,59 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once -#include "HttpServer.hpp" -#include "../Filesystem/VFSFix.hpp" #include "../Filesystem/VFS.hpp" +#include "../Filesystem/VFSFix.hpp" +#include "HttpServer.hpp" -namespace Tesses::Framework::Http -{ - using ServerRequestHandler = std::function; - - - class RouteServer : public IHttpServer - { - class RouteServerRoute { - public: - std::vector> parts; - std::string method; - ServerRequestHandler handler; +namespace Tesses::Framework::Http { +using ServerRequestHandler = std::function; - RouteServerRoute() = default; - RouteServerRoute(std::string route, std::string method, ServerRequestHandler handler); - bool Equals(Tesses::Framework::Filesystem::VFSPath& path, HttpDictionary& args); - }; - std::vector routes; - std::shared_ptr root; - public: - RouteServer() = default; - RouteServer(std::shared_ptr root); - void Get(std::string pattern, ServerRequestHandler handler); - void Post(std::string pattern, ServerRequestHandler handler); - void Put(std::string pattern, ServerRequestHandler handler); - void Patch(std::string pattern, ServerRequestHandler handler); +class RouteServer : public IHttpServer { + class RouteServerRoute { + public: + std::vector> parts; + std::string method; + ServerRequestHandler handler; - void Delete(std::string pattern, ServerRequestHandler handler); - - void Trace(std::string pattern, ServerRequestHandler handler); - void Options(std::string pattern, ServerRequestHandler handler); - void Add(std::string method, std::string pattern, ServerRequestHandler handler); - bool Handle(ServerContext& ctx); - + RouteServerRoute() = default; + RouteServerRoute(std::string route, std::string method, + ServerRequestHandler handler); + bool Equals(Tesses::Framework::Filesystem::VFSPath &path, + HttpDictionary &args); }; -} \ No newline at end of file + std::vector routes; + std::shared_ptr root; + + public: + RouteServer() = default; + RouteServer(std::shared_ptr root); + void Get(std::string pattern, ServerRequestHandler handler); + void Post(std::string pattern, ServerRequestHandler handler); + void Put(std::string pattern, ServerRequestHandler handler); + void Patch(std::string pattern, ServerRequestHandler handler); + + void Delete(std::string pattern, ServerRequestHandler handler); + + void Trace(std::string pattern, ServerRequestHandler handler); + void Options(std::string pattern, ServerRequestHandler handler); + void Add(std::string method, std::string pattern, + ServerRequestHandler handler); + bool Handle(ServerContext &ctx); +}; +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Http/WebSocket.hpp b/include/TessesFramework/Http/WebSocket.hpp index aff277c..4207a22 100644 --- a/include/TessesFramework/Http/WebSocket.hpp +++ b/include/TessesFramework/Http/WebSocket.hpp @@ -1,43 +1,68 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once -#include +#include #include +#include #include #include -#include -namespace Tesses::Framework::Http -{ - class WebSocketMessage { - public: - std::vector data; - bool isBinary; - WebSocketMessage(); - WebSocketMessage(std::vector data); - WebSocketMessage(const void* data, size_t len); - WebSocketMessage(std::string message); - std::string ToString(); +namespace Tesses::Framework::Http { +class WebSocketMessage { + public: + std::vector data; + bool isBinary; + WebSocketMessage(); + WebSocketMessage(std::vector data); + WebSocketMessage(const void *data, size_t len); + WebSocketMessage(std::string message); + std::string ToString(); +}; +void SendWebSocketMessage(std::function cb, + std::string text); +class WebSocketConnection { - - - }; - void SendWebSocketMessage(std::function cb, std::string text); - class WebSocketConnection { - - public: - virtual void OnOpen(std::function sendMessage, std::function ping,std::function close)=0; - virtual void OnReceive(WebSocketMessage& message)=0; - virtual void OnClose(bool clean)=0; - virtual ~WebSocketConnection(); - }; - class CallbackWebSocketConnection : public WebSocketConnection { - public: - std::function,std::function,std::function)> onOpen; - std::function onReceive; - std::function onClose; - CallbackWebSocketConnection(); - CallbackWebSocketConnection(std::function,std::function,std::function)> onOpen, std::function onReceive, std::function onClose); + public: + virtual void OnOpen(std::function sendMessage, + std::function ping, + std::function close) = 0; + virtual void OnReceive(WebSocketMessage &message) = 0; + virtual void OnClose(bool clean) = 0; + virtual ~WebSocketConnection(); +}; +class CallbackWebSocketConnection : public WebSocketConnection { + public: + std::function, + std::function, std::function)> + onOpen; + std::function onReceive; + std::function onClose; + CallbackWebSocketConnection(); + CallbackWebSocketConnection( + std::function, + std::function, std::function)> + onOpen, + std::function onReceive, + std::function onClose); - void OnOpen(std::function sendMessage, std::function ping,std::function closeFn); - void OnReceive(WebSocketMessage& message); - void OnClose(bool clean); - }; -} \ No newline at end of file + void OnOpen(std::function sendMessage, + std::function ping, std::function closeFn); + void OnReceive(WebSocketMessage &message); + void OnClose(bool clean); +}; +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/include/TessesFramework/Lazy.hpp b/include/TessesFramework/Lazy.hpp index 679c840..be8f71e 100644 --- a/include/TessesFramework/Lazy.hpp +++ b/include/TessesFramework/Lazy.hpp @@ -1,52 +1,61 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "Common.hpp" #include "Threading/Mutex.hpp" #include namespace Tesses::Framework { -template - class Lazy { - Threading::Mutex mtx; - T value; - bool hasInit=false; - std::function init; - std::function free; - public: - Lazy(std::function init, std::function free) - { - this->init = init; - this->free = free; - } - Lazy(std::function init) : Lazy(init, [](T item)->void {}) - { +template class Lazy { + Threading::Mutex mtx; + T value; + bool hasInit = false; + std::function init; + std::function free; - } - bool HasInit() - { - mtx.Lock(); - bool hI = this->hasInit; - mtx.Unlock(); - return hI; - } - T& GetValue() - { - mtx.Lock(); - if(hasInit) - { - mtx.Unlock(); - return this->value; - } - else - { - this->value = this->init(); - this->hasInit=true; - mtx.Unlock(); - return this->value; - } - mtx.Unlock(); - } - ~Lazy() - { - if(hasInit) this->free(this->value); - } - }; -} + public: + Lazy(std::function init, std::function free) { + this->init = init; + this->free = free; + } + Lazy(std::function init) : Lazy(init, [](T item) -> void {}) {} + bool HasInit() { + mtx.Lock(); + bool hI = this->hasInit; + mtx.Unlock(); + return hI; + } + T &GetValue() { + mtx.Lock(); + if (hasInit) { + mtx.Unlock(); + return this->value; + } else { + this->value = this->init(); + this->hasInit = true; + mtx.Unlock(); + return this->value; + } + mtx.Unlock(); + } + ~Lazy() { + if (hasInit) + this->free(this->value); + } +}; +} // namespace Tesses::Framework diff --git a/include/TessesFramework/Mail/Smtp.hpp b/include/TessesFramework/Mail/Smtp.hpp index d692bb8..ee316cb 100644 --- a/include/TessesFramework/Mail/Smtp.hpp +++ b/include/TessesFramework/Mail/Smtp.hpp @@ -1,51 +1,66 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Streams/Stream.hpp" -namespace Tesses::Framework::Mail -{ -class SMTPBody -{ - public: - std::string mimeType; - virtual void Write(std::shared_ptr strm)=0; - virtual ~SMTPBody(); +namespace Tesses::Framework::Mail { +class SMTPBody { + public: + std::string mimeType; + virtual void + Write(std::shared_ptr strm) = 0; + virtual ~SMTPBody(); }; -class SMTPStringBody : public SMTPBody -{ - public: - SMTPStringBody(); - SMTPStringBody(std::string text, std::string mimeType); - std::string text; - void Write(std::shared_ptr strm); +class SMTPStringBody : public SMTPBody { + public: + SMTPStringBody(); + SMTPStringBody(std::string text, std::string mimeType); + std::string text; + void Write(std::shared_ptr strm); }; -class SMTPStreamBody : public SMTPBody -{ +class SMTPStreamBody : public SMTPBody { std::shared_ptr stream; - public: - SMTPStreamBody(std::string mimeType,std::shared_ptr stream); - void Write(std::shared_ptr strm); - ~SMTPStreamBody(); + public: + SMTPStreamBody(std::string mimeType, + std::shared_ptr stream); + void Write(std::shared_ptr strm); + ~SMTPStreamBody(); }; - class SMTPClient { std::shared_ptr strm; - - public: - SMTPClient(std::shared_ptr strm); - std::string domain; - std::string username; - std::string password; - std::string from; - std::string from_name; - std::string to; - std::string subject; - std::shared_ptr body; - std::vector>> attachments; - void Send(); - ~SMTPClient(); + + public: + SMTPClient(std::shared_ptr strm); + std::string domain; + std::string username; + std::string password; + std::string from; + std::string from_name; + std::string to; + std::string subject; + std::shared_ptr body; + std::vector>> attachments; + void Send(); + ~SMTPClient(); }; -} \ No newline at end of file +} // namespace Tesses::Framework::Mail \ No newline at end of file diff --git a/include/TessesFramework/Platform/Environment.hpp b/include/TessesFramework/Platform/Environment.hpp index 349e29d..1692d6b 100644 --- a/include/TessesFramework/Platform/Environment.hpp +++ b/include/TessesFramework/Platform/Environment.hpp @@ -1,51 +1,69 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once -#include "TessesFramework/Filesystem/VFSFix.hpp" #include "TessesFramework/Filesystem/LocalFS.hpp" +#include "TessesFramework/Filesystem/VFSFix.hpp" #include -namespace Tesses::Framework::Platform::Environment -{ - extern const char EnvPathSeperator; +namespace Tesses::Framework::Platform::Environment { +extern const char EnvPathSeperator; - struct PortableAppConfig { - - std::optional desktop; - std::optional documents; - std::optional music; - std::optional pictures; - std::optional videos; - std::optional downloads; - std::optional user; - std::optional config; - std::optional state; - std::optional data; - std::optional cache; - std::optional temp; - }; +struct PortableAppConfig { - extern PortableAppConfig portable_config; + std::optional desktop; + std::optional documents; + std::optional music; + std::optional pictures; + std::optional videos; + std::optional downloads; + std::optional user; + std::optional config; + std::optional state; + std::optional data; + std::optional cache; + std::optional temp; +}; - namespace SpecialFolders { - Tesses::Framework::Filesystem::VFSPath GetTemp(); - Tesses::Framework::Filesystem::VFSPath GetHomeFolder(); - Tesses::Framework::Filesystem::VFSPath GetDownloads(); - Tesses::Framework::Filesystem::VFSPath GetMusic(); - Tesses::Framework::Filesystem::VFSPath GetPictures(); - Tesses::Framework::Filesystem::VFSPath GetVideos(); - Tesses::Framework::Filesystem::VFSPath GetDocuments(); - Tesses::Framework::Filesystem::VFSPath GetConfig(); - Tesses::Framework::Filesystem::VFSPath GetDesktop(); - Tesses::Framework::Filesystem::VFSPath GetState(); - Tesses::Framework::Filesystem::VFSPath GetCache(); - Tesses::Framework::Filesystem::VFSPath GetData(); - } +extern PortableAppConfig portable_config; - std::string GetPlatform(); +namespace SpecialFolders { +Tesses::Framework::Filesystem::VFSPath GetTemp(); +Tesses::Framework::Filesystem::VFSPath GetHomeFolder(); +Tesses::Framework::Filesystem::VFSPath GetDownloads(); +Tesses::Framework::Filesystem::VFSPath GetMusic(); +Tesses::Framework::Filesystem::VFSPath GetPictures(); +Tesses::Framework::Filesystem::VFSPath GetVideos(); +Tesses::Framework::Filesystem::VFSPath GetDocuments(); +Tesses::Framework::Filesystem::VFSPath GetConfig(); +Tesses::Framework::Filesystem::VFSPath GetDesktop(); +Tesses::Framework::Filesystem::VFSPath GetState(); +Tesses::Framework::Filesystem::VFSPath GetCache(); +Tesses::Framework::Filesystem::VFSPath GetData(); +} // namespace SpecialFolders - Tesses::Framework::Filesystem::VFSPath GetRealExecutablePath(Tesses::Framework::Filesystem::VFSPath realPath); +std::string GetPlatform(); - std::optional GetVariable(std::string name); - void SetVariable(std::string name, std::optional var); +Tesses::Framework::Filesystem::VFSPath +GetRealExecutablePath(Tesses::Framework::Filesystem::VFSPath realPath); - void GetEnvironmentVariables(std::vector>& env); -} +std::optional GetVariable(std::string name); +void SetVariable(std::string name, std::optional var); +void GetEnvironmentVariables( + std::vector> &env); +} // namespace Tesses::Framework::Platform::Environment diff --git a/include/TessesFramework/Platform/Process.hpp b/include/TessesFramework/Platform/Process.hpp index 7067115..a352f57 100644 --- a/include/TessesFramework/Platform/Process.hpp +++ b/include/TessesFramework/Platform/Process.hpp @@ -1,50 +1,71 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once -#include -#include -#include "TessesFramework/Streams/Stream.hpp" #include "TessesFramework/HiddenField.hpp" +#include "TessesFramework/Streams/Stream.hpp" #include +#include +#include #if defined(_WIN32) #define SIGKILL 9 #endif namespace Tesses::Framework::Platform { class Process { - private: - HiddenField hidden; - bool exited = false; - int exitCode=0; - public: - std::string name; - std::vector args; - std::string workingDirectory; - std::vector> env; - bool includeThisEnv; - bool redirectStdIn=false; - bool redirectStdOut=false; - bool redirectStdErr=false; - bool HasExited(); + private: + HiddenField hidden; + bool exited = false; + int exitCode = 0; + public: + std::string name; + std::vector args; + std::string workingDirectory; + std::vector> env; + bool includeThisEnv; + bool redirectStdIn = false; + bool redirectStdOut = false; + bool redirectStdErr = false; + bool HasExited(); - void CloseStdInNow(); - - std::shared_ptr GetStdinStream(); + void CloseStdInNow(); - std::shared_ptr GetStdoutStream(); - std::shared_ptr GetStderrStream(); + std::shared_ptr GetStdinStream(); - Process(); - Process(std::string name, std::vector args,bool includeThisEnv=true); - Process(std::string name, std::vector args, std::vector> env,bool includeThisEnv=false); - Process(std::string name, std::vector args, std::vector env,bool includeThisEnv=false); - + std::shared_ptr GetStdoutStream(); + std::shared_ptr GetStderrStream(); - bool Start(); + Process(); + Process(std::string name, std::vector args, + bool includeThisEnv = true); + Process(std::string name, std::vector args, + std::vector> env, + bool includeThisEnv = false); + Process(std::string name, std::vector args, + std::vector env, bool includeThisEnv = false); - void Kill(int signal); + bool Start(); - int WaitForExit(); + void Kill(int signal); - ~Process(); + int WaitForExit(); + + ~Process(); }; -} \ No newline at end of file +} // namespace Tesses::Framework::Platform \ No newline at end of file diff --git a/include/TessesFramework/Random.hpp b/include/TessesFramework/Random.hpp index 17a5e50..8a891f8 100644 --- a/include/TessesFramework/Random.hpp +++ b/include/TessesFramework/Random.hpp @@ -1,15 +1,34 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "Common.hpp" namespace Tesses::Framework { - class Random { - uint64_t num; - public: - Random(); - Random(uint64_t seed); - uint64_t Next(); - uint32_t Next(uint32_t max); - int32_t Next(int32_t min,int32_t max); - uint8_t NextByte(); - }; -} \ No newline at end of file +class Random { + uint64_t num; + + public: + Random(); + Random(uint64_t seed); + uint64_t Next(); + uint32_t Next(uint32_t max); + int32_t Next(int32_t min, int32_t max); + uint8_t NextByte(); +}; +} // namespace Tesses::Framework \ No newline at end of file diff --git a/include/TessesFramework/Serialization/Bencode.hpp b/include/TessesFramework/Serialization/Bencode.hpp index fd45d74..68fe5fb 100644 --- a/include/TessesFramework/Serialization/Bencode.hpp +++ b/include/TessesFramework/Serialization/Bencode.hpp @@ -1,59 +1,83 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once -#include -#include -#include -#include #include "../Streams/Stream.hpp" #include "../TextStreams/TextWriter.hpp" #include "Json.hpp" +#include +#include +#include +#include namespace Tesses::Framework::Serialization::Bencode { - class BeArray; - class BeDictionary; - class BeString; - using BeUndefined = std::monostate; +class BeArray; +class BeDictionary; +class BeString; +using BeUndefined = std::monostate; - using BeToken = std::variant; +using BeToken = + std::variant; - class BeArray { - public: - std::vector tokens; - }; +class BeArray { + public: + std::vector tokens; +}; - class BeDictionary { - public: - std::vector> tokens; - BeToken GetValue(BeString key) const; - void SetValue(BeString key, BeToken value); - }; - class BeString { - public: - BeString(); - BeString(const std::string& text); - BeString(const char* text); - BeString(const std::vector& data); - std::vector data; +class BeDictionary { + public: + std::vector> tokens; + BeToken GetValue(BeString key) const; + void SetValue(BeString key, BeToken value); +}; +class BeString { + public: + BeString(); + BeString(const std::string &text); + BeString(const char *text); + BeString(const std::vector &data); + std::vector data; - operator std::string() const; - }; - bool operator==(const BeString& lStr, const BeString& rStr); - bool operator==(const BeString& lStr, const std::string& rStr); - bool operator==(const std::string& lStr, const BeString& rStr); - bool operator==(const BeString& lStr, const char* rStr); - bool operator==(const char* lStr, const BeString& rStr); - - bool operator!=(const BeString& lStr, const BeString& rStr); - bool operator!=(const BeString& lStr, const std::string& rStr); - bool operator!=(const std::string& lStr, const BeString& rStr); - bool operator!=(const BeString& lStr, const char* rStr); - bool operator!=(const char* lStr, const BeString& rStr); - - class Bencode { - public: - static void Save(std::shared_ptr strm,const BeToken& value); - static BeToken Load(std::shared_ptr strm); - //This cannot be converted back to torrent, this may (probably will) be out of order - static Json::JToken ToJson(const BeToken& tkn); - //This may (probably will) be out of order - static void Print(std::shared_ptr writer, BeToken tkn); - }; -} \ No newline at end of file + operator std::string() const; +}; +bool operator==(const BeString &lStr, const BeString &rStr); +bool operator==(const BeString &lStr, const std::string &rStr); +bool operator==(const std::string &lStr, const BeString &rStr); +bool operator==(const BeString &lStr, const char *rStr); +bool operator==(const char *lStr, const BeString &rStr); + +bool operator!=(const BeString &lStr, const BeString &rStr); +bool operator!=(const BeString &lStr, const std::string &rStr); +bool operator!=(const std::string &lStr, const BeString &rStr); +bool operator!=(const BeString &lStr, const char *rStr); +bool operator!=(const char *lStr, const BeString &rStr); + +class Bencode { + public: + static void Save(std::shared_ptr strm, + const BeToken &value); + static BeToken + Load(std::shared_ptr strm); + // This cannot be converted back to torrent, this may (probably will) be out + // of order + static Json::JToken ToJson(const BeToken &tkn); + // This may (probably will) be out of order + static void + Print(std::shared_ptr writer, + BeToken tkn); +}; +} // namespace Tesses::Framework::Serialization::Bencode \ No newline at end of file diff --git a/include/TessesFramework/Serialization/BitConverter.hpp b/include/TessesFramework/Serialization/BitConverter.hpp index d5ebb07..ea00c97 100644 --- a/include/TessesFramework/Serialization/BitConverter.hpp +++ b/include/TessesFramework/Serialization/BitConverter.hpp @@ -1,82 +1,93 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Common.hpp" #include "../Uuid.hpp" -namespace Tesses::Framework::Serialization -{ - +namespace Tesses::Framework::Serialization { + /** * @brief A bit converter - * + * */ class BitConverter { - public: - - static double ToDoubleBits(uint64_t v); - - static uint64_t ToUintBits(double v); - static float ToFloatBits(uint32_t v); - - static uint32_t ToUint32Bits(float v); - - static double ToDoubleBE(uint8_t& b); - static float ToFloatBE(uint8_t& b); - - static uint64_t ToUint64BE(uint8_t& b); - static uint32_t ToUint32BE(uint8_t& b); - static uint16_t ToUint16BE(uint8_t& b); + public: + static double ToDoubleBits(uint64_t v); - static double ToDoubleLE(uint8_t& b); - static float ToFloatLE(uint8_t& b); - static uint64_t ToUint64LE(uint8_t& b); - static uint32_t ToUint32LE(uint8_t& b); - static uint16_t ToUint16LE(uint8_t& b); + static uint64_t ToUintBits(double v); + static float ToFloatBits(uint32_t v); - static int64_t ToSint64BE(uint8_t& b); - static int32_t ToSint32BE(uint8_t& b); - static int16_t ToSint16BE(uint8_t& b); - static int64_t ToSint64LE(uint8_t& b); - static int32_t ToSint32LE(uint8_t& b); - static int16_t ToSint16LE(uint8_t& b); + static uint32_t ToUint32Bits(float v); - static Uuid ToUuid(uint8_t& b); + static double ToDoubleBE(uint8_t &b); + static float ToFloatBE(uint8_t &b); + static uint64_t ToUint64BE(uint8_t &b); + static uint32_t ToUint32BE(uint8_t &b); + static uint16_t ToUint16BE(uint8_t &b); - static void ToUuid(uint8_t& b, Uuid& uuid); + static double ToDoubleLE(uint8_t &b); + static float ToFloatLE(uint8_t &b); + static uint64_t ToUint64LE(uint8_t &b); + static uint32_t ToUint32LE(uint8_t &b); + static uint16_t ToUint16LE(uint8_t &b); + static int64_t ToSint64BE(uint8_t &b); + static int32_t ToSint32BE(uint8_t &b); + static int16_t ToSint16BE(uint8_t &b); + static int64_t ToSint64LE(uint8_t &b); + static int32_t ToSint32LE(uint8_t &b); + static int16_t ToSint16LE(uint8_t &b); - static void FromDoubleBE(uint8_t& b, double v); - static void FromFloatBE(uint8_t& b, float v); - static void FromUint64BE(uint8_t& b, uint64_t v); - static void FromUint32BE(uint8_t& b, uint32_t v); - static void FromUint16BE(uint8_t& b, uint16_t v); + static Uuid ToUuid(uint8_t &b); - static void FromDoubleLE(uint8_t& b, double v); - static void FromFloatLE(uint8_t& b, float v); - static void FromUint64LE(uint8_t& b, uint64_t v); - static void FromUint32LE(uint8_t& b, uint32_t v); - static void FromUint16LE(uint8_t& b, uint16_t v); - - static void FromSint64BE(uint8_t& b, int64_t v); - static void FromSint32BE(uint8_t& b, int32_t v); - static void FromSint16BE(uint8_t& b, int16_t v); - - static void FromSint64LE(uint8_t& b, int64_t v); - static void FromSint32LE(uint8_t& b, int32_t v); - static void FromSint16LE(uint8_t& b, int16_t v); - - static void FromUuid(uint8_t& b, const Uuid& uuid); + static void ToUuid(uint8_t &b, Uuid &uuid); + static void FromDoubleBE(uint8_t &b, double v); + static void FromFloatBE(uint8_t &b, float v); + static void FromUint64BE(uint8_t &b, uint64_t v); + static void FromUint32BE(uint8_t &b, uint32_t v); + static void FromUint16BE(uint8_t &b, uint16_t v); + static void FromDoubleLE(uint8_t &b, double v); + static void FromFloatLE(uint8_t &b, float v); + static void FromUint64LE(uint8_t &b, uint64_t v); + static void FromUint32LE(uint8_t &b, uint32_t v); + static void FromUint16LE(uint8_t &b, uint16_t v); - static inline bool IsLittleEndian() - { - uint8_t a[2]; - a[0] = 0x01; - a[1] = 0xA4; - uint16_t num=0; - memcpy(&num,&a, 2); - return num != 420; - } + static void FromSint64BE(uint8_t &b, int64_t v); + static void FromSint32BE(uint8_t &b, int32_t v); + static void FromSint16BE(uint8_t &b, int16_t v); + + static void FromSint64LE(uint8_t &b, int64_t v); + static void FromSint32LE(uint8_t &b, int32_t v); + static void FromSint16LE(uint8_t &b, int16_t v); + + static void FromUuid(uint8_t &b, const Uuid &uuid); + + static inline bool IsLittleEndian() { + uint8_t a[2]; + a[0] = 0x01; + a[1] = 0xA4; + uint16_t num = 0; + memcpy(&num, &a, 2); + return num != 420; + } }; -} \ No newline at end of file +} // namespace Tesses::Framework::Serialization \ No newline at end of file diff --git a/include/TessesFramework/Serialization/Json.hpp b/include/TessesFramework/Serialization/Json.hpp index cd74163..efbb291 100644 --- a/include/TessesFramework/Serialization/Json.hpp +++ b/include/TessesFramework/Serialization/Json.hpp @@ -1,94 +1,101 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once -#include -#include -#include -#include -#include #include -namespace Tesses::Framework::Serialization::Json -{ - class JArray; - class JObject; - using JUndefined = std::monostate; +#include +#include +#include +#include +#include +namespace Tesses::Framework::Serialization::Json { +class JArray; +class JObject; +using JUndefined = std::monostate; - using JToken = std::variant; +using JToken = std::variant; - class JOItem; +class JOItem; - class JArray - { - std::vector items; - public: - JArray(); - JArray(std::initializer_list items); - std::vector& GetVector(); - void Add(JToken item); - void RemoveAt(size_t index); - size_t Count(); - void SetAt(size_t index, JToken item); - JToken GetAt(size_t index); - void Clear(); +class JArray { + std::vector items; - std::vector::iterator begin(); - std::vector::iterator end(); - }; - - + public: + JArray(); + JArray(std::initializer_list items); + std::vector &GetVector(); + void Add(JToken item); + void RemoveAt(size_t index); + size_t Count(); + void SetAt(size_t index, JToken item); + JToken GetAt(size_t index); + void Clear(); - + std::vector::iterator begin(); + std::vector::iterator end(); +}; - class JObject { - std::map map; - public: - JObject(); - JObject(std::initializer_list items); - void SetValue(std::string key, JToken item); - template - bool TryGetValueAsType(std::string key, T& value) - { - auto obj = GetValue(key); - if(std::holds_alternative(obj)) - { - value = std::get(obj); - return true; - } - return false; - } +class JObject { + std::map map; - JToken GetValue(std::string key); - void Remove(std::string key); - std::map& GetMap(); - std::map::iterator begin(); - std::map::iterator end(); - }; - template - bool TryGetJToken(JToken json, T& item) - { - if(std::holds_alternative(json)) - { - item = std::get(json); + public: + JObject(); + JObject(std::initializer_list items); + void SetValue(std::string key, JToken item); + template bool TryGetValueAsType(std::string key, T &value) { + auto obj = GetValue(key); + if (std::holds_alternative(obj)) { + value = std::get(obj); return true; } return false; } - class JOItem { - public: - JOItem(); - JOItem(std::string key, JToken value); - - std::string key; - JToken value; - }; - - - class Json - { - static std::string tab(std::string str); - public: - static JToken Decode(std::string str); - static std::string Encode(JToken tkn, bool indent=true); - static JArray DocDecode(std::string str); - static std::string DocEncode(JArray array,bool indent=true); - }; + JToken GetValue(std::string key); + void Remove(std::string key); + std::map &GetMap(); + std::map::iterator begin(); + std::map::iterator end(); +}; +template bool TryGetJToken(JToken json, T &item) { + if (std::holds_alternative(json)) { + item = std::get(json); + return true; + } + return false; } +class JOItem { + public: + JOItem(); + JOItem(std::string key, JToken value); + + std::string key; + JToken value; +}; + +class Json { + static std::string tab(std::string str); + + public: + static JToken Decode(std::string str); + static std::string Encode(JToken tkn, bool indent = true); + static JArray DocDecode(std::string str); + static std::string DocEncode(JArray array, bool indent = true); +}; +} // namespace Tesses::Framework::Serialization::Json diff --git a/include/TessesFramework/Serialization/SQLite.hpp b/include/TessesFramework/Serialization/SQLite.hpp index 52e71ea..c92c86e 100644 --- a/include/TessesFramework/Serialization/SQLite.hpp +++ b/include/TessesFramework/Serialization/SQLite.hpp @@ -1,18 +1,42 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once -#include "TessesFramework/Filesystem/VFSFix.hpp" #include "TessesFramework/Filesystem/VFS.hpp" +#include "TessesFramework/Filesystem/VFSFix.hpp" #include namespace Tesses::Framework::Serialization { - class SQLiteDatabase { - private: - void* data; - static int collector(void* user, int count,char** vals, char** keys); - public: - SQLiteDatabase(Tesses::Framework::Filesystem::VFSPath path); - static std::string Escape(std::string text); - static bool IsEnabled(); - void Exec(std::string statement,std::vector>>>& results); - std::vector>>> Exec(std::string statement); - ~SQLiteDatabase(); - }; -} \ No newline at end of file +class SQLiteDatabase { + private: + void *data; + static int collector(void *user, int count, char **vals, char **keys); + + public: + SQLiteDatabase(Tesses::Framework::Filesystem::VFSPath path); + static std::string Escape(std::string text); + static bool IsEnabled(); + void + Exec(std::string statement, + std::vector< + std::vector>>> + &results); + std::vector>>> + Exec(std::string statement); + ~SQLiteDatabase(); +}; +} // namespace Tesses::Framework::Serialization \ No newline at end of file diff --git a/include/TessesFramework/Streams/BufferedStream.hpp b/include/TessesFramework/Streams/BufferedStream.hpp index 2526fc0..d0ea5a1 100644 --- a/include/TessesFramework/Streams/BufferedStream.hpp +++ b/include/TessesFramework/Streams/BufferedStream.hpp @@ -1,26 +1,43 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "Stream.hpp" -namespace Tesses::Framework::Streams -{ - class BufferedStream : public Stream - { - private: - std::shared_ptr strm; - uint8_t* buffer; - size_t bufferSize; - size_t offset; - size_t read; - public: - BufferedStream(std::shared_ptr strm, size_t bufferSize=1024); - bool EndOfStream(); - bool CanRead(); - bool CanWrite(); - size_t Read(uint8_t* buff, size_t sz); - size_t Write(const uint8_t* buff, size_t sz); +namespace Tesses::Framework::Streams { +class BufferedStream : public Stream { + private: + std::shared_ptr strm; + uint8_t *buffer; + size_t bufferSize; + size_t offset; + size_t read; - ~BufferedStream(); - void Close(); - }; + public: + BufferedStream(std::shared_ptr strm, size_t bufferSize = 1024); + bool EndOfStream(); + bool CanRead(); + bool CanWrite(); + size_t Read(uint8_t *buff, size_t sz); + size_t Write(const uint8_t *buff, size_t sz); -} \ No newline at end of file + ~BufferedStream(); + void Close(); +}; + +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/include/TessesFramework/Streams/ByteReader.hpp b/include/TessesFramework/Streams/ByteReader.hpp index f4c7ada..d758138 100644 --- a/include/TessesFramework/Streams/ByteReader.hpp +++ b/include/TessesFramework/Streams/ByteReader.hpp @@ -1,33 +1,51 @@ -#pragma once -#include "Stream.hpp" -#include "../Uuid.hpp" +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan -namespace Tesses::Framework::Streams -{ - class ByteReader { - std::shared_ptr strm; - public: - std::shared_ptr GetStream(); - ByteReader(std::shared_ptr strm); - uint8_t ReadU8(); - uint16_t ReadU16BE(); - uint16_t ReadU16LE(); - uint32_t ReadU32BE(); - uint32_t ReadU32LE(); - uint64_t ReadU64BE(); - uint64_t ReadU64LE(); - int8_t ReadI8(); - int16_t ReadI16BE(); - int16_t ReadI16LE(); - int32_t ReadI32BE(); - int32_t ReadI32LE(); - int64_t ReadI64BE(); - int64_t ReadI64LE(); - float ReadF32BE(); - float ReadF32LE(); - double ReadF64BE(); - double ReadF64LE(); - Uuid ReadUuid(); - void ReadUuid(Uuid& uuid); - }; -} \ No newline at end of file + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once +#include "../Uuid.hpp" +#include "Stream.hpp" + +namespace Tesses::Framework::Streams { +class ByteReader { + std::shared_ptr strm; + + public: + std::shared_ptr GetStream(); + ByteReader(std::shared_ptr strm); + uint8_t ReadU8(); + uint16_t ReadU16BE(); + uint16_t ReadU16LE(); + uint32_t ReadU32BE(); + uint32_t ReadU32LE(); + uint64_t ReadU64BE(); + uint64_t ReadU64LE(); + int8_t ReadI8(); + int16_t ReadI16BE(); + int16_t ReadI16LE(); + int32_t ReadI32BE(); + int32_t ReadI32LE(); + int64_t ReadI64BE(); + int64_t ReadI64LE(); + float ReadF32BE(); + float ReadF32LE(); + double ReadF64BE(); + double ReadF64LE(); + Uuid ReadUuid(); + void ReadUuid(Uuid &uuid); +}; +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/include/TessesFramework/Streams/ByteWriter.hpp b/include/TessesFramework/Streams/ByteWriter.hpp index 4fc1537..6ad1ef0 100644 --- a/include/TessesFramework/Streams/ByteWriter.hpp +++ b/include/TessesFramework/Streams/ByteWriter.hpp @@ -1,32 +1,49 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once -#include "Stream.hpp" #include "../Uuid.hpp" -namespace Tesses::Framework::Streams -{ - class ByteWriter { - std::shared_ptr strm; - - public: - std::shared_ptr GetStream(); - ByteWriter(std::shared_ptr strm); - void WriteU8(uint8_t v); - void WriteU16BE(uint16_t v); - void WriteU16LE(uint16_t v); - void WriteU32BE(uint32_t v); - void WriteU32LE(uint32_t v); - void WriteU64BE(uint64_t v); - void WriteU64LE(uint64_t v); - void WriteI8(int8_t v); - void WriteI16BE(int16_t v); - void WriteI16LE(int16_t v); - void WriteI32BE(int32_t v); - void WriteI32LE(int32_t v); - void WriteI64BE(int64_t v); - void WriteI64LE(int64_t v); - void WriteF32BE(float v); - void WriteF32LE(float v); - void WriteF64BE(double v); - void WriteF64LE(double v); - void WriteUuid(const Uuid& uuid); - }; -} \ No newline at end of file +#include "Stream.hpp" +namespace Tesses::Framework::Streams { +class ByteWriter { + std::shared_ptr strm; + + public: + std::shared_ptr GetStream(); + ByteWriter(std::shared_ptr strm); + void WriteU8(uint8_t v); + void WriteU16BE(uint16_t v); + void WriteU16LE(uint16_t v); + void WriteU32BE(uint32_t v); + void WriteU32LE(uint32_t v); + void WriteU64BE(uint64_t v); + void WriteU64LE(uint64_t v); + void WriteI8(int8_t v); + void WriteI16BE(int16_t v); + void WriteI16LE(int16_t v); + void WriteI32BE(int32_t v); + void WriteI32LE(int32_t v); + void WriteI64BE(int64_t v); + void WriteI64LE(int64_t v); + void WriteF32BE(float v); + void WriteF32LE(float v); + void WriteF64BE(double v); + void WriteF64LE(double v); + void WriteUuid(const Uuid &uuid); +}; +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/include/TessesFramework/Streams/FileStream.hpp b/include/TessesFramework/Streams/FileStream.hpp index 7a40ff1..0bf39a0 100644 --- a/include/TessesFramework/Streams/FileStream.hpp +++ b/include/TessesFramework/Streams/FileStream.hpp @@ -1,28 +1,46 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "Stream.hpp" #include -namespace Tesses::Framework::Streams -{ - class FileStream : public Stream { - bool canRead; - bool canWrite; - bool canSeek; - bool owns; - FILE* f; - void SetMode(std::string mode); - public: - FileStream(std::filesystem::path p, std::string mode); - FileStream(FILE* f, bool owns, std::string mode , bool canSeek=true); - size_t Read(uint8_t* buff, size_t sz); - size_t Write(const uint8_t* buff, size_t sz); - bool EndOfStream(); - bool CanRead(); - bool CanWrite(); - bool CanSeek(); - int64_t GetPosition(); - void Flush(); - void Seek(int64_t pos, SeekOrigin whence); - ~FileStream(); - void Close(); - }; -} \ No newline at end of file +namespace Tesses::Framework::Streams { +class FileStream : public Stream { + bool canRead; + bool canWrite; + bool canSeek; + bool owns; + FILE *f; + void SetMode(std::string mode); + + public: + FileStream(std::filesystem::path p, std::string mode); + FileStream(FILE *f, bool owns, std::string mode, bool canSeek = true); + size_t Read(uint8_t *buff, size_t sz); + size_t Write(const uint8_t *buff, size_t sz); + bool EndOfStream(); + bool CanRead(); + bool CanWrite(); + bool CanSeek(); + int64_t GetPosition(); + void Flush(); + void Seek(int64_t pos, SeekOrigin whence); + ~FileStream(); + void Close(); +}; +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/include/TessesFramework/Streams/MemoryStream.hpp b/include/TessesFramework/Streams/MemoryStream.hpp index 720c8e8..53baf35 100644 --- a/include/TessesFramework/Streams/MemoryStream.hpp +++ b/include/TessesFramework/Streams/MemoryStream.hpp @@ -1,22 +1,40 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "Stream.hpp" -namespace Tesses::Framework::Streams -{ - class MemoryStream : public Stream { - std::vector buffer; - size_t offset; - bool writable; - public: - MemoryStream(bool isWritable); - std::vector& GetBuffer(); - size_t Read(uint8_t* buff, size_t sz); - size_t Write(const uint8_t* buff, size_t sz); - bool CanRead(); - bool CanWrite(); - bool CanSeek(); - int64_t GetLength(); - int64_t GetPosition(); - void Seek(int64_t pos, SeekOrigin whence); - void Close(); - }; -} \ No newline at end of file +namespace Tesses::Framework::Streams { +class MemoryStream : public Stream { + std::vector buffer; + size_t offset; + bool writable; + + public: + MemoryStream(bool isWritable); + std::vector &GetBuffer(); + size_t Read(uint8_t *buff, size_t sz); + size_t Write(const uint8_t *buff, size_t sz); + bool CanRead(); + bool CanWrite(); + bool CanSeek(); + int64_t GetLength(); + int64_t GetPosition(); + void Seek(int64_t pos, SeekOrigin whence); + void Close(); +}; +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/include/TessesFramework/Streams/NetworkStream.hpp b/include/TessesFramework/Streams/NetworkStream.hpp index 951f7fd..8ca1087 100644 --- a/include/TessesFramework/Streams/NetworkStream.hpp +++ b/include/TessesFramework/Streams/NetworkStream.hpp @@ -1,64 +1,87 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "Stream.hpp" -namespace Tesses::Framework::Streams -{ - class NetworkStream; +namespace Tesses::Framework::Streams { +class NetworkStream; - class TcpServer { - int32_t sock; - bool owns; - bool valid; - public: - TcpServer(int32_t backlog); - TcpServer(int32_t sock,bool owns); - TcpServer(uint16_t port, int32_t backlog); - TcpServer(std::string ip, uint16_t port, int32_t backlog); - TcpServer(std::string unixPath,int32_t backlog); - std::shared_ptr GetStream(std::string& ip, uint16_t& port); - uint16_t GetPort(); - ~TcpServer(); - bool IsValid(); - void Close(); - }; - enum class SocketType { - ST_IPv4_TCP, - ST_IPv4_UDP, - ST_IPv6_TCP, - ST_IPv6_UDP, - ST_UNIX - }; - class NetworkStream : public Stream { - int32_t sock; - bool owns; - bool success; - bool endOfStream; - public: - bool DataAvailable(int timeout=0); - bool EndOfStream(); - bool CanRead(); - bool CanWrite(); - NetworkStream(SocketType type); - NetworkStream(std::string unixPath,bool isServer); - NetworkStream(std::string ipOrFqdn, uint16_t port, bool datagram,bool broadcast,bool supportIPv6); - NetworkStream(int32_t sock, bool owns); - uint16_t GetPort(); - void Listen(int32_t backlog); - void Bind(std::string ip, uint16_t port); - void SetBroadcast(bool bC); - void SetReuseAddress(bool reuse); - void SetReusePort(bool reuse); - void SetMulticastTTL(uint8_t ttl); - void SetMulticastMembership(std::string multicastAddress, std::string ifaceIP="0.0.0.0"); - std::shared_ptr Accept(std::string& ip, uint16_t& port); - size_t Read(uint8_t* buff, size_t sz); - size_t Write(const uint8_t* buff, size_t sz); - size_t ReadFrom(uint8_t* buff, size_t sz, std::string& ip, uint16_t& port); - size_t WriteTo(const uint8_t* buff, size_t sz, std::string ip, uint16_t port); - static std::vector> GetIPs(bool ipV6=false); +class TcpServer { + int32_t sock; + bool owns; + bool valid; - ~NetworkStream(); - void SetNoDelay(bool noDelay); - void Close(); - }; -} + public: + TcpServer(int32_t backlog); + TcpServer(int32_t sock, bool owns); + TcpServer(uint16_t port, int32_t backlog); + TcpServer(std::string ip, uint16_t port, int32_t backlog); + TcpServer(std::string unixPath, int32_t backlog); + std::shared_ptr GetStream(std::string &ip, uint16_t &port); + uint16_t GetPort(); + ~TcpServer(); + bool IsValid(); + void Close(); +}; +enum class SocketType { + ST_IPv4_TCP, + ST_IPv4_UDP, + ST_IPv6_TCP, + ST_IPv6_UDP, + ST_UNIX +}; +class NetworkStream : public Stream { + int32_t sock; + bool owns; + bool success; + bool endOfStream; + + public: + bool DataAvailable(int timeout = 0); + bool EndOfStream(); + bool CanRead(); + bool CanWrite(); + NetworkStream(SocketType type); + NetworkStream(std::string unixPath, bool isServer); + NetworkStream(std::string ipOrFqdn, uint16_t port, bool datagram, + bool broadcast, bool supportIPv6); + NetworkStream(int32_t sock, bool owns); + uint16_t GetPort(); + void Listen(int32_t backlog); + void Bind(std::string ip, uint16_t port); + void SetBroadcast(bool bC); + void SetReuseAddress(bool reuse); + void SetReusePort(bool reuse); + void SetMulticastTTL(uint8_t ttl); + void SetMulticastMembership(std::string multicastAddress, + std::string ifaceIP = "0.0.0.0"); + std::shared_ptr Accept(std::string &ip, uint16_t &port); + size_t Read(uint8_t *buff, size_t sz); + size_t Write(const uint8_t *buff, size_t sz); + size_t ReadFrom(uint8_t *buff, size_t sz, std::string &ip, uint16_t &port); + size_t WriteTo(const uint8_t *buff, size_t sz, std::string ip, + uint16_t port); + static std::vector> + GetIPs(bool ipV6 = false); + + ~NetworkStream(); + void SetNoDelay(bool noDelay); + void Close(); +}; +} // namespace Tesses::Framework::Streams diff --git a/include/TessesFramework/Streams/PtyStream.hpp b/include/TessesFramework/Streams/PtyStream.hpp index cd364f6..7dce1e2 100644 --- a/include/TessesFramework/Streams/PtyStream.hpp +++ b/include/TessesFramework/Streams/PtyStream.hpp @@ -1,30 +1,47 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "Stream.hpp" -namespace Tesses::Framework::Streams -{ - struct WindowSize { - uint16_t Width; - uint16_t Height; - uint16_t Columns; - uint16_t Rows; - }; - class PtyStream : public Stream - { - int socket; - int64_t pid; - bool eos; - WindowSize wS; - - public: - PtyStream(WindowSize sz,std::string filename, std::vector args,std::vector env); - bool EndOfStream(); - bool CanRead(); - bool CanWrite(); - size_t Read(uint8_t* buff, size_t sz); - size_t Write(const uint8_t* buff, size_t sz); - void Resize(WindowSize sz); - WindowSize GetWindowSize(); - ~PtyStream(); - void Close(); - }; -} \ No newline at end of file +namespace Tesses::Framework::Streams { +struct WindowSize { + uint16_t Width; + uint16_t Height; + uint16_t Columns; + uint16_t Rows; +}; +class PtyStream : public Stream { + int socket; + int64_t pid; + bool eos; + WindowSize wS; + + public: + PtyStream(WindowSize sz, std::string filename, + std::vector args, std::vector env); + bool EndOfStream(); + bool CanRead(); + bool CanWrite(); + size_t Read(uint8_t *buff, size_t sz); + size_t Write(const uint8_t *buff, size_t sz); + void Resize(WindowSize sz); + WindowSize GetWindowSize(); + ~PtyStream(); + void Close(); +}; +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/include/TessesFramework/Streams/Stream.hpp b/include/TessesFramework/Streams/Stream.hpp index 09ab6ab..dc14adc 100644 --- a/include/TessesFramework/Streams/Stream.hpp +++ b/include/TessesFramework/Streams/Stream.hpp @@ -1,31 +1,45 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Common.hpp" -namespace Tesses::Framework::Streams -{ - enum class SeekOrigin : uint8_t { - Begin=0, - Current=1, - End=2 - }; - class Stream { - public: - int32_t ReadByte(); - void WriteByte(uint8_t b); - virtual bool EndOfStream(); - virtual size_t Read(uint8_t* buff, size_t sz); - virtual size_t Write(const uint8_t* buff, size_t sz); - size_t ReadBlock(uint8_t* buff, size_t sz); - void WriteBlock(const uint8_t* buff, size_t sz); - virtual bool CanRead(); - virtual bool CanWrite(); - virtual bool CanSeek(); - virtual int64_t GetPosition(); - virtual int64_t GetLength(); - virtual void Flush(); - virtual void Seek(int64_t pos, SeekOrigin whence); - void CopyTo(std::shared_ptr strm, size_t buffSize=1024); - void CopyToLimit(std::shared_ptr strm,uint64_t len, size_t buffSize=1024); - virtual void Close(); - virtual ~Stream(); - }; -} \ No newline at end of file +namespace Tesses::Framework::Streams { +enum class SeekOrigin : uint8_t { Begin = 0, Current = 1, End = 2 }; +class Stream { + public: + int32_t ReadByte(); + void WriteByte(uint8_t b); + virtual bool EndOfStream(); + virtual size_t Read(uint8_t *buff, size_t sz); + virtual size_t Write(const uint8_t *buff, size_t sz); + size_t ReadBlock(uint8_t *buff, size_t sz); + void WriteBlock(const uint8_t *buff, size_t sz); + virtual bool CanRead(); + virtual bool CanWrite(); + virtual bool CanSeek(); + virtual int64_t GetPosition(); + virtual int64_t GetLength(); + virtual void Flush(); + virtual void Seek(int64_t pos, SeekOrigin whence); + void CopyTo(std::shared_ptr strm, size_t buffSize = 1024); + void CopyToLimit(std::shared_ptr strm, uint64_t len, + size_t buffSize = 1024); + virtual void Close(); + virtual ~Stream(); +}; +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/include/TessesFramework/TessesFramework.hpp b/include/TessesFramework/TessesFramework.hpp index f359ec3..2354913 100644 --- a/include/TessesFramework/TessesFramework.hpp +++ b/include/TessesFramework/TessesFramework.hpp @@ -1,49 +1,68 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once +#include "Args.hpp" +#include "BitTorrent/TorrentFile.hpp" +#include "Crypto/ClientTLSStream.hpp" +#include "Crypto/Crypto.hpp" #include "Date/Date.hpp" -#include "Http/HttpServer.hpp" -#include "Http/HttpClient.hpp" -#include "Http/FileServer.hpp" -#include "Http/CallbackServer.hpp" -#include "Http/MountableServer.hpp" -#include "Http/ContentDisposition.hpp" -#include "Http/ChangeableServer.hpp" -#include "Http/CGIServer.hpp" +#include "Filesystem/FSHelpers.hpp" +#include "Filesystem/LocalFS.hpp" +#include "Filesystem/MountableFilesystem.hpp" +#include "Filesystem/NullFilesystem.hpp" +#include "Filesystem/RelativeFilesystem.hpp" +#include "Filesystem/SubdirFilesystem.hpp" +#include "HiddenField.hpp" #include "Http/BasicAuthServer.hpp" +#include "Http/CGIServer.hpp" +#include "Http/CallbackServer.hpp" +#include "Http/ChangeableServer.hpp" +#include "Http/ContentDisposition.hpp" +#include "Http/FileServer.hpp" +#include "Http/HttpClient.hpp" +#include "Http/HttpServer.hpp" +#include "Http/MountableServer.hpp" #include "Http/RouteServer.hpp" -#include "Streams/FileStream.hpp" -#include "Streams/MemoryStream.hpp" -#include "Streams/NetworkStream.hpp" +#include "Lazy.hpp" +#include "Mail/Smtp.hpp" +#include "Platform/Environment.hpp" +#include "Platform/Process.hpp" +#include "Random.hpp" +#include "Serialization/Bencode.hpp" +#include "Serialization/BitConverter.hpp" +#include "Serialization/Json.hpp" +#include "Serialization/SQLite.hpp" #include "Streams/BufferedStream.hpp" #include "Streams/ByteReader.hpp" #include "Streams/ByteWriter.hpp" -#include "TextStreams/StreamReader.hpp" -#include "TextStreams/StreamWriter.hpp" +#include "Streams/FileStream.hpp" +#include "Streams/MemoryStream.hpp" +#include "Streams/NetworkStream.hpp" +#include "Text/HeaderGenerator.hpp" +#include "Text/StringConverter.hpp" #include "TextStreams/StdIOReader.hpp" #include "TextStreams/StdIOWriter.hpp" +#include "TextStreams/StreamReader.hpp" +#include "TextStreams/StreamWriter.hpp" #include "TextStreams/StringReader.hpp" #include "TextStreams/StringWriter.hpp" -#include "Text/StringConverter.hpp" -#include "Text/HeaderGenerator.hpp" -#include "Threading/Thread.hpp" #include "Threading/Mutex.hpp" +#include "Threading/Thread.hpp" #include "Threading/ThreadPool.hpp" -#include "Filesystem/LocalFS.hpp" -#include "Filesystem/SubdirFilesystem.hpp" -#include "Filesystem/NullFilesystem.hpp" -#include "Filesystem/MountableFilesystem.hpp" -#include "Filesystem/RelativeFilesystem.hpp" -#include "Filesystem/FSHelpers.hpp" -#include "Crypto/ClientTLSStream.hpp" -#include "Crypto/Crypto.hpp" -#include "Lazy.hpp" -#include "Mail/Smtp.hpp" -#include "HiddenField.hpp" -#include "Serialization/Json.hpp" -#include "Serialization/SQLite.hpp" -#include "Serialization/Bencode.hpp" -#include "Platform/Environment.hpp" -#include "Platform/Process.hpp" -#include "Serialization/BitConverter.hpp" -#include "Args.hpp" -#include "BitTorrent/TorrentFile.hpp" -#include "Random.hpp" +#include "Console.hpp" \ No newline at end of file diff --git a/include/TessesFramework/Text/HeaderGenerator.hpp b/include/TessesFramework/Text/HeaderGenerator.hpp index 14bbd68..de73a50 100644 --- a/include/TessesFramework/Text/HeaderGenerator.hpp +++ b/include/TessesFramework/Text/HeaderGenerator.hpp @@ -1,11 +1,34 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Streams/MemoryStream.hpp" #include "../TextStreams/StringWriter.hpp" namespace Tesses::Framework::Text { - void GenerateCHeaderFile(std::shared_ptr strm,std::string name, std::shared_ptr writer); - - std::string GenerateCHeaderFile(std::shared_ptr strm,std::string name); - void GenerateCHeaderFile(const std::vector& data,std::string name, std::shared_ptr writer); - std::string GenerateCHeaderFile(const std::vector& data,std::string name); -} \ No newline at end of file +void GenerateCHeaderFile(std::shared_ptr strm, + std::string name, + std::shared_ptr writer); + +std::string GenerateCHeaderFile(std::shared_ptr strm, + std::string name); +void GenerateCHeaderFile(const std::vector &data, std::string name, + std::shared_ptr writer); +std::string GenerateCHeaderFile(const std::vector &data, + std::string name); +} // namespace Tesses::Framework::Text \ No newline at end of file diff --git a/include/TessesFramework/Text/StringConverter.hpp b/include/TessesFramework/Text/StringConverter.hpp index 93cadb4..e9924e4 100644 --- a/include/TessesFramework/Text/StringConverter.hpp +++ b/include/TessesFramework/Text/StringConverter.hpp @@ -1,20 +1,44 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once -#include #include +#include namespace Tesses::Framework::Text::StringConverter { - class UTF8 { - public: - static void FromUTF16(std::basic_string& utf8, const std::basic_string& utf16); - static void FromUTF32(std::basic_string& utf8, const std::basic_string& utf32); - }; - class UTF16 { - public: - static void FromUTF8(std::basic_string& utf16, const std::basic_string& utf8); - static void FromUTF32(std::basic_string& utf16, const std::basic_string& utf32); - }; - class UTF32 { - public: - static void FromUTF8(std::basic_string& utf32, const std::basic_string& utf8); - static void FromUTF16(std::basic_string& utf32, const std::basic_string& utf16); - }; -} \ No newline at end of file +class UTF8 { + public: + static void FromUTF16(std::basic_string &utf8, + const std::basic_string &utf16); + static void FromUTF32(std::basic_string &utf8, + const std::basic_string &utf32); +}; +class UTF16 { + public: + static void FromUTF8(std::basic_string &utf16, + const std::basic_string &utf8); + static void FromUTF32(std::basic_string &utf16, + const std::basic_string &utf32); +}; +class UTF32 { + public: + static void FromUTF8(std::basic_string &utf32, + const std::basic_string &utf8); + static void FromUTF16(std::basic_string &utf32, + const std::basic_string &utf16); +}; +} // namespace Tesses::Framework::Text::StringConverter \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/StdIOReader.hpp b/include/TessesFramework/TextStreams/StdIOReader.hpp index abf6883..d883c6b 100644 --- a/include/TessesFramework/TextStreams/StdIOReader.hpp +++ b/include/TessesFramework/TextStreams/StdIOReader.hpp @@ -1,13 +1,32 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TextReader.hpp" +namespace Tesses::Framework::TextStreams { +class ConsoleReader : public TextReader { + public: + ConsoleReader(); + bool ReadBlock(std::string &str, size_t sz); +}; -namespace Tesses::Framework::TextStreams -{ - class ConsoleReader : public TextReader { - public: - ConsoleReader(); - bool ReadBlock(std::string& str,size_t sz); - }; +ConsoleReader StdIn(); - ConsoleReader StdIn(); -} \ No newline at end of file + + +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/StdIOWriter.hpp b/include/TessesFramework/TextStreams/StdIOWriter.hpp index 3b4c293..efdbac8 100644 --- a/include/TessesFramework/TextStreams/StdIOWriter.hpp +++ b/include/TessesFramework/TextStreams/StdIOWriter.hpp @@ -1,15 +1,32 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TextWriter.hpp" +namespace Tesses::Framework::TextStreams { +class ConsoleWriter : public TextWriter { + bool isError; -namespace Tesses::Framework::TextStreams -{ - class ConsoleWriter : public TextWriter { - bool isError; - public: - ConsoleWriter(bool isError=false); - void WriteData(const char* text, size_t len); - }; + public: + ConsoleWriter(bool isError = false); + void WriteData(const char *text, size_t len); +}; - ConsoleWriter StdOut(); - ConsoleWriter StdErr(); -} \ No newline at end of file +ConsoleWriter StdOut(); +ConsoleWriter StdErr(); +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/StreamReader.hpp b/include/TessesFramework/TextStreams/StreamReader.hpp index 745865d..e7a531e 100644 --- a/include/TessesFramework/TextStreams/StreamReader.hpp +++ b/include/TessesFramework/TextStreams/StreamReader.hpp @@ -1,19 +1,36 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once -#include "TextReader.hpp" #include "../Streams/Stream.hpp" -namespace Tesses::Framework::TextStreams -{ - class StreamReader : public TextReader - { - private: - std::shared_ptr strm; - bool owns; - public: - std::shared_ptr GetStream(); - StreamReader(std::shared_ptr strm); - StreamReader(std::filesystem::path filename); - bool ReadBlock(std::string& str,size_t sz); - bool Rewind(); - ~StreamReader(); - }; -} \ No newline at end of file +#include "TextReader.hpp" +namespace Tesses::Framework::TextStreams { +class StreamReader : public TextReader { + private: + std::shared_ptr strm; + bool owns; + + public: + std::shared_ptr GetStream(); + StreamReader(std::shared_ptr strm); + StreamReader(std::filesystem::path filename); + bool ReadBlock(std::string &str, size_t sz); + bool Rewind(); + ~StreamReader(); +}; +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/StreamWriter.hpp b/include/TessesFramework/TextStreams/StreamWriter.hpp index 321ebad..1e494dc 100644 --- a/include/TessesFramework/TextStreams/StreamWriter.hpp +++ b/include/TessesFramework/TextStreams/StreamWriter.hpp @@ -1,17 +1,35 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Streams/Stream.hpp" #include "TextWriter.hpp" -namespace Tesses::Framework::TextStreams -{ - class StreamWriter : public TextWriter { - private: - std::shared_ptr strm; - public: - std::shared_ptr GetStream(); - StreamWriter(std::shared_ptr strm); - StreamWriter(std::filesystem::path filename, bool append=false); - void WriteData(const char* text, size_t len); - ~StreamWriter(); - }; -} \ No newline at end of file +namespace Tesses::Framework::TextStreams { +class StreamWriter : public TextWriter { + private: + std::shared_ptr strm; + + public: + std::shared_ptr GetStream(); + StreamWriter(std::shared_ptr strm); + StreamWriter(std::filesystem::path filename, bool append = false); + void WriteData(const char *text, size_t len); + ~StreamWriter(); +}; +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/StringReader.hpp b/include/TessesFramework/TextStreams/StringReader.hpp index 8bd8f7c..48b11a0 100644 --- a/include/TessesFramework/TextStreams/StringReader.hpp +++ b/include/TessesFramework/TextStreams/StringReader.hpp @@ -1,15 +1,34 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TextReader.hpp" namespace Tesses::Framework::TextStreams { - class StringReader : public TextReader { - std::string str; - size_t offset; - public: - StringReader(); - StringReader(std::string str); - size_t& GetOffset(); - std::string& GetString(); - bool Rewind(); - bool ReadBlock(std::string& str,size_t sz); - }; -} \ No newline at end of file +class StringReader : public TextReader { + std::string str; + size_t offset; + + public: + StringReader(); + StringReader(std::string str); + size_t &GetOffset(); + std::string &GetString(); + bool Rewind(); + bool ReadBlock(std::string &str, size_t sz); +}; +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/StringWriter.hpp b/include/TessesFramework/TextStreams/StringWriter.hpp index b6c002d..909be69 100644 --- a/include/TessesFramework/TextStreams/StringWriter.hpp +++ b/include/TessesFramework/TextStreams/StringWriter.hpp @@ -1,15 +1,33 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "TextWriter.hpp" -namespace Tesses::Framework::TextStreams -{ - class StringWriter : public TextWriter { - private: - std::string text; - public: - std::string& GetString(); - StringWriter(); - StringWriter(std::string str); - void WriteData(const char* text, size_t len); - }; -} +namespace Tesses::Framework::TextStreams { +class StringWriter : public TextWriter { + private: + std::string text; + + public: + std::string &GetString(); + StringWriter(); + StringWriter(std::string str); + void WriteData(const char *text, size_t len); +}; +} // namespace Tesses::Framework::TextStreams diff --git a/include/TessesFramework/TextStreams/TextReader.hpp b/include/TessesFramework/TextStreams/TextReader.hpp index 1f7436e..1b09034 100644 --- a/include/TessesFramework/TextStreams/TextReader.hpp +++ b/include/TessesFramework/TextStreams/TextReader.hpp @@ -1,22 +1,39 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Common.hpp" #include "TextWriter.hpp" -namespace Tesses::Framework::TextStreams -{ - class TextReader - { - bool eof=false; - public: - virtual bool Rewind(); - virtual bool ReadBlock(std::string& str,size_t sz)=0; - int32_t ReadChar(); - std::string ReadLine(); - bool ReadLine(std::string& str); - bool ReadLineHttp(std::string& str); - void ReadAllLines(std::vector& lines); - std::string ReadToEnd(); - void ReadToEnd(std::string& str); - void CopyTo(TextWriter& writer, size_t bufSz=1024); - virtual ~TextReader(); - }; -} \ No newline at end of file +namespace Tesses::Framework::TextStreams { +class TextReader { + bool eof = false; + + public: + virtual bool Rewind(); + virtual bool ReadBlock(std::string &str, size_t sz) = 0; + int32_t ReadChar(); + std::string ReadLine(); + bool ReadLine(std::string &str); + bool ReadLineHttp(std::string &str); + void ReadAllLines(std::vector &lines); + std::string ReadToEnd(); + void ReadToEnd(std::string &str); + void CopyTo(TextWriter &writer, size_t bufSz = 1024); + virtual ~TextReader(); +}; +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/TextWriter.hpp b/include/TessesFramework/TextStreams/TextWriter.hpp index 5c75351..0a4e4e6 100644 --- a/include/TessesFramework/TextStreams/TextWriter.hpp +++ b/include/TessesFramework/TextStreams/TextWriter.hpp @@ -1,75 +1,83 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../Common.hpp" -namespace Tesses::Framework::TextStreams -{ - class NewLine {}; //dummy class - class TextWriter { - public: - TextWriter(); - std::string newline; - virtual void WriteData(const char* text, size_t len)=0; - void Write(int64_t n); - void Write(uint64_t n); - void Write(const void* ptr); - void Write(const char* ptr); - void Write(char c); - void Write(double d); - void Write(std::string text); - inline TextWriter& operator<<(int64_t n) - { - Write(n); - return *this; - } - inline TextWriter& operator<<(uint64_t n) - { - Write(n); - return *this; - } - inline TextWriter& operator<<(const void* n) - { - Write(n); - return *this; - } - inline TextWriter& operator<<(const char* n) - { - Write(n); - return *this; - } - inline TextWriter& operator<<(char n) - { - Write(n); - return *this; - } - inline TextWriter& operator<<(double n) - { - Write(n); - return *this; - } - inline TextWriter& operator<<(std::string n) - { - Write(n); - return *this; - } - - inline TextWriter& operator<<(NewLine nl) - { - WriteLine(); - return *this; - } - - void WriteLine(std::string txt); - void WriteLine(int64_t n); - void WriteLine(uint64_t n); - void WriteLine(const void* ptr); - void WriteLine(const char* ptr); - void WriteLine(char c); - void WriteLine(double d); - - void WriteLine(); - - virtual ~TextWriter(); - }; +namespace Tesses::Framework::TextStreams { +class NewLine {}; // dummy class +class TextWriter { + public: + TextWriter(); + std::string newline; + virtual void WriteData(const char *text, size_t len) = 0; + void Write(int64_t n); + void Write(uint64_t n); + void Write(const void *ptr); + void Write(const char *ptr); + void Write(char c); + void Write(double d); + void Write(std::string text); + inline TextWriter &operator<<(int64_t n) { + Write(n); + return *this; + } + inline TextWriter &operator<<(uint64_t n) { + Write(n); + return *this; + } + inline TextWriter &operator<<(const void *n) { + Write(n); + return *this; + } + inline TextWriter &operator<<(const char *n) { + Write(n); + return *this; + } + inline TextWriter &operator<<(char n) { + Write(n); + return *this; + } + inline TextWriter &operator<<(double n) { + Write(n); + return *this; + } + inline TextWriter &operator<<(std::string n) { + Write(n); + return *this; + } - -} \ No newline at end of file + inline TextWriter &operator<<(NewLine nl) { + WriteLine(); + return *this; + } + + void WriteLine(std::string txt); + void WriteLine(int64_t n); + void WriteLine(uint64_t n); + void WriteLine(const void *ptr); + void WriteLine(const char *ptr); + void WriteLine(char c); + void WriteLine(double d); + + void WriteLine(); + + virtual ~TextWriter(); +}; + +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/include/TessesFramework/Threading/Mutex.hpp b/include/TessesFramework/Threading/Mutex.hpp index 50114c3..10d2c4d 100644 --- a/include/TessesFramework/Threading/Mutex.hpp +++ b/include/TessesFramework/Threading/Mutex.hpp @@ -1,15 +1,33 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "../HiddenField.hpp" -namespace Tesses::Framework::Threading -{ - class Mutex { - HiddenField data; - public: - Mutex(); - void Lock(); - void Unlock(); - bool TryLock(); - ~Mutex(); - }; -} \ No newline at end of file +namespace Tesses::Framework::Threading { +class Mutex { + HiddenField data; + + public: + Mutex(); + void Lock(); + void Unlock(); + bool TryLock(); + ~Mutex(); +}; +} // namespace Tesses::Framework::Threading \ No newline at end of file diff --git a/include/TessesFramework/Threading/Thread.hpp b/include/TessesFramework/Threading/Thread.hpp index a57abff..8430ad8 100644 --- a/include/TessesFramework/Threading/Thread.hpp +++ b/include/TessesFramework/Threading/Thread.hpp @@ -1,3 +1,21 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include #if defined(_WIN32) @@ -9,17 +27,15 @@ #else #include #endif -#include #include "../HiddenField.hpp" -namespace Tesses::Framework::Threading -{ - class Thread - { - HiddenField data; - - public: - Thread(std::function fn); - void Join(); - void Detach(); - }; -} \ No newline at end of file +#include +namespace Tesses::Framework::Threading { +class Thread { + HiddenField data; + + public: + Thread(std::function fn); + void Join(); + void Detach(); +}; +} // namespace Tesses::Framework::Threading \ No newline at end of file diff --git a/include/TessesFramework/Threading/ThreadPool.hpp b/include/TessesFramework/Threading/ThreadPool.hpp index 04f0f3d..66638de 100644 --- a/include/TessesFramework/Threading/ThreadPool.hpp +++ b/include/TessesFramework/Threading/ThreadPool.hpp @@ -1,25 +1,42 @@ -#pragma once -#include -#include "Thread.hpp" -#include "Mutex.hpp" -#include -#include +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan -namespace Tesses::Framework::Threading -{ - -class ThreadPool -{ - std::vector threads; + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once +#include "Mutex.hpp" +#include "Thread.hpp" +#include +#include +#include + +namespace Tesses::Framework::Threading { + +class ThreadPool { + std::vector threads; std::queue> callbacks; Mutex mtx; volatile bool isRunning; - public: - static size_t GetNumberOfCores(); - ThreadPool(size_t threads); - size_t ThreadCount(); - bool Empty(); - void Schedule(std::function cb); - ~ThreadPool(); + + public: + static size_t GetNumberOfCores(); + ThreadPool(size_t threads); + size_t ThreadCount(); + bool Empty(); + void Schedule(std::function cb); + ~ThreadPool(); }; -} \ No newline at end of file +} // namespace Tesses::Framework::Threading \ No newline at end of file diff --git a/include/TessesFramework/Uuid.hpp b/include/TessesFramework/Uuid.hpp index efbf198..c0ca2d4 100644 --- a/include/TessesFramework/Uuid.hpp +++ b/include/TessesFramework/Uuid.hpp @@ -1,37 +1,56 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #pragma once #include "Common.hpp" namespace Tesses::Framework { - enum class UuidStringifyConfig { - IsUppercase=0b001, - HasCurly=0b010, - HasDashes=0b100, - UppercaseCompact = IsUppercase, - LowercaseCompact = 0, - UppercaseNoCurly = IsUppercase | HasDashes, - LowercaseNoCurly = HasDashes, - UppercaseCurly = IsUppercase | HasDashes | HasCurly, - LowercaseCurly = HasDashes | HasCurly - }; - struct Uuid { - Uuid() = default; - uint32_t time_low = 0; - uint16_t time_mid = 0; - uint16_t time_hi_and_version = 0; - uint8_t clock_seq_hi_and_reserved = 0; - uint8_t clock_seq_low = 0; - uint8_t node[6] = {0,0,0,0,0,0}; - - static Uuid Generate(); - static void Generate(Uuid& uuid); +enum class UuidStringifyConfig { + IsUppercase = 0b001, + HasCurly = 0b010, + HasDashes = 0b100, + UppercaseCompact = IsUppercase, + LowercaseCompact = 0, + UppercaseNoCurly = IsUppercase | HasDashes, + LowercaseNoCurly = HasDashes, + UppercaseCurly = IsUppercase | HasDashes | HasCurly, + LowercaseCurly = HasDashes | HasCurly +}; +struct Uuid { + Uuid() = default; + uint32_t time_low = 0; + uint16_t time_mid = 0; + uint16_t time_hi_and_version = 0; + uint8_t clock_seq_hi_and_reserved = 0; + uint8_t clock_seq_low = 0; + uint8_t node[6] = {0, 0, 0, 0, 0, 0}; - static bool TryParse(std::string text, Uuid& uuid); + static Uuid Generate(); + static void Generate(Uuid &uuid); - std::string ToString(UuidStringifyConfig cfg = UuidStringifyConfig::UppercaseCurly) const; + static bool TryParse(std::string text, Uuid &uuid); - bool IsEmpty() const; - }; + std::string ToString( + UuidStringifyConfig cfg = UuidStringifyConfig::UppercaseCurly) const; - bool operator==(const Uuid& left, const Uuid& right); - bool operator!=(const Uuid& left, const Uuid& right); -} \ No newline at end of file + bool IsEmpty() const; +}; + +bool operator==(const Uuid &left, const Uuid &right); +bool operator!=(const Uuid &left, const Uuid &right); +} // namespace Tesses::Framework \ No newline at end of file diff --git a/pkgconfig/CMakeLists.txt b/pkgconfig/CMakeLists.txt index b41ab4e..85e6e4a 100644 --- a/pkgconfig/CMakeLists.txt +++ b/pkgconfig/CMakeLists.txt @@ -10,16 +10,8 @@ else() set(PKGCONFIG_DEPS "") endif() -if(TESSESFRAMEWORK_ENABLE_STATIC) -configure_file(tessesframework_static.pc.in tessesframework_static.pc @ONLY) -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/tessesframework_static.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) -endif() - -if(TESSESFRAMEWORK_ENABLE_SHARED) configure_file(tessesframework.pc.in tessesframework.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/tessesframework.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) -endif() + diff --git a/pkgconfig/tessesframework.pc.in b/pkgconfig/tessesframework.pc.in index 56850ae..52fd53d 100644 --- a/pkgconfig/tessesframework.pc.in +++ b/pkgconfig/tessesframework.pc.in @@ -7,7 +7,5 @@ Description: @PKGCONFIG_PROJECT_DESCRIPTION@ URL: @PKGCONFIG_PROJECT_HOMEPAGE_URL@ Version: @PROJECT_VERSION@ -@PKGCONFIG_DEPS@ - Cflags: -I"${includedir}" -Libs: -L"${libdir}" -ltessesframework_shared \ No newline at end of file +Libs: -L"${libdir}" -ltessesframework \ No newline at end of file diff --git a/pkgconfig/tessesframework_static.pc.in b/pkgconfig/tessesframework_static.pc.in deleted file mode 100644 index 5670f7a..0000000 --- a/pkgconfig/tessesframework_static.pc.in +++ /dev/null @@ -1,13 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -includedir=@PKGCONFIG_INCLUDEDIR@ -libdir=@PKGCONFIG_LIBDIR@ - -Name: @PROJECT_NAME@ -Description: @PKGCONFIG_PROJECT_DESCRIPTION@ -URL: @PKGCONFIG_PROJECT_HOMEPAGE_URL@ -Version: @PROJECT_VERSION@ - -@PKGCONFIG_DEPS@ - -Cflags: -I"${includedir}" -Libs: -L"${libdir}" -ltessesframework \ No newline at end of file diff --git a/src/Args.cpp b/src/Args.cpp index d48f44e..6bb0eba 100644 --- a/src/Args.cpp +++ b/src/Args.cpp @@ -1,60 +1,72 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/TessesFramework.hpp" namespace Tesses::Framework { - Args::Args(std::vector args) - { - if(args.size() < 1) return; - filename = args[0]; - bool onlyPos=false; +Args::Args(std::vector args) { + if (args.size() < 1) + return; + filename = args[0]; + bool onlyPos = false; - for(size_t i = 1; i < args.size(); i++) - { - std::string& arg = args[i]; - if(arg == "--") - { - onlyPos=true; - continue; - } - if(!onlyPos && arg.size() > 2 && arg[0] == '-' && arg[1] == '-') - { - auto p = Tesses::Framework::Http::HttpUtils::SplitString(arg.substr(2),"=",2); - if(p.size() == 1) - flags.push_back(p[0]); - else if(p.size() == 2) - options.push_back(std::pair(p[0],p[1])); - } - else { - positional.push_back(arg); - } + for (size_t i = 1; i < args.size(); i++) { + std::string &arg = args[i]; + if (arg == "--") { + onlyPos = true; + continue; } - - } - Args::Args(int argc, char** argv) - { - if(argc < 1) return; - filename = argv[0]; - bool onlyPos=false; - - for(int i = 1; i < argc; i++) - { - std::string_view arg = argv[i]; - if(arg == "--") - { - onlyPos=true; - continue; - } - if(!onlyPos && arg.size() > 2 && arg[0] == '-' && arg[1] == '-') - { - auto p = Tesses::Framework::Http::HttpUtils::SplitString((std::string)arg.substr(2),"=",2); - if(p.size() == 1) - flags.push_back(p[0]); - else if(p.size() == 2) - options.push_back(std::pair(p[0],p[1])); - } - else { - positional.push_back((std::string)arg); - } + if (!onlyPos && arg.size() > 2 && arg[0] == '-' && arg[1] == '-') { + auto p = Tesses::Framework::Http::HttpUtils::SplitString( + arg.substr(2), "=", 2); + if (p.size() == 1) + flags.push_back(p[0]); + else if (p.size() == 2) + options.push_back( + std::pair(p[0], p[1])); + } else { + positional.push_back(arg); } - } -} \ No newline at end of file +} +Args::Args(int argc, char **argv) { + if (argc < 1) + return; + filename = argv[0]; + bool onlyPos = false; + + for (int i = 1; i < argc; i++) { + std::string_view arg = argv[i]; + if (arg == "--") { + onlyPos = true; + continue; + } + if (!onlyPos && arg.size() > 2 && arg[0] == '-' && arg[1] == '-') { + auto p = Tesses::Framework::Http::HttpUtils::SplitString( + (std::string)arg.substr(2), "=", 2); + if (p.size() == 1) + flags.push_back(p[0]); + else if (p.size() == 2) + options.push_back( + std::pair(p[0], p[1])); + } else { + positional.push_back((std::string)arg); + } + } +} +} // namespace Tesses::Framework \ No newline at end of file diff --git a/src/BitTorrent/TorrentFile.cpp b/src/BitTorrent/TorrentFile.cpp index cf87cbe..16c034b 100644 --- a/src/BitTorrent/TorrentFile.cpp +++ b/src/BitTorrent/TorrentFile.cpp @@ -1,379 +1,374 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/BitTorrent/TorrentFile.hpp" -#include "TessesFramework/Streams/MemoryStream.hpp" #include "TessesFramework/Crypto/Crypto.hpp" #include "TessesFramework/Http/HttpUtils.hpp" -namespace Tesses::Framework::BitTorrent -{ - - TorrentFileInfo::TorrentFileInfo() - { - this->fileListOrLength = 0; - } - TorrentFileInfo::TorrentFileInfo(const Serialization::Bencode::BeDictionary& tkn) - { - Load(tkn); - } - int64_t TorrentFileInfo::GetTorrentFileSize() - { - if(std::holds_alternative(this->fileListOrLength)) - return std::get(fileListOrLength); - else if(std::holds_alternative>(this->fileListOrLength)) { - auto& files= std::get>(this->fileListOrLength); - int64_t no=0; - for(auto itm : files) - { - no += itm.length; - } - return no; +#include "TessesFramework/Streams/MemoryStream.hpp" +namespace Tesses::Framework::BitTorrent { + +TorrentFileInfo::TorrentFileInfo() { this->fileListOrLength = 0; } +TorrentFileInfo::TorrentFileInfo( + const Serialization::Bencode::BeDictionary &tkn) { + Load(tkn); +} +int64_t TorrentFileInfo::GetTorrentFileSize() { + if (std::holds_alternative(this->fileListOrLength)) + return std::get(fileListOrLength); + else if (std::holds_alternative>( + this->fileListOrLength)) { + auto &files = + std::get>(this->fileListOrLength); + int64_t no = 0; + for (auto itm : files) { + no += itm.length; } - return 0; + return no; } - Serialization::Bencode::BeDictionary& TorrentFileInfo::GenerateInfoDictionary() - { - this->info = {}; - this->info.tokens.emplace_back("name",this->name); - this->info.tokens.emplace_back("piece length",this->piece_length); - this->info.tokens.emplace_back("pieces",this->pieces); - if(this->isPrivate) - this->info.tokens.emplace_back("private",1); + return 0; +} +Serialization::Bencode::BeDictionary & +TorrentFileInfo::GenerateInfoDictionary() { + this->info = {}; + this->info.tokens.emplace_back("name", this->name); + this->info.tokens.emplace_back("piece length", this->piece_length); + this->info.tokens.emplace_back("pieces", this->pieces); + if (this->isPrivate) + this->info.tokens.emplace_back("private", 1); - if(std::holds_alternative(this->fileListOrLength)) - { - this->info.tokens.emplace_back("length",std::get(this->fileListOrLength)); + if (std::holds_alternative(this->fileListOrLength)) { + this->info.tokens.emplace_back( + "length", std::get(this->fileListOrLength)); + } else if (std::holds_alternative>( + this->fileListOrLength)) { + Serialization::Bencode::BeArray a; + for (auto &item : + std::get>(this->fileListOrLength)) { + Serialization::Bencode::BeDictionary dict; + dict.tokens.emplace_back("length", item.length); + Serialization::Bencode::BeArray path; + for (auto &p : item.path.path) + path.tokens.push_back(p); + dict.tokens.emplace_back("path", path); + + a.tokens.push_back(dict); } - else if(std::holds_alternative>(this->fileListOrLength)) - { - Serialization::Bencode::BeArray a; - for(auto& item : std::get>(this->fileListOrLength)) - { - Serialization::Bencode::BeDictionary dict; - dict.tokens.emplace_back("length",item.length); - Serialization::Bencode::BeArray path; - for(auto& p : item.path.path) - path.tokens.push_back(p); - dict.tokens.emplace_back("path",path); - - a.tokens.push_back(dict); - } - this->info.tokens.emplace_back("files", a); - } - return this->info; - } - Serialization::Bencode::BeDictionary& TorrentFileInfo::GetInfoDictionary() - { - return this->info; + this->info.tokens.emplace_back("files", a); } + return this->info; +} +Serialization::Bencode::BeDictionary &TorrentFileInfo::GetInfoDictionary() { + return this->info; +} - void TorrentFileInfo::Load(const Serialization::Bencode::BeDictionary& dict) - { - this->info = dict; - auto o=dict.GetValue("name"); - if(std::holds_alternative(o)) - this->name = std::get(o); - else - this->name = {}; - - o=dict.GetValue("piece length"); - if(std::holds_alternative(o)) - this->piece_length = std::get(o); - else - this->piece_length = DEFAULT_PIECE_LENGTH; - - o=dict.GetValue("private"); - if(std::holds_alternative(o)) - this->isPrivate = std::get(o); - else - this->isPrivate = 0; - - o=dict.GetValue("pieces"); - if(std::holds_alternative(o)) - this->pieces = std::get(o); - else - this->pieces = {}; - - - - o=dict.GetValue("files"); - if(std::holds_alternative(o)) - { - std::vector ents; +void TorrentFileInfo::Load(const Serialization::Bencode::BeDictionary &dict) { + this->info = dict; + auto o = dict.GetValue("name"); + if (std::holds_alternative(o)) + this->name = std::get(o); + else + this->name = {}; - for(auto& item : std::get(o).tokens) - { - if(std::holds_alternative(item)) - { - TorrentFileEntry fe; - fe.path.relative=true; - - auto& d2=std::get(item); - auto o2=d2.GetValue("length"); - if(std::holds_alternative(o2)) - { - fe.length = std::get(o2); - } - o2=d2.GetValue("path"); - if(std::holds_alternative(o2)) - { - auto& arr=std::get(o2); - for(auto& itm : arr.tokens) - { - if(std::holds_alternative(itm)) - { - fe.path.path.push_back(std::get(itm)); - } + o = dict.GetValue("piece length"); + if (std::holds_alternative(o)) + this->piece_length = std::get(o); + else + this->piece_length = DEFAULT_PIECE_LENGTH; + + o = dict.GetValue("private"); + if (std::holds_alternative(o)) + this->isPrivate = std::get(o); + else + this->isPrivate = 0; + + o = dict.GetValue("pieces"); + if (std::holds_alternative(o)) + this->pieces = std::get(o); + else + this->pieces = {}; + + o = dict.GetValue("files"); + if (std::holds_alternative(o)) { + std::vector ents; + + for (auto &item : std::get(o).tokens) { + if (std::holds_alternative( + item)) { + TorrentFileEntry fe; + fe.path.relative = true; + + auto &d2 = std::get(item); + auto o2 = d2.GetValue("length"); + if (std::holds_alternative(o2)) { + fe.length = std::get(o2); + } + o2 = d2.GetValue("path"); + if (std::holds_alternative( + o2)) { + auto &arr = std::get(o2); + for (auto &itm : arr.tokens) { + if (std::holds_alternative< + Serialization::Bencode::BeString>(itm)) { + fe.path.path.push_back( + std::get( + itm)); } } - ents.push_back(fe); } - } - this->fileListOrLength = ents; - } - else { - o = dict.GetValue("length"); - if(std::holds_alternative(o)) - { - this->fileListOrLength = std::get(o); - } - else { - this->fileListOrLength = (int64_t)0; + ents.push_back(fe); } } - - } - Serialization::Bencode::BeString TorrentFileInfo::GetInfoHash() - { - auto strm = std::make_shared(true); - Serialization::Bencode::Bencode::Save(strm,this->info); - strm->Seek(0L,Streams::SeekOrigin::Begin); - return Crypto::Sha1::ComputeHash(strm); - } - - - TorrentFile::TorrentFile() - { - this->created_by = "TessesFrameworkTorrent"; - this->creation_date = (int64_t)time(NULL); - } - TorrentFile::TorrentFile(const Serialization::Bencode::BeDictionary& tkn) - { - auto o=tkn.GetValue("info"); - if(std::holds_alternative(o)) - { - this->info.Load(std::get(o)); + this->fileListOrLength = ents; + } else { + o = dict.GetValue("length"); + if (std::holds_alternative(o)) { + this->fileListOrLength = std::get(o); + } else { + this->fileListOrLength = (int64_t)0; } - o=tkn.GetValue("announce"); - if(std::holds_alternative(o)) - { - this->announce = std::get(o); - } - o=tkn.GetValue("announce-list"); - if(std::holds_alternative(o)) - { - auto& ls = std::get(o); - for(auto& item : ls.tokens) - { - if(std::holds_alternative(item)) - { - auto ls2 = std::get(item); - auto item2=ls2.tokens.at(0); - if(std::holds_alternative(item2)) - { - this->announce_list.push_back(std::get(item2)); - } + } +} +Serialization::Bencode::BeString TorrentFileInfo::GetInfoHash() { + auto strm = std::make_shared(true); + Serialization::Bencode::Bencode::Save(strm, this->info); + strm->Seek(0L, Streams::SeekOrigin::Begin); + return Crypto::Sha1::ComputeHash(strm); +} + +TorrentFile::TorrentFile() { + this->created_by = "TessesFrameworkTorrent"; + this->creation_date = (int64_t)time(NULL); +} +TorrentFile::TorrentFile(const Serialization::Bencode::BeDictionary &tkn) { + auto o = tkn.GetValue("info"); + if (std::holds_alternative(o)) { + this->info.Load(std::get(o)); + } + o = tkn.GetValue("announce"); + if (std::holds_alternative(o)) { + this->announce = std::get(o); + } + o = tkn.GetValue("announce-list"); + if (std::holds_alternative(o)) { + auto &ls = std::get(o); + for (auto &item : ls.tokens) { + if (std::holds_alternative(item)) { + auto ls2 = std::get(item); + auto item2 = ls2.tokens.at(0); + if (std::holds_alternative( + item2)) { + this->announce_list.push_back( + std::get(item2)); } } } - o=tkn.GetValue("creation date"); - if(std::holds_alternative(o)) - { - this->creation_date = std::get(o); - } - o=tkn.GetValue("comment"); - if(std::holds_alternative(o)) - { - this->comment = std::get(o); - } - o=tkn.GetValue("created by"); - if(std::holds_alternative(o)) - { - this->created_by = std::get(o); - } - o=tkn.GetValue("url-list"); - if(std::holds_alternative(o)) - { - auto& li =std::get(o); - for(auto& itm : li.tokens) - { - if(std::holds_alternative(itm)) - this->url_list.push_back(std::get(itm)); - } + } + o = tkn.GetValue("creation date"); + if (std::holds_alternative(o)) { + this->creation_date = std::get(o); + } + o = tkn.GetValue("comment"); + if (std::holds_alternative(o)) { + this->comment = std::get(o); + } + o = tkn.GetValue("created by"); + if (std::holds_alternative(o)) { + this->created_by = std::get(o); + } + o = tkn.GetValue("url-list"); + if (std::holds_alternative(o)) { + auto &li = std::get(o); + for (auto &itm : li.tokens) { + if (std::holds_alternative(itm)) + this->url_list.push_back( + std::get(itm)); } } +} - Serialization::Bencode::BeDictionary TorrentFile::ToDictionary() - { - Serialization::Bencode::BeDictionary dict; - dict.SetValue("info",this->info.GetInfoDictionary()); - dict.SetValue("announce",this->announce); - if(!this->announce_list.empty()) - { - Serialization::Bencode::BeArray a; - for(auto& item : this->announce_list) - { - Serialization::Bencode::BeArray a2; - a2.tokens.push_back(item); - a.tokens.push_back(a2); - } - dict.SetValue("announce-list",a); +Serialization::Bencode::BeDictionary TorrentFile::ToDictionary() { + Serialization::Bencode::BeDictionary dict; + dict.SetValue("info", this->info.GetInfoDictionary()); + dict.SetValue("announce", this->announce); + if (!this->announce_list.empty()) { + Serialization::Bencode::BeArray a; + for (auto &item : this->announce_list) { + Serialization::Bencode::BeArray a2; + a2.tokens.push_back(item); + a.tokens.push_back(a2); } - if(!this->url_list.empty()) - { - Serialization::Bencode::BeArray ls; - for(auto& itm : this->url_list) - { - ls.tokens.push_back(itm); - } - dict.SetValue("url-list",ls); + dict.SetValue("announce-list", a); + } + if (!this->url_list.empty()) { + Serialization::Bencode::BeArray ls; + for (auto &itm : this->url_list) { + ls.tokens.push_back(itm); } - - dict.SetValue("created by", this->created_by); - dict.SetValue("creation date", this->creation_date); - dict.SetValue("comment",this->comment); - return dict; + dict.SetValue("url-list", ls); } - void TorrentFile::Print(std::shared_ptr writer) - { - writer->WriteLine("Announce: " + (std::string)this->announce); - writer->WriteLine("Announce List:"); - for(auto& item : this->announce_list) - { - writer->WriteLine("\t" + (std::string)item); - } - writer->WriteLine("Comment: " + (std::string)this->comment); - writer->WriteLine("Created By: " + (std::string)this->created_by); - Date::DateTime dt(this->creation_date); - dt.SetToLocal(); - writer->WriteLine("Creation Date: " + dt.ToString()); - writer->WriteLine("Info Hash: " + Http::HttpUtils::BytesToHex(this->info.GetInfoHash().data)); - writer->WriteLine("Info:"); - writer->WriteLine("\tName: " + (std::string)this->info.name); - writer->WriteLine(this->info.isPrivate ? "\tPrivate: true" : "\tPrivate: false"); - writer->WriteLine("\tPiece Length: " + TF_FileSize(this->info.piece_length)); - if(std::holds_alternative(this->info.fileListOrLength)) - { - writer->WriteLine("\tIs Single File: true"); - writer->WriteLine("\tFile length: " + TF_FileSize((uint64_t)std::get(this->info.fileListOrLength))); - } - else if(std::holds_alternative>(this->info.fileListOrLength)) - { - writer->WriteLine("\tIs Single File: false"); - writer->WriteLine("\tFiles:"); - auto& files = std::get>(this->info.fileListOrLength); - for(auto& file : files) - { - writer->WriteLine("\t\tPath: " + file.path.ToString()); - writer->WriteLine("\t\tLength: " + TF_FileSize(file.length)); - writer->WriteLine(); - } + dict.SetValue("created by", this->created_by); + dict.SetValue("creation date", this->creation_date); + dict.SetValue("comment", this->comment); + return dict; +} + +void TorrentFile::Print(std::shared_ptr writer) { + writer->WriteLine("Announce: " + (std::string)this->announce); + writer->WriteLine("Announce List:"); + for (auto &item : this->announce_list) { + writer->WriteLine("\t" + (std::string)item); + } + writer->WriteLine("Comment: " + (std::string)this->comment); + writer->WriteLine("Created By: " + (std::string)this->created_by); + Date::DateTime dt(this->creation_date); + dt.SetToLocal(); + writer->WriteLine("Creation Date: " + dt.ToString()); + writer->WriteLine("Info Hash: " + Http::HttpUtils::BytesToHex( + this->info.GetInfoHash().data)); + writer->WriteLine("Info:"); + writer->WriteLine("\tName: " + (std::string)this->info.name); + writer->WriteLine(this->info.isPrivate ? "\tPrivate: true" + : "\tPrivate: false"); + writer->WriteLine("\tPiece Length: " + + TF_FileSize(this->info.piece_length)); + if (std::holds_alternative(this->info.fileListOrLength)) { + writer->WriteLine("\tIs Single File: true"); + writer->WriteLine("\tFile length: " + + TF_FileSize((uint64_t)std::get( + this->info.fileListOrLength))); + } else if (std::holds_alternative>( + this->info.fileListOrLength)) { + writer->WriteLine("\tIs Single File: false"); + writer->WriteLine("\tFiles:"); + auto &files = std::get>( + this->info.fileListOrLength); + for (auto &file : files) { + writer->WriteLine("\t\tPath: " + file.path.ToString()); + writer->WriteLine("\t\tLength: " + TF_FileSize(file.length)); + writer->WriteLine(); } } - - std::shared_ptr TorrentFileInfo::GetStreamFromFilesystem(std::shared_ptr vfs, const Tesses::Framework::Filesystem::VFSPath& path) - { - if(std::holds_alternative>(this->fileListOrLength)) - { - return std::make_shared(vfs,path / this->name, std::get>(this->fileListOrLength)); - } - else if(std::holds_alternative(this->fileListOrLength)) { - return std::make_shared(vfs,path / this->name, (uint64_t)std::get(this->fileListOrLength)); - } - return nullptr; +} +std::shared_ptr TorrentFileInfo::GetStreamFromFilesystem( + std::shared_ptr vfs, + const Tesses::Framework::Filesystem::VFSPath &path) { + if (std::holds_alternative>( + this->fileListOrLength)) { + return std::make_shared( + vfs, path / this->name, + std::get>(this->fileListOrLength)); + } else if (std::holds_alternative(this->fileListOrLength)) { + return std::make_shared( + vfs, path / this->name, + (uint64_t)std::get(this->fileListOrLength)); } + return nullptr; +} - static void ParsePieces(TorrentFileInfo* fi,std::shared_ptr rwa, int64_t flength) - { - int64_t pieces = fi->pieces.data.size()/20; - std::vector buffer; - buffer.resize((size_t)fi->piece_length); - size_t lastPieceSize = (size_t)(flength % (int64_t)buffer.size()); - for(int64_t i = 0; i < pieces; i++) - { - size_t len = buffer.size(); - if(i == pieces-1 && lastPieceSize != 0) len = std::min(len,lastPieceSize); - rwa->ReadBlockAt(i*buffer.size(), buffer.data(), len); - auto hsh=Crypto::Sha1::ComputeHash(buffer.data(),len); - std::copy(hsh.begin(),hsh.end(),fi->pieces.data.begin()+(i*20)); - } +static void ParsePieces(TorrentFileInfo *fi, std::shared_ptr rwa, + int64_t flength) { + int64_t pieces = fi->pieces.data.size() / 20; + std::vector buffer; + buffer.resize((size_t)fi->piece_length); + size_t lastPieceSize = (size_t)(flength % (int64_t)buffer.size()); + for (int64_t i = 0; i < pieces; i++) { + size_t len = buffer.size(); + if (i == pieces - 1 && lastPieceSize != 0) + len = std::min(len, lastPieceSize); + rwa->ReadBlockAt(i * buffer.size(), buffer.data(), len); + auto hsh = Crypto::Sha1::ComputeHash(buffer.data(), len); + std::copy(hsh.begin(), hsh.end(), fi->pieces.data.begin() + (i * 20)); } - void TorrentFileInfo::CreateFromFilesystem(std::shared_ptr vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate, int64_t pieceLength) - { - this->isPrivate=isPrivate; - this->piece_length = pieceLength; - this->pieces.data.clear(); - this->name = path.GetFileName(); - int64_t len=0; - if(vfs->FileExists(path)) - { - auto strm = vfs->OpenFile(path,"rb"); - len = strm->GetLength(); - this->fileListOrLength= len; - int64_t pieces = len / piece_length; - if((len % piece_length) != 0) pieces++; +} +void TorrentFileInfo::CreateFromFilesystem( + std::shared_ptr vfs, + const Tesses::Framework::Filesystem::VFSPath &path, bool isPrivate, + int64_t pieceLength) { + this->isPrivate = isPrivate; + this->piece_length = pieceLength; + this->pieces.data.clear(); + this->name = path.GetFileName(); + int64_t len = 0; + if (vfs->FileExists(path)) { + auto strm = vfs->OpenFile(path, "rb"); + len = strm->GetLength(); + this->fileListOrLength = len; + int64_t pieces = len / piece_length; + if ((len % piece_length) != 0) + pieces++; - this->pieces.data.resize(pieces*20); - } - else if(vfs->DirectoryExists(path)) - { - std::vector ents; - std::function crawl; - crawl= [&](Tesses::Framework::Filesystem::VFSPath inFS, Tesses::Framework::Filesystem::VFSPath inTorrent)->void{ - for(auto ent : vfs->EnumeratePaths(inFS)) - { - if(vfs->FileExists(ent)) - { - auto strm = vfs->OpenFile(ent,"rb"); - auto flen = strm->GetLength(); - ents.emplace_back(inTorrent / ent.GetFileName(),flen); + this->pieces.data.resize(pieces * 20); + } else if (vfs->DirectoryExists(path)) { + std::vector ents; + std::function + crawl; + crawl = [&](Tesses::Framework::Filesystem::VFSPath inFS, + Tesses::Framework::Filesystem::VFSPath inTorrent) -> void { + for (auto ent : vfs->EnumeratePaths(inFS)) { + if (vfs->FileExists(ent)) { + auto strm = vfs->OpenFile(ent, "rb"); + auto flen = strm->GetLength(); + ents.emplace_back(inTorrent / ent.GetFileName(), flen); - len += flen; - } - else if(vfs->DirectoryExists(ent)) - { - crawl(ent,inTorrent / ent.GetFileName()); - } + len += flen; + } else if (vfs->DirectoryExists(ent)) { + crawl(ent, inTorrent / ent.GetFileName()); } - }; - Tesses::Framework::Filesystem::VFSPath p2; - p2.relative=true; - crawl(path, p2); + } + }; + Tesses::Framework::Filesystem::VFSPath p2; + p2.relative = true; + crawl(path, p2); - this->fileListOrLength = ents; - int64_t pieces = len / piece_length; - if((len % piece_length) != 0) pieces++; + this->fileListOrLength = ents; + int64_t pieces = len / piece_length; + if ((len % piece_length) != 0) + pieces++; - this->pieces.data.resize(pieces*20); - } - else { - throw std::runtime_error("File or directory does not exist"); - } - ParsePieces(this,this->GetStreamFromFilesystem(vfs,path.GetParent()),len); - - - this->GenerateInfoDictionary(); + this->pieces.data.resize(pieces * 20); + } else { + throw std::runtime_error("File or directory does not exist"); } + ParsePieces(this, this->GetStreamFromFilesystem(vfs, path.GetParent()), + len); - void TorrentFile::CreateTorrent(std::shared_ptr torrent_file_stream,const std::vector& trackers,const std::vector& webseeds, std::shared_ptr vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate, int64_t pieceLength, Serialization::Bencode::BeString comment, Serialization::Bencode::BeString created_by) - { - TorrentFile file; - file.announce = trackers.at(0); - file.announce_list = trackers; - file.url_list = webseeds; - file.comment = comment; - file.created_by = created_by; - file.info.CreateFromFilesystem(vfs,path,isPrivate,pieceLength); - auto dict=file.ToDictionary(); - Serialization::Bencode::Bencode::Save(torrent_file_stream,dict); - } -} \ No newline at end of file + this->GenerateInfoDictionary(); +} + +void TorrentFile::CreateTorrent( + std::shared_ptr torrent_file_stream, + const std::vector &trackers, + const std::vector &webseeds, + std::shared_ptr vfs, + const Tesses::Framework::Filesystem::VFSPath &path, bool isPrivate, + int64_t pieceLength, Serialization::Bencode::BeString comment, + Serialization::Bencode::BeString created_by) { + TorrentFile file; + file.announce = trackers.at(0); + file.announce_list = trackers; + file.url_list = webseeds; + file.comment = comment; + file.created_by = created_by; + file.info.CreateFromFilesystem(vfs, path, isPrivate, pieceLength); + auto dict = file.ToDictionary(); + Serialization::Bencode::Bencode::Save(torrent_file_stream, dict); +} +} // namespace Tesses::Framework::BitTorrent \ No newline at end of file diff --git a/src/BitTorrent/TorrentStream.cpp b/src/BitTorrent/TorrentStream.cpp index 6fcb7e0..bbf9128 100644 --- a/src/BitTorrent/TorrentStream.cpp +++ b/src/BitTorrent/TorrentStream.cpp @@ -1,122 +1,146 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/BitTorrent/TorrentStream.hpp" -namespace Tesses::Framework::BitTorrent -{ - TorrentFileEntry::TorrentFileEntry() - { +namespace Tesses::Framework::BitTorrent { +TorrentFileEntry::TorrentFileEntry() {} +TorrentFileEntry::TorrentFileEntry(Tesses::Framework::Filesystem::VFSPath path, + int64_t length) + : path(path), length(length) {} +ReadWriteAt::~ReadWriteAt() {} + +TorrentFileStream::TorrentFileStream( + std::shared_ptr vfs, + Tesses::Framework::Filesystem::VFSPath path, uint64_t length) + : vfs(vfs), path(path), length(length) {} +bool TorrentFileStream::ReadBlockAt(uint64_t offset, uint8_t *data, + size_t len) { + if (!vfs->FileExists(path)) + return false; + auto strm = vfs->OpenFile(path, "rb"); + strm->Seek((int64_t)offset, Streams::SeekOrigin::Begin); + strm->ReadBlock(data, len); + return true; +} +void TorrentFileStream::WriteBlockAt(uint64_t offset, const uint8_t *data, + size_t len) { + len = + (size_t)std::min((int64_t)len, (int64_t)this->length - (int64_t)offset); + if (len == 0) + return; + + mtx.Lock(); + auto strm = vfs->OpenFile(path, "wb"); + strm->Seek((int64_t)offset, Streams::SeekOrigin::Begin); + strm->WriteBlock(data, len); + mtx.Unlock(); +} + +TorrentDirectoryStream::TorrentDirectoryStream( + std::shared_ptr vfs, + Tesses::Framework::Filesystem::VFSPath path, + std::vector entries) + : vfs(vfs), path(path), entries(entries) { + if (!vfs->DirectoryExists(path)) + this->vfs->CreateDirectory(path); + this->mtxs.resize(entries.size()); +} + +// From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed +// under MIT based on code repo +bool TorrentDirectoryStream::ReadBlockAt(uint64_t offset, uint8_t *data, + size_t len) { + uint64_t currentOffset = 0; + uint64_t end = offset + len; + for (size_t i = 0; i < this->entries.size(); i++) { + if (offset < currentOffset && end < currentOffset) { + currentOffset += this->entries[i].length; + continue; + } + + if (offset > currentOffset + this->entries[i].length && + end > currentOffset + this->entries[i].length) { + currentOffset += this->entries[i].length; + continue; + } + + auto path = this->path / this->entries[i].path; + if (!vfs->FileExists(path)) + return false; + + int64_t fstart = + std::max((int64_t)0, (int64_t)offset - (int64_t)currentOffset); + int64_t fend = std::min((int64_t)end - (int64_t)currentOffset, + (int64_t)this->entries[i].length); + int flength = (int)(fend - fstart); + int bstart = + std::max((int)0, (int)((int64_t)currentOffset - (int64_t)offset)); + auto strm = vfs->OpenFile(path, "rb"); + strm->Seek(fstart, Streams::SeekOrigin::Begin); + strm->ReadBlock(data + bstart, flength); + + currentOffset += this->entries[i].length; } - TorrentFileEntry::TorrentFileEntry(Tesses::Framework::Filesystem::VFSPath path, int64_t length): path(path), length(length) - {} + return true; +} - ReadWriteAt::~ReadWriteAt(){} +// From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed +// under MIT based on code repo +void TorrentDirectoryStream::WriteBlockAt(uint64_t offset, const uint8_t *data, + size_t len) { + uint64_t currentOffset = 0; + uint64_t end = offset + len; + for (size_t i = 0; i < this->entries.size(); i++) { + if (offset < currentOffset && end < currentOffset) { + currentOffset += this->entries[i].length; + continue; + } - TorrentFileStream::TorrentFileStream(std::shared_ptr vfs, Tesses::Framework::Filesystem::VFSPath path,uint64_t length) : vfs(vfs), path(path), length(length) - { - } - bool TorrentFileStream::ReadBlockAt(uint64_t offset, uint8_t* data, size_t len) - { - if(!vfs->FileExists(path)) return false; - auto strm = vfs->OpenFile(path,"rb"); - strm->Seek((int64_t)offset,Streams::SeekOrigin::Begin); - strm->ReadBlock(data,len); - return true; - } - void TorrentFileStream::WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len) - { - len = (size_t)std::min((int64_t)len, (int64_t)this->length-(int64_t)offset); - if(len == 0) return; + if (offset > currentOffset + this->entries[i].length && + end > currentOffset + this->entries[i].length) { + currentOffset += this->entries[i].length; + continue; + } - mtx.Lock(); - auto strm = vfs->OpenFile(path,"wb"); - strm->Seek((int64_t)offset,Streams::SeekOrigin::Begin); - strm->WriteBlock(data,len); - mtx.Unlock(); - } + auto path = this->path / this->entries[i].path; + auto parent = path.GetParent(); + if (!vfs->DirectoryExists(parent)) + vfs->CreateDirectory(parent); - TorrentDirectoryStream::TorrentDirectoryStream(std::shared_ptr vfs, Tesses::Framework::Filesystem::VFSPath path,std::vector entries) : vfs(vfs), path(path), entries(entries) - { - if(!vfs->DirectoryExists(path)) - this->vfs->CreateDirectory(path); - this->mtxs.resize(entries.size()); - } + int64_t fstart = + std::max((int64_t)0, (int64_t)offset - (int64_t)currentOffset); + int64_t fend = std::min((int64_t)end - (int64_t)currentOffset, + (int64_t)this->entries[i].length); + int flength = (int)(fend - fstart); + int bstart = + std::max((int)0, (int)((int64_t)currentOffset - (int64_t)offset)); - // From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed under MIT based on code repo - bool TorrentDirectoryStream::ReadBlockAt(uint64_t offset, uint8_t* data, size_t len) - { - uint64_t currentOffset = 0; - uint64_t end = offset + len; - for(size_t i = 0; i < this->entries.size(); i++) + this->mtxs[i].Lock(); { - if(offset < currentOffset && end < currentOffset) { - currentOffset += this->entries[i].length; - continue; - } - - if(offset > currentOffset + this->entries[i].length && end > currentOffset + this->entries[i].length ){ - currentOffset += this->entries[i].length; - continue; - } - - auto path = this->path / this->entries[i].path; - if(!vfs->FileExists(path)) return false; - - int64_t fstart = std::max((int64_t)0,(int64_t)offset - (int64_t)currentOffset); - int64_t fend = std::min((int64_t)end - (int64_t)currentOffset, (int64_t)this->entries[i].length); - int flength = (int)(fend - fstart); - int bstart = std::max((int)0,(int)((int64_t)currentOffset - (int64_t)offset)); - auto strm = vfs->OpenFile(path,"rb"); + auto strm = vfs->OpenFile(path, "wb"); strm->Seek(fstart, Streams::SeekOrigin::Begin); - strm->ReadBlock(data+bstart,flength); - - - - currentOffset += this->entries[i].length; - - + strm->WriteBlock(data + bstart, flength); } - return true; - } + this->mtxs[i].Unlock(); - // From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed under MIT based on code repo - void TorrentDirectoryStream::WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len) - { - uint64_t currentOffset = 0; - uint64_t end = offset + len; - for(size_t i = 0; i < this->entries.size(); i++) - { - if(offset < currentOffset && end < currentOffset) { - currentOffset += this->entries[i].length; - continue; - } - - if(offset > currentOffset + this->entries[i].length && end > currentOffset + this->entries[i].length ){ - currentOffset += this->entries[i].length; - continue; - } - - auto path = this->path / this->entries[i].path; - auto parent = path.GetParent(); - if(!vfs->DirectoryExists(parent)) - vfs->CreateDirectory(parent); - - int64_t fstart = std::max((int64_t)0,(int64_t)offset - (int64_t)currentOffset); - int64_t fend = std::min((int64_t)end - (int64_t)currentOffset, (int64_t)this->entries[i].length); - int flength = (int)(fend - fstart); - int bstart = std::max((int)0,(int)((int64_t)currentOffset - (int64_t)offset)); - - this->mtxs[i].Lock(); - { - auto strm = vfs->OpenFile(path,"wb"); - strm->Seek(fstart, Streams::SeekOrigin::Begin); - strm->WriteBlock(data+bstart,flength); - } - this->mtxs[i].Unlock(); - - - currentOffset += this->entries[i].length; - - - } - } -} \ No newline at end of file + currentOffset += this->entries[i].length; + } +} +} // namespace Tesses::Framework::BitTorrent \ No newline at end of file diff --git a/src/Console.cpp b/src/Console.cpp new file mode 100644 index 0000000..61f392f --- /dev/null +++ b/src/Console.cpp @@ -0,0 +1,869 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "TessesFramework/Console.hpp" +#include "TessesFramework/Http/HttpUtils.hpp" +#include "TessesFramework/Text/StringConverter.hpp" + +#if __has_include() +#include +#include +#include +#endif +#if defined(_WIN32) +#include +#include +#endif + +#include + +namespace Tesses::Framework { + +size_t Console::List(std::vector &strs) { + if (!IsTTY()) + return std::string::npos; + + auto echo = Console::GetEcho(); + auto canonical = Console::GetCanonical(); + Console::SetEcho(false); + Console::SetCanonical(false); + size_t i = 0; + while (true) { + Console::Clear(); + auto size = Console::GetSize(); + // Console::SetBackgroundColor(ConsoleColor::CC_WHITE,true); + // Console::SetForegroundColor(ConsoleColor::CC_BLACK,true); + + size_t page = size.second == 0 ? 0 : (i / (size.second)); + size_t offsetInPage = size.second == 0 ? 0 : (i % (size.second)); + + for (int item = 0; item < size.second; ++item) { + if (item == offsetInPage) { + Console::SetBackgroundColor(ConsoleColor::CC_WHITE, true); + Console::SetForegroundColor(ConsoleColor::CC_BLACK, false); + } else { + Console::SetBackgroundColor(ConsoleColor::CC_BLACK, false); + Console::SetForegroundColor(ConsoleColor::CC_WHITE, true); + } + + size_t myOffset = (size_t)item + page * (size.second); + + if (myOffset < strs.size()) { + if (item == size.second - 1) { + Console::Write(strs[myOffset]); + } else { + Console::WriteLine(strs[myOffset]); + } + } + } + + Console::SetBackgroundColor(ConsoleColor::CC_BLACK, false); + Console::SetForegroundColor(ConsoleColor::CC_WHITE, true); + + int code = Console::Read(); + + if (code == 10) + break; + if (code == -1) + break; + + if (code == 27) { + code = Console::Read(); + if (code == 91) { + code = Console::Read(); + if (code == 65) { + i--; + if (i >= strs.size()) + i = strs.size() - 1; + } else if (code == 66) { + i++; + if (i >= strs.size()) + i = 0; + } else if (code == -1) + break; + } else if (code == -1) + break; + } + } + Console::SetEcho(echo); + Console::SetCanonical(canonical); + Console::SetBackgroundColor(ConsoleColor::CC_BLACK, false); + Console::SetForegroundColor(ConsoleColor::CC_WHITE, true); + return i; +} + +void Console::SetForegroundColor(ConsoleColor col, bool alt) { + + if (!IsTTY()) + return; +#if __has_include() + if (alt) { + printf("\x1b[%im", ((int)col) + 90); // this should be Write + } else { + printf("\x1b[%im", ((int)col) + 30); // this too + } +#elif defined(_WIN32) + + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + if (hConsole == INVALID_HANDLE_VALUE) + return; + CONSOLE_SCREEN_BUFFER_INFO bi; + + GetConsoleScreenBufferInfo(hConsole, &bi); + + bi.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | + FOREGROUND_INTENSITY); + + if (col == ConsoleColor::CC_RED || col == ConsoleColor::CC_YELLOW || + col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE) + bi.wAttributes |= FOREGROUND_RED; + + if (col == ConsoleColor::CC_GREEN || col == ConsoleColor::CC_YELLOW || + col == ConsoleColor::CC_CYAN || col == ConsoleColor::CC_WHITE) + bi.wAttributes |= FOREGROUND_GREEN; + + if (col == ConsoleColor::CC_BLUE || col == ConsoleColor::CC_CYAN || + col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE) + bi.wAttributes |= FOREGROUND_BLUE; + + if (alt) + bi.wAttributes |= FOREGROUND_INTENSITY; + + SetConsoleTextAttribute(hConsole, bi.wAttributes); + +#else + if (alt) { + printf("\x1b[%im", ((int)col) + 90); // this should be Write + } else { + printf("\x1b[%im", ((int)col) + 30); // this too + } +#endif +} + +void Console::SetBackgroundColor(ConsoleColor col, bool alt) { + if (!IsTTY()) + return; +#if __has_include() + if (alt) { + printf("\x1b[%im", ((int)col) + 100); // this should be Write + } else { + printf("\x1b[%im", ((int)col) + 40); // this too + } +#elif defined(_WIN32) + + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + if (hConsole == INVALID_HANDLE_VALUE) + return; + CONSOLE_SCREEN_BUFFER_INFO bi; + + GetConsoleScreenBufferInfo(hConsole, &bi); + + bi.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | + BACKGROUND_INTENSITY); + + if (col == ConsoleColor::CC_RED || col == ConsoleColor::CC_YELLOW || + col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE) + bi.wAttributes |= BACKGROUND_RED; + + if (col == ConsoleColor::CC_GREEN || col == ConsoleColor::CC_YELLOW || + col == ConsoleColor::CC_CYAN || col == ConsoleColor::CC_WHITE) + bi.wAttributes |= BACKGROUND_GREEN; + + if (col == ConsoleColor::CC_BLUE || col == ConsoleColor::CC_CYAN || + col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE) + bi.wAttributes |= BACKGROUND_BLUE; + + if (alt) + bi.wAttributes |= BACKGROUND_INTENSITY; + + SetConsoleTextAttribute(hConsole, bi.wAttributes); +#else + if (alt) { + printf("\x1b[%im", ((int)col) + 100); // this should be Write + } else { + printf("\x1b[%im", ((int)col) + 40); // this too + } +#endif +} + +bool Console::SetEcho(bool echo) { + if (!IsTTY()) + return false; +#if __has_include() + struct termios raw; + if (tcgetattr(0, &raw) != 0) + return false; + if (echo) { + raw.c_lflag |= ECHO; + } else { + raw.c_lflag &= ~(ECHO); + } + + if (tcsetattr(0, TCSAFLUSH, &raw) != 0) + return false; + +#elif defined(_WIN32) + HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE); + if (hConsole == INVALID_HANDLE_VALUE) + return false; + DWORD dwMode; + + if (!GetConsoleMode(hConsole, &dwMode)) + return false; + if (echo) + dwMode |= ENABLE_ECHO_INPUT; + else + dwMode &= ~(ENABLE_ECHO_INPUT); + if (SetConsoleMode(hConsole, dwMode)) + return true; +#endif + return false; +} +bool Console::GetEcho() { + if (!IsTTY()) + return false; +#if __has_include() + + struct termios raw; + if (tcgetattr(0, &raw) != 0) + return true; + return (raw.c_lflag & ECHO) > 0; +#elif defined(_WIN32) + HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE); + if (hConsole == INVALID_HANDLE_VALUE) + return false; + DWORD dwMode; + + if (GetConsoleMode(hConsole, &dwMode)) { + return (dwMode & ENABLE_ECHO_INPUT) != 0; + } +#endif + + return true; +} +bool Console::GetCanonical() { + if (!IsTTY()) + return false; +#if __has_include() + struct termios raw; + if (tcgetattr(0, &raw) != 0) + return false; + return (raw.c_lflag & ICANON) > 0; +#elif defined(_WIN32) + HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE); + if (hConsole == INVALID_HANDLE_VALUE) + return false; + DWORD dwMode; + + if (GetConsoleMode(hConsole, &dwMode)) { + return (dwMode & ENABLE_LINE_INPUT) != 0; + } +#endif + return false; +} +bool Console::SetCanonical(bool can) { + if (!IsTTY()) + return false; +#if __has_include() + struct termios raw; + if (tcgetattr(0, &raw) != 0) + return false; + if (can) { + raw.c_lflag |= ICANON; + } else { + raw.c_lflag &= ~(ICANON); + } + + if (tcsetattr(0, TCSAFLUSH, &raw) != 0) + return false; + +#elif defined(_WIN32) + HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE); + if (hConsole == INVALID_HANDLE_VALUE) + return false; + DWORD dwMode; + + if (!GetConsoleMode(hConsole, &dwMode)) + return false; + if (can) + dwMode |= ENABLE_LINE_INPUT; + else + dwMode &= ~(ENABLE_LINE_INPUT); + if (SetConsoleMode(hConsole, dwMode)) + return true; +#endif + return false; +} + +bool Console::GetSignals() { + if (!IsTTY()) + return true; +#if __has_include() + struct termios raw; + if (tcgetattr(0, &raw) != 0) + return false; + return (raw.c_lflag & ISIG) > 0; + +#endif + return false; +} +bool Console::SetSignals(bool sig) { + if (!IsTTY()) + return false; +#if __has_include() + struct termios raw; + if (tcgetattr(0, &raw) != 0) + return false; + if (sig) { + raw.c_lflag |= ISIG; + } else { + raw.c_lflag &= ~(ISIG); + } + + if (tcsetattr(0, TCSAFLUSH, &raw) != 0) + return false; + +#endif + return false; +} + +// ISIG + +std::string Console::ReadPassword() { + bool echo = GetEcho(); + if (!SetEcho(false)) { + Write("\nWARN: the password will be visible: "); + } + std::string text = ReadLine(); + SetEcho(echo); + Write("\n"); + + return text; +} + +#if defined(_WIN32) +thread_local std::string key; +thread_local int keyOffset = -1; +#endif + +int Console::Read() { +#if defined(WIN32) + if (keyOffset >= 0 && keyOffset < key.size()) { + return (int)key[keyOffset++]; + } else { + keyOffset = -1; + } + if (!Console::GetCanonical()) { + + HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); + if (hIn == INVALID_HANDLE_VALUE) + return -1; + + DWORD nRead; + + INPUT_RECORD ir; + + while (ReadConsoleInputW(hIn, &ir, 1, &nRead)) { + if (nRead == 0) + return -1; + if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) { + if (ir.Event.KeyEvent.wVirtualKeyCode == VK_UP) { + key = {27, 91, 65}; + keyOffset = 1; + return key[0]; + } else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_DOWN) { + key = {27, 91, 66}; + keyOffset = 1; + return key[0]; + } else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_LEFT) { + key = {27, 91, 68}; + keyOffset = 1; + return key[0]; + } else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_RIGHT) { + key = {27, 91, 67}; + keyOffset = 1; + return key[0]; + } else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) { + return 27; + } else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_DELETE) { + key = {27, 91, 51, 126}; + keyOffset = 1; + return key[0]; + } else if (ir.Event.KeyEvent.wVirtualKeyCode == VK_RETURN) { + return 10; + } + + else if (ir.Event.KeyEvent.uChar.UnicodeChar != 0) { + if (ir.Event.KeyEvent.uChar.UnicodeChar <= 127) { + return (int)ir.Event.KeyEvent.uChar.UnicodeChar; + } else if (ir.Event.KeyEvent.uChar.UnicodeChar >= 0xD800 && + ir.Event.KeyEvent.uChar.UnicodeChar <= 0xDBFF) { + + std::u16string str = { + {(char16_t)ir.Event.KeyEvent.uChar.UnicodeChar, 0}}; + while (ReadConsoleInputW(hIn, &ir, 1, &nRead)) { + if (ir.EventType == KEY_EVENT && + ir.Event.KeyEvent.bKeyDown) { + + if (nRead == 0) + return -1; + if (ir.Event.KeyEvent.uChar.UnicodeChar >= + 0xDC00 && + ir.Event.KeyEvent.uChar.UnicodeChar <= + 0xDFFF) { + str[1] = + (char16_t) + ir.Event.KeyEvent.uChar.UnicodeChar; + break; + } else { + return -1; + } + } + } + + key.clear(); + keyOffset = 1; + Tesses::Framework::Text::StringConverter::UTF8:: + FromUTF16(key, str); + if (!key.empty()) + return key[0]; + return -1; + + } else if (ir.Event.KeyEvent.uChar.UnicodeChar > 127) { + // normal unicode char + key.clear(); + keyOffset = 1; + + std::u16string str = { + {(char16_t)ir.Event.KeyEvent.uChar.UnicodeChar}}; + + Tesses::Framework::Text::StringConverter::UTF8:: + FromUTF16(key, str); + + if (!key.empty()) + return key[0]; + return -1; + } + } + } + } + + return -1; + } else { + WCHAR chr; + DWORD chrRead; + HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); + if (hIn == INVALID_HANDLE_VALUE) + return -1; + if (ReadConsoleW(hIn, &chr, 1, &chrRead, NULL)) { + if (chrRead == 0) + return -1; + + if (chr <= 127) + return (int)chr; + else if (chr >= 0xD800 && chr <= 0xDBFF) { + std::u16string str = {{(char16_t)chr, 0}}; + + if (ReadConsoleW(hIn, &chr, 1, &chrRead, NULL)) { + if (chrRead == 0) + return -1; + if (chr >= 0xDC00 && chr <= 0xDFFF) { + str[1] = (char16_t)chr; + + } else { + return -1; + } + } + + key.clear(); + keyOffset = 1; + Tesses::Framework::Text::StringConverter::UTF8::FromUTF16(key, + str); + if (!key.empty()) + return key[0]; + return -1; + } else if (chr > 127) { + // normal unicode char + key.clear(); + keyOffset = 1; + + std::u16string str = {{(char16_t)chr}}; + + Tesses::Framework::Text::StringConverter::UTF8::FromUTF16(key, + str); + + if (!key.empty()) + return key[0]; + return -1; + } + } + return -1; + } +#else + int res = fgetc(stdin); + if (res == EOF) + return -1; // make sure it's -1 + return res; +#endif +} + +std::string Console::ReadLine() { + std::string text; + int c = Read(); + while (c != '\n' && c != -1) { + text += (char)c; + + c = Read(); + } + if (!text.empty() && text.back() == '\r') + text.resize(text.size() - 1); + return text; +} +#if defined(_WIN32) +void writeTextWin32(HANDLE hndl, const std::string &text) { + std::u16string str = {}; + + Text::StringConverter::UTF16::FromUTF8(str, text); + + auto buff = str.data(); + DWORD left = (DWORD)str.size(); + + DWORD read = 0; + do { + + if (WriteConsoleW(hndl, buff, left, &read, NULL)) { + buff += read; + left -= read; + } + } while (read > 0); +} +#else +void writeTextUnix(FILE *f, std::string_view view) { + size_t left = view.size(); + const char *ptr = view.data(); + size_t read = 0; + do { + read = fwrite(ptr, 1, left, f); + ptr += read; + left -= read; + } while (read != 0); +} +#endif +void Console::WriteToStream(std::string_view view, bool isStderr) { +#if defined(_WIN32) + HANDLE hOut = GetStdHandle(isStderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + return; + std::string text; + + for (size_t i = 0; i < view.size(); ++i) { + if (view[i] == '\x1b') { + if (!text.empty()) { + writeTextWin32(hOut, text); + // fwrite(text.c_str(), 1, text.size(), f); + text.clear(); + } + i++; + if (i < view.size()) { + if (view[i] == '[') { + i++; + for (; i < view.size(); ++i) { + text += view[i]; + if (view[i] >= 0x40 && view[i] <= 0x7e) + break; + } + if (text.size() > 0 && text.back() == 'H') { + MoveToHome(); + } + if (text.size() > 0 && text.back() == 'J') { + if (text[0] == '2') { + ClearRetainPosition( + ClearBehaviour::CB_ENTIRESCREEN); + } + + if (text[0] == '1') { + ClearRetainPosition( + ClearBehaviour::CB_CURSORANDABOVE); + } + if (text[0] == '0') { + ClearRetainPosition( + ClearBehaviour::CB_CURSORANDBELOW); + } + } + + if (text.size() > 0 && text.back() == 'm') { + + try { + auto num = + std::stol(text.substr(0, text.size() - 1)); + if (num >= 30 && num <= 37) { + SetForegroundColor((ConsoleColor)(num - 30), + false); + } else if (num >= 40 && num <= 47) { + SetBackgroundColor((ConsoleColor)(num - 40), + false); + } else if (num >= 90 && num <= 97) { + SetForegroundColor((ConsoleColor)(num - 90), + true); + } else if (num >= 100 && num <= 107) { + SetBackgroundColor((ConsoleColor)(num - 100), + true); + } + } catch (...) { + } + } + + text.clear(); + + } else { + text += "\x1B"; + text += view[i]; + } + } + + } else if (view[i] == '\n') { + text += "\r\n"; + + writeTextWin32(hOut, text); + text.clear(); + } else { + text += view[i]; + } + } + + if (!text.empty()) { + + writeTextWin32(hOut, text); + text.clear(); + } + +#else + writeTextUnix(isStderr ? stderr : stdout, view); +#endif +} + +void Console::Write(std::string text) { WriteToStream(text, false); } +void Console::WriteLine(std::string text) { + Write(text); + Write("\n"); +} + +void Console::WriteLineView(std::string_view view) { + std::string lf = "\n"; + WriteToStream(view, false); + WriteToStream(lf, false); +} +void Console::WriteView(std::string_view view) { WriteToStream(view, false); } +void Console::Error(std::string text) { WriteToStream(text, true); } +void Console::ErrorLine(std::string text) { + Error(text); + Error("\n"); +} + +void Console::ErrorLineView(std::string_view view) { + std::string lf = "\n"; + WriteToStream(view, true); + WriteToStream(lf, true); +} +void Console::ErrorView(std::string_view view) { WriteToStream(view, true); } +bool Console::IsTTY() { +#if defined(_WIN32) + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + return false; + DWORD tmp; + if (GetConsoleMode(hOut, &tmp)) + return true; +#elif __has_include() + if (isatty(STDOUT_FILENO)) + return true; +#endif + + return false; +} + +void Console::ProgressBar(double pdbl) { + bool showBar = IsTTY(); + if (pdbl < 0) + pdbl = 0; + if (pdbl > 1) + pdbl = 1; + + WriteView("\r"); + if (showBar) { + auto sz = GetSize(); + + int totalBlocks = sz.first - 10; + if (totalBlocks > 0) { + WriteView("[\033[0;32m"); + int i; + int off = pdbl * totalBlocks; + for (int i = 0; i < totalBlocks; i++) { + if (i < off) + WriteView("="); + else + WriteView(" "); + } + + WriteView("\033[0m] "); + } + } + + std::string mesg = Http::HttpUtils::LeftPad( + std::to_string((int)(pdbl * 100)) + "%", 4, ' '); + WriteView(mesg); + Flush(); +} +void Console::ProgressBar(int v) { ProgressBar((double)v / 100.0); } + +std::pair Console::GetSize() { + if (!Console::IsTTY()) + return std::pair(0, 0); + +#if __has_include() + struct winsize ws; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) { + return std::make_pair(ws.ws_col, ws.ws_row); + } +#elif defined(_WIN32) + + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + if (hConsole == INVALID_HANDLE_VALUE) + return std::make_pair(0, 0); + CONSOLE_SCREEN_BUFFER_INFO bi; + + GetConsoleScreenBufferInfo(hConsole, &bi); + + return std::make_pair(bi.srWindow.Right - bi.srWindow.Left + 1, + bi.srWindow.Bottom - bi.srWindow.Top + 1); +#endif + + return std::make_pair(0, 0); +} + +void Console::SetPosition(int x, int y) { + if (!IsTTY()) + return; +#if __has_include() + printf("\x1B[%i;%iH", y + 1, x + 1); +#elif defined(_WIN32) + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + return; + COORD coord = {(SHORT)x, (SHORT)y}; + SetConsoleCursorPosition(hOut, coord); +#else + printf("\x1B[%i;%iH", y + 1, x + 1); + Flush(); +#endif +} + +void Console::Flush() { fflush(stdout); } + +void Console::Clear() { + ClearRetainPosition(ClearBehaviour::CB_ENTIRESCREEN); + MoveToHome(); +} + +void Console::ClearRetainPosition(ClearBehaviour cb) { +#if __has_include() + if (isatty(STDOUT_FILENO)) { + printf("\x1b[%iJ", (int)cb); + Flush(); + } +#elif defined(_WIN32) + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + return; + CONSOLE_SCREEN_BUFFER_INFO bi; + + GetConsoleScreenBufferInfo(hOut, &bi); + + switch (cb) { + case ClearBehaviour::CB_CURSORANDABOVE: { + COORD home = {0, 0}; + + DWORD cells = + ((bi.dwCursorPosition.Y) * bi.dwSize.X) + bi.dwCursorPosition.X + 1; + DWORD written; + + FillConsoleOutputCharacter(hOut, ' ', cells, home, &written); + FillConsoleOutputAttribute(hOut, bi.wAttributes, cells, home, &written); + + } break; + case ClearBehaviour::CB_CURSORANDBELOW: { + if (bi.dwCursorPosition.X == 0) { + COORD home = bi.dwCursorPosition; + DWORD cells = bi.dwSize.X * (bi.dwSize.Y - bi.dwCursorPosition.Y); + DWORD written; + + FillConsoleOutputCharacter(hOut, ' ', cells, home, &written); + FillConsoleOutputAttribute(hOut, bi.wAttributes, cells, home, + &written); + } else { + COORD home = bi.dwCursorPosition; + DWORD cells = bi.dwSize.X - home.X; + DWORD written; + + FillConsoleOutputCharacter(hOut, ' ', cells, home, &written); + FillConsoleOutputAttribute(hOut, bi.wAttributes, cells, home, + &written); + + if (home.Y + 1 < bi.dwSize.Y) { + home = {0, (SHORT)(bi.dwCursorPosition.Y + 1)}; + cells = bi.dwSize.X * (bi.dwSize.Y - home.Y); + + FillConsoleOutputCharacter(hOut, ' ', cells, home, &written); + FillConsoleOutputAttribute(hOut, bi.wAttributes, cells, home, + &written); + } + } + } break; + case ClearBehaviour::CB_ENTIRESCREEN: { + COORD home = {0, 0}; + + DWORD cells = bi.dwSize.X * bi.dwSize.Y; + DWORD written; + + FillConsoleOutputCharacter(hOut, ' ', cells, home, &written); + FillConsoleOutputAttribute(hOut, bi.wAttributes, cells, home, &written); + } break; + } + +#else + printf("\x1b[%iJ", (int)cb); + Flush(); +#endif +} +void Console::MoveToHome() { +#if __has_include() + if (isatty(STDOUT_FILENO)) { + Write("\x1b[H"); + Flush(); + } +#elif defined(_WIN32) + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + return; + SetConsoleCursorPosition(hOut, {0, 0}); +#else + Write("\x1b[H"); + Flush(); +#endif +} +} // namespace Tesses::Framework \ No newline at end of file diff --git a/src/Crypto/MbedTLS/ClientTLSStream.cpp b/src/Crypto/MbedTLS/ClientTLSStream.cpp index b486f3e..862e4c3 100644 --- a/src/Crypto/MbedTLS/ClientTLSStream.cpp +++ b/src/Crypto/MbedTLS/ClientTLSStream.cpp @@ -1,3 +1,21 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Crypto/ClientTLSStream.hpp" #if defined(TESSESFRAMEWORK_ENABLE_MBED) @@ -8,241 +26,220 @@ using StreamReader = Tesses::Framework::TextStreams::StreamReader; #endif -#include #include -#include -#include -#include +#include #include +#include +#include +#include #endif #include using namespace Tesses::Framework::Streams; +namespace Tesses::Framework::Crypto { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) +class ClientTLSPrivateData { + public: + bool eos; + bool success; + std::shared_ptr strm; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt cachain; + ~ClientTLSPrivateData() { + mbedtls_x509_crt_free(&cachain); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + mbedtls_ssl_config_free(&conf); + mbedtls_ssl_free(&ssl); + } +}; +static int strm_send(void *ctx, const unsigned char *buf, size_t len) { + auto priv = static_cast(ctx); + return (int)priv->strm->Write(buf, len); +} +static int strm_recv(void *ctx, unsigned char *buf, size_t len) { + auto priv = static_cast(ctx); + return (int)priv->strm->Read(buf, len); +} +#endif -namespace Tesses::Framework::Crypto -{ - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - class ClientTLSPrivateData { - public: - bool eos; - bool success; - std::shared_ptr strm; - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ssl_context ssl; - mbedtls_ssl_config conf; - mbedtls_x509_crt cachain; - ~ClientTLSPrivateData() - { - mbedtls_x509_crt_free(&cachain); - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - mbedtls_ssl_config_free(&conf); - mbedtls_ssl_free(&ssl); - } - }; - static int strm_send(void* ctx,const unsigned char* buf,size_t len) - { - auto priv = static_cast(ctx); - return (int)priv->strm->Write(buf, len); - - } - static int strm_recv(void* ctx,unsigned char* buf,size_t len) - { - auto priv = static_cast(ctx); - return (int)priv->strm->Read(buf, len); - - } - #endif - - std::string ClientTLSStream::GetCertChain() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - #if defined(TESSESFRAMEWORK_EMBED_CERT_BUNDLE) - return std::string((const char*)CERTIFICATECHAIN,CERTIFICATECHAIN_SIZE); - #else - #if defined(TESSESFRAMEWORK_CERT_BUNDLE_FILE) - StreamReader sr(TESSESFRAMEWORK_CERT_BUNDLE_FILE); - return sr.ReadToEnd(); - #endif - #endif - #endif - return ""; +std::string ClientTLSStream::GetCertChain() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) +#if defined(TESSESFRAMEWORK_EMBED_CERT_BUNDLE) + return std::string((const char *)CERTIFICATECHAIN, CERTIFICATECHAIN_SIZE); +#else +#if defined(TESSESFRAMEWORK_CERT_BUNDLE_FILE) + StreamReader sr(TESSESFRAMEWORK_CERT_BUNDLE_FILE); + return sr.ReadToEnd(); +#endif +#endif +#endif + return ""; +} + +ClientTLSStream::ClientTLSStream( + std::shared_ptr innerStream, + bool verify, std::string domain) + : ClientTLSStream(innerStream, verify, domain, "") {} + +ClientTLSStream::ClientTLSStream( + std::shared_ptr innerStream, + bool verify, std::string domain, std::string cert) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + if (cert.empty()) { + cert = GetCertChain(); } - ClientTLSStream::ClientTLSStream(std::shared_ptr innerStream, bool verify, std::string domain) : ClientTLSStream(innerStream,verify,domain,"") - { - + ClientTLSPrivateData *data = new ClientTLSPrivateData(); + this->privateData = static_cast(data); + data->eos = false; + data->success = false; + data->strm = innerStream; + + mbedtls_ssl_init(&data->ssl); + mbedtls_ssl_config_init(&data->conf); + mbedtls_x509_crt_init(&data->cachain); + mbedtls_ctr_drbg_init(&data->ctr_drbg); + mbedtls_entropy_init(&data->entropy); + + const char *pers = "TessesFramework"; + + int ret = 0; + + if ((ret = mbedtls_ctr_drbg_seed( + &data->ctr_drbg, mbedtls_entropy_func, &data->entropy, + (const unsigned char *)pers, strlen(pers))) != 0) { + printf("FAILED mbedtls_ctr_drbg_seed\n"); + return; } - ClientTLSStream::ClientTLSStream(std::shared_ptr innerStream, bool verify, std::string domain, std::string cert) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - if(cert.empty()) - { - cert = GetCertChain(); - } + if (ret != 0) { + printf("FAILED mbedtls_x509_crt_parse cert %i\n", ret); + return; + } + ret = mbedtls_x509_crt_parse( + &data->cachain, (const unsigned char *)cert.c_str(), cert.size() + 1); - ClientTLSPrivateData* data = new ClientTLSPrivateData(); - this->privateData = static_cast(data); - data->eos=false; - data->success=false; - data->strm = innerStream; - + if (ret != 0) { + printf("FAILED mbedtls_x509_crt_parse chain %i\n", ret); + return; + } - mbedtls_ssl_init(&data->ssl); - mbedtls_ssl_config_init(&data->conf); - mbedtls_x509_crt_init(&data->cachain); - mbedtls_ctr_drbg_init(&data->ctr_drbg); - mbedtls_entropy_init(&data->entropy); - - const char* pers = "TessesFramework"; - - int ret=0; - - - - if ((ret = mbedtls_ctr_drbg_seed(&data->ctr_drbg, mbedtls_entropy_func, &data->entropy, - (const unsigned char *) pers, - strlen(pers))) != 0) - { - printf("FAILED mbedtls_ctr_drbg_seed\n"); - return; - } - - if(ret != 0) { printf("FAILED mbedtls_x509_crt_parse cert %i\n",ret); return;} - ret = mbedtls_x509_crt_parse(&data->cachain, (const unsigned char *) cert.c_str(), - cert.size()+1); - - if(ret != 0) {printf("FAILED mbedtls_x509_crt_parse chain %i\n",ret); return;} - - - - if((ret = mbedtls_ssl_config_defaults(&data->conf, - MBEDTLS_SSL_IS_CLIENT, + if ((ret = mbedtls_ssl_config_defaults(&data->conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT)) != 0) - { - char buffer[100]; - mbedtls_strerror(ret,buffer,sizeof(buffer)); - printf("FAILED mbedtls_ssl_conf_defaults %s\n",buffer); - return; - } + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + char buffer[100]; + mbedtls_strerror(ret, buffer, sizeof(buffer)); + printf("FAILED mbedtls_ssl_conf_defaults %s\n", buffer); + return; + } - - - - mbedtls_ssl_conf_rng(&data->conf, mbedtls_ctr_drbg_random, &data->ctr_drbg); + mbedtls_ssl_conf_rng(&data->conf, mbedtls_ctr_drbg_random, &data->ctr_drbg); - /* #if defined(MBEDTLS_SSL_CACHE_C) - mbedtls_ssl_conf_session_cache(&conf, &cache, - mbedtls_ssl_cache_get, - mbedtls_ssl_cache_set); + /* #if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_conf_session_cache(&conf, &cache, + mbedtls_ssl_cache_get, + mbedtls_ssl_cache_set); #endif*/ - mbedtls_ssl_conf_authmode(&data->conf, verify ? MBEDTLS_SSL_VERIFY_REQUIRED: MBEDTLS_SSL_VERIFY_NONE); - mbedtls_ssl_conf_ca_chain(&data->conf, &data->cachain, NULL); + mbedtls_ssl_conf_authmode(&data->conf, verify ? MBEDTLS_SSL_VERIFY_REQUIRED + : MBEDTLS_SSL_VERIFY_NONE); + mbedtls_ssl_conf_ca_chain(&data->conf, &data->cachain, NULL); - - mbedtls_ssl_set_bio(&data->ssl, static_cast(data),strm_send,strm_recv, NULL); - if((ret=mbedtls_ssl_setup(&data->ssl,&data->conf) != 0)) - { - printf("FAILED mbedtls_ssl_setup %i\n",ret); - return; - } - if((ret=mbedtls_ssl_set_hostname(&data->ssl,domain.c_str()) != 0)) - { - printf("FAILED mbedtls_ssl_set_hostname %i\n",ret); - return; - } - if((ret = mbedtls_ssl_handshake(&data->ssl)) != 0) - { - char buffer[100]; - mbedtls_strerror(ret,buffer,sizeof(buffer)); - printf("FAILED mbedtls_ssl_handshake %s\n",buffer); - return; - } - uint32_t flags; - if ((flags = mbedtls_ssl_get_verify_result(&data->ssl)) != 0) { + mbedtls_ssl_set_bio(&data->ssl, static_cast(data), strm_send, + strm_recv, NULL); + if ((ret = mbedtls_ssl_setup(&data->ssl, &data->conf) != 0)) { + printf("FAILED mbedtls_ssl_setup %i\n", ret); + return; + } + if ((ret = mbedtls_ssl_set_hostname(&data->ssl, domain.c_str()) != 0)) { + printf("FAILED mbedtls_ssl_set_hostname %i\n", ret); + return; + } + if ((ret = mbedtls_ssl_handshake(&data->ssl)) != 0) { + char buffer[100]; + mbedtls_strerror(ret, buffer, sizeof(buffer)); + printf("FAILED mbedtls_ssl_handshake %s\n", buffer); + return; + } + uint32_t flags; + if ((flags = mbedtls_ssl_get_verify_result(&data->ssl)) != 0) { #if !defined(MBEDTLS_X509_REMOVE_INFO) char vrfy_buf[512]; #endif - - #if !defined(MBEDTLS_X509_REMOVE_INFO) mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", flags); - #endif - if(verify) - return; + if (verify) + return; } - data->success=true; + data->success = true; - #endif - } - size_t ClientTLSStream::Read(uint8_t* buffer, size_t len) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto priv = static_cast(this->privateData); - if(!priv->success) return 0; - if(priv->eos) return 0; - int r = mbedtls_ssl_read(&priv->ssl,buffer,len); - +#endif +} +size_t ClientTLSStream::Read(uint8_t *buffer, size_t len) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto priv = static_cast(this->privateData); + if (!priv->success) + return 0; + if (priv->eos) + return 0; + int r = mbedtls_ssl_read(&priv->ssl, buffer, len); + if (r == -30848) { + priv->eos = true; + return 0; + } + return (size_t)r; +#else + return (size_t)0; +#endif +} +size_t ClientTLSStream::Write(const uint8_t *buffer, size_t len) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto priv = static_cast(this->privateData); + if (!priv->success) + return 0; + int r = mbedtls_ssl_write(&priv->ssl, buffer, len); + return (size_t)r; +#else + return (size_t)0; +#endif +} - if(r == -30848) - { - priv->eos = true; - return 0; - } - return (size_t)r; - #else - return (size_t)0; - #endif - } - size_t ClientTLSStream::Write(const uint8_t* buffer, size_t len) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto priv = static_cast(this->privateData); - if(!priv->success) return 0; - int r = mbedtls_ssl_write(&priv->ssl,buffer,len); - return (size_t)r; - #else - return (size_t)0; - #endif - } - - bool ClientTLSStream::CanRead() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - return !(!static_cast(this->privateData)->success || static_cast(this->privateData)->eos); - #else - return false; - #endif - } - bool ClientTLSStream::CanWrite() - { - - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - return !(!static_cast(this->privateData)->success || static_cast(this->privateData)->eos); - #else - return false; - #endif - } - bool ClientTLSStream::EndOfStream() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - return !static_cast(this->privateData)->success || static_cast(this->privateData)->eos; - #else - return true; - #endif - } - ClientTLSStream::~ClientTLSStream() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - delete static_cast(this->privateData); - #endif - } -} \ No newline at end of file +bool ClientTLSStream::CanRead() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + return !(!static_cast(this->privateData)->success || + static_cast(this->privateData)->eos); +#else + return false; +#endif +} +bool ClientTLSStream::CanWrite() { + +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + return !(!static_cast(this->privateData)->success || + static_cast(this->privateData)->eos); +#else + return false; +#endif +} +bool ClientTLSStream::EndOfStream() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + return !static_cast(this->privateData)->success || + static_cast(this->privateData)->eos; +#else + return true; +#endif +} +ClientTLSStream::~ClientTLSStream() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + delete static_cast(this->privateData); +#endif +} +} // namespace Tesses::Framework::Crypto \ No newline at end of file diff --git a/src/Crypto/MbedTLS/Crypto.cpp b/src/Crypto/MbedTLS/Crypto.cpp index aa52a35..fc8ea85 100644 --- a/src/Crypto/MbedTLS/Crypto.cpp +++ b/src/Crypto/MbedTLS/Crypto.cpp @@ -1,375 +1,377 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Crypto/Crypto.hpp" #if defined(TESSESFRAMEWORK_ENABLE_MBED) +#include #include #include #include -#include -#include -#include #include +#include +#include #endif #include -namespace Tesses::Framework::Crypto -{ - bool HaveCrypto() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - return true; - #else - return false; - #endif - } - std::string Base64_Encode(std::vector data) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - std::string str={}; - size_t olen=0; - mbedtls_base64_encode((uint8_t*)str.data(), 0, &olen, data.data(),data.size()); - str.resize(olen-1); - - - if(mbedtls_base64_encode((uint8_t*)str.data(), olen, &olen, data.data(),data.size())==0) - { - return str; - } - - #endif - return ""; - } - std::vector Base64_Decode(std::string str) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - size_t olen=0; - - std::vector data; - - mbedtls_base64_decode(data.data(), 0, &olen, (const uint8_t*)str.data(),str.size()); - - data.resize(olen); - - - if(mbedtls_base64_decode(data.data(), olen, &olen, (const uint8_t*)str.data(),str.size())==0) - { - return data; - } - #endif - return {}; - } - Sha1::Sha1() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - mbedtls_sha1_context* ctx = new mbedtls_sha1_context(); - this->inner = ctx; - mbedtls_sha1_init(ctx); - - #endif - } - bool Sha1::Start() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - mbedtls_sha1_starts(ctx); - return true; - #endif - return false; - } - bool Sha1::Update(const uint8_t* buffer, size_t sz) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - mbedtls_sha1_update(ctx,buffer,sz); - return true; - #endif - return false; - } - bool Sha1::Update(std::shared_ptr strm) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - if(strm == nullptr) return false; - uint8_t buffer[1024]; - size_t read; - do { - read = strm->Read(buffer,sizeof(buffer)); - if(!Update(buffer,read)) return false; - } while(read != 0); - return true; - #endif - return false; - } - - std::vector Sha1::Finish() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - std::vector hash; - hash.resize(20); - mbedtls_sha1_finish(ctx,hash.data()); - return hash; - #endif - return {}; - } - Sha1::~Sha1() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - mbedtls_sha1_free(ctx); - delete ctx; - #endif - } - std::vector Sha1::ComputeHash(const uint8_t* buffer, size_t len) - { - Sha1 sha1; - if(!sha1.Start()) return {}; - if(!sha1.Update(buffer,len)) return {}; - return sha1.Finish(); - } - std::vector Sha1::ComputeHash(std::shared_ptr strm) - { - Sha1 sha1; - if(!sha1.Start()) return {}; - if(!sha1.Update(strm)) return {}; - return sha1.Finish(); - } - - - Sha256::Sha256() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - mbedtls_sha256_context* ctx = new mbedtls_sha256_context(); - this->inner = ctx; - mbedtls_sha256_init(ctx); - - #endif - } - bool Sha256::Start(bool is224) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - this->is224=is224; - mbedtls_sha256_starts(ctx,is224); - return true; - #endif - return false; - } - bool Sha256::Is224() - { - return this->is224; - } - bool Sha256::Update(const uint8_t* buffer, size_t sz) - { - - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - mbedtls_sha256_update(ctx,buffer,sz); - return true; - #endif - return false; - } - bool Sha256::Update(std::shared_ptr strm) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - if(strm == nullptr) return false; - uint8_t buffer[1024]; - size_t read; - do { - read = strm->Read(buffer,sizeof(buffer)); - if(!Update(buffer,read)) return false; - } while(read != 0); - return true; - #endif - return false; - } - - std::vector Sha256::Finish() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - std::vector hash; - hash.resize(32); - mbedtls_sha256_finish(ctx,hash.data()); - return hash; - #endif - return {}; - } - Sha256::~Sha256() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - mbedtls_sha256_free(ctx); - delete ctx; - #endif - } - std::vector Sha256::ComputeHash(const uint8_t* buffer, size_t len,bool is224) - { - Sha256 sha256; - if(!sha256.Start(is224)) return {}; - if(!sha256.Update(buffer,len)) return {}; - return sha256.Finish(); - } - std::vector Sha256::ComputeHash(std::shared_ptr strm,bool is224) - { - Sha256 sha256; - if(!sha256.Start(is224)) return {}; - if(!sha256.Update(strm)) return {}; - return sha256.Finish(); - } - - - Sha512::Sha512() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - mbedtls_sha512_context* ctx = new mbedtls_sha512_context(); - this->inner = ctx; - mbedtls_sha512_init(ctx); - #endif - } - bool Sha512::Start(bool is384) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - this->is384=is384; - mbedtls_sha512_starts(ctx,is384); - return true; - #endif - return false; - } - bool Sha512::Is384() - { - return this->is384; - } - bool Sha512::Update(const uint8_t* buffer, size_t sz) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - mbedtls_sha512_update(ctx,buffer,sz); - return true; - #endif - return false; - } - bool Sha512::Update(std::shared_ptr strm) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - if(strm == nullptr) return false; - uint8_t buffer[1024]; - size_t read; - do { - read = strm->Read(buffer,sizeof(buffer)); - if(!Update(buffer,read)) return false; - } while(read != 0); - return true; - #endif - return false; - } - - std::vector Sha512::Finish() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - std::vector hash; - hash.resize(64); - mbedtls_sha512_finish(ctx,hash.data()); - return hash; - #endif - return {}; - } - Sha512::~Sha512() - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - auto ctx = static_cast(this->inner); - mbedtls_sha512_free(ctx); - delete ctx; - #endif - } - - std::vector Sha512::ComputeHash(const uint8_t* buffer, size_t len,bool is384) - { - Sha512 sha512; - if(!sha512.Start(is384)) return {}; - if(!sha512.Update(buffer,len)) return {}; - return sha512.Finish(); - } - std::vector Sha512::ComputeHash(std::shared_ptr strm,bool is384) - { - Sha512 sha512; - if(!sha512.Start(is384)) return {}; - if(!sha512.Update(strm)) return {}; - return sha512.Finish(); - } - - - - bool PBKDF2(std::vector& output,std::string pass, std::vector& salt, long itterations, ShaVersion version) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - - mbedtls_md_context_t ctx; - mbedtls_md_init(&ctx); - const mbedtls_md_info_t* info = NULL; - switch(version) - { - case ShaVersion::VERSION_SHA1: - info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); - break; - case ShaVersion::VERSION_SHA224: - info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA224); - break; - case ShaVersion::VERSION_SHA256: - info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); - break; - default: - case ShaVersion::VERSION_SHA384: - info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384); - break; - case ShaVersion::VERSION_SHA512: - info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); - break; - } - - - mbedtls_md_setup(&ctx, info, 1); - - - - if(mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char*)pass.c_str(), pass.size(), salt.data(), salt.size(), (uint32_t)itterations,(uint32_t)output.size(),output.data()) == 0) - { - mbedtls_md_free(&ctx); - return true; - } - mbedtls_md_free(&ctx); - return false; - #else - return false; - #endif - } - bool RandomBytes(std::vector& output, std::string personal_str) - { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - mbedtls_entropy_context entropy={0}; - mbedtls_ctr_drbg_context ctr_drbg={0}; - - mbedtls_entropy_init(&entropy); - mbedtls_ctr_drbg_init(&ctr_drbg); - - int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) personal_str.c_str(), personal_str.size()); - if(ret != 0) - { - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - return false; - } - ret = mbedtls_ctr_drbg_random(&ctr_drbg, output.data(),output.size()); - if (ret != 0) - { - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - return false; - } - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - return true; - #endif - return false; - } +namespace Tesses::Framework::Crypto { +bool HaveCrypto() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + return true; +#else + return false; +#endif } +std::string Base64_Encode(std::vector data) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + std::string str = {}; + size_t olen = 0; + mbedtls_base64_encode((uint8_t *)str.data(), 0, &olen, data.data(), + data.size()); + str.resize(olen - 1); + + if (mbedtls_base64_encode((uint8_t *)str.data(), olen, &olen, data.data(), + data.size()) == 0) { + return str; + } + +#endif + return ""; +} +std::vector Base64_Decode(std::string str) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + size_t olen = 0; + + std::vector data; + + mbedtls_base64_decode(data.data(), 0, &olen, (const uint8_t *)str.data(), + str.size()); + + data.resize(olen); + + if (mbedtls_base64_decode(data.data(), olen, &olen, + (const uint8_t *)str.data(), str.size()) == 0) { + return data; + } +#endif + return {}; +} +Sha1::Sha1() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + mbedtls_sha1_context *ctx = new mbedtls_sha1_context(); + this->inner = ctx; + mbedtls_sha1_init(ctx); + +#endif +} +bool Sha1::Start() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + mbedtls_sha1_starts(ctx); + return true; +#endif + return false; +} +bool Sha1::Update(const uint8_t *buffer, size_t sz) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + mbedtls_sha1_update(ctx, buffer, sz); + return true; +#endif + return false; +} +bool Sha1::Update(std::shared_ptr strm) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + if (strm == nullptr) + return false; + uint8_t buffer[1024]; + size_t read; + do { + read = strm->Read(buffer, sizeof(buffer)); + if (!Update(buffer, read)) + return false; + } while (read != 0); + return true; +#endif + return false; +} + +std::vector Sha1::Finish() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + std::vector hash; + hash.resize(20); + mbedtls_sha1_finish(ctx, hash.data()); + return hash; +#endif + return {}; +} +Sha1::~Sha1() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + mbedtls_sha1_free(ctx); + delete ctx; +#endif +} +std::vector Sha1::ComputeHash(const uint8_t *buffer, size_t len) { + Sha1 sha1; + if (!sha1.Start()) + return {}; + if (!sha1.Update(buffer, len)) + return {}; + return sha1.Finish(); +} +std::vector +Sha1::ComputeHash(std::shared_ptr strm) { + Sha1 sha1; + if (!sha1.Start()) + return {}; + if (!sha1.Update(strm)) + return {}; + return sha1.Finish(); +} + +Sha256::Sha256() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + mbedtls_sha256_context *ctx = new mbedtls_sha256_context(); + this->inner = ctx; + mbedtls_sha256_init(ctx); + +#endif +} +bool Sha256::Start(bool is224) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + this->is224 = is224; + mbedtls_sha256_starts(ctx, is224); + return true; +#endif + return false; +} +bool Sha256::Is224() { return this->is224; } +bool Sha256::Update(const uint8_t *buffer, size_t sz) { + +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + mbedtls_sha256_update(ctx, buffer, sz); + return true; +#endif + return false; +} +bool Sha256::Update(std::shared_ptr strm) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + if (strm == nullptr) + return false; + uint8_t buffer[1024]; + size_t read; + do { + read = strm->Read(buffer, sizeof(buffer)); + if (!Update(buffer, read)) + return false; + } while (read != 0); + return true; +#endif + return false; +} + +std::vector Sha256::Finish() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + std::vector hash; + hash.resize(32); + mbedtls_sha256_finish(ctx, hash.data()); + return hash; +#endif + return {}; +} +Sha256::~Sha256() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + mbedtls_sha256_free(ctx); + delete ctx; +#endif +} +std::vector Sha256::ComputeHash(const uint8_t *buffer, size_t len, + bool is224) { + Sha256 sha256; + if (!sha256.Start(is224)) + return {}; + if (!sha256.Update(buffer, len)) + return {}; + return sha256.Finish(); +} +std::vector +Sha256::ComputeHash(std::shared_ptr strm, + bool is224) { + Sha256 sha256; + if (!sha256.Start(is224)) + return {}; + if (!sha256.Update(strm)) + return {}; + return sha256.Finish(); +} + +Sha512::Sha512() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + mbedtls_sha512_context *ctx = new mbedtls_sha512_context(); + this->inner = ctx; + mbedtls_sha512_init(ctx); +#endif +} +bool Sha512::Start(bool is384) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + this->is384 = is384; + mbedtls_sha512_starts(ctx, is384); + return true; +#endif + return false; +} +bool Sha512::Is384() { return this->is384; } +bool Sha512::Update(const uint8_t *buffer, size_t sz) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + mbedtls_sha512_update(ctx, buffer, sz); + return true; +#endif + return false; +} +bool Sha512::Update(std::shared_ptr strm) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + if (strm == nullptr) + return false; + uint8_t buffer[1024]; + size_t read; + do { + read = strm->Read(buffer, sizeof(buffer)); + if (!Update(buffer, read)) + return false; + } while (read != 0); + return true; +#endif + return false; +} + +std::vector Sha512::Finish() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + std::vector hash; + hash.resize(64); + mbedtls_sha512_finish(ctx, hash.data()); + return hash; +#endif + return {}; +} +Sha512::~Sha512() { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + auto ctx = static_cast(this->inner); + mbedtls_sha512_free(ctx); + delete ctx; +#endif +} + +std::vector Sha512::ComputeHash(const uint8_t *buffer, size_t len, + bool is384) { + Sha512 sha512; + if (!sha512.Start(is384)) + return {}; + if (!sha512.Update(buffer, len)) + return {}; + return sha512.Finish(); +} +std::vector +Sha512::ComputeHash(std::shared_ptr strm, + bool is384) { + Sha512 sha512; + if (!sha512.Start(is384)) + return {}; + if (!sha512.Update(strm)) + return {}; + return sha512.Finish(); +} + +bool PBKDF2(std::vector &output, std::string pass, + std::vector &salt, long itterations, ShaVersion version) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + + mbedtls_md_context_t ctx; + mbedtls_md_init(&ctx); + const mbedtls_md_info_t *info = NULL; + switch (version) { + case ShaVersion::VERSION_SHA1: + info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); + break; + case ShaVersion::VERSION_SHA224: + info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA224); + break; + case ShaVersion::VERSION_SHA256: + info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + break; + default: + case ShaVersion::VERSION_SHA384: + info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384); + break; + case ShaVersion::VERSION_SHA512: + info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); + break; + } + + mbedtls_md_setup(&ctx, info, 1); + + if (mbedtls_pkcs5_pbkdf2_hmac( + &ctx, (const unsigned char *)pass.c_str(), pass.size(), salt.data(), + salt.size(), (uint32_t)itterations, (uint32_t)output.size(), + output.data()) == 0) { + mbedtls_md_free(&ctx); + return true; + } + mbedtls_md_free(&ctx); + return false; +#else + return false; +#endif +} +bool RandomBytes(std::vector &output, std::string personal_str) { +#if defined(TESSESFRAMEWORK_ENABLE_MBED) + mbedtls_entropy_context entropy = {0}; + mbedtls_ctr_drbg_context ctr_drbg = {0}; + + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + + int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *)personal_str.c_str(), + personal_str.size()); + if (ret != 0) { + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + return false; + } + ret = mbedtls_ctr_drbg_random(&ctr_drbg, output.data(), output.size()); + if (ret != 0) { + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + return false; + } + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + return true; +#endif + return false; +} +} // namespace Tesses::Framework::Crypto diff --git a/src/Date/Date.cpp b/src/Date/Date.cpp index 72babd1..9a85855 100644 --- a/src/Date/Date.cpp +++ b/src/Date/Date.cpp @@ -1,6 +1,24 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Date/Date.hpp" -#include "TessesFramework/Http/HttpUtils.hpp" #include "../HowardHinnant_date/date.h" +#include "TessesFramework/Http/HttpUtils.hpp" #if defined(__FreeBSD__) #include #endif @@ -9,934 +27,772 @@ #include using namespace std::chrono; using namespace date; -namespace Tesses::Framework::Date -{ - int GetTimeZone() - { - +namespace Tesses::Framework::Date { +int GetTimeZone() { - #if defined(__SWITCH__) || defined(_WIN32) || defined(GEKKO) || defined(__PS2__) - return (int)(-_timezone); - #elif defined(__FreeBSD__) - struct timeval tv; - struct timezone tz; - gettimeofday(&tv,&tz); - return (tz.tz_minuteswest/60); - #else - return (int)(-timezone); - #endif - } - bool TimeZoneSupportDST() - { - #if defined(__SWITCH__) || defined(_WIN32) || defined(GEKKO) || defined(__PS2__) - return _daylight == 1; - #elif defined(__FreeBSD__) - struct timeval tv; - struct timezone tz; - gettimeofday(&tv,&tz); - return tz.tz_dsttime!=0; - #else - return daylight == 1; - #endif - } - DateTime::DateTime() - { - - } - static int64_t div_floor(int64_t a, int64_t b) - { - int64_t v = a / b; - if(a < 0 && (a % b) != 0) v--; - return v; - } - void DateTime::FromEpochNoConvert(int64_t gmt) - { - - auto epoch = date::sys_days{date::January/1/1970}; - epoch += date::days(div_floor(gmt,86400)); +#if defined(__SWITCH__) || defined(_WIN32) || defined(GEKKO) || defined(__PS2__) + return (int)(-_timezone); +#elif defined(__FreeBSD__) + struct timeval tv; + struct timezone tz; + gettimeofday(&tv, &tz); + return (tz.tz_minuteswest / 60); +#else + return (int)(-timezone); +#endif +} +bool TimeZoneSupportDST() { +#if defined(__SWITCH__) || defined(_WIN32) || defined(GEKKO) || defined(__PS2__) + return _daylight == 1; +#elif defined(__FreeBSD__) + struct timeval tv; + struct timezone tz; + gettimeofday(&tv, &tz); + return tz.tz_dsttime != 0; +#else + return daylight == 1; +#endif +} +DateTime::DateTime() {} +static int64_t div_floor(int64_t a, int64_t b) { + int64_t v = a / b; + if (a < 0 && (a % b) != 0) + v--; + return v; +} +void DateTime::FromEpochNoConvert(int64_t gmt) { + auto epoch = date::sys_days{date::January / 1 / 1970}; + epoch += date::days(div_floor(gmt, 86400)); - //date::days sys_days_since_epoch = date::days(gmt); + // date::days sys_days_since_epoch = date::days(gmt); + // Convert sys_days to year_month_day + date::year_month_day ymd = date::year_month_day(epoch); + int64_t secs = gmt % 86400; + if (secs < 0) + secs += 86400; - // Convert sys_days to year_month_day - date::year_month_day ymd = date::year_month_day(epoch); - int64_t secs = gmt % 86400; - if(secs < 0) secs += 86400; + second = secs % 60; + secs /= 60; + minute = secs % 60; + secs /= 60; + hour = secs % 24; + day = (int)(unsigned)ymd.day(); + month = (int)(unsigned)ymd.month(); + year = (int)ymd.year(); +} +DateTime::DateTime(int64_t epoch) { + this->isLocal = false; + this->FromEpochNoConvert(epoch); +} +void DateTime::Set(int64_t epoch) { + this->isLocal = false; + this->FromEpochNoConvert(epoch); +} +void DateTime::Set(int year, int month, int day, int hour, int minute, + int seconds, bool isLocal) { + this->year = year; + this->month = month; + this->day = day; + this->hour = hour; + this->minute = minute; + this->second = seconds; + this->isLocal = isLocal; +} +DateTime::DateTime(int year, int month, int day, int hour, int minute, + int seconds, bool isLocal) { + this->year = year; + this->month = month; + this->day = day; + this->hour = hour; + this->minute = minute; + this->second = seconds; + this->isLocal = isLocal; +} +int DateTime::Year() const { return this->year; } +int DateTime::Month() const { return this->month; } +int DateTime::Day() const { return this->day; } +int DateTime::Hour() const { return this->hour; } +int DateTime::Minute() const { return this->minute; } +int DateTime::Second() const { return this->second; } +bool DateTime::IsLocal() const { return this->isLocal; } +int DateTime::DayOfWeek() const { + date::year_month_day ymd(date::year(year), date::month((uint32_t)month), + date::day((uint32_t)day)); + date::sys_days d = ymd; + date::year_month_weekday ymw(d); + return ymw.weekday().c_encoding() % 7; +} +void DateTime::SetToLocal() { + if (this->isLocal) + return; + auto local = this->ToEpochNoConvert(); + local += GetTimeZone(); + if (TimeZoneSupportDST()) { - second = secs % 60; - secs /= 60; - minute = secs % 60; - secs /= 60; - hour = secs % 24; + auto epoch = date::sys_days{date::January / 1 / 1970}; + epoch += date::days(div_floor(local, 86400)); - day = (int)(unsigned)ymd.day(); - month = (int)(unsigned)ymd.month(); - year = (int)ymd.year(); - } - DateTime::DateTime(int64_t epoch) - { - this->isLocal=false; - this->FromEpochNoConvert(epoch); - } - void DateTime::Set(int64_t epoch) - { - this->isLocal=false; - this->FromEpochNoConvert(epoch); - } - void DateTime::Set(int year, int month, int day, int hour, int minute, int seconds, bool isLocal) - { - this->year = year; - this->month = month; - this->day = day; - this->hour = hour; - this->minute = minute; - this->second = seconds; - this->isLocal = isLocal; - } - DateTime::DateTime(int year, int month, int day, int hour, int minute, int seconds, bool isLocal) - { - this->year = year; - this->month = month; - this->day = day; - this->hour = hour; - this->minute = minute; - this->second = seconds; - this->isLocal = isLocal; - } - int DateTime::Year() const - { - return this->year; - } - int DateTime::Month() const - { - return this->month; - } - int DateTime::Day() const - { - return this->day; - } - int DateTime::Hour() const - { - return this->hour; - } - int DateTime::Minute() const - { - return this->minute; - } - int DateTime::Second() const - { - return this->second; - } - bool DateTime::IsLocal() const - { - return this->isLocal; - } - int DateTime::DayOfWeek() const - { - date::year_month_day ymd(date::year(year),date::month((uint32_t)month),date::day((uint32_t)day)); - date::sys_days d = ymd; - date::year_month_weekday ymw(d); - return ymw.weekday().c_encoding() % 7; + bool isDST = false; - } - void DateTime::SetToLocal() - { - if(this->isLocal) return; - auto local = this->ToEpochNoConvert(); - local += GetTimeZone(); - if(TimeZoneSupportDST()) - { - - auto epoch = date::sys_days{date::January/1/1970}; - epoch += date::days(div_floor(local,86400)); - - bool isDST = false; - - date::year_month_day ymd(epoch); - - auto month = (unsigned)ymd.month(); - - if(month > 3 && month < 11) - { - isDST=true; + date::year_month_day ymd(epoch); + + auto month = (unsigned)ymd.month(); + + if (month > 3 && month < 11) { + isDST = true; + } else if (month == 3) { + auto day = (unsigned)ymd.day(); + if (day > 14) + isDST = true; + else if (day >= 8 && day <= 14) { + date::year_month_weekday ymw(epoch); + auto dow = ymw.weekday().c_encoding(); + auto secondSunday = day - dow; + if (secondSunday < 8) + secondSunday += 7; + + if (day > secondSunday) + isDST = true; + else if (day == secondSunday) { + int64_t secs = local % 86400; + if (secs < 0) + secs += 86400; + + secs /= 3600; + auto _hours = secs % 24; + if (_hours >= 2) + isDST = true; } - else if(month == 3) - { - auto day = (unsigned)ymd.day(); - if(day > 14) isDST=true; - else if(day >= 8 && day <= 14) - { - date::year_month_weekday ymw(epoch); - auto dow=ymw.weekday().c_encoding(); - auto secondSunday = day - dow; - if(secondSunday < 8) secondSunday+=7; - - if(day > secondSunday) isDST=true; - else if(day == secondSunday) - { - int64_t secs = local % 86400; - if(secs < 0) secs += 86400; + } + } else if (month == 11) { + auto day = (unsigned)ymd.day(); + if (day >= 1 && day <= 7) { + date::year_month_weekday ymw(epoch); + auto dow = ymw.weekday().c_encoding(); + int32_t firstSunday = (int32_t)day - (int32_t)dow; + if (firstSunday < 1) + firstSunday += 7; - secs /= 3600; - auto _hours = secs % 24; - if(_hours >= 2) isDST=true; - } - } - } - else if(month == 11) - { - auto day = (unsigned)ymd.day(); - if(day >= 1 && day <= 7) - { - date::year_month_weekday ymw(epoch); - auto dow=ymw.weekday().c_encoding(); - int32_t firstSunday = (int32_t)day - (int32_t)dow; - if(firstSunday < 1) firstSunday+=7; - - if(day < firstSunday) isDST=true; - else if(day == firstSunday) - { - int64_t secs = local % 86400; - if(secs < 0) secs += 86400; + if (day < firstSunday) + isDST = true; + else if (day == firstSunday) { + int64_t secs = local % 86400; + if (secs < 0) + secs += 86400; - secs /= 3600; - auto _hours = secs % 24; - if(_hours <=1) isDST=true; - } - } + secs /= 3600; + auto _hours = secs % 24; + if (_hours <= 1) + isDST = true; } - - if(isDST) local += 3600; - - } - this->isLocal=true; - this->FromEpochNoConvert(local); - } - DateTime DateTime::ToLocal() const - { - DateTime dt = *this; - dt.SetToLocal(); - return dt; - } - void DateTime::SetToUTC() - { - if(!this->isLocal) return; - auto local = this->ToEpochNoConvert(); - local -= GetTimeZone(); - if(TimeZoneSupportDST()) - { - { - auto epoch = date::sys_days{date::January/1/1970}; - epoch += date::days(div_floor(local,86400)); - - bool isDST = false; - - date::year_month_day ymd(epoch); - - auto month = (unsigned)ymd.month(); - - if(month > 3 && month < 11) - { - isDST=true; - } - else if(month == 3) - { - auto day = (unsigned)ymd.day(); - if(day > 14) isDST=true; - else if(day >= 8 && day <= 14) - { - date::year_month_weekday ymw(epoch); - auto dow=ymw.weekday().c_encoding(); - auto secondSunday = day - dow; - if(secondSunday < 8) secondSunday+=7; - - if(day > secondSunday) isDST=true; - else if(day == secondSunday) - { - int64_t secs = local % 86400; - if(secs < 0) secs += 86400; - - secs /= 3600; - auto _hours = secs % 24; - if(_hours >= 2) isDST=true; - } - } - } - else if(month == 11) - { - auto day = (unsigned)ymd.day(); - if(day >= 1 && day <= 7) - { - date::year_month_weekday ymw(epoch); - auto dow=ymw.weekday().c_encoding(); - int32_t firstSunday = (int32_t)day - (int32_t)dow; - if(firstSunday < 1) firstSunday+=7; - - if(day < firstSunday) isDST=true; - else if(day == firstSunday) - { - int64_t secs = local % 86400; - if(secs < 0) secs += 86400; - - secs /= 3600; - auto _hours = secs % 24; - if(_hours <=1) isDST=true; - } - } - } - - if(isDST) local -= 3600; } } - this->isLocal=false; - this->FromEpochNoConvert(local); - + + if (isDST) + local += 3600; } - DateTime DateTime::ToUTC() const - { - DateTime dt = *this; - dt.SetToUTC(); - return dt; - } - int64_t DateTime::ToEpoch() const - { - if(this->isLocal) + this->isLocal = true; + this->FromEpochNoConvert(local); +} +DateTime DateTime::ToLocal() const { + DateTime dt = *this; + dt.SetToLocal(); + return dt; +} +void DateTime::SetToUTC() { + if (!this->isLocal) + return; + auto local = this->ToEpochNoConvert(); + local -= GetTimeZone(); + if (TimeZoneSupportDST()) { { - DateTime dt = this->ToUTC(); - return dt.ToEpochNoConvert(); - } - return this->ToEpochNoConvert(); - } - int64_t DateTime::ToEpochNoConvert() const - { - date::year y = (date::year)year; - date::month m = (date::month)month; - date::day d = (date::day)day; - date::year_month_day ymd(y,m,d); - std::chrono::duration hr = hours(this->hour) + minutes(this->minute) + seconds(this->second); - sys_days sd = ymd; - auto res = sd - date::sys_days{date::January/1/1970}; - return (res.count() * 86400) + hr.count(); //bad + auto epoch = date::sys_days{date::January / 1 / 1970}; + epoch += date::days(div_floor(local, 86400)); - } - DateTime DateTime::NowUTC() - { - DateTime theTime((int64_t)time(NULL)); - return theTime; - } - void DateTime::SetToNowUTC() - { - this->Set((int64_t)time(NULL)); - } - void DateTime::SetYear(int y) - { - this->year = y; - } - void DateTime::SetMonth(int m) - { - this->month = m; - } - void DateTime::SetDay(int d) - { - this->day = d; - } - void DateTime::SetHour(int h) - { - this->hour = h; - } - void DateTime::SetMinute(int m) - { - this->minute = m; - } - void DateTime::SetSecond(int s) - { - this->second = s; - } - void DateTime::SetLocal(bool local) - { - this->isLocal=local; - } - void DateTime::SetToNow() - { - int64_t local = (int64_t)time(NULL); - local += GetTimeZone(); - if(TimeZoneSupportDST()) - { - - auto epoch = date::sys_days{date::January/1/1970}; - epoch += date::days(div_floor(local,86400)); - - bool isDST = false; - - date::year_month_day ymd(epoch); - - auto month = (unsigned)ymd.month(); - - if(month > 3 && month < 11) - { - isDST=true; - } - else if(month == 3) - { - auto day = (unsigned)ymd.day(); - if(day > 14) isDST=true; - else if(day >= 8 && day <= 14) - { - date::year_month_weekday ymw(epoch); - auto dow=ymw.weekday().c_encoding(); - auto secondSunday = day - dow; - if(secondSunday < 8) secondSunday+=7; - - if(day > secondSunday) isDST=true; - else if(day == secondSunday) - { - int64_t secs = local % 86400; - if(secs < 0) secs += 86400; + bool isDST = false; - secs /= 3600; - auto _hours = secs % 24; - if(_hours >= 2) isDST=true; - } - } - } - else if(month == 11) - { - auto day = (unsigned)ymd.day(); - if(day >= 1 && day <= 7) - { - date::year_month_weekday ymw(epoch); - auto dow=ymw.weekday().c_encoding(); - int32_t firstSunday = (int32_t)day - (int32_t)dow; - if(firstSunday < 1) firstSunday+=7; - - if(day < firstSunday) isDST=true; - else if(day == firstSunday) - { - int64_t secs = local % 86400; - if(secs < 0) secs += 86400; + date::year_month_day ymd(epoch); - secs /= 3600; - auto _hours = secs % 24; - if(_hours <=1) isDST=true; - } + auto month = (unsigned)ymd.month(); + + if (month > 3 && month < 11) { + isDST = true; + } else if (month == 3) { + auto day = (unsigned)ymd.day(); + if (day > 14) + isDST = true; + else if (day >= 8 && day <= 14) { + date::year_month_weekday ymw(epoch); + auto dow = ymw.weekday().c_encoding(); + auto secondSunday = day - dow; + if (secondSunday < 8) + secondSunday += 7; + + if (day > secondSunday) + isDST = true; + else if (day == secondSunday) { + int64_t secs = local % 86400; + if (secs < 0) + secs += 86400; + + secs /= 3600; + auto _hours = secs % 24; + if (_hours >= 2) + isDST = true; } } - - if(isDST) local += 3600; - - } - this->isLocal=true; - this->FromEpochNoConvert(local); - } - DateTime DateTime::Now() - { - DateTime dt; - dt.SetToNow(); - return dt; - - } - - const char* weekday_short[] = { - "Sun", - "Mon", - "Tue", - "Wed", - "Thu", - "Fri", - "Sat" - }; - const char* months_short[] = { - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec" - }; - const char* weekday_long[] = { - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday" - }; - const char* months_long[] = { - "January", - "Febuary", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December" - }; - bool DateTime::TryParseHttpDate(std::string txt, DateTime& dt) - { - //Mon, 24 Jul 2018 11:00:00 GMT - auto split = Http::HttpUtils::SplitString(txt,", ",2); - if(split.size() != 2) return false; - bool validDay = false; - for(size_t i = 0; i < 7; i++) - { - std::string_view d = weekday_short[i]; - if(d == split[0]) validDay=true; - } - if(!validDay) return false; - //DAY MON YEAR HH:MM:SS GMT - split = Http::HttpUtils::SplitString(split[1]," ",5); - if(split.size() != 5) return false; - if(split[4] != "GMT") return false; - int day=0; - int mon=0; - int year=0; - int hour=0; - int minute=0; - int second=0; + } else if (month == 11) { + auto day = (unsigned)ymd.day(); + if (day >= 1 && day <= 7) { + date::year_month_weekday ymw(epoch); + auto dow = ymw.weekday().c_encoding(); + int32_t firstSunday = (int32_t)day - (int32_t)dow; + if (firstSunday < 1) + firstSunday += 7; - try { - day = std::stoi(split[0]); - if(day < 1 && day > 31) return false; - - } catch(std::exception& ex) { + if (day < firstSunday) + isDST = true; + else if (day == firstSunday) { + int64_t secs = local % 86400; + if (secs < 0) + secs += 86400; + + secs /= 3600; + auto _hours = secs % 24; + if (_hours <= 1) + isDST = true; + } + } + } + + if (isDST) + local -= 3600; + } + } + this->isLocal = false; + this->FromEpochNoConvert(local); +} +DateTime DateTime::ToUTC() const { + DateTime dt = *this; + dt.SetToUTC(); + return dt; +} +int64_t DateTime::ToEpoch() const { + if (this->isLocal) { + DateTime dt = this->ToUTC(); + return dt.ToEpochNoConvert(); + } + return this->ToEpochNoConvert(); +} +int64_t DateTime::ToEpochNoConvert() const { + date::year y = (date::year)year; + date::month m = (date::month)month; + date::day d = (date::day)day; + date::year_month_day ymd(y, m, d); + std::chrono::duration hr = + hours(this->hour) + minutes(this->minute) + seconds(this->second); + date::sys_days sd = ymd; + auto res = sd - date::sys_days{date::January / 1 / 1970}; + return (res.count() * 86400) + hr.count(); // bad +} +DateTime DateTime::NowUTC() { + DateTime theTime((int64_t)time(NULL)); + return theTime; +} +void DateTime::SetToNowUTC() { this->Set((int64_t)time(NULL)); } +void DateTime::SetYear(int y) { this->year = y; } +void DateTime::SetMonth(int m) { this->month = m; } +void DateTime::SetDay(int d) { this->day = d; } +void DateTime::SetHour(int h) { this->hour = h; } +void DateTime::SetMinute(int m) { this->minute = m; } +void DateTime::SetSecond(int s) { this->second = s; } +void DateTime::SetLocal(bool local) { this->isLocal = local; } +void DateTime::SetToNow() { + int64_t local = (int64_t)time(NULL); + local += GetTimeZone(); + if (TimeZoneSupportDST()) { + + auto epoch = date::sys_days{date::January / 1 / 1970}; + epoch += date::days(div_floor(local, 86400)); + + bool isDST = false; + + date::year_month_day ymd(epoch); + + auto month = (unsigned)ymd.month(); + + if (month > 3 && month < 11) { + isDST = true; + } else if (month == 3) { + auto day = (unsigned)ymd.day(); + if (day > 14) + isDST = true; + else if (day >= 8 && day <= 14) { + date::year_month_weekday ymw(epoch); + auto dow = ymw.weekday().c_encoding(); + auto secondSunday = day - dow; + if (secondSunday < 8) + secondSunday += 7; + + if (day > secondSunday) + isDST = true; + else if (day == secondSunday) { + int64_t secs = local % 86400; + if (secs < 0) + secs += 86400; + + secs /= 3600; + auto _hours = secs % 24; + if (_hours >= 2) + isDST = true; + } + } + } else if (month == 11) { + auto day = (unsigned)ymd.day(); + if (day >= 1 && day <= 7) { + date::year_month_weekday ymw(epoch); + auto dow = ymw.weekday().c_encoding(); + int32_t firstSunday = (int32_t)day - (int32_t)dow; + if (firstSunday < 1) + firstSunday += 7; + + if (day < firstSunday) + isDST = true; + else if (day == firstSunday) { + int64_t secs = local % 86400; + if (secs < 0) + secs += 86400; + + secs /= 3600; + auto _hours = secs % 24; + if (_hours <= 1) + isDST = true; + } + } + } + + if (isDST) + local += 3600; + } + this->isLocal = true; + this->FromEpochNoConvert(local); +} +DateTime DateTime::Now() { + DateTime dt; + dt.SetToNow(); + return dt; +} + +const char *weekday_short[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +const char *months_short[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +const char *weekday_long[] = {"Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"}; +const char *months_long[] = {"January", "Febuary", "March", "April", + "May", "June", "July", "August", + "September", "October", "November", "December"}; +bool DateTime::TryParseHttpDate(std::string txt, DateTime &dt) { + // Mon, 24 Jul 2018 11:00:00 GMT + auto split = Http::HttpUtils::SplitString(txt, ", ", 2); + if (split.size() != 2) + return false; + bool validDay = false; + for (size_t i = 0; i < 7; i++) { + std::string_view d = weekday_short[i]; + if (d == split[0]) + validDay = true; + } + if (!validDay) + return false; + // DAY MON YEAR HH:MM:SS GMT + split = Http::HttpUtils::SplitString(split[1], " ", 5); + if (split.size() != 5) + return false; + if (split[4] != "GMT") + return false; + int day = 0; + int mon = 0; + int year = 0; + int hour = 0; + int minute = 0; + int second = 0; + + try { + day = std::stoi(split[0]); + if (day < 1 && day > 31) return false; + + } catch (std::exception &ex) { + return false; + } + for (int i = 0; i < 12; mon++) { + std::string_view d = months_short[i]; + if (d == split[1]) { + mon = i + 1; + break; } - for(int i = 0; i < 12; mon++) - { - std::string_view d = months_short[i]; - if(d == split[1]) - { - mon = i + 1; + } + if (mon == 0) + return false; + try { + year = std::stoi(split[2]); + + } catch (std::exception &ex) { + return false; + } + split = Http::HttpUtils::SplitString(split[3], ":", 3); + if (split.size() != 3) + return false; + try { + hour = std::stoi(split[0]); + + minute = std::stoi(split[1]); + + second = std::stoi(split[2]); + + } catch (std::exception &ex) { + return false; + } + + dt.day = day; + dt.month = mon; + dt.year = year; + dt.hour = hour; + dt.minute = minute; + dt.second = second; + return true; +} +std::string DateTime::ToHttpDate() const { + auto utc = this->ToUTC(); + std::string weekday = weekday_short[utc.DayOfWeek()]; + std::string month = months_short[utc.month - 1]; + std::stringstream strm(std::ios_base::out); + strm << weekday << ", " << std::setfill('0') << std::setw(2) << utc.day; + strm << " " << month << " " << std::setfill('0') << std::setw(4) + << utc.year; + strm << " " << std::setfill('0') << std::setw(2) << utc.hour; + strm << ":" << std::setfill('0') << std::setw(2) << utc.minute; + strm << ":" << std::setfill('0') << std::setw(2) << utc.second; + strm << " GMT"; + + return strm.str(); +} +std::string DateTime::ToString() const { return ToString("%Y/%m/%d %H:%M:%S"); } + +std::string DateTime::ToString(std::string fmt) const { + auto weekday = this->DayOfWeek(); + + std::string text = ""; + for (size_t i = 0; i < fmt.size(); i++) { + if (fmt[i] == '%') { + i++; + if (i < fmt.size()) + switch (fmt[i]) { + case 'a': + text.append(weekday_short[weekday]); + break; + case 'A': + text.append(weekday_long[weekday]); + break; + case 'S': + text.append(Http::HttpUtils::LeftPad(std::to_string(second), + 2, '0')); + break; + case 'm': + text.append(Http::HttpUtils::LeftPad(std::to_string(month), + 2, '0')); + break; + case 'd': + text.append( + Http::HttpUtils::LeftPad(std::to_string(day), 2, '0')); + break; + case 'e': + text.append( + Http::HttpUtils::LeftPad(std::to_string(day), 2, ' ')); + break; + case 'M': + text.append(Http::HttpUtils::LeftPad(std::to_string(minute), + 2, '0')); + break; + case 'H': + text.append( + Http::HttpUtils::LeftPad(std::to_string(hour), 2, '0')); + break; + case 'F': + text.append( + Http::HttpUtils::LeftPad(std::to_string(year), 4, '0')); + text.push_back('-'); + text.append(Http::HttpUtils::LeftPad(std::to_string(month), + 2, '0')); + text.push_back('-'); + text.append( + Http::HttpUtils::LeftPad(std::to_string(day), 2, '0')); + break; + case 'D': + text.append(Http::HttpUtils::LeftPad(std::to_string(month), + 2, '0')); + text.push_back('/'); + text.append( + Http::HttpUtils::LeftPad(std::to_string(day), 2, '0')); + text.push_back('/'); + text.append(Http::HttpUtils::LeftPad( + std::to_string(year % 100), 2, '0')); + break; + case 'y': + text.append(Http::HttpUtils::LeftPad( + std::to_string(year % 100), 2, '0')); + break; + case 'r': { + auto hours = hour % 12; + if (hours == 0) + hours = 12; + text.append(Http::HttpUtils::LeftPad(std::to_string(hours), + 2, '0')); + text.push_back(':'); + text.append(Http::HttpUtils::LeftPad(std::to_string(minute), + 2, '0')); + text.push_back(':'); + text.append(Http::HttpUtils::LeftPad(std::to_string(second), + 2, '0')); + text.append(hour >= 12 ? " PM" : " AM"); + } break; + case 'R': { + text.append( + Http::HttpUtils::LeftPad(std::to_string(hour), 2, '0')); + text.push_back(':'); + text.append(Http::HttpUtils::LeftPad(std::to_string(minute), + 2, '0')); + } break; + case 'T': { + + text.append( + Http::HttpUtils::LeftPad(std::to_string(hour), 2, '0')); + text.push_back(':'); + text.append(Http::HttpUtils::LeftPad(std::to_string(minute), + 2, '0')); + text.push_back(':'); + text.append(Http::HttpUtils::LeftPad(std::to_string(second), + 2, '0')); + + } + break; - } - } - if(mon == 0) return false; - try { - year = std::stoi(split[2]); - - } catch(std::exception& ex) { - return false; - } - split = Http::HttpUtils::SplitString(split[3],":",3); - if(split.size() != 3) return false; - try { - hour = std::stoi(split[0]); + case 'u': { + int dow = weekday + 6; + dow %= 7; + text.append(std::to_string(dow + 1)); + } break; + case 'w': { - minute = std::stoi(split[1]); + text.append(std::to_string(weekday)); + } break; + case 'c': { + text.append(weekday_short[weekday]); + text.push_back(' '); + text.append(months_short[month]); + text.push_back(' '); + text.append( + Http::HttpUtils::LeftPad(std::to_string(day), 2, '0')); + text.push_back(' '); + text.append( + Http::HttpUtils::LeftPad(std::to_string(hour), 2, '0')); + text.push_back(':'); + text.append(Http::HttpUtils::LeftPad(std::to_string(minute), + 2, '0')); + text.push_back(':'); + text.append(Http::HttpUtils::LeftPad(std::to_string(second), + 2, '0')); + text.push_back(' '); + text.append( + Http::HttpUtils::LeftPad(std::to_string(year), 4, '0')); - second = std::stoi(split[2]); - - } catch(std::exception& ex) { - return false; - } - - dt.day = day; - dt.month = mon; - dt.year = year; - dt.hour = hour; - dt.minute = minute; - dt.second = second; - return true; - } - std::string DateTime::ToHttpDate() const - { - auto utc=this->ToUTC(); - std::string weekday=weekday_short[utc.DayOfWeek()]; - std::string month = months_short[utc.month-1]; - std::stringstream strm(std::ios_base::out); - strm << weekday << ", " << std::setfill('0') << std::setw(2) << utc.day; - strm << " " << month << " " << std::setfill('0') << std::setw(4) << utc.year; - strm << " " << std::setfill('0') << std::setw(2) << utc.hour; - strm << ":" << std::setfill('0') << std::setw(2) << utc.minute; - strm << ":" << std::setfill('0') << std::setw(2) << utc.second; - strm << " GMT"; - - return strm.str(); - - } - std::string DateTime::ToString() const - { - return ToString("%Y/%m/%d %H:%M:%S"); - } - - std::string DateTime::ToString(std::string fmt) const - { - auto weekday = this->DayOfWeek(); - - - std::string text = ""; - for(size_t i = 0; i < fmt.size(); i++) - { - if(fmt[i]=='%') - { - i++; - if(i < fmt.size()) - switch(fmt[i]) - { - case 'a': - text.append(weekday_short[weekday]); - break; - case 'A': - text.append(weekday_long[weekday]); - break; - case 'S': - text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0')); - break; - case 'm': - text.append(Http::HttpUtils::LeftPad(std::to_string(month),2,'0')); - break; - case 'd': - text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0')); - break; - case 'e': - text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,' ')); - break; - case 'M': - text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0')); - break; - case 'H': - text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0')); - break; - case 'F': - text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0')); - text.push_back('-'); - text.append(Http::HttpUtils::LeftPad(std::to_string(month),2,'0')); - text.push_back('-'); - text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0')); - break; - case 'D': - text.append(Http::HttpUtils::LeftPad(std::to_string(month),2,'0')); - text.push_back('/'); - text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0')); - text.push_back('/'); - text.append(Http::HttpUtils::LeftPad(std::to_string(year % 100),2,'0')); - break; - case 'y': - text.append(Http::HttpUtils::LeftPad(std::to_string(year % 100),2,'0')); + } break; + case 'C': + text.append(Http::HttpUtils::LeftPad( + std::to_string(year / 100), 2, '0')); break; - case 'r': - { - auto hours = hour % 12; - if(hours == 0) hours=12; - text.append(Http::HttpUtils::LeftPad(std::to_string(hours),2,'0')); - text.push_back(':'); - text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0')); - text.push_back(':'); - text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0')); - text.append(hour >= 12 ? " PM" : " AM"); - } + case 'Y': + text.append( + Http::HttpUtils::LeftPad(std::to_string(year), 4, '0')); break; - case 'R': - { - text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0')); - text.push_back(':'); - text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0')); - } + case 'p': + text.append(hour >= 12 ? "PM" : "AM"); break; - case 'T': - { - - text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0')); - text.push_back(':'); - text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0')); - text.push_back(':'); - text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0')); - - } - + case 'I': { + auto hours = hour % 12; + if (hours == 0) + hours = 12; + text.append(Http::HttpUtils::LeftPad(std::to_string(hours), + 2, '0')); + } break; + case 'h': + case 'b': + text.append(months_short[month - 1]); break; - case 'u': - { - int dow = weekday + 6; - dow %= 7; - text.append(std::to_string(dow+1)); - } + case 'B': + text.append(months_long[month - 1]); break; - case 'w': - { - - text.append(std::to_string(weekday)); - } + case '%': + text.push_back('%'); break; - case 'c': - { - text.append(weekday_short[weekday]); - text.push_back(' '); - text.append(months_short[month]); - text.push_back(' '); - text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0')); - text.push_back(' '); - text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0')); - text.push_back(':'); - text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0')); - text.push_back(':'); - text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0')); - text.push_back(' '); - text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0')); - - - } - break; - case 'C': - text.append(Http::HttpUtils::LeftPad(std::to_string(year / 100),2,'0')); + case 'n': + text.push_back('\n'); break; - case 'Y': - text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0')); + case 't': + text.push_back('\t'); break; - case 'p': - text.append(hour >= 12 ? "PM" : "AM"); - break; - case 'I': - { - auto hours = hour % 12; - if(hours == 0) hours=12; - text.append(Http::HttpUtils::LeftPad(std::to_string(hours),2,'0')); - } - break; - case 'h': - case 'b': - text.append(months_short[month-1]); - break; - case 'B': - text.append(months_long[month-1]); - break; - case '%': - text.push_back('%'); - break; - case 'n': - text.push_back('\n'); - break; - case 't': - text.push_back('\t'); - break; - } - } - else - { - text.push_back(fmt[i]); - } - } - return text; - } - TimeSpan::TimeSpan() - { - this->totalSeconds = 0; - } - TimeSpan::TimeSpan(int64_t totalSeconds) - { - this->totalSeconds = totalSeconds; - - } - TimeSpan::TimeSpan(int hours, int minutes, int seconds) : TimeSpan(0,hours,minutes,seconds) - { - - } - TimeSpan::TimeSpan(int days,int hours, int minutes, int seconds) - { - this->totalSeconds = (int64_t)days * 86400; - this->totalSeconds += (int64_t)hours * 3600; - this->totalSeconds += (int64_t)minutes * 60; - this->totalSeconds += (int64_t)seconds; - } - void TimeSpan::Set(int days, int hours, int minutes, int seconds) - { - this->totalSeconds = (int64_t)days * 86400; - this->totalSeconds += (int64_t)hours * 3600; - this->totalSeconds += (int64_t)minutes * 60; - this->totalSeconds += (int64_t)seconds; - } - void TimeSpan::Set(int hours, int minutes, int seconds) - { - this->totalSeconds = (int64_t)hours * 3600; - this->totalSeconds += (int64_t)minutes * 60; - this->totalSeconds += (int64_t)seconds; - } - - void TimeSpan::SetDays(int d) - { - Set(d,this->Hours(),this->Minutes(),this->Seconds()); - } - void TimeSpan::SetHours(int h) - { - Set(this->Days(),h,this->Minutes(), this->Seconds()); - } - void TimeSpan::SetMinutes(int m) - { - Set(this->Days(),this->Hours(),m,this->Seconds()); - } - void TimeSpan::SetSeconds(int s) - { - Set(this->Days(),this->Hours(),this->Minutes(),s); - } - - int TimeSpan::Days() const - { - - return (int)(this->totalSeconds / 86400); - } - int TimeSpan::Hours() const - { - - return (int)((this->totalSeconds / 3600) % 24); - } - int TimeSpan::Minutes() const - { - return (int)((this->totalSeconds / 60) % 60); - } - int TimeSpan::Seconds() const - { - return (int)(this->totalSeconds % 60); - } - - - int64_t TimeSpan::TotalSeconds() const - { - return this->totalSeconds; - } - int64_t TimeSpan::TotalMinutes() const - { - return this->totalSeconds / 60; - } - int64_t TimeSpan::TotalHours() const - { - return this->totalSeconds / 3600; - } - - void TimeSpan::AddSeconds(int64_t seconds) - { - this->totalSeconds += seconds; - } - void TimeSpan::AddMinutes(int64_t minutes) - { - this->totalSeconds += minutes * 60; - } - void TimeSpan::AddHours(int64_t hours) - { - this->totalSeconds += hours * 3600; - } - void TimeSpan::AddDays(int64_t days) - { - this->totalSeconds += days * 86400; - } - - void TimeSpan::SetTotalSeconds(int64_t totalSeconds) - { - this->totalSeconds = totalSeconds; - } - void TimeSpan::SetTotalMinutes(int64_t totalMinutes) - { - this->totalSeconds = totalMinutes * 60; - } - void TimeSpan::SetTotalHours(int64_t totalHours) - { - this->totalSeconds = totalHours * 3600; - } - - std::string TimeSpan::ToString(bool slim) const - { - std::string str={}; - if(this->totalSeconds < 0) - str += "-"; - if(slim && this->totalSeconds > -36000 && this->totalSeconds < 36000) - { - //0:00 - //00:00 - //0:00:00 - if(this->totalSeconds <= -3600 || this->totalSeconds >= 3600) - { - //hours must force multi digit minutes - str += std::to_string(this->Hours()); - str += ":"; - str += Http::HttpUtils::LeftPad(std::to_string(this->Minutes()),2,'0'); - } - else - { - str += std::to_string(this->Minutes()); - } - - str += ":"; - str += Http::HttpUtils::LeftPad(std::to_string(this->Seconds()),2,'0'); + } else { + text.push_back(fmt[i]); } - else - { - //00:00:00 - //0.00:00:00 - - if(this->totalSeconds <= -86400 || this->totalSeconds >= 86400) - { - str += std::to_string(this->Days()); - str += "."; - } - - str += Http::HttpUtils::LeftPad(std::to_string(this->Hours()),2,'0'); - str += ":"; - str += Http::HttpUtils::LeftPad(std::to_string(this->Minutes()),2,'0'); - str += ":"; - str += Http::HttpUtils::LeftPad(std::to_string(this->Seconds()),2,'0'); - - } - return str; } + return text; +} +TimeSpan::TimeSpan() { this->totalSeconds = 0; } +TimeSpan::TimeSpan(int64_t totalSeconds) { this->totalSeconds = totalSeconds; } +TimeSpan::TimeSpan(int hours, int minutes, int seconds) + : TimeSpan(0, hours, minutes, seconds) {} +TimeSpan::TimeSpan(int days, int hours, int minutes, int seconds) { + this->totalSeconds = (int64_t)days * 86400; + this->totalSeconds += (int64_t)hours * 3600; + this->totalSeconds += (int64_t)minutes * 60; + this->totalSeconds += (int64_t)seconds; +} +void TimeSpan::Set(int days, int hours, int minutes, int seconds) { + this->totalSeconds = (int64_t)days * 86400; + this->totalSeconds += (int64_t)hours * 3600; + this->totalSeconds += (int64_t)minutes * 60; + this->totalSeconds += (int64_t)seconds; +} +void TimeSpan::Set(int hours, int minutes, int seconds) { + this->totalSeconds = (int64_t)hours * 3600; + this->totalSeconds += (int64_t)minutes * 60; + this->totalSeconds += (int64_t)seconds; +} - bool TimeSpan::TryParse(std::string text, TimeSpan& span) - { - if(text.empty()) return false; - bool negative = text[0] == '-'; - int64_t totalSeconds = 0; - +void TimeSpan::SetDays(int d) { + Set(d, this->Hours(), this->Minutes(), this->Seconds()); +} +void TimeSpan::SetHours(int h) { + Set(this->Days(), h, this->Minutes(), this->Seconds()); +} +void TimeSpan::SetMinutes(int m) { + Set(this->Days(), this->Hours(), m, this->Seconds()); +} +void TimeSpan::SetSeconds(int s) { + Set(this->Days(), this->Hours(), this->Minutes(), s); +} - try{ - +int TimeSpan::Days() const { return (int)(this->totalSeconds / 86400); } +int TimeSpan::Hours() const { return (int)((this->totalSeconds / 3600) % 24); } +int TimeSpan::Minutes() const { return (int)((this->totalSeconds / 60) % 60); } +int TimeSpan::Seconds() const { return (int)(this->totalSeconds % 60); } + +int64_t TimeSpan::TotalSeconds() const { return this->totalSeconds; } +int64_t TimeSpan::TotalMinutes() const { return this->totalSeconds / 60; } +int64_t TimeSpan::TotalHours() const { return this->totalSeconds / 3600; } + +void TimeSpan::AddSeconds(int64_t seconds) { this->totalSeconds += seconds; } +void TimeSpan::AddMinutes(int64_t minutes) { + this->totalSeconds += minutes * 60; +} +void TimeSpan::AddHours(int64_t hours) { this->totalSeconds += hours * 3600; } +void TimeSpan::AddDays(int64_t days) { this->totalSeconds += days * 86400; } + +void TimeSpan::SetTotalSeconds(int64_t totalSeconds) { + this->totalSeconds = totalSeconds; +} +void TimeSpan::SetTotalMinutes(int64_t totalMinutes) { + this->totalSeconds = totalMinutes * 60; +} +void TimeSpan::SetTotalHours(int64_t totalHours) { + this->totalSeconds = totalHours * 3600; +} + +std::string TimeSpan::ToString(bool slim) const { + std::string str = {}; + if (this->totalSeconds < 0) + str += "-"; + if (slim && this->totalSeconds > -36000 && this->totalSeconds < 36000) { + // 0:00 + // 00:00 + // 0:00:00 + if (this->totalSeconds <= -3600 || this->totalSeconds >= 3600) { + // hours must force multi digit minutes + str += std::to_string(this->Hours()); + str += ":"; + str += Http::HttpUtils::LeftPad(std::to_string(this->Minutes()), 2, + '0'); + } else { + str += std::to_string(this->Minutes()); + } + + str += ":"; + str += + Http::HttpUtils::LeftPad(std::to_string(this->Seconds()), 2, '0'); + } else { + // 00:00:00 + // 0.00:00:00 + + if (this->totalSeconds <= -86400 || this->totalSeconds >= 86400) { + str += std::to_string(this->Days()); + str += "."; + } + + str += Http::HttpUtils::LeftPad(std::to_string(this->Hours()), 2, '0'); + str += ":"; + str += + Http::HttpUtils::LeftPad(std::to_string(this->Minutes()), 2, '0'); + str += ":"; + str += + Http::HttpUtils::LeftPad(std::to_string(this->Seconds()), 2, '0'); + } + return str; +} + +bool TimeSpan::TryParse(std::string text, TimeSpan &span) { + if (text.empty()) + return false; + bool negative = text[0] == '-'; + int64_t totalSeconds = 0; + + try { std::string colonPart = text.substr(negative ? 1 : 0); - auto res = Http::HttpUtils::SplitString(colonPart,":"); + auto res = Http::HttpUtils::SplitString(colonPart, ":"); - if(res.size() < 2 || res.size() > 3) return false; + if (res.size() < 2 || res.size() > 3) + return false; std::string hour = res[0]; - size_t index=hour.find('.'); - if(index != std::string::npos) - { - totalSeconds += std::stoll(hour.substr(0,index)) * 86400; - hour = hour.substr(index+1); + size_t index = hour.find('.'); + if (index != std::string::npos) { + totalSeconds += std::stoll(hour.substr(0, index)) * 86400; + hour = hour.substr(index + 1); } - - if(res.size() == 2) - { - //mm:ss - + if (res.size() == 2) { + // mm:ss totalSeconds += std::stoll(hour) * 60; totalSeconds += std::stoll(res[1]); - } - else if(res.size() == 3) - { + } else if (res.size() == 3) { totalSeconds += std::stoll(hour) * 3600; totalSeconds += std::stoll(res[1]) * 60; totalSeconds += std::stoll(res[2]); - } - else return false; - - }catch(...) {return false;} - - if(negative) totalSeconds = -totalSeconds; - - span.SetTotalSeconds(totalSeconds); - return true; + } else + return false; + } catch (...) { + return false; } + + if (negative) + totalSeconds = -totalSeconds; + + span.SetTotalSeconds(totalSeconds); + return true; } +} // namespace Tesses::Framework::Date diff --git a/src/Filesystem/FSHelpers.cpp b/src/Filesystem/FSHelpers.cpp index f237fb5..73a04bb 100644 --- a/src/Filesystem/FSHelpers.cpp +++ b/src/Filesystem/FSHelpers.cpp @@ -1,184 +1,197 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Filesystem/FSHelpers.hpp" #include "TessesFramework/TextStreams/StreamReader.hpp" #include "TessesFramework/TextStreams/StreamWriter.hpp" -namespace Tesses::Framework::Filesystem::Helpers -{ - void ReadAllText(std::shared_ptr vfs, VFSPath path, std::string& text) - { - auto file = vfs->OpenFile(path,"rb"); - if(file->CanRead()) - { - TextStreams::StreamReader reader(file); - reader.ReadToEnd(text); - } +namespace Tesses::Framework::Filesystem::Helpers { +void ReadAllText(std::shared_ptr vfs, VFSPath path, std::string &text) { + auto file = vfs->OpenFile(path, "rb"); + if (file->CanRead()) { + TextStreams::StreamReader reader(file); + reader.ReadToEnd(text); } - void ReadAllLines(std::shared_ptr vfs, VFSPath path, std::vector& lines) - { - auto file = vfs->OpenFile(path,"rb"); - if(file->CanRead()) - { - TextStreams::StreamReader reader(file); - reader.ReadAllLines(lines); - } +} +void ReadAllLines(std::shared_ptr vfs, VFSPath path, + std::vector &lines) { + auto file = vfs->OpenFile(path, "rb"); + if (file->CanRead()) { + TextStreams::StreamReader reader(file); + reader.ReadAllLines(lines); } - void ReadAllBytes(std::shared_ptr vfs, VFSPath path, std::vector& array) - { - auto file = vfs->OpenFile(path,"rb"); - if(file->CanRead()) - { - if(file->CanSeek()) - { - size_t length = (size_t)file->GetLength(); - array.resize(length); - file->ReadBlock(array.data(), array.size()); - } - else - { - size_t totalSize = 0; - size_t read = 0; - do { - array.resize(totalSize+1024); - read = file->ReadBlock(array.data()+totalSize,1024); - totalSize += read; - } while(read != 0); +} +void ReadAllBytes(std::shared_ptr vfs, VFSPath path, + std::vector &array) { + auto file = vfs->OpenFile(path, "rb"); + if (file->CanRead()) { + if (file->CanSeek()) { + size_t length = (size_t)file->GetLength(); + array.resize(length); + file->ReadBlock(array.data(), array.size()); + } else { + size_t totalSize = 0; + size_t read = 0; + do { + array.resize(totalSize + 1024); + read = file->ReadBlock(array.data() + totalSize, 1024); + totalSize += read; + } while (read != 0); - array.resize(totalSize); - } + array.resize(totalSize); } } - std::string ReadAllText(std::shared_ptr vfs, VFSPath path) - { - std::string text; - ReadAllText(vfs,path,text); - return text; +} +std::string ReadAllText(std::shared_ptr vfs, VFSPath path) { + std::string text; + ReadAllText(vfs, path, text); + return text; +} +std::vector ReadAllLines(std::shared_ptr vfs, VFSPath path) { + std::vector lines; + ReadAllLines(vfs, path, lines); + return lines; +} +std::vector ReadAllBytes(std::shared_ptr vfs, VFSPath path) { + std::vector bytes; + ReadAllBytes(vfs, path, bytes); + return bytes; +} +void WriteAllText(std::shared_ptr vfs, VFSPath path, + const std::string &text) { + auto file = vfs->OpenFile(path, "wb"); + if (file->CanWrite()) { + TextStreams::StreamWriter writer(file); + writer.Write(text); } - std::vector ReadAllLines(std::shared_ptr vfs, VFSPath path) - { - std::vector lines; - ReadAllLines(vfs,path,lines); - return lines; - } - std::vector ReadAllBytes(std::shared_ptr vfs, VFSPath path) - { - std::vector bytes; - ReadAllBytes(vfs,path,bytes); - return bytes; - } - void WriteAllText(std::shared_ptr vfs, VFSPath path, const std::string& text) - { - auto file = vfs->OpenFile(path,"wb"); - if(file->CanWrite()) - { - TextStreams::StreamWriter writer(file); - writer.Write(text); +} +void WriteAllLines(std::shared_ptr vfs, VFSPath path, + const std::vector &parts) { + auto file = vfs->OpenFile(path, "wb"); + if (file->CanWrite()) { + TextStreams::StreamWriter writer(file); + for (auto &line : parts) { + writer.WriteLine(line); } } - void WriteAllLines(std::shared_ptr vfs, VFSPath path, const std::vector& parts) - { - auto file = vfs->OpenFile(path,"wb"); - if(file->CanWrite()) - { - TextStreams::StreamWriter writer(file); - for(auto& line : parts) - { - writer.WriteLine(line); - } - } - } - void WriteAllBytes(std::shared_ptr vfs, VFSPath path, const std::vector& bytes) - { - auto file = vfs->OpenFile(path,"wb"); - if(file->CanWrite()) - { - file->WriteBlock(bytes.data(),bytes.size()); - } +} +void WriteAllBytes(std::shared_ptr vfs, VFSPath path, + const std::vector &bytes) { + auto file = vfs->OpenFile(path, "wb"); + if (file->CanWrite()) { + file->WriteBlock(bytes.data(), bytes.size()); } +} - void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, bool overwrite) - { - if(!overwrite && vfsDest->FileExists(pathDest)) return; - if(!vfsSrc->FileExists(pathSrc)) return; - auto src=vfsSrc->OpenFile(pathSrc,"wb"); - auto dest = vfsDest->OpenFile(pathDest,"wb"); - if(src->CanRead() && dest->CanWrite()) +void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, + std::shared_ptr vfsDest, VFSPath pathDest, bool overwrite) { + if (!overwrite && vfsDest->FileExists(pathDest)) + return; + if (!vfsSrc->FileExists(pathSrc)) + return; + auto src = vfsSrc->OpenFile(pathSrc, "wb"); + auto dest = vfsDest->OpenFile(pathDest, "wb"); + if (src->CanRead() && dest->CanWrite()) src->CopyTo(dest); +} + +void CopyStreamProgress( + std::shared_ptr src, std::shared_ptr dest, + std::function progress) { + int64_t length = 0; + try { + length = src->GetLength(); + } catch (...) { + length = 0; } + if (length == 0) + length = (int64_t)1 << 62; // a big number so its always 0% if the + // stream does not have a length - void CopyStreamProgress(std::shared_ptr src,std::shared_ptr dest, std::function progress) - { - int64_t length=0; - try { - length = src->GetLength(); - } catch(...) { - length=0; - } - if(length == 0) length = (int64_t)1<<62; // a big number so its always 0% if the stream does not have a length + int64_t offset = 0; + size_t read = 0; + std::vector data(4096); - int64_t offset = 0; - size_t read = 0; - std::vector data(4096); - - - do { - read = src->ReadBlock(data.data(),data.size()); - dest->WriteBlock(data.data(),read); - offset += (int64_t)read; - if(read != 0) - progress(offset,length); - } while(read != 0); + do { + read = src->ReadBlock(data.data(), data.size()); + dest->WriteBlock(data.data(), read); + offset += (int64_t)read; + if (read != 0) + progress(offset, length); + } while (read != 0); - if(offset > 0) - progress(offset,offset); - } - void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, std::function progress, bool overwrite) - { - if(!overwrite && vfsDest->FileExists(pathDest)) return; - if(!vfsSrc->FileExists(pathSrc)) return; - auto src=vfsSrc->OpenFile(pathSrc,"wb"); - auto dest = vfsDest->OpenFile(pathDest,"wb"); - if(src->CanRead() && dest->CanWrite()) - CopyStreamProgress(src,dest,progress); - } - void CopyDirectory(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest,bool overwrite) - { + if (offset > 0) + progress(offset, offset); +} +void CopyFile(std::shared_ptr vfsSrc, VFSPath pathSrc, + std::shared_ptr vfsDest, VFSPath pathDest, + std::function progress, + bool overwrite) { + if (!overwrite && vfsDest->FileExists(pathDest)) + return; + if (!vfsSrc->FileExists(pathSrc)) + return; + auto src = vfsSrc->OpenFile(pathSrc, "wb"); + auto dest = vfsDest->OpenFile(pathDest, "wb"); + if (src->CanRead() && dest->CanWrite()) + CopyStreamProgress(src, dest, progress); +} +void CopyDirectory(std::shared_ptr vfsSrc, VFSPath pathSrc, + std::shared_ptr vfsDest, VFSPath pathDest, + bool overwrite) { - - if(vfsSrc->DirectoryExists(pathSrc)) - { - vfsDest->CreateDirectory(pathDest); - for(auto& srcPath : vfsSrc->EnumeratePaths(pathSrc)) - { - if(vfsSrc->DirectoryExists(srcPath)) - { - CopyDirectory(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName()); - } - if(vfsSrc->FileExists(srcPath)) - { - CopyFile(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName(),overwrite); - } + if (vfsSrc->DirectoryExists(pathSrc)) { + vfsDest->CreateDirectory(pathDest); + for (auto &srcPath : vfsSrc->EnumeratePaths(pathSrc)) { + if (vfsSrc->DirectoryExists(srcPath)) { + CopyDirectory(vfsSrc, srcPath, vfsDest, + pathDest / srcPath.GetFileName()); + } + if (vfsSrc->FileExists(srcPath)) { + CopyFile(vfsSrc, srcPath, vfsDest, + pathDest / srcPath.GetFileName(), overwrite); } } } - void CopyDirectory(std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, VFSPath pathDest, std::function progress, bool overwrite) - { - if(vfsSrc->DirectoryExists(pathSrc)) - { - vfsDest->CreateDirectory(pathDest); - for(auto& srcPath : vfsSrc->EnumeratePaths(pathSrc)) - { - if(vfsSrc->DirectoryExists(srcPath)) - { - CopyDirectory(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName(),progress,overwrite); - } - if(vfsSrc->FileExists(srcPath)) - { - CopyFile(vfsSrc,srcPath,vfsDest,pathDest / srcPath.GetFileName(),[progress,srcPath](int64_t offset, int64_t length)->void { - progress(offset,length,srcPath); - },overwrite); - } +} +void CopyDirectory( + std::shared_ptr vfsSrc, VFSPath pathSrc, std::shared_ptr vfsDest, + VFSPath pathDest, + std::function + progress, + bool overwrite) { + if (vfsSrc->DirectoryExists(pathSrc)) { + vfsDest->CreateDirectory(pathDest); + for (auto &srcPath : vfsSrc->EnumeratePaths(pathSrc)) { + if (vfsSrc->DirectoryExists(srcPath)) { + CopyDirectory(vfsSrc, srcPath, vfsDest, + pathDest / srcPath.GetFileName(), progress, + overwrite); + } + if (vfsSrc->FileExists(srcPath)) { + CopyFile( + vfsSrc, srcPath, vfsDest, pathDest / srcPath.GetFileName(), + [progress, srcPath](int64_t offset, int64_t length) + -> void { progress(offset, length, srcPath); }, + overwrite); } } } +} -} \ No newline at end of file +} // namespace Tesses::Framework::Filesystem::Helpers \ No newline at end of file diff --git a/src/Filesystem/LocalFS.cpp b/src/Filesystem/LocalFS.cpp index f9e4e9a..cc2a123 100644 --- a/src/Filesystem/LocalFS.cpp +++ b/src/Filesystem/LocalFS.cpp @@ -1,3 +1,21 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Filesystem/LocalFS.hpp" #include "TessesFramework/Streams/FileStream.hpp" #include @@ -6,13 +24,14 @@ #include #include #if defined(_WIN32) -#include + #include "TessesFramework/Filesystem/VFSFix.hpp" +#include #undef min #else -#include #include #include +#include #endif #include "TessesFramework/Threading/Thread.hpp" @@ -21,465 +40,484 @@ #include #include #endif -namespace Tesses::Framework::Filesystem -{ - #if defined(_WIN32) - static void TimetToFileTime(time_t t, LPFILETIME pft) { - ULARGE_INTEGER time_value; - time_value.QuadPart = (t * 10000000LL) + 116444736000000000LL; - pft->dwLowDateTime = time_value.LowPart; - pft->dwHighDateTime = time_value.HighPart; - } - #endif - bool LocalFilesystem::Stat(VFSPath path, StatData& sfs) +namespace Tesses::Framework::Filesystem { +#if defined(_WIN32) +static void TimetToFileTime(time_t t, LPFILETIME pft) { + ULARGE_INTEGER time_value; + time_value.QuadPart = (t * 10000000LL) + 116444736000000000LL; + pft->dwLowDateTime = time_value.LowPart; + pft->dwHighDateTime = time_value.HighPart; +} +#endif +bool LocalFilesystem::Stat(VFSPath path, StatData &sfs) { + + std::string s = VFSPathToSystem(path); +#if defined(_WIN32) + + struct __stat64 st; + if (_stat64(s.c_str(), &st) == 0) +#else + struct stat st; + if (stat(s.c_str(), &st) == 0) +#endif + { - std::string s = VFSPathToSystem(path); - #if defined(_WIN32) + sfs.Device = (uint64_t)st.st_dev; + sfs.Inode = (uint64_t)st.st_ino; + sfs.Mode = (uint32_t)st.st_mode; + sfs.HardLinks = (uint64_t)st.st_nlink; + sfs.UserId = (uint32_t)st.st_uid; + sfs.GroupId = (uint32_t)st.st_gid; + sfs.DeviceId = (uint64_t)st.st_rdev; + sfs.Size = (uint64_t)st.st_size; +#if defined(_WIN32) + sfs.BlockSize = 512; + sfs.BlockCount = sfs.Size / sfs.BlockSize; +#else + sfs.BlockSize = (uint64_t)st.st_blksize; + sfs.BlockCount = (uint64_t)st.st_blocks; +#endif + sfs.LastAccess = Date::DateTime((int64_t)st.st_atime); + sfs.LastModified = Date::DateTime((int64_t)st.st_mtime); + sfs.LastStatus = Date::DateTime((int64_t)st.st_ctime); + return true; + } + return false; +} - struct __stat64 st; - if(_stat64(s.c_str(),&st) == 0) - #else - struct stat st; - if(stat(s.c_str(),&st) == 0) - #endif - - { +void LocalFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess) { + std::string s = VFSPathToSystem(path); +#if defined(TESSESFRAMEWORK_ENABLE_SETDATE) +#if defined(_WIN32) + FILETIME lastWriteF; + FILETIME lastAccessF; - sfs.Device = (uint64_t)st.st_dev; - sfs.Inode = (uint64_t)st.st_ino; - sfs.Mode = (uint32_t)st.st_mode; - sfs.HardLinks = (uint64_t)st.st_nlink; - sfs.UserId = (uint32_t)st.st_uid; - sfs.GroupId = (uint32_t)st.st_gid; - sfs.DeviceId = (uint64_t)st.st_rdev; - sfs.Size = (uint64_t)st.st_size; - #if defined(_WIN32) - sfs.BlockSize = 512; - sfs.BlockCount = sfs.Size / sfs.BlockSize; - #else - sfs.BlockSize = (uint64_t)st.st_blksize; - sfs.BlockCount = (uint64_t)st.st_blocks; - #endif - sfs.LastAccess = Date::DateTime((int64_t)st.st_atime); - sfs.LastModified = Date::DateTime((int64_t)st.st_mtime); - sfs.LastStatus = Date::DateTime((int64_t)st.st_ctime); - return true; - } - return false; + TimetToFileTime((time_t)lastWrite.ToEpoch(), &lastWriteF); + TimetToFileTime((time_t)lastAccess.ToEpoch(), &lastAccessF); + HANDLE hFile = + CreateFileA(s.c_str(), FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, // For directories + NULL); + if (hFile != INVALID_HANDLE_VALUE) { + SetFileTime(hFile, NULL, &lastAccessF, &lastWriteF); + CloseHandle(hFile); } +#else + struct utimbuf utim; + utim.actime = (time_t)lastAccess.ToEpoch(); + utim.modtime = (time_t)lastWrite.ToEpoch(); + utime(s.c_str(), &utim); +#endif +#endif +} +VFSPath LocalFilesystem::ReadLink(VFSPath path) { + auto res = + std::filesystem::read_symlink(this->VFSPathToSystem(path)).string(); + return this->SystemToVFSPath(res.c_str()); +} +std::shared_ptr +LocalFilesystem::OpenFile(VFSPath path, std::string mode) { + return std::make_shared( + VFSPathToSystem(path), mode); +} - void LocalFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess) - { - std::string s = VFSPathToSystem(path); - #if defined(TESSESFRAMEWORK_ENABLE_SETDATE) - #if defined(_WIN32) - FILETIME lastWriteF; - FILETIME lastAccessF; +void LocalFilesystem::DeleteDirectory(VFSPath path) { + std::filesystem::remove(VFSPathToSystem(path)); +} +void LocalFilesystem::DeleteFile(VFSPath path) { + std::filesystem::remove(VFSPathToSystem(path)); +} +void LocalFilesystem::CreateDirectory(VFSPath path) { + std::filesystem::create_directories(VFSPathToSystem(path)); +} +void LocalFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) { + if (std::filesystem::is_directory(VFSPathToSystem(existingFile))) { + std::filesystem::create_directory_symlink(VFSPathToSystem(existingFile), + VFSPathToSystem(symlinkFile)); + } else { + std::filesystem::create_symlink(VFSPathToSystem(existingFile), + VFSPathToSystem(symlinkFile)); + } +} +void LocalFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName) { + std::filesystem::create_hard_link(VFSPathToSystem(existingFile), + VFSPathToSystem(newName)); +} +void LocalFilesystem::MoveFile(VFSPath src, VFSPath dest) { + std::filesystem::rename(VFSPathToSystem(src), VFSPathToSystem(dest)); +} +void LocalFilesystem::MoveDirectory(VFSPath src, VFSPath dest) { + std::filesystem::rename(VFSPathToSystem(src), VFSPathToSystem(dest)); +} +std::string LocalFilesystem::VFSPathToSystem(VFSPath path) { +#if defined(_WIN32) + bool first = true; + std::string p = {}; + for (auto item : path.path) { + if (!(first && !item.empty() && item.back() == ':') && + !(first && path.relative)) + p.push_back('\\'); + p.append(item); + first = false; + } + return p; - TimetToFileTime((time_t)lastWrite.ToEpoch(),&lastWriteF); - TimetToFileTime((time_t)lastAccess.ToEpoch(),&lastAccessF); - HANDLE hFile = CreateFileA( - s.c_str(), - FILE_WRITE_ATTRIBUTES, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, // For directories - NULL - ); - if(hFile != INVALID_HANDLE_VALUE) - { - SetFileTime( - hFile, - NULL, - &lastAccessF, - &lastWriteF - ); - CloseHandle(hFile); - } - #else - struct utimbuf utim; - utim.actime = (time_t)lastAccess.ToEpoch(); - utim.modtime = (time_t)lastWrite.ToEpoch(); - utime(s.c_str(),&utim); - #endif - #endif - } - VFSPath LocalFilesystem::ReadLink(VFSPath path) - { - auto res = std::filesystem::read_symlink(this->VFSPathToSystem(path)).string(); - return this->SystemToVFSPath(res.c_str()); - } - std::shared_ptr LocalFilesystem::OpenFile(VFSPath path, std::string mode) - { - return std::make_shared(VFSPathToSystem(path), mode); - } +#else + return path.ToString(); +#endif +} +VFSPath LocalFilesystem::SystemToVFSPath(std::string path) { + VFSPath p; + p.path = VFSPath::SplitPath(path); + p.relative = true; + if (!path.empty()) { + if (path.front() == '/') + p.relative = false; + if (!p.path.empty()) { + auto firstPartPath = p.path.front(); - void LocalFilesystem::DeleteDirectory(VFSPath path) - { - std::filesystem::remove(VFSPathToSystem(path)); - } - void LocalFilesystem::DeleteFile(VFSPath path) - { - std::filesystem::remove(VFSPathToSystem(path)); - } - void LocalFilesystem::CreateDirectory(VFSPath path) - { - std::filesystem::create_directories(VFSPathToSystem(path)); - } - void LocalFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) - { - if(std::filesystem::is_directory(VFSPathToSystem(existingFile))) - { - std::filesystem::create_directory_symlink(VFSPathToSystem(existingFile),VFSPathToSystem(symlinkFile)); - } - else - { - std::filesystem::create_symlink(VFSPathToSystem(existingFile),VFSPathToSystem(symlinkFile)); + if (!firstPartPath.empty() && firstPartPath.back() == ':') + p.relative = false; } } - void LocalFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName) - { - std::filesystem::create_hard_link(VFSPathToSystem(existingFile),VFSPathToSystem(newName)); - } - void LocalFilesystem::MoveFile(VFSPath src, VFSPath dest) - { - std::filesystem::rename(VFSPathToSystem(src),VFSPathToSystem(dest)); - } - void LocalFilesystem::MoveDirectory(VFSPath src, VFSPath dest) - { - std::filesystem::rename(VFSPathToSystem(src),VFSPathToSystem(dest)); - } - std::string LocalFilesystem::VFSPathToSystem(VFSPath path) - { - #if defined(_WIN32) - bool first=true; - std::string p = {}; - for(auto item : path.path) - { - if(!(first && !item.empty() && item.back()==':') && !(first && path.relative)) - p.push_back('\\'); - p.append(item); - first=false; - } - return p; + return p; +} - #else - return path.ToString(); - #endif - } - VFSPath LocalFilesystem::SystemToVFSPath(std::string path) - { - VFSPath p; - p.path = VFSPath::SplitPath(path); - p.relative=true; - if(!path.empty()) - { - if(path.front() == '/') p.relative=false; - if(!p.path.empty()) - { - auto firstPartPath = p.path.front(); +VFSPathEnumerator LocalFilesystem::EnumeratePaths(VFSPath path) { + std::filesystem::path sysPath = VFSPathToSystem(path); + if (!std::filesystem::is_directory(sysPath)) + return VFSPathEnumerator(); - if(!firstPartPath.empty() && firstPartPath.back() == ':') p.relative=false; - } - } - return p; - } - - VFSPathEnumerator LocalFilesystem::EnumeratePaths(VFSPath path) - { - std::filesystem::path sysPath = VFSPathToSystem(path); - if(!std::filesystem::is_directory(sysPath)) return VFSPathEnumerator(); - - auto dir = new std::filesystem::directory_iterator(sysPath); - return VFSPathEnumerator([dir,path](VFSPath& path0)->bool { - std::filesystem::directory_iterator& ittr = *dir; - if(ittr != std::filesystem::directory_iterator()) - { + auto dir = new std::filesystem::directory_iterator(sysPath); + return VFSPathEnumerator( + [dir, path](VFSPath &path0) -> bool { + std::filesystem::directory_iterator &ittr = *dir; + if (ittr != std::filesystem::directory_iterator()) { path0 = VFSPath(path, ittr->path().filename().string()); ittr++; return true; } return false; - },[dir]()->void{ - delete dir; - }); - } - bool LocalFilesystem::StatVFS(VFSPath path, StatVFSData& data) - { - auto pathStr = this->VFSPathToSystem(path); - #if defined(_WIN32) - //not supporting windows yet - VFS::StatVFS(path, data); + }, + [dir]() -> void { delete dir; }); +} +bool LocalFilesystem::StatVFS(VFSPath path, StatVFSData &data) { + auto pathStr = this->VFSPathToSystem(path); +#if defined(_WIN32) + // not supporting windows yet + VFS::StatVFS(path, data); + return true; +#else + struct statvfs vfs; + if (statvfs(pathStr.c_str(), &vfs) == 0) { + data.BlockSize = vfs.f_bsize; + data.FragmentSize = vfs.f_frsize; + data.Blocks = vfs.f_blocks; + data.BlocksFree = vfs.f_bfree; + data.BlocksAvailable = vfs.f_bavail; + data.TotalInodes = vfs.f_files; + data.FreeInodes = vfs.f_ffree; + data.AvailableInodes = vfs.f_favail; + data.Id = vfs.f_fsid; + data.Flags = vfs.f_flag; + data.MaxNameLength = vfs.f_namemax; return true; - #else - struct statvfs vfs; - if(statvfs(pathStr.c_str(), &vfs) == 0) - { - data.BlockSize = vfs.f_bsize; - data.FragmentSize = vfs.f_frsize; - data.Blocks = vfs.f_blocks; - data.BlocksFree = vfs.f_bfree; - data.BlocksAvailable = vfs.f_bavail; - data.TotalInodes = vfs.f_files; - data.FreeInodes = vfs.f_ffree; - data.AvailableInodes = vfs.f_favail; - data.Id = vfs.f_fsid; - data.Flags = vfs.f_flag; - data.MaxNameLength = vfs.f_namemax; - return true; - } - return false; - #endif } + return false; +#endif +} - void LocalFilesystem::Chmod(VFSPath path, uint32_t mode) - { - auto pathStr = this->VFSPathToSystem(path); - #if defined(_WIN32) +void LocalFilesystem::Chmod(VFSPath path, uint32_t mode) { + auto pathStr = this->VFSPathToSystem(path); +#if defined(_WIN32) - #else - chmod(pathStr.c_str(), (mode_t)mode); - #endif - } - void LocalFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) - { - auto pathStr = this->VFSPathToSystem(path); - #if defined(_WIN32) +#else + chmod(pathStr.c_str(), (mode_t)mode); +#endif +} +void LocalFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) { + auto pathStr = this->VFSPathToSystem(path); +#if defined(_WIN32) - #else - chown(pathStr.c_str(), (uid_t)uid, (gid_t)gid); - #endif - } +#else + chown(pathStr.c_str(), (uid_t)uid, (gid_t)gid); +#endif +} - - FIFOCreationResult LocalFilesystem::CreateFIFO(VFSPath path, uint32_t mod) - { - auto pathStr = this->VFSPathToSystem(path); - #if defined(_WIN32) +FIFOCreationResult LocalFilesystem::CreateFIFO(VFSPath path, uint32_t mod) { + auto pathStr = this->VFSPathToSystem(path); +#if defined(_WIN32) + return FIFOCreationResult::Unsupported; +#else + int res = mkfifo(pathStr.c_str(), (mode_t)mod); + if (res == 0) + return FIFOCreationResult::Success; + else if (res == -1) { + switch (res) { + case EEXIST: + return FIFOCreationResult::Exists; + case ENOTSUP: return FIFOCreationResult::Unsupported; - #else - int res = mkfifo(pathStr.c_str(), (mode_t)mod); - if(res == 0) return FIFOCreationResult::Success; - else if(res == -1) - { - switch(res) - { - case EEXIST: - return FIFOCreationResult::Exists; - case ENOTSUP: - return FIFOCreationResult::Unsupported; - case EACCES: - return FIFOCreationResult::Denied; - case ENOSPC: - return FIFOCreationResult::OutOfInodes; - case EROFS: - return FIFOCreationResult::ReadOnlyFS; - } - } - - return FIFOCreationResult::UnknownError; - #endif - } - - - void LocalFilesystem::Lock(VFSPath path) - { - auto p2 = VFSPathToSystem(path); - const char* fopenPath = p2.c_str(); - while(true) - { - FILE* fp = fopen(fopenPath,"wx"); - if(fp) { - fclose(fp); - break; - } + case EACCES: + return FIFOCreationResult::Denied; + case ENOSPC: + return FIFOCreationResult::OutOfInodes; + case EROFS: + return FIFOCreationResult::ReadOnlyFS; } } - void LocalFilesystem::Unlock(VFSPath path) - { - std::error_code error; - std::filesystem::remove(VFSPathToSystem(path),error); + + return FIFOCreationResult::UnknownError; +#endif +} + +void LocalFilesystem::Lock(VFSPath path) { + auto p2 = VFSPathToSystem(path); + const char *fopenPath = p2.c_str(); + while (true) { + FILE *fp = fopen(fopenPath, "wx"); + if (fp) { + fclose(fp); + break; + } } - #if defined(__linux__) +} +void LocalFilesystem::Unlock(VFSPath path) { + std::error_code error; + std::filesystem::remove(VFSPathToSystem(path), error); +} +#if defined(__linux__) - class INotifyWatcher : public FSWatcher { - std::shared_ptr thrd; - static uint32_t to_linux_mask(FSWatcherEventType flags) - { - uint32_t lflags = 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Accessed) != 0) ? IN_ACCESS : 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::AttributeChanged) != 0) ? IN_ATTRIB : 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Writen) != 0) ? IN_CLOSE_WRITE : 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Read) != 0) ? IN_CLOSE_NOWRITE : 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Created) != 0) ? IN_CREATE : 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Deleted) != 0) ? IN_DELETE : 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::WatchEntryDeleted) != 0) ? IN_DELETE_SELF : 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Modified) != 0) ? IN_MODIFY : 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::WatchEntryMoved) != 0) ? IN_MOVE_SELF : 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::MoveOld) != 0) ? IN_MOVED_FROM : 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::MoveNew) != 0) ? IN_MOVED_TO : 0; - lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Opened) != 0) ? IN_OPEN : 0; +class INotifyWatcher : public FSWatcher { + std::shared_ptr thrd; + static uint32_t to_linux_mask(FSWatcherEventType flags) { + uint32_t lflags = 0; + lflags |= + (((uint32_t)flags & (uint32_t)FSWatcherEventType::Accessed) != 0) + ? IN_ACCESS + : 0; + lflags |= (((uint32_t)flags & + (uint32_t)FSWatcherEventType::AttributeChanged) != 0) + ? IN_ATTRIB + : 0; + lflags |= + (((uint32_t)flags & (uint32_t)FSWatcherEventType::Writen) != 0) + ? IN_CLOSE_WRITE + : 0; + lflags |= (((uint32_t)flags & (uint32_t)FSWatcherEventType::Read) != 0) + ? IN_CLOSE_NOWRITE + : 0; + lflags |= + (((uint32_t)flags & (uint32_t)FSWatcherEventType::Created) != 0) + ? IN_CREATE + : 0; + lflags |= + (((uint32_t)flags & (uint32_t)FSWatcherEventType::Deleted) != 0) + ? IN_DELETE + : 0; + lflags |= (((uint32_t)flags & + (uint32_t)FSWatcherEventType::WatchEntryDeleted) != 0) + ? IN_DELETE_SELF + : 0; + lflags |= + (((uint32_t)flags & (uint32_t)FSWatcherEventType::Modified) != 0) + ? IN_MODIFY + : 0; + lflags |= (((uint32_t)flags & + (uint32_t)FSWatcherEventType::WatchEntryMoved) != 0) + ? IN_MOVE_SELF + : 0; + lflags |= + (((uint32_t)flags & (uint32_t)FSWatcherEventType::MoveOld) != 0) + ? IN_MOVED_FROM + : 0; + lflags |= + (((uint32_t)flags & (uint32_t)FSWatcherEventType::MoveNew) != 0) + ? IN_MOVED_TO + : 0; + lflags |= + (((uint32_t)flags & (uint32_t)FSWatcherEventType::Opened) != 0) + ? IN_OPEN + : 0; - return lflags; - } - static FSWatcherEventType from_linux_mask(uint32_t lflags) - { - uint32_t flags = 0; - flags |= ((lflags & IN_ACCESS) != 0) ? (uint32_t)FSWatcherEventType::Accessed : 0; - flags |= ((lflags & IN_ATTRIB) != 0) ? (uint32_t)FSWatcherEventType::AttributeChanged : 0; - flags |= ((lflags & IN_CLOSE_WRITE) != 0) ? (uint32_t)FSWatcherEventType::Writen : 0; - flags |= ((lflags & IN_CLOSE_NOWRITE) != 0) ? (uint32_t)FSWatcherEventType::Read : 0; - flags |= ((lflags & IN_CREATE) != 0) ? (uint32_t)FSWatcherEventType::Created : 0; - flags |= ((lflags & IN_DELETE) != 0) ? (uint32_t)FSWatcherEventType::Deleted : 0; - flags |= ((lflags & IN_DELETE_SELF) != 0) ? (uint32_t)FSWatcherEventType::WatchEntryDeleted : 0; - flags |= ((lflags & IN_MODIFY) != 0) ? (uint32_t)FSWatcherEventType::Modified : 0; - flags |= ((lflags & IN_MOVE_SELF) != 0) ? (uint32_t)FSWatcherEventType::WatchEntryMoved : 0; - flags |= ((lflags & IN_MOVED_FROM) != 0) ? (uint32_t)FSWatcherEventType::MoveOld : 0; - flags |= ((lflags & IN_MOVED_TO) != 0) ? (uint32_t)FSWatcherEventType::MoveNew : 0; - flags |= ((lflags & IN_OPEN) != 0) ? (uint32_t)FSWatcherEventType::Opened : 0; + return lflags; + } + static FSWatcherEventType from_linux_mask(uint32_t lflags) { + uint32_t flags = 0; + flags |= ((lflags & IN_ACCESS) != 0) + ? (uint32_t)FSWatcherEventType::Accessed + : 0; + flags |= ((lflags & IN_ATTRIB) != 0) + ? (uint32_t)FSWatcherEventType::AttributeChanged + : 0; + flags |= ((lflags & IN_CLOSE_WRITE) != 0) + ? (uint32_t)FSWatcherEventType::Writen + : 0; + flags |= ((lflags & IN_CLOSE_NOWRITE) != 0) + ? (uint32_t)FSWatcherEventType::Read + : 0; + flags |= ((lflags & IN_CREATE) != 0) + ? (uint32_t)FSWatcherEventType::Created + : 0; + flags |= ((lflags & IN_DELETE) != 0) + ? (uint32_t)FSWatcherEventType::Deleted + : 0; + flags |= ((lflags & IN_DELETE_SELF) != 0) + ? (uint32_t)FSWatcherEventType::WatchEntryDeleted + : 0; + flags |= ((lflags & IN_MODIFY) != 0) + ? (uint32_t)FSWatcherEventType::Modified + : 0; + flags |= ((lflags & IN_MOVE_SELF) != 0) + ? (uint32_t)FSWatcherEventType::WatchEntryMoved + : 0; + flags |= ((lflags & IN_MOVED_FROM) != 0) + ? (uint32_t)FSWatcherEventType::MoveOld + : 0; + flags |= ((lflags & IN_MOVED_TO) != 0) + ? (uint32_t)FSWatcherEventType::MoveNew + : 0; + flags |= ((lflags & IN_OPEN) != 0) + ? (uint32_t)FSWatcherEventType::Opened + : 0; - return (FSWatcherEventType)flags; - } - public: - INotifyWatcher(std::shared_ptr vfs, VFSPath path) : FSWatcher(vfs,path) - { + return (FSWatcherEventType)flags; + } + public: + INotifyWatcher(std::shared_ptr vfs, VFSPath path) + : FSWatcher(vfs, path) {} + + protected: + void SetEnabledImpl(bool enabled) { + if (enabled) { + int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (fd == -1) { + throw std::runtime_error("Cannot init inotify"); } + auto str = this->GetFilesystem()->VFSPathToSystem(this->GetPath()); - protected: + int watch = + inotify_add_watch(fd, str.c_str(), to_linux_mask(this->events)); - void SetEnabledImpl(bool enabled) - { - if(enabled) - { - int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); - if (fd == -1) - { - throw std::runtime_error("Cannot init inotify"); - } - auto str = this->GetFilesystem()->VFSPathToSystem(this->GetPath()); + thrd = std::make_shared([this, watch, + fd]() -> void { + int cnt = 0; + struct pollfd pfd = {.fd = fd, .events = POLLIN}; + std::vector> mvFroms; + char buf[4096] + __attribute__((aligned(__alignof__(struct inotify_event)))); + const struct inotify_event *event; + ssize_t size; - int watch = inotify_add_watch(fd, str.c_str(),to_linux_mask(this->events)); + bool fail = false; - thrd = std::make_shared([this,watch,fd]()-> void { - int cnt = 0; - struct pollfd pfd = {.fd = fd, .events = POLLIN}; - std::vector> mvFroms; - char buf[4096] - __attribute__ ((aligned(__alignof__(struct inotify_event)))); - const struct inotify_event *event; - ssize_t size; + FSWatcherEvent evt; + evt.dest = this->GetPath(); + while (!fail && this->enabled) { + cnt = poll(&pfd, 1, -1); + if (cnt == -1) + break; - bool fail=false; + if (cnt > 0) { + if (pfd.revents & POLLIN) { + for (;;) { + size = read(fd, buf, sizeof(buf)); + if (size == -1 && errno != EAGAIN) { + fail = true; + break; + } - FSWatcherEvent evt; - evt.dest = this->GetPath(); - while(!fail && this->enabled) - { - cnt = poll(&pfd,1,-1); - if(cnt == -1) break; + if (size <= 0) + break; - if(cnt > 0) - { - if(pfd.revents & POLLIN) - { - for (;;) { - size = read(fd, buf, sizeof(buf)); - if (size == -1 && errno != EAGAIN) { - fail=true; - break; - } + for (char *ptr = buf; ptr < buf + size; + ptr += sizeof(struct inotify_event) + + event->len) { - if (size <= 0) - break; + event = (const struct inotify_event *)ptr; + VFSPath path = this->GetPath(); - for (char *ptr = buf; ptr < buf + size; - ptr += sizeof(struct inotify_event) + event->len) { + if (event->len) + path = path / + std::string(event->name, + (size_t)event->len); - event = (const struct inotify_event *) ptr; - VFSPath path = this->GetPath(); - - if(event->len) - path = path / std::string(event->name, (size_t)event->len); - - if(((uint32_t)this->events & (uint32_t)FSWatcherEventType::Moved) == (uint32_t)FSWatcherEventType::Moved && event->mask & IN_MOVED_FROM) - { - mvFroms.emplace_back(path,event->cookie); - } - else if(((uint32_t)this->events & (uint32_t)FSWatcherEventType::Moved) == (uint32_t)FSWatcherEventType::Moved && event->mask & IN_MOVED_TO) - { - for(auto ittr = mvFroms.begin(); ittr != mvFroms.end(); ittr++) - { - if(ittr->second == event->cookie) - { - evt.src = ittr->first; - mvFroms.erase(ittr); - break; - } - } - evt.isDir = (event->mask & IN_ISDIR); - evt.dest = path; - evt.type = FSWatcherEventType::Moved; - if(this->event) - this->event(evt); - } - else { - - evt.isDir = (event->mask & IN_ISDIR); - evt.src = path; - evt.type = from_linux_mask(event->mask);; - if(this->event) - this->event(evt); - } - if(event->mask & IN_MOVE_SELF) - { - close(fd); - return; - } - if(event->mask & IN_DELETE_SELF) - { - close(fd); - return; + if (((uint32_t)this->events & + (uint32_t)FSWatcherEventType::Moved) == + (uint32_t) + FSWatcherEventType::Moved && + event->mask & IN_MOVED_FROM) { + mvFroms.emplace_back(path, + event->cookie); + } else if ( + ((uint32_t)this->events & + (uint32_t)FSWatcherEventType::Moved) == + (uint32_t) + FSWatcherEventType::Moved && + event->mask & IN_MOVED_TO) { + for (auto ittr = mvFroms.begin(); + ittr != mvFroms.end(); ittr++) { + if (ittr->second == event->cookie) { + evt.src = ittr->first; + mvFroms.erase(ittr); + break; } } + evt.isDir = (event->mask & IN_ISDIR); + evt.dest = path; + evt.type = FSWatcherEventType::Moved; + if (this->event) + this->event(evt); + } else { + + evt.isDir = (event->mask & IN_ISDIR); + evt.src = path; + evt.type = from_linux_mask(event->mask); + ; + if (this->event) + this->event(evt); + } + if (event->mask & IN_MOVE_SELF) { + close(fd); + return; + } + if (event->mask & IN_DELETE_SELF) { + close(fd); + return; } } } } - - close(fd); - }); - - + } } - else - { - thrd = nullptr; - } - } - public: - ~INotifyWatcher() - { - this->enabled = false; - } - }; - #endif + close(fd); + }); - - std::shared_ptr LocalFilesystem::CreateWatcher(std::shared_ptr vfs, VFSPath path) - { - #if defined(__linux__) - return std::make_shared(vfs, path); - #endif - return VFS::CreateWatcher(vfs,path); + } else { + thrd = nullptr; + } } - std::shared_ptr LocalFS = std::make_shared(); - + public: + ~INotifyWatcher() { this->enabled = false; } +}; +#endif +std::shared_ptr +LocalFilesystem::CreateWatcher(std::shared_ptr vfs, VFSPath path) { +#if defined(__linux__) + return std::make_shared(vfs, path); +#endif + return VFS::CreateWatcher(vfs, path); } +std::shared_ptr LocalFS = std::make_shared(); + +} // namespace Tesses::Framework::Filesystem + // C:/Users/Jim/Joel diff --git a/src/Filesystem/MountableFilesystem.cpp b/src/Filesystem/MountableFilesystem.cpp index 3546d2e..1da03b1 100644 --- a/src/Filesystem/MountableFilesystem.cpp +++ b/src/Filesystem/MountableFilesystem.cpp @@ -1,552 +1,491 @@ #include "TessesFramework/Filesystem/MountableFilesystem.hpp" #include "TessesFramework/Filesystem/NullFilesystem.hpp" #include -namespace Tesses::Framework::Filesystem -{ - MountableFilesystem::MountableFilesystem() : MountableFilesystem(std::make_shared()) - { +namespace Tesses::Framework::Filesystem { +MountableFilesystem::MountableFilesystem() + : MountableFilesystem(std::make_shared()) {} +MountableFilesystem::MountableFilesystem(std::shared_ptr root) { + this->root = root; +} +MountableDirectory::~MountableDirectory() { + for (auto dir : this->dirs) + delete dir; +} - } - MountableFilesystem::MountableFilesystem(std::shared_ptr root) - { - this->root = root; - } - MountableDirectory::~MountableDirectory() - { - for(auto dir : this->dirs) delete dir; - } +MountableFilesystem::~MountableFilesystem() { + for (auto item : this->directories) + delete item; +} - MountableFilesystem::~MountableFilesystem() - { - for(auto item : this->directories) delete item; - } - - - - - void MountableFilesystem::GetFS(VFSPath srcPath, VFSPath& destRoot, VFSPath& destPath, std::shared_ptr& vfs) - { - if(srcPath.path.empty()) return; - for(auto item : this->directories) - { - if(srcPath.path.front() == item->name) - { - if(item->vfs != nullptr) - { - vfs = item->vfs; - VFSPath srcPath1(std::vector(srcPath.path.begin()+1,srcPath.path.end())); - srcPath1.relative=false; - destPath = srcPath1; - destRoot = VFSPath(VFSPath(),item->name); - - - } - VFSPath srcPath2(std::vector(srcPath.path.begin()+1,srcPath.path.end())); - srcPath2.relative=false; - item->GetFS(srcPath2,VFSPath(VFSPath(),item->name), destRoot,destPath,vfs); - return; +void MountableFilesystem::GetFS(VFSPath srcPath, VFSPath &destRoot, + VFSPath &destPath, std::shared_ptr &vfs) { + if (srcPath.path.empty()) + return; + for (auto item : this->directories) { + if (srcPath.path.front() == item->name) { + if (item->vfs != nullptr) { + vfs = item->vfs; + VFSPath srcPath1( + std::vector(srcPath.path.begin() + 1, srcPath.path.end())); + srcPath1.relative = false; + destPath = srcPath1; + destRoot = VFSPath(VFSPath(), item->name); } + VFSPath srcPath2( + std::vector(srcPath.path.begin() + 1, srcPath.path.end())); + srcPath2.relative = false; + item->GetFS(srcPath2, VFSPath(VFSPath(), item->name), destRoot, + destPath, vfs); + return; } } +} +void MountableDirectory::GetFS(VFSPath srcPath, VFSPath curDir, + VFSPath &destRoot, VFSPath &destPath, + std::shared_ptr &vfs) { + if (srcPath.path.empty()) + return; + for (auto item : this->dirs) { + if (!srcPath.path.empty() && srcPath.path.front() == item->name) { + if (item->vfs != nullptr) { + vfs = item->vfs; + VFSPath srcPath1( + std::vector(srcPath.path.begin() + 1, srcPath.path.end())); - void MountableDirectory::GetFS(VFSPath srcPath, VFSPath curDir, VFSPath& destRoot, VFSPath& destPath, std::shared_ptr& vfs) - { - if(srcPath.path.empty()) return; - for(auto item : this->dirs) - { - if(!srcPath.path.empty() && srcPath.path.front() == item->name) - { - if(item->vfs != nullptr) - { - vfs = item->vfs; - - VFSPath srcPath1(std::vector(srcPath.path.begin()+1,srcPath.path.end())); - - srcPath1.relative=false; - destPath = srcPath1; - destRoot = curDir; - - } - VFSPath srcPath2(std::vector(srcPath.path.begin()+1,srcPath.path.end())); - srcPath2.relative=false; - item->GetFS(srcPath2,VFSPath(curDir,item->name), destRoot,destPath,vfs); - return; + srcPath1.relative = false; + destPath = srcPath1; + destRoot = curDir; } + VFSPath srcPath2( + std::vector(srcPath.path.begin() + 1, srcPath.path.end())); + srcPath2.relative = false; + item->GetFS(srcPath2, VFSPath(curDir, item->name), destRoot, + destPath, vfs); + return; } } - - VFSPath MountableFilesystem::ReadLink(VFSPath path) - { - path = path.CollapseRelativeParents(); - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - if(vfs != nullptr) - return VFSPath(destRoot,vfs->ReadLink(destPath)); - return VFSPath(); - } - - bool MountableFilesystem::StatVFS(VFSPath path, StatVFSData& data) - { - path = path.CollapseRelativeParents(); - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - if(vfs != nullptr) - return vfs->StatVFS(destPath,data); - return false; - } - - bool MountableFilesystem::Stat(VFSPath path, StatData& data) - { - path = path.CollapseRelativeParents(); - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - if(vfs != nullptr) - return vfs->Stat(destPath,data); - return false; - } - - - void MountableFilesystem::Chmod(VFSPath path, uint32_t mode) - { - path = path.CollapseRelativeParents(); - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - if(vfs != nullptr) - vfs->Chmod(destPath,mode); - } - - void MountableFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) - { - path = path.CollapseRelativeParents(); - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - if(vfs != nullptr) - vfs->Chown(destPath,uid, gid); - } - - - FIFOCreationResult MountableFilesystem::CreateFIFO(VFSPath path, uint32_t mod) - { - path = path.CollapseRelativeParents(); - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - if(vfs != nullptr) - return vfs->CreateFIFO(destPath,mod); - return FIFOCreationResult::UnknownError; - } - - - std::shared_ptr MountableFilesystem::OpenFile(VFSPath path, std::string mode) - { - path = path.CollapseRelativeParents(); - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - - if(vfs != nullptr) - return vfs->OpenFile(destPath,mode); - return nullptr; - } - void MountableFilesystem::CreateDirectory(VFSPath path) - { - path = path.CollapseRelativeParents(); - - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - - if(destPath.path.empty()) return; - - if(vfs != nullptr) - vfs->CreateDirectory(destPath); - - } - - - void MountableFilesystem::DeleteDirectory(VFSPath path) - { - path = path.CollapseRelativeParents(); - - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - - if(destPath.path.empty()) return; - - if(vfs != nullptr) - vfs->DeleteDirectory(destPath); - - } - - - - - void MountableFilesystem::DeleteFile(VFSPath path) - { - path = path.CollapseRelativeParents(); - - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - - if(vfs != nullptr) - vfs->DeleteFile(destPath); - - } - void MountableFilesystem::Lock(VFSPath path) - { - path = path.CollapseRelativeParents(); - - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - - if(vfs != nullptr) - vfs->Lock(destPath); - - } - void MountableFilesystem::Unlock(VFSPath path) - { - path = path.CollapseRelativeParents(); - - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - - if(vfs != nullptr) - vfs->Unlock(destPath); - - } - void MountableFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess) - { - - path = path.CollapseRelativeParents(); - - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; - - GetFS(path, destRoot, destPath, vfs); - - if(vfs != nullptr) - vfs->SetDate(destPath,lastWrite,lastAccess); - } - void MountableFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) - { - existingFile = existingFile.CollapseRelativeParents(); - symlinkFile = existingFile.CollapseRelativeParents(); - - VFSPath existingDestRoot; - VFSPath existingDestPath = existingFile; - std::shared_ptr existingVFS = root; - VFSPath symlinkDestRoot; - VFSPath symlinkDestPath = symlinkFile; - std::shared_ptr symlinkVFS = root; - - GetFS(existingFile, existingDestRoot, existingDestPath, existingVFS); - GetFS(symlinkFile, symlinkDestRoot, symlinkDestPath, symlinkVFS); - - if(existingVFS != nullptr && existingVFS == symlinkVFS) - existingVFS->CreateSymlink(existingDestPath, symlinkDestPath); - } - - void MountableFilesystem::MoveDirectory(VFSPath src, VFSPath dest) - { - src = src.CollapseRelativeParents(); - dest = dest.CollapseRelativeParents(); - - VFSPath srcDestRoot; - VFSPath srcDestPath = src; - std::shared_ptr srcVFS = root; - VFSPath destDestRoot; - VFSPath destDestPath = dest; - std::shared_ptr destVFS = root; - - GetFS(src, srcDestRoot, srcDestPath, srcVFS); - GetFS(dest, destDestRoot, destDestPath, destVFS); - - if(srcVFS != nullptr && srcVFS == destVFS) - srcVFS->MoveDirectory(srcDestPath, destDestPath); - } - void MountableFilesystem::MoveFile(VFSPath src, VFSPath dest) - { - src = src.CollapseRelativeParents(); - dest = dest.CollapseRelativeParents(); - - VFSPath srcDestRoot; - VFSPath srcDestPath = src; - std::shared_ptr srcVFS = root; - VFSPath destDestRoot; - VFSPath destDestPath = dest; - std::shared_ptr destVFS = root; - - GetFS(src, srcDestRoot, srcDestPath, srcVFS); - GetFS(dest, destDestRoot, destDestPath, destVFS); - - if(srcVFS != nullptr && srcVFS == destVFS) - srcVFS->MoveFile(srcDestPath, destDestPath); - } - void MountableFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName) - { - existingFile = existingFile.CollapseRelativeParents(); - newName = existingFile.CollapseRelativeParents(); - - VFSPath existingDestRoot; - VFSPath existingDestPath = existingFile; - std::shared_ptr existingVFS = root; - VFSPath newNameRoot; - VFSPath newNamePath = newName; - std::shared_ptr newNameVFS = root; - - GetFS(existingFile, existingDestRoot, existingDestPath, existingVFS); - GetFS(newName, newNameRoot, newNamePath, newNameVFS); - - if(existingVFS != nullptr && existingVFS == newNameVFS) - existingVFS->CreateHardlink(existingDestPath, newNamePath); - } - class MountableEnumerationState { - public: - VFSPathEnumerator* enumerator; - std::vector dirs; - size_t index; - }; - VFSPathEnumerator MountableFilesystem::EnumeratePaths(VFSPath path) - { - - path = path.CollapseRelativeParents(); - bool mydirs = path.path.empty(); - std::vector* dirs = &this->directories; - - if(!path.path.empty()) - for(auto p : path.path) - { - mydirs=true; - bool hasSet=false; - - for(auto itm : *dirs) - { - if(itm->name == p) - { - hasSet=true; +} + +VFSPath MountableFilesystem::ReadLink(VFSPath path) { + path = path.CollapseRelativeParents(); + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + if (vfs != nullptr) + return VFSPath(destRoot, vfs->ReadLink(destPath)); + return VFSPath(); +} + +bool MountableFilesystem::StatVFS(VFSPath path, StatVFSData &data) { + path = path.CollapseRelativeParents(); + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + if (vfs != nullptr) + return vfs->StatVFS(destPath, data); + return false; +} + +bool MountableFilesystem::Stat(VFSPath path, StatData &data) { + path = path.CollapseRelativeParents(); + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + if (vfs != nullptr) + return vfs->Stat(destPath, data); + return false; +} + +void MountableFilesystem::Chmod(VFSPath path, uint32_t mode) { + path = path.CollapseRelativeParents(); + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + if (vfs != nullptr) + vfs->Chmod(destPath, mode); +} + +void MountableFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) { + path = path.CollapseRelativeParents(); + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + if (vfs != nullptr) + vfs->Chown(destPath, uid, gid); +} + +FIFOCreationResult MountableFilesystem::CreateFIFO(VFSPath path, uint32_t mod) { + path = path.CollapseRelativeParents(); + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + if (vfs != nullptr) + return vfs->CreateFIFO(destPath, mod); + return FIFOCreationResult::UnknownError; +} + +std::shared_ptr +MountableFilesystem::OpenFile(VFSPath path, std::string mode) { + path = path.CollapseRelativeParents(); + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + + if (vfs != nullptr) + return vfs->OpenFile(destPath, mode); + return nullptr; +} +void MountableFilesystem::CreateDirectory(VFSPath path) { + path = path.CollapseRelativeParents(); + + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + + if (destPath.path.empty()) + return; + + if (vfs != nullptr) + vfs->CreateDirectory(destPath); +} + +void MountableFilesystem::DeleteDirectory(VFSPath path) { + path = path.CollapseRelativeParents(); + + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + + if (destPath.path.empty()) + return; + + if (vfs != nullptr) + vfs->DeleteDirectory(destPath); +} + +void MountableFilesystem::DeleteFile(VFSPath path) { + path = path.CollapseRelativeParents(); + + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + + if (vfs != nullptr) + vfs->DeleteFile(destPath); +} +void MountableFilesystem::Lock(VFSPath path) { + path = path.CollapseRelativeParents(); + + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + + if (vfs != nullptr) + vfs->Lock(destPath); +} +void MountableFilesystem::Unlock(VFSPath path) { + path = path.CollapseRelativeParents(); + + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + + if (vfs != nullptr) + vfs->Unlock(destPath); +} +void MountableFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess) { + + path = path.CollapseRelativeParents(); + + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; + + GetFS(path, destRoot, destPath, vfs); + + if (vfs != nullptr) + vfs->SetDate(destPath, lastWrite, lastAccess); +} +void MountableFilesystem::CreateSymlink(VFSPath existingFile, + VFSPath symlinkFile) { + existingFile = existingFile.CollapseRelativeParents(); + symlinkFile = existingFile.CollapseRelativeParents(); + + VFSPath existingDestRoot; + VFSPath existingDestPath = existingFile; + std::shared_ptr existingVFS = root; + VFSPath symlinkDestRoot; + VFSPath symlinkDestPath = symlinkFile; + std::shared_ptr symlinkVFS = root; + + GetFS(existingFile, existingDestRoot, existingDestPath, existingVFS); + GetFS(symlinkFile, symlinkDestRoot, symlinkDestPath, symlinkVFS); + + if (existingVFS != nullptr && existingVFS == symlinkVFS) + existingVFS->CreateSymlink(existingDestPath, symlinkDestPath); +} + +void MountableFilesystem::MoveDirectory(VFSPath src, VFSPath dest) { + src = src.CollapseRelativeParents(); + dest = dest.CollapseRelativeParents(); + + VFSPath srcDestRoot; + VFSPath srcDestPath = src; + std::shared_ptr srcVFS = root; + VFSPath destDestRoot; + VFSPath destDestPath = dest; + std::shared_ptr destVFS = root; + + GetFS(src, srcDestRoot, srcDestPath, srcVFS); + GetFS(dest, destDestRoot, destDestPath, destVFS); + + if (srcVFS != nullptr && srcVFS == destVFS) + srcVFS->MoveDirectory(srcDestPath, destDestPath); +} +void MountableFilesystem::MoveFile(VFSPath src, VFSPath dest) { + src = src.CollapseRelativeParents(); + dest = dest.CollapseRelativeParents(); + + VFSPath srcDestRoot; + VFSPath srcDestPath = src; + std::shared_ptr srcVFS = root; + VFSPath destDestRoot; + VFSPath destDestPath = dest; + std::shared_ptr destVFS = root; + + GetFS(src, srcDestRoot, srcDestPath, srcVFS); + GetFS(dest, destDestRoot, destDestPath, destVFS); + + if (srcVFS != nullptr && srcVFS == destVFS) + srcVFS->MoveFile(srcDestPath, destDestPath); +} +void MountableFilesystem::CreateHardlink(VFSPath existingFile, + VFSPath newName) { + existingFile = existingFile.CollapseRelativeParents(); + newName = existingFile.CollapseRelativeParents(); + + VFSPath existingDestRoot; + VFSPath existingDestPath = existingFile; + std::shared_ptr existingVFS = root; + VFSPath newNameRoot; + VFSPath newNamePath = newName; + std::shared_ptr newNameVFS = root; + + GetFS(existingFile, existingDestRoot, existingDestPath, existingVFS); + GetFS(newName, newNameRoot, newNamePath, newNameVFS); + + if (existingVFS != nullptr && existingVFS == newNameVFS) + existingVFS->CreateHardlink(existingDestPath, newNamePath); +} +class MountableEnumerationState { + public: + VFSPathEnumerator *enumerator; + std::vector dirs; + size_t index; +}; +VFSPathEnumerator MountableFilesystem::EnumeratePaths(VFSPath path) { + + path = path.CollapseRelativeParents(); + bool mydirs = path.path.empty(); + std::vector *dirs = &this->directories; + + if (!path.path.empty()) + for (auto p : path.path) { + mydirs = true; + bool hasSet = false; + + for (auto itm : *dirs) { + if (itm->name == p) { + hasSet = true; dirs = &itm->dirs; break; } } - if(!hasSet) - { - mydirs=false; + if (!hasSet) { + mydirs = false; break; } } - VFSPath destRoot; - VFSPath destPath = path; - std::shared_ptr vfs = root; + VFSPath destRoot; + VFSPath destPath = path; + std::shared_ptr vfs = root; - GetFS(path, destRoot, destPath, vfs); + GetFS(path, destRoot, destPath, vfs); - - MountableEnumerationState* state = new MountableEnumerationState(); - state->dirs = *dirs; - state->index = 0; - if(vfs->DirectoryExists(destPath) || !mydirs) + MountableEnumerationState *state = new MountableEnumerationState(); + state->dirs = *dirs; + state->index = 0; + if (vfs->DirectoryExists(destPath) || !mydirs) state->enumerator = vfs->EnumeratePaths(destPath).MakePointer(); - else + else state->enumerator = nullptr; - return VFSPathEnumerator([state,path](VFSPath& path0)->bool{ - - while(state->enumerator != nullptr && state->enumerator->MoveNext()) - { + return VFSPathEnumerator( + [state, path](VFSPath &path0) -> bool { + while (state->enumerator != nullptr && + state->enumerator->MoveNext()) { auto fname = state->enumerator->Current.GetFileName(); - bool mustContinue=false; - for(auto item : state->dirs) - { - if(item->name == fname) - { - mustContinue=true; + bool mustContinue = false; + for (auto item : state->dirs) { + if (item->name == fname) { + mustContinue = true; break; } } - if(mustContinue) continue; + if (mustContinue) + continue; path0 = path / fname; return true; } - if(state->enumerator != nullptr) - { + if (state->enumerator != nullptr) { delete state->enumerator; state->enumerator = nullptr; } - if(state->index < state->dirs.size()) - { - path0 = path / state->dirs[state->index++]->name; + if (state->index < state->dirs.size()) { + path0 = path / state->dirs[state->index++]->name; return true; } return false; - },[state]()->void{ - if(state->enumerator) delete state->enumerator; + }, + [state]() -> void { + if (state->enumerator) + delete state->enumerator; delete state; }); +} + +void MountableFilesystem::Mount(VFSPath path, std::shared_ptr fs) { + path = path.CollapseRelativeParents(); + + if (path.path.empty()) { + return; } - void MountableFilesystem::Mount(VFSPath path, std::shared_ptr fs) - { - path = path.CollapseRelativeParents(); - - if(path.path.empty()) - { - return; - } - - auto* fsLs = &this->directories; - bool needToCreate=true; - for(auto index = path.path.begin(); index < path.path.end()-1; index++) - { - needToCreate=true; - for(auto item : *fsLs) - { - if(item->name == *index) - { - needToCreate=false; - fsLs = &(item->dirs); - break; - } - } - if(needToCreate) - { - MountableDirectory* dir = new MountableDirectory(); - dir->name = *index; - dir->owns=false; - dir->vfs=NULL; - - fsLs->push_back(dir); - fsLs = &(dir->dirs); - } - } - - needToCreate=true; - std::string lastDir = path.GetFileName(); - - for(auto item : *fsLs) - { - if(item->name == lastDir) - { - needToCreate=false; - - item->vfs = fs; + auto *fsLs = &this->directories; + bool needToCreate = true; + for (auto index = path.path.begin(); index < path.path.end() - 1; index++) { + needToCreate = true; + for (auto item : *fsLs) { + if (item->name == *index) { + needToCreate = false; + fsLs = &(item->dirs); break; } } + if (needToCreate) { + MountableDirectory *dir = new MountableDirectory(); + dir->name = *index; + dir->owns = false; + dir->vfs = NULL; - if(needToCreate) - { - MountableDirectory* dir = new MountableDirectory(); - dir->name = lastDir; - - dir->vfs=fs; - fsLs->push_back(dir); - } - - } - - static bool myumount(MountableDirectory* dir,VFSPath path) - { - if(path.path.empty()) - { - dir->vfs = nullptr; - } - - if(dir->dirs.empty()) - { - delete dir; - return true; - } - - for(auto index = dir->dirs.begin(); index < dir->dirs.end(); index++) - { - auto item = *index; - if(!path.path.empty() && path.path.front() == item->name) - { - VFSPath srcPath2(std::vector(path.path.begin()+1,path.path.end())); - - - - if(myumount(item,srcPath2)) - { - dir->dirs.erase(index); - } - - if(dir->dirs.empty()) - { - delete dir; - return true; - } - return false; - } - } - return false; - } - - void MountableFilesystem::Unmount(VFSPath path) - { - path = path.CollapseRelativeParents(); - - for(auto index = this->directories.begin(); index < this->directories.end(); index++) - { - auto item = *index; - if(!path.path.empty() && path.path.front() == item->name) - { - VFSPath srcPath2(std::vector(path.path.begin()+1,path.path.end())); - - if(myumount(item,srcPath2)) - { - this->directories.erase(index); - } - return; - } + fsLs->push_back(dir); + fsLs = &(dir->dirs); } } - std::string MountableFilesystem::VFSPathToSystem(VFSPath path) - { - return path.ToString(); + needToCreate = true; + std::string lastDir = path.GetFileName(); + + for (auto item : *fsLs) { + if (item->name == lastDir) { + needToCreate = false; + + item->vfs = fs; + break; + } } - VFSPath MountableFilesystem::SystemToVFSPath(std::string path) - { - return VFSPath(path); + + if (needToCreate) { + MountableDirectory *dir = new MountableDirectory(); + dir->name = lastDir; + + dir->vfs = fs; + fsLs->push_back(dir); } } + +static bool myumount(MountableDirectory *dir, VFSPath path) { + if (path.path.empty()) { + dir->vfs = nullptr; + } + + if (dir->dirs.empty()) { + delete dir; + return true; + } + + for (auto index = dir->dirs.begin(); index < dir->dirs.end(); index++) { + auto item = *index; + if (!path.path.empty() && path.path.front() == item->name) { + VFSPath srcPath2( + std::vector(path.path.begin() + 1, path.path.end())); + + if (myumount(item, srcPath2)) { + dir->dirs.erase(index); + } + + if (dir->dirs.empty()) { + delete dir; + return true; + } + return false; + } + } + return false; +} + +void MountableFilesystem::Unmount(VFSPath path) { + path = path.CollapseRelativeParents(); + + for (auto index = this->directories.begin(); + index < this->directories.end(); index++) { + auto item = *index; + if (!path.path.empty() && path.path.front() == item->name) { + VFSPath srcPath2( + std::vector(path.path.begin() + 1, path.path.end())); + + if (myumount(item, srcPath2)) { + this->directories.erase(index); + } + return; + } + } +} + +std::string MountableFilesystem::VFSPathToSystem(VFSPath path) { + return path.ToString(); +} +VFSPath MountableFilesystem::SystemToVFSPath(std::string path) { + return VFSPath(path); +} +} // namespace Tesses::Framework::Filesystem diff --git a/src/Filesystem/NullFilesystem.cpp b/src/Filesystem/NullFilesystem.cpp index 5e9c4b1..d0b1399 100644 --- a/src/Filesystem/NullFilesystem.cpp +++ b/src/Filesystem/NullFilesystem.cpp @@ -1,28 +1,20 @@ #include "TessesFramework/Filesystem/NullFilesystem.hpp" -namespace Tesses::Framework::Filesystem -{ - std::shared_ptr NullFilesystem::OpenFile(VFSPath path, std::string mode) - { - return nullptr; - } - - - VFSPathEnumerator NullFilesystem::EnumeratePaths(VFSPath path) - { - return VFSPathEnumerator(); - } - std::string NullFilesystem::VFSPathToSystem(VFSPath path) - { - return path.ToString(); - } - VFSPath NullFilesystem::SystemToVFSPath(std::string path) - { - return VFSPath(path); - } - - bool NullFilesystem::Stat(VFSPath path, StatData& data) - { - return false; - } +namespace Tesses::Framework::Filesystem { +std::shared_ptr +NullFilesystem::OpenFile(VFSPath path, std::string mode) { + return nullptr; } + +VFSPathEnumerator NullFilesystem::EnumeratePaths(VFSPath path) { + return VFSPathEnumerator(); +} +std::string NullFilesystem::VFSPathToSystem(VFSPath path) { + return path.ToString(); +} +VFSPath NullFilesystem::SystemToVFSPath(std::string path) { + return VFSPath(path); +} + +bool NullFilesystem::Stat(VFSPath path, StatData &data) { return false; } +} // namespace Tesses::Framework::Filesystem diff --git a/src/Filesystem/RelativeFilesystem.cpp b/src/Filesystem/RelativeFilesystem.cpp index b09d4a2..df78d9a 100644 --- a/src/Filesystem/RelativeFilesystem.cpp +++ b/src/Filesystem/RelativeFilesystem.cpp @@ -1,206 +1,163 @@ #include "TessesFramework/Filesystem/RelativeFilesystem.hpp" -namespace Tesses::Framework::Filesystem -{ - VFSPath RelativeFilesystem::ToParent(VFSPath path) - { - if(path.relative) - { - return path.MakeAbsolute(GetWorking()); - } - else - { - return path; - } +namespace Tesses::Framework::Filesystem { +VFSPath RelativeFilesystem::ToParent(VFSPath path) { + if (path.relative) { + return path.MakeAbsolute(GetWorking()); + } else { + return path; } +} - RelativeFilesystem::RelativeFilesystem(std::shared_ptr vfs, VFSPath working) : vfs(vfs), working(working) - { +RelativeFilesystem::RelativeFilesystem(std::shared_ptr vfs, + VFSPath working) + : vfs(vfs), working(working) {} +VFSPath RelativeFilesystem::GetWorking() { + mtx.Lock(); + auto p = this->working; + mtx.Unlock(); + return p; +} +void RelativeFilesystem::SetWorking(VFSPath path) { + mtx.Lock(); + this->working = path; + mtx.Unlock(); +} +std::shared_ptr RelativeFilesystem::GetVFS() { return vfs; } - } - VFSPath RelativeFilesystem::GetWorking() - { - mtx.Lock(); - auto p = this->working; - mtx.Unlock(); - return p; - } - void RelativeFilesystem::SetWorking(VFSPath path) - { - mtx.Lock(); - this->working=path; - mtx.Unlock(); - } - std::shared_ptr RelativeFilesystem::GetVFS() - { - return vfs; - } +std::shared_ptr +RelativeFilesystem::OpenFile(VFSPath path, std::string mode) { + return this->vfs->OpenFile(ToParent(path), mode); +} +void RelativeFilesystem::CreateDirectory(VFSPath path) { + this->vfs->CreateDirectory(ToParent(path)); +} +void RelativeFilesystem::DeleteDirectory(VFSPath path) { + this->vfs->DeleteDirectory(ToParent(path)); +} +void RelativeFilesystem::DeleteFile(VFSPath path) { + this->vfs->DeleteFile(ToParent(path)); +} +void RelativeFilesystem::CreateSymlink(VFSPath existingFile, + VFSPath symlinkFile) { + this->vfs->CreateSymlink(existingFile, ToParent(symlinkFile)); +} +VFSPathEnumerator RelativeFilesystem::EnumeratePaths(VFSPath path) { + VFSPathEnumerator *enumerator = + this->vfs->EnumeratePaths(ToParent(path)).MakePointer(); - std::shared_ptr RelativeFilesystem::OpenFile(VFSPath path, std::string mode) - { - return this->vfs->OpenFile(ToParent(path),mode); - } - void RelativeFilesystem::CreateDirectory(VFSPath path) - { - this->vfs->CreateDirectory(ToParent(path)); - } - void RelativeFilesystem::DeleteDirectory(VFSPath path) - { - this->vfs->DeleteDirectory(ToParent(path)); - } - void RelativeFilesystem::DeleteFile(VFSPath path) - { - this->vfs->DeleteFile(ToParent(path)); - } - void RelativeFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) - { - this->vfs->CreateSymlink(existingFile, ToParent(symlinkFile)); - } - VFSPathEnumerator RelativeFilesystem::EnumeratePaths(VFSPath path) - { - VFSPathEnumerator* enumerator = this->vfs->EnumeratePaths(ToParent(path)).MakePointer(); - - return VFSPathEnumerator([enumerator,path,this](VFSPath& path0)->bool{ - if(enumerator->MoveNext()) - { + return VFSPathEnumerator( + [enumerator, path, this](VFSPath &path0) -> bool { + if (enumerator->MoveNext()) { path0 = path / enumerator->Current.GetFileName(); return true; } return false; - },[enumerator]()->void{ - delete enumerator; - }); - } - void RelativeFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName) - { - if(existingFile.relative || newName.relative) - { - auto working = GetWorking(); - if(existingFile.relative) - existingFile = existingFile.MakeAbsolute(working); - - if(newName.relative) - newName = newName.MakeAbsolute(working); - } - vfs->CreateHardlink(existingFile, newName); - } - void RelativeFilesystem::MoveFile(VFSPath src, VFSPath dest) - { - if(src.relative || dest.relative) - { - auto working = GetWorking(); - if(src.relative) - src = src.MakeAbsolute(working); - - if(dest.relative) - dest = dest.MakeAbsolute(working); - } - vfs->MoveFile(src, dest); - } - void RelativeFilesystem::MoveDirectory(VFSPath src, VFSPath dest) - { - if(src.relative || dest.relative) - { - auto working = GetWorking(); - if(src.relative) - src = src.MakeAbsolute(working); - - if(dest.relative) - dest = dest.MakeAbsolute(working); - } - vfs->MoveDirectory(src, dest); - } - void RelativeFilesystem::DeleteDirectoryRecurse(VFSPath path) - { - vfs->DeleteDirectoryRecurse(ToParent(path)); - } - VFSPath RelativeFilesystem::ReadLink(VFSPath path) - { - return vfs->ReadLink(ToParent(path)); - } - std::string RelativeFilesystem::VFSPathToSystem(VFSPath path) - { - return vfs->VFSPathToSystem(path); - } - VFSPath RelativeFilesystem::SystemToVFSPath(std::string path) - { - return vfs->SystemToVFSPath(path); - } - void RelativeFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess) - { - vfs->SetDate(ToParent(path),lastWrite,lastAccess); - } - bool RelativeFilesystem::StatVFS(VFSPath path, StatVFSData& vfsData) - { - return vfs->StatVFS(ToParent(path), vfsData); - } - bool RelativeFilesystem::Stat(VFSPath path, StatData& data) - { - return vfs->Stat(ToParent(path),data); - } + }, + [enumerator]() -> void { delete enumerator; }); +} +void RelativeFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName) { + if (existingFile.relative || newName.relative) { + auto working = GetWorking(); + if (existingFile.relative) + existingFile = existingFile.MakeAbsolute(working); - void RelativeFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) - { - vfs->Chown(ToParent(path),uid,gid); + if (newName.relative) + newName = newName.MakeAbsolute(working); } - void RelativeFilesystem::Chmod(VFSPath path, uint32_t mode) - { - vfs->Chmod(ToParent(path), mode); - } - FIFOCreationResult RelativeFilesystem::CreateFIFO(VFSPath path, uint32_t mode) - { - return vfs->CreateFIFO(ToParent(path),mode); - } - void RelativeFilesystem::Lock(VFSPath path) - { - vfs->Lock(ToParent(path)); - } - void RelativeFilesystem::Unlock(VFSPath path) - { - vfs->Unlock(ToParent(path)); - } - + vfs->CreateHardlink(existingFile, newName); +} +void RelativeFilesystem::MoveFile(VFSPath src, VFSPath dest) { + if (src.relative || dest.relative) { + auto working = GetWorking(); + if (src.relative) + src = src.MakeAbsolute(working); - RelativeFilesystem::Watcher::Watcher(std::shared_ptr vfs, VFSPath path) : FSWatcher(vfs, path) - { - this->watcher = FSWatcher::Create(vfs->vfs, vfs->ToParent(path)); - this->watcher->event = [vfs,this,path](FSWatcherEvent & evt)-> void{ - if(path.relative) - { - auto working = vfs->GetWorking(); - FSWatcherEvent e2=evt; - if(evt.IsEvent(FSWatcherEventType::Moved)) - { - e2.dest = e2.dest.MakeRelative(working); - } - e2.src = e2.src.MakeRelative(working); + if (dest.relative) + dest = dest.MakeAbsolute(working); + } + vfs->MoveFile(src, dest); +} +void RelativeFilesystem::MoveDirectory(VFSPath src, VFSPath dest) { + if (src.relative || dest.relative) { + auto working = GetWorking(); + if (src.relative) + src = src.MakeAbsolute(working); - if(this->event) this->event(e2); + if (dest.relative) + dest = dest.MakeAbsolute(working); + } + vfs->MoveDirectory(src, dest); +} +void RelativeFilesystem::DeleteDirectoryRecurse(VFSPath path) { + vfs->DeleteDirectoryRecurse(ToParent(path)); +} +VFSPath RelativeFilesystem::ReadLink(VFSPath path) { + return vfs->ReadLink(ToParent(path)); +} +std::string RelativeFilesystem::VFSPathToSystem(VFSPath path) { + return vfs->VFSPathToSystem(path); +} +VFSPath RelativeFilesystem::SystemToVFSPath(std::string path) { + return vfs->SystemToVFSPath(path); +} +void RelativeFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess) { + vfs->SetDate(ToParent(path), lastWrite, lastAccess); +} +bool RelativeFilesystem::StatVFS(VFSPath path, StatVFSData &vfsData) { + return vfs->StatVFS(ToParent(path), vfsData); +} +bool RelativeFilesystem::Stat(VFSPath path, StatData &data) { + return vfs->Stat(ToParent(path), data); +} + +void RelativeFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) { + vfs->Chown(ToParent(path), uid, gid); +} +void RelativeFilesystem::Chmod(VFSPath path, uint32_t mode) { + vfs->Chmod(ToParent(path), mode); +} +FIFOCreationResult RelativeFilesystem::CreateFIFO(VFSPath path, uint32_t mode) { + return vfs->CreateFIFO(ToParent(path), mode); +} +void RelativeFilesystem::Lock(VFSPath path) { vfs->Lock(ToParent(path)); } +void RelativeFilesystem::Unlock(VFSPath path) { vfs->Unlock(ToParent(path)); } + +RelativeFilesystem::Watcher::Watcher(std::shared_ptr vfs, + VFSPath path) + : FSWatcher(vfs, path) { + this->watcher = FSWatcher::Create(vfs->vfs, vfs->ToParent(path)); + this->watcher->event = [vfs, this, path](FSWatcherEvent &evt) -> void { + if (path.relative) { + auto working = vfs->GetWorking(); + FSWatcherEvent e2 = evt; + if (evt.IsEvent(FSWatcherEventType::Moved)) { + e2.dest = e2.dest.MakeRelative(working); } - else { - if(this->event) - this->event(evt); - } - }; - } + e2.src = e2.src.MakeRelative(working); - void RelativeFilesystem::Watcher::SetEnabledImpl(bool enabled) - { - this->enabled = enabled; - this->watcher->events = this->events; - this->watcher->SetEnabled(enabled); - } - RelativeFilesystem::Watcher::~Watcher() - { - this->watcher->SetEnabled(false); - } - std::shared_ptr RelativeFilesystem::CreateWatcher(std::shared_ptr vfs, VFSPath path) - { - auto sdfs = std::dynamic_pointer_cast(vfs); - if(sdfs) - { - return std::make_shared(sdfs,path); + if (this->event) + this->event(e2); + } else { + if (this->event) + this->event(evt); } - return VFS::CreateWatcher(vfs,path); + }; +} + +void RelativeFilesystem::Watcher::SetEnabledImpl(bool enabled) { + this->enabled = enabled; + this->watcher->events = this->events; + this->watcher->SetEnabled(enabled); +} +RelativeFilesystem::Watcher::~Watcher() { this->watcher->SetEnabled(false); } +std::shared_ptr +RelativeFilesystem::CreateWatcher(std::shared_ptr vfs, VFSPath path) { + auto sdfs = std::dynamic_pointer_cast(vfs); + if (sdfs) { + return std::make_shared(sdfs, path); } -} \ No newline at end of file + return VFS::CreateWatcher(vfs, path); +} +} // namespace Tesses::Framework::Filesystem \ No newline at end of file diff --git a/src/Filesystem/SubdirFilesystem.cpp b/src/Filesystem/SubdirFilesystem.cpp index e0f3d4f..12ebde2 100644 --- a/src/Filesystem/SubdirFilesystem.cpp +++ b/src/Filesystem/SubdirFilesystem.cpp @@ -1,185 +1,150 @@ #include "TessesFramework/Filesystem/SubdirFilesystem.hpp" #include "TessesFramework/Filesystem/LocalFS.hpp" #include -namespace Tesses::Framework::Filesystem -{ - VFSPath SubdirFilesystem::ReadLink(VFSPath path) - { - return FromParent(this->parent->ReadLink(ToParent(path))); - } - VFSPath SubdirFilesystem::FromParent(VFSPath path) - { - // /a/b/c - // /a/b/c - VFSPath newPath; - newPath.relative=false; +namespace Tesses::Framework::Filesystem { +VFSPath SubdirFilesystem::ReadLink(VFSPath path) { + return FromParent(this->parent->ReadLink(ToParent(path))); +} +VFSPath SubdirFilesystem::FromParent(VFSPath path) { + // /a/b/c + // /a/b/c + VFSPath newPath; + newPath.relative = false; - if(path.path.size() >= this->path.path.size()) - { - newPath.path.reserve(path.path.size()-this->path.path.size()); - for(size_t i = this->path.path.size(); i < path.path.size();i++) - { - newPath.path.push_back(path.path[i]); - } + if (path.path.size() >= this->path.path.size()) { + newPath.path.reserve(path.path.size() - this->path.path.size()); + for (size_t i = this->path.path.size(); i < path.path.size(); i++) { + newPath.path.push_back(path.path[i]); } - return newPath; } + return newPath; +} - VFSPath SubdirFilesystem::ToParent(VFSPath path) - { - return this->path / path.CollapseRelativeParents(); - } - SubdirFilesystem::SubdirFilesystem(std::shared_ptr parent, VFSPath path) - { - this->parent = parent; - if(path.relative && std::dynamic_pointer_cast(parent) != nullptr) - { - Tesses::Framework::Filesystem::LocalFilesystem lfs; - auto curDir = std::filesystem::current_path(); - auto myPath = lfs.SystemToVFSPath(curDir.string()) / path; - this->path = myPath.CollapseRelativeParents(); - } - else +VFSPath SubdirFilesystem::ToParent(VFSPath path) { + return this->path / path.CollapseRelativeParents(); +} +SubdirFilesystem::SubdirFilesystem(std::shared_ptr parent, VFSPath path) { + this->parent = parent; + if (path.relative && + std::dynamic_pointer_cast(parent) != nullptr) { + Tesses::Framework::Filesystem::LocalFilesystem lfs; + auto curDir = std::filesystem::current_path(); + auto myPath = lfs.SystemToVFSPath(curDir.string()) / path; + this->path = myPath.CollapseRelativeParents(); + } else this->path = path; +} +std::shared_ptr +SubdirFilesystem::OpenFile(VFSPath path, std::string mode) { + return this->parent->OpenFile(ToParent(path), mode); +} +void SubdirFilesystem::CreateDirectory(VFSPath path) { + this->parent->CreateDirectory(ToParent(path)); +} +void SubdirFilesystem::DeleteDirectory(VFSPath path) { + this->parent->DeleteDirectory(ToParent(path)); +} +void SubdirFilesystem::DeleteFile(VFSPath path) { + this->parent->DeleteFile(ToParent(path)); +} +void SubdirFilesystem::Lock(VFSPath path) { + this->parent->Lock(ToParent(path)); +} - } - std::shared_ptr SubdirFilesystem::OpenFile(VFSPath path, std::string mode) - { - return this->parent->OpenFile(ToParent(path),mode); - } - void SubdirFilesystem::CreateDirectory(VFSPath path) - { - this->parent->CreateDirectory(ToParent(path)); - } - void SubdirFilesystem::DeleteDirectory(VFSPath path) - { - this->parent->DeleteDirectory(ToParent(path)); - } - void SubdirFilesystem::DeleteFile(VFSPath path) - { - this->parent->DeleteFile(ToParent(path)); - } - void SubdirFilesystem::Lock(VFSPath path) - { - this->parent->Lock(ToParent(path)); - } +void SubdirFilesystem::Unlock(VFSPath path) { + this->parent->Unlock(ToParent(path)); +} +void SubdirFilesystem::CreateSymlink(VFSPath existingFile, + VFSPath symlinkFile) { + this->parent->CreateSymlink(ToParent(existingFile), ToParent(symlinkFile)); +} - void SubdirFilesystem::Unlock(VFSPath path) - { - this->parent->Unlock(ToParent(path)); - } - void SubdirFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) - { - this->parent->CreateSymlink(ToParent(existingFile),ToParent(symlinkFile)); - } +VFSPathEnumerator SubdirFilesystem::EnumeratePaths(VFSPath path) { + VFSPathEnumerator *enumerator = + this->parent->EnumeratePaths(ToParent(path)).MakePointer(); - VFSPathEnumerator SubdirFilesystem::EnumeratePaths(VFSPath path) - { - VFSPathEnumerator* enumerator = this->parent->EnumeratePaths(ToParent(path)).MakePointer(); - - return VFSPathEnumerator([enumerator,this](VFSPath& path0)->bool{ - if(enumerator->MoveNext()) - { + return VFSPathEnumerator( + [enumerator, this](VFSPath &path0) -> bool { + if (enumerator->MoveNext()) { path0 = FromParent(enumerator->Current); return true; } return false; - },[enumerator]()->void{ - delete enumerator; - }); - } - - void SubdirFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess) - { - this->parent->SetDate(ToParent(path),lastWrite,lastAccess); - } - - void SubdirFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName) - { - this->parent->CreateHardlink(ToParent(existingFile),ToParent(newName)); - } - void SubdirFilesystem::MoveFile(VFSPath src, VFSPath dest) - { - this->parent->MoveFile(ToParent(src),ToParent(dest)); - } - void SubdirFilesystem::MoveDirectory(VFSPath src, VFSPath dest) - { - this->parent->MoveDirectory(ToParent(src),ToParent(dest)); - } - std::string SubdirFilesystem::VFSPathToSystem(VFSPath path) - { - return this->parent->VFSPathToSystem(ToParent(path)); - } - VFSPath SubdirFilesystem::SystemToVFSPath(std::string path) - { - return FromParent(this->parent->SystemToVFSPath(path)); - } - void SubdirFilesystem::DeleteDirectoryRecurse(VFSPath path) - { - this->parent->DeleteDirectoryRecurse(ToParent(path)); - } - - - SubdirFilesystem::~SubdirFilesystem() - { - - } - bool SubdirFilesystem::StatVFS(VFSPath path, StatVFSData& vfsData) - { - return this->parent->StatVFS(ToParent(path), vfsData); - } - bool SubdirFilesystem::Stat(VFSPath path, StatData& data) - { - return this->parent->Stat(ToParent(path), data); - } - void SubdirFilesystem::Chmod(VFSPath path, uint32_t mode) - { - return this->parent->Chmod(ToParent(path), mode); - } - void SubdirFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) - { - return this->parent->Chown(ToParent(path), uid, gid); - } - - - FIFOCreationResult SubdirFilesystem::CreateFIFO(VFSPath path, uint32_t mod) - { - return this->parent->CreateFIFO(path, mod); - } - - SubdirFilesystem::Watcher::Watcher(std::shared_ptr vfs, VFSPath path) : FSWatcher(vfs, path) - { - this->watcher = FSWatcher::Create(vfs->parent, vfs->ToParent(path)); - this->watcher->event = [vfs,this](FSWatcherEvent & evt)-> void{ - FSWatcherEvent e2=evt; - if(evt.IsEvent(FSWatcherEventType::Moved)) - { - e2.dest = vfs->FromParent(e2.dest); - } - e2.src = vfs->FromParent(e2.src); - - if(this->event) this->event(e2); - }; - } - - void SubdirFilesystem::Watcher::SetEnabledImpl(bool enabled) - { - this->enabled = enabled; - this->watcher->events = this->events; - this->watcher->SetEnabled(enabled); - } - SubdirFilesystem::Watcher::~Watcher() - { - this->watcher->SetEnabled(false); - } - std::shared_ptr SubdirFilesystem::CreateWatcher(std::shared_ptr vfs, VFSPath path) - { - auto sdfs = std::dynamic_pointer_cast(vfs); - if(sdfs) - { - return std::make_shared(sdfs,path); - } - return VFS::CreateWatcher(vfs,path); - } - + }, + [enumerator]() -> void { delete enumerator; }); } + +void SubdirFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess) { + this->parent->SetDate(ToParent(path), lastWrite, lastAccess); +} + +void SubdirFilesystem::CreateHardlink(VFSPath existingFile, VFSPath newName) { + this->parent->CreateHardlink(ToParent(existingFile), ToParent(newName)); +} +void SubdirFilesystem::MoveFile(VFSPath src, VFSPath dest) { + this->parent->MoveFile(ToParent(src), ToParent(dest)); +} +void SubdirFilesystem::MoveDirectory(VFSPath src, VFSPath dest) { + this->parent->MoveDirectory(ToParent(src), ToParent(dest)); +} +std::string SubdirFilesystem::VFSPathToSystem(VFSPath path) { + return this->parent->VFSPathToSystem(ToParent(path)); +} +VFSPath SubdirFilesystem::SystemToVFSPath(std::string path) { + return FromParent(this->parent->SystemToVFSPath(path)); +} +void SubdirFilesystem::DeleteDirectoryRecurse(VFSPath path) { + this->parent->DeleteDirectoryRecurse(ToParent(path)); +} + +SubdirFilesystem::~SubdirFilesystem() {} +bool SubdirFilesystem::StatVFS(VFSPath path, StatVFSData &vfsData) { + return this->parent->StatVFS(ToParent(path), vfsData); +} +bool SubdirFilesystem::Stat(VFSPath path, StatData &data) { + return this->parent->Stat(ToParent(path), data); +} +void SubdirFilesystem::Chmod(VFSPath path, uint32_t mode) { + return this->parent->Chmod(ToParent(path), mode); +} +void SubdirFilesystem::Chown(VFSPath path, uint32_t uid, uint32_t gid) { + return this->parent->Chown(ToParent(path), uid, gid); +} + +FIFOCreationResult SubdirFilesystem::CreateFIFO(VFSPath path, uint32_t mod) { + return this->parent->CreateFIFO(path, mod); +} + +SubdirFilesystem::Watcher::Watcher(std::shared_ptr vfs, + VFSPath path) + : FSWatcher(vfs, path) { + this->watcher = FSWatcher::Create(vfs->parent, vfs->ToParent(path)); + this->watcher->event = [vfs, this](FSWatcherEvent &evt) -> void { + FSWatcherEvent e2 = evt; + if (evt.IsEvent(FSWatcherEventType::Moved)) { + e2.dest = vfs->FromParent(e2.dest); + } + e2.src = vfs->FromParent(e2.src); + + if (this->event) + this->event(e2); + }; +} + +void SubdirFilesystem::Watcher::SetEnabledImpl(bool enabled) { + this->enabled = enabled; + this->watcher->events = this->events; + this->watcher->SetEnabled(enabled); +} +SubdirFilesystem::Watcher::~Watcher() { this->watcher->SetEnabled(false); } +std::shared_ptr +SubdirFilesystem::CreateWatcher(std::shared_ptr vfs, VFSPath path) { + auto sdfs = std::dynamic_pointer_cast(vfs); + if (sdfs) { + return std::make_shared(sdfs, path); + } + return VFS::CreateWatcher(vfs, path); +} + +} // namespace Tesses::Framework::Filesystem diff --git a/src/Filesystem/TempFS.cpp b/src/Filesystem/TempFS.cpp index 96e5b58..fc669e0 100644 --- a/src/Filesystem/TempFS.cpp +++ b/src/Filesystem/TempFS.cpp @@ -1,189 +1,183 @@ -#include "TessesFramework/Threading/Mutex.hpp" #include "TessesFramework/Filesystem/TempFS.hpp" #include "TessesFramework/Filesystem/LocalFS.hpp" #include "TessesFramework/Filesystem/SubdirFilesystem.hpp" #include "TessesFramework/Platform/Environment.hpp" +#include "TessesFramework/Threading/Mutex.hpp" namespace Tesses::Framework::Filesystem { - Tesses::Framework::Threading::Mutex umtx; - int64_t uidx=0; - void UniqueString(std::string& text) - { - umtx.Lock(); - text += std::to_string((int64_t)time(NULL)); - text += "_"; - text += std::to_string(uidx); +Tesses::Framework::Threading::Mutex umtx; +int64_t uidx = 0; +void UniqueString(std::string &text) { + umtx.Lock(); + text += std::to_string((int64_t)time(NULL)); + text += "_"; + text += std::to_string(uidx); - uidx++; - - umtx.Unlock(); - } - - TempFS::TempFS(bool deleteOnDestroy) : TempFS(std::make_shared(LocalFS, Platform::Environment::SpecialFolders::GetTemp()), deleteOnDestroy) - { - - } - TempFS::TempFS(std::shared_ptr vfs,bool deleteOnDestroy) - { - this->parent = vfs; - this->deleteOnDestroy=deleteOnDestroy; - this->tmp_str = "tf_tmp_"; - UniqueString(this->tmp_str); - VFSPath p; - p.relative = false; - p.path.push_back(this->tmp_str); - this->parent->CreateDirectory(p); - this->vfs = std::make_shared(this->parent,p); - } - - std::string TempFS::TempDirectoryName() - { - return this->tmp_str; - } - std::shared_ptr TempFS::OpenFile(VFSPath path, std::string mode) - { - if(this->vfs == nullptr) return nullptr; - return this->vfs->OpenFile(path,mode); - } - void TempFS::CreateDirectory(VFSPath path) - { - if(this->vfs == nullptr) return; - this->vfs->CreateDirectory(path); - } - void TempFS::DeleteDirectory(VFSPath path) - { - if(this->vfs == nullptr) return; - this->vfs->DeleteDirectory(path); - } - - void TempFS::DeleteFile(VFSPath path) - { - if(this->vfs == nullptr) return; - this->vfs->DeleteFile(path); - } - void TempFS::Lock(VFSPath path) - { - if(this->vfs == nullptr) return; - this->vfs->Lock(path); - } - void TempFS::Unlock(VFSPath path) - { - if(this->vfs == nullptr) return; - this->vfs->Unlock(path); - } - void TempFS::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) - { - if(this->vfs == nullptr) return; - this->vfs->CreateSymlink(existingFile, symlinkFile); - } - VFSPathEnumerator TempFS::EnumeratePaths(VFSPath path) - { - - if(this->vfs == nullptr) return VFSPathEnumerator(); - - return this->vfs->EnumeratePaths(path); - } - void TempFS::CreateHardlink(VFSPath existingFile, VFSPath newName) - { - - if(this->vfs == nullptr) return; - this->vfs->CreateHardlink(existingFile,newName); - } - void TempFS::MoveFile(VFSPath src, VFSPath dest) - { - - if(this->vfs == nullptr) return; - this->vfs->MoveFile(src,dest); - } - void TempFS::MoveDirectory(VFSPath src, VFSPath dest) - { - if(this->vfs == nullptr) return; - this->vfs->MoveDirectory(src,dest); - } - void TempFS::DeleteDirectoryRecurse(VFSPath path) - { - - if(this->vfs == nullptr) return; - this->vfs->DeleteDirectoryRecurse(path); - } - VFSPath TempFS::ReadLink(VFSPath path) - { - - if(this->vfs == nullptr) return VFSPath(); - return this->vfs->ReadLink(path); - } - std::string TempFS::VFSPathToSystem(VFSPath path) - { - - if(this->vfs == nullptr) return ""; - return this->vfs->VFSPathToSystem(path); - } - VFSPath TempFS::SystemToVFSPath(std::string path) - { - - if(this->vfs == nullptr) return VFSPath(); - return this->vfs->SystemToVFSPath(path); - } - - void TempFS::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess) - { - - if(this->vfs == nullptr) return; - this->vfs->SetDate(path,lastWrite,lastAccess); - } - bool TempFS::StatVFS(VFSPath path, StatVFSData& vfsData) - { - - if(this->vfs == nullptr) return false; - return this->vfs->StatVFS(path, vfsData); - } - bool TempFS::Stat(VFSPath path, StatData& data) - { - - if(this->vfs == nullptr) return false; - return this->vfs->Stat(path, data); - } - - void TempFS::Chmod(VFSPath path, uint32_t mode) - { - - if(this->vfs == nullptr) return; - this->vfs->Chmod(path,mode); - } - void TempFS::Chown(VFSPath path, uint32_t uid, uint32_t gid) - { - - if(this->vfs == nullptr) return; - this->vfs->Chown(path,uid, gid); - } - FIFOCreationResult TempFS::CreateFIFO(VFSPath path, uint32_t mod) - { - if(this->vfs == nullptr) return FIFOCreationResult::UnknownError; - return this->vfs->CreateFIFO(path, mod); - } - void TempFS::Close() - { - - VFSPath p; - p.relative = false; - p.path.push_back(this->tmp_str); - this->vfs = nullptr; - if(this->deleteOnDestroy && this->parent->DirectoryExists(p)) - this->parent->DeleteDirectoryRecurse(p); - } - std::shared_ptr TempFS::CreateWatcher(std::shared_ptr vfs, VFSPath path) - { - return FSWatcher::Create(vfs,path); - } - - TempFS::~TempFS() - { - VFSPath p; - p.relative = false; - p.path.push_back(this->tmp_str); - this->vfs = nullptr; - if(this->deleteOnDestroy && this->parent->DirectoryExists(p)) - this->parent->DeleteDirectoryRecurse(p); - - } + uidx++; + umtx.Unlock(); } + +TempFS::TempFS(bool deleteOnDestroy) + : TempFS(std::make_shared( + LocalFS, Platform::Environment::SpecialFolders::GetTemp()), + deleteOnDestroy) {} +TempFS::TempFS(std::shared_ptr vfs, bool deleteOnDestroy) { + this->parent = vfs; + this->deleteOnDestroy = deleteOnDestroy; + this->tmp_str = "tf_tmp_"; + UniqueString(this->tmp_str); + VFSPath p; + p.relative = false; + p.path.push_back(this->tmp_str); + this->parent->CreateDirectory(p); + this->vfs = std::make_shared(this->parent, p); +} + +std::string TempFS::TempDirectoryName() { return this->tmp_str; } +std::shared_ptr +TempFS::OpenFile(VFSPath path, std::string mode) { + if (this->vfs == nullptr) + return nullptr; + return this->vfs->OpenFile(path, mode); +} +void TempFS::CreateDirectory(VFSPath path) { + if (this->vfs == nullptr) + return; + this->vfs->CreateDirectory(path); +} +void TempFS::DeleteDirectory(VFSPath path) { + if (this->vfs == nullptr) + return; + this->vfs->DeleteDirectory(path); +} + +void TempFS::DeleteFile(VFSPath path) { + if (this->vfs == nullptr) + return; + this->vfs->DeleteFile(path); +} +void TempFS::Lock(VFSPath path) { + if (this->vfs == nullptr) + return; + this->vfs->Lock(path); +} +void TempFS::Unlock(VFSPath path) { + if (this->vfs == nullptr) + return; + this->vfs->Unlock(path); +} +void TempFS::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) { + if (this->vfs == nullptr) + return; + this->vfs->CreateSymlink(existingFile, symlinkFile); +} +VFSPathEnumerator TempFS::EnumeratePaths(VFSPath path) { + + if (this->vfs == nullptr) + return VFSPathEnumerator(); + + return this->vfs->EnumeratePaths(path); +} +void TempFS::CreateHardlink(VFSPath existingFile, VFSPath newName) { + + if (this->vfs == nullptr) + return; + this->vfs->CreateHardlink(existingFile, newName); +} +void TempFS::MoveFile(VFSPath src, VFSPath dest) { + + if (this->vfs == nullptr) + return; + this->vfs->MoveFile(src, dest); +} +void TempFS::MoveDirectory(VFSPath src, VFSPath dest) { + if (this->vfs == nullptr) + return; + this->vfs->MoveDirectory(src, dest); +} +void TempFS::DeleteDirectoryRecurse(VFSPath path) { + + if (this->vfs == nullptr) + return; + this->vfs->DeleteDirectoryRecurse(path); +} +VFSPath TempFS::ReadLink(VFSPath path) { + + if (this->vfs == nullptr) + return VFSPath(); + return this->vfs->ReadLink(path); +} +std::string TempFS::VFSPathToSystem(VFSPath path) { + + if (this->vfs == nullptr) + return ""; + return this->vfs->VFSPathToSystem(path); +} +VFSPath TempFS::SystemToVFSPath(std::string path) { + + if (this->vfs == nullptr) + return VFSPath(); + return this->vfs->SystemToVFSPath(path); +} + +void TempFS::SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess) { + + if (this->vfs == nullptr) + return; + this->vfs->SetDate(path, lastWrite, lastAccess); +} +bool TempFS::StatVFS(VFSPath path, StatVFSData &vfsData) { + + if (this->vfs == nullptr) + return false; + return this->vfs->StatVFS(path, vfsData); +} +bool TempFS::Stat(VFSPath path, StatData &data) { + + if (this->vfs == nullptr) + return false; + return this->vfs->Stat(path, data); +} + +void TempFS::Chmod(VFSPath path, uint32_t mode) { + + if (this->vfs == nullptr) + return; + this->vfs->Chmod(path, mode); +} +void TempFS::Chown(VFSPath path, uint32_t uid, uint32_t gid) { + + if (this->vfs == nullptr) + return; + this->vfs->Chown(path, uid, gid); +} +FIFOCreationResult TempFS::CreateFIFO(VFSPath path, uint32_t mod) { + if (this->vfs == nullptr) + return FIFOCreationResult::UnknownError; + return this->vfs->CreateFIFO(path, mod); +} +void TempFS::Close() { + + VFSPath p; + p.relative = false; + p.path.push_back(this->tmp_str); + this->vfs = nullptr; + if (this->deleteOnDestroy && this->parent->DirectoryExists(p)) + this->parent->DeleteDirectoryRecurse(p); +} +std::shared_ptr TempFS::CreateWatcher(std::shared_ptr vfs, + VFSPath path) { + return FSWatcher::Create(vfs, path); +} + +TempFS::~TempFS() { + VFSPath p; + p.relative = false; + p.path.push_back(this->tmp_str); + this->vfs = nullptr; + if (this->deleteOnDestroy && this->parent->DirectoryExists(p)) + this->parent->DeleteDirectoryRecurse(p); +} + +} // namespace Tesses::Framework::Filesystem diff --git a/src/Filesystem/VFS.cpp b/src/Filesystem/VFS.cpp index 6f7a34b..df9a367 100644 --- a/src/Filesystem/VFS.cpp +++ b/src/Filesystem/VFS.cpp @@ -1,778 +1,604 @@ #include "TessesFramework/Filesystem/VFS.hpp" -#include "TessesFramework/Http/HttpUtils.hpp" #include "TessesFramework/Filesystem/LocalFS.hpp" +#include "TessesFramework/Http/HttpUtils.hpp" #include -namespace Tesses::Framework::Filesystem -{ - VFSPathEnumeratorItterator::VFSPathEnumeratorItterator() - { - this->enumerator=nullptr; +namespace Tesses::Framework::Filesystem { +VFSPathEnumeratorItterator::VFSPathEnumeratorItterator() { + this->enumerator = nullptr; +} +VFSPathEnumeratorItterator::VFSPathEnumeratorItterator( + VFSPathEnumerator *enumerator) { + this->enumerator = enumerator; +} +VFSPathEnumeratorItterator &VFSPathEnumeratorItterator::operator++(int) { + enumerator->MoveNext(); + return *this; +} +VFSPathEnumeratorItterator &VFSPathEnumeratorItterator::operator++() { + enumerator->MoveNext(); + return *this; +} + +VFSPath &VFSPathEnumeratorItterator::operator*() { + std::filesystem::directory_iterator i; + + if (enumerator != nullptr) + return enumerator->Current; + return this->e; +} +VFSPath *VFSPathEnumeratorItterator::operator->() { + if (enumerator != nullptr) + return &enumerator->Current; + return nullptr; +} +bool VFSPathEnumeratorItterator::operator!=(VFSPathEnumeratorItterator right) { + if (enumerator == right.enumerator) { + return false; } - VFSPathEnumeratorItterator::VFSPathEnumeratorItterator(VFSPathEnumerator* enumerator) - { - this->enumerator = enumerator; + if (right.enumerator == nullptr) { + auto r = !enumerator->IsDone(); + + return r; } - VFSPathEnumeratorItterator& VFSPathEnumeratorItterator::operator++(int) - { - enumerator->MoveNext(); - return *this; + return true; +} +bool VFSPathEnumeratorItterator::operator==(VFSPathEnumeratorItterator right) { + if (enumerator == right.enumerator) { + return true; } - VFSPathEnumeratorItterator& VFSPathEnumeratorItterator::operator++() - { - enumerator->MoveNext(); - return *this; + if (right.enumerator == nullptr) + return enumerator->IsDone(); + return false; +} +VFSPathEnumerator::VFSPathEnumerator() { data = nullptr; } +VFSPathEnumerator *VFSPathEnumerator::MakePointer() { + VFSPathEnumerator *enumerator = new VFSPathEnumerator(); + enumerator->Current = Current; + enumerator->data = data; + return enumerator; +} +VFSPathEnumerator::VFSPathEnumerator(std::function moveNext, + std::function destroy) { + data = std::make_shared(moveNext, destroy); +} +bool VFSPathEnumerator::MoveNext() { + if (this->data) { + auto r = data->moveNext(Current); + if (!r) + data->eof = true; + return r; + } + return false; +} +bool VFSPathEnumerator::IsDone() { + + if (this->data) { + return data->eof; + } + return true; +} + +VFSPathEnumeratorItterator VFSPathEnumerator::begin() { + MoveNext(); + VFSPathEnumeratorItterator ittr(this); + return ittr; +} + +VFSPathEnumeratorItterator VFSPathEnumerator::end() { + return VFSPathEnumeratorItterator(); +} +VFSPath operator/(VFSPath p, VFSPath p2) { return VFSPath(p, p2); } +VFSPath operator/(VFSPath p, std::string p2) { return VFSPath(p, p2); } +VFSPath operator/(std::string p, VFSPath p2) { return VFSPath(p, p2); } +VFSPath operator+(VFSPath p, VFSPath p2) { + VFSPath pout; + pout.relative = p.relative; + if (p.path.size() > 1) { + pout.path.insert(pout.path.end(), p.path.begin(), p.path.end() - 1); + } + std::string mid = {}; + if (!p.path.empty()) { + mid.append(p.path.back()); } - - VFSPath& VFSPathEnumeratorItterator::operator*() - { - std::filesystem::directory_iterator i; - - if(enumerator != nullptr) - return enumerator->Current; - return this->e; + if (!p2.path.empty()) { + mid.append(p2.path.front()); } - VFSPath* VFSPathEnumeratorItterator::operator->() - { - if(enumerator != nullptr) - return &enumerator->Current; - return nullptr; + pout.path.push_back(mid); + + if (p2.path.size() > 1) { + pout.path.insert(pout.path.end(), p2.path.begin() + 1, p2.path.end()); } - bool VFSPathEnumeratorItterator::operator!=(VFSPathEnumeratorItterator right) - { - if(enumerator == right.enumerator) - { + + return pout; +} +VFSPath operator+(VFSPath p, std::string p2) { return p + VFSPath(p2); } +VFSPath operator+(std::string p, VFSPath p2) { return VFSPath(p) + p2; } +bool operator==(VFSPath p, VFSPath p2) { + if (p.relative != p2.relative) + return false; + if (p.path.size() != p2.path.size()) + return false; + for (size_t i = 0; i < p.path.size(); i++) + if (p.path[i] != p2.path[i]) return false; - } - if(right.enumerator == nullptr) - { - auto r = !enumerator->IsDone(); - - return r; - } + return true; +} +bool operator!=(VFSPath p, VFSPath p2) { + if (p.relative != p2.relative) return true; - } - bool VFSPathEnumeratorItterator::operator==(VFSPathEnumeratorItterator right) - { - if(enumerator == right.enumerator) - { + if (p.path.size() != p2.path.size()) + return true; + for (size_t i = 0; i < p.path.size(); i++) + if (p.path[i] != p2.path[i]) return true; - } - if(right.enumerator == nullptr) - return enumerator->IsDone(); - return false; + return false; +} +bool operator==(std::string p, VFSPath p2) { return VFSPath(p) == p2; } +bool operator!=(std::string p, VFSPath p2) { return VFSPath(p) != p2; } +bool operator==(VFSPath p, std::string p2) { return p == VFSPath(p2); } +bool operator!=(VFSPath p, std::string p2) { return p != VFSPath(p2); } +VFSPath VFS::ReadLink(VFSPath path) { return VFSPath("/"); } +VFSPath VFSPath::GetAbsoluteCurrentDirectory() { + auto p = std::filesystem::current_path(); + return LocalFS->SystemToVFSPath(p.string()); +} +void VFSPath::SetAbsoluteCurrentDirectory(VFSPath path) { + auto res = LocalFS->VFSPathToSystem(path); + std::filesystem::path mpath = res; + std::filesystem::current_path(mpath); +} +VFSPath VFSPath::MakeAbsolute() const { + return MakeAbsolute(GetAbsoluteCurrentDirectory()); +} +VFSPath VFSPath::MakeAbsolute(VFSPath curDir) const { + if (!this->relative) + return *this; + VFSPath p2 = curDir / *this; + return p2.CollapseRelativeParents(); +} +VFSPath VFSPath::MakeRelative() const { + return MakeRelative(GetAbsoluteCurrentDirectory()); +} +FIFOCreationResult VFS::CreateFIFO(VFSPath path, uint32_t mode) { + return FIFOCreationResult::Unsupported; +} +bool VFS::DirectoryExists(VFSPath path) { + StatData data; + if (this->Stat(path, data)) { + return data.IsDirectory(); } - VFSPathEnumerator::VFSPathEnumerator() - { - data = nullptr; + return false; +} +bool VFS::RegularFileExists(VFSPath path) { + StatData data; + if (this->Stat(path, data)) { + return data.IsRegularFile(); } - VFSPathEnumerator* VFSPathEnumerator::MakePointer() - { - VFSPathEnumerator* enumerator = new VFSPathEnumerator(); - enumerator->Current = Current; - enumerator->data = data; - return enumerator; + return false; +} +bool VFS::SymlinkExists(VFSPath path) { + StatData data; + if (this->Stat(path, data)) { + return data.IsSymlink(); } - VFSPathEnumerator::VFSPathEnumerator(std::function moveNext, std::function destroy) - { - data = std::make_shared(moveNext,destroy); + return false; +} +bool VFS::CharacterDeviceExists(VFSPath path) { + StatData data; + if (this->Stat(path, data)) { + return data.IsCharDevice(); } - bool VFSPathEnumerator::MoveNext() - { - if(this->data) - { - auto r = data->moveNext(Current); - if(!r) data->eof=true; - return r; - } - return false; + return false; +} +bool VFS::BlockDeviceExists(VFSPath path) { + StatData data; + if (this->Stat(path, data)) { + return data.IsBlockDevice(); } - bool VFSPathEnumerator::IsDone() - { + return false; +} +bool VFS::SocketFileExists(VFSPath path) { + StatData data; + if (this->Stat(path, data)) { + return data.IsSocket(); + } + return false; +} +bool VFS::FIFOFileExists(VFSPath path) { + StatData data; + if (this->Stat(path, data)) { + return data.IsFIFO(); + } + return false; +} +bool VFS::FileExists(VFSPath path) { + StatData data; + if (this->Stat(path, data)) { + return !data.IsDirectory(); + } + return false; +} +bool VFS::SpecialFileExists(VFSPath path) { + StatData data; + if (this->Stat(path, data)) { + return data.IsSpecial(); + } + return false; +} +VFSPath VFSPath::MakeRelative(VFSPath toMakeRelativeTo) const { - if(this->data) - { - return data->eof; - } - return true; + if (this->relative) + return *this; + + size_t i; + size_t len = std::min(toMakeRelativeTo.path.size(), this->path.size()); + for (i = 0; i < len; i++) { + if (this->path[i] != toMakeRelativeTo.path[i]) + break; } - VFSPathEnumeratorItterator VFSPathEnumerator::begin() - { - MoveNext(); - VFSPathEnumeratorItterator ittr(this); - return ittr; - } - - VFSPathEnumeratorItterator VFSPathEnumerator::end() - { - return VFSPathEnumeratorItterator(); - } - VFSPath operator/(VFSPath p, VFSPath p2) - { - return VFSPath(p,p2); - } - VFSPath operator/(VFSPath p, std::string p2) - { - return VFSPath(p,p2); - } - VFSPath operator/(std::string p, VFSPath p2) - { - return VFSPath(p,p2); - } - VFSPath operator+(VFSPath p, VFSPath p2) - { - VFSPath pout; - pout.relative = p.relative; - if(p.path.size()>1) - { - pout.path.insert(pout.path.end(),p.path.begin(),p.path.end()-1); - } - std::string mid={}; - if(!p.path.empty()) - { - mid.append(p.path.back()); - } - - if(!p2.path.empty()) - { - mid.append(p2.path.front()); - } - pout.path.push_back(mid); - - if(p2.path.size()>1) - { - pout.path.insert(pout.path.end(),p2.path.begin()+1,p2.path.end()); - } - - return pout; - } - VFSPath operator+(VFSPath p, std::string p2) - { - return p + VFSPath(p2); - } - VFSPath operator+(std::string p, VFSPath p2) - { - return VFSPath(p) + p2; - } - bool operator==(VFSPath p,VFSPath p2) - { - if(p.relative != p2.relative) return false; - if(p.path.size() != p2.path.size()) return false; - for(size_t i = 0; i < p.path.size(); i++) - if(p.path[i] != p2.path[i]) return false; - return true; - } - bool operator!=(VFSPath p,VFSPath p2) - { - if(p.relative != p2.relative) return true; - if(p.path.size() != p2.path.size()) return true; - for(size_t i = 0; i < p.path.size(); i++) - if(p.path[i] != p2.path[i]) return true; - return false; - - } - bool operator==(std::string p,VFSPath p2) - { - return VFSPath(p) == p2; - } - bool operator!=(std::string p,VFSPath p2) - { - return VFSPath(p) != p2; - } - bool operator==(VFSPath p,std::string p2) - { - return p == VFSPath(p2); - } - bool operator!=(VFSPath p,std::string p2) - { - return p != VFSPath(p2); - } - VFSPath VFS::ReadLink(VFSPath path) - { - return VFSPath("/"); - } - VFSPath VFSPath::GetAbsoluteCurrentDirectory() - { - auto p = std::filesystem::current_path(); - return LocalFS->SystemToVFSPath(p.string()); - } - void VFSPath::SetAbsoluteCurrentDirectory(VFSPath path) - { - auto res = LocalFS->VFSPathToSystem(path); - std::filesystem::path mpath=res; - std::filesystem::current_path(mpath); - } - VFSPath VFSPath::MakeAbsolute() const - { - return MakeAbsolute(GetAbsoluteCurrentDirectory()); - } - VFSPath VFSPath::MakeAbsolute(VFSPath curDir) const - { - if (!this->relative) return *this; - VFSPath p2 = curDir / *this; - return p2.CollapseRelativeParents(); - } - VFSPath VFSPath::MakeRelative() const - { - return MakeRelative(GetAbsoluteCurrentDirectory()); - } - FIFOCreationResult VFS::CreateFIFO(VFSPath path, uint32_t mode) - { - return FIFOCreationResult::Unsupported; - } - bool VFS::DirectoryExists(VFSPath path) - { - StatData data; - if(this->Stat(path, data)) - { - return data.IsDirectory(); - } - return false; - } - bool VFS::RegularFileExists(VFSPath path) - { - StatData data; - if(this->Stat(path, data)) - { - return data.IsRegularFile(); - } - return false; - } - bool VFS::SymlinkExists(VFSPath path) - { - StatData data; - if(this->Stat(path, data)) - { - return data.IsSymlink(); - } - return false; - } - bool VFS::CharacterDeviceExists(VFSPath path) - { - StatData data; - if(this->Stat(path, data)) - { - return data.IsCharDevice(); - } - return false; - } - bool VFS::BlockDeviceExists(VFSPath path) - { - StatData data; - if(this->Stat(path, data)) - { - return data.IsBlockDevice(); - } - return false; - } - bool VFS::SocketFileExists(VFSPath path) - { - StatData data; - if(this->Stat(path, data)) - { - return data.IsSocket(); - } - return false; - } - bool VFS::FIFOFileExists(VFSPath path) - { - StatData data; - if(this->Stat(path, data)) - { - return data.IsFIFO(); - } - return false; - } - bool VFS::FileExists(VFSPath path) - { - StatData data; - if(this->Stat(path, data)) - { - return !data.IsDirectory(); - } - return false; - } - bool VFS::SpecialFileExists(VFSPath path) - { - StatData data; - if(this->Stat(path, data)) - { - return data.IsSpecial(); - } - return false; - } - VFSPath VFSPath::MakeRelative(VFSPath toMakeRelativeTo) const - { - - if(this->relative) return *this; - - - - size_t i; - size_t len = std::min(toMakeRelativeTo.path.size(),this->path.size()); - for(i = 0; i < len; i++) - { - if(this->path[i] != toMakeRelativeTo.path[i]) break; - } - - if(i == this->path.size()-1 && i == toMakeRelativeTo.path.size()-1) - { - std::vector paths{this->path[this->path.size()-1]}; - VFSPath path(paths); - path.relative = true; - return path; - } - - std::vector parts(this->path.begin()+i, this->path.end()); - - if(i < toMakeRelativeTo.path.size()) - { - for(; i < toMakeRelativeTo.path.size();i++) - { - parts.insert(parts.begin(),".."); - } - } - VFSPath p2(parts); - p2.relative = true; - return p2; - } - VFSPath VFSPath::CollapseRelativeParents() const - { - std::vector parts; - - for(auto item : this->path) - { - if(item == "..") - { - if(!parts.empty()) - { - parts.erase(parts.end()-1); - } - } - else if(item == ".") - { - //do nothing but don't emit this - } - else - { - parts.push_back(item); - } - } - VFSPath newpath; - newpath.relative = relative; - newpath.path = parts; - return newpath; - } - VFSPath VFSPath::CurrentDirectoryAsRelative() - { - VFSPath path; - path.relative=true; + if (i == this->path.size() - 1 && i == toMakeRelativeTo.path.size() - 1) { + std::vector paths{this->path[this->path.size() - 1]}; + VFSPath path(paths); + path.relative = true; return path; } - std::vector VFSPath::SplitPath(std::string path) - { - std::vector parts; - std::string curPath = {}; - for(auto c : path) - { - if(c == '/') - { - if(!curPath.empty()) - { - parts.push_back(curPath); - curPath = {}; - } + std::vector parts(this->path.begin() + i, this->path.end()); + + if (i < toMakeRelativeTo.path.size()) { + for (; i < toMakeRelativeTo.path.size(); i++) { + parts.insert(parts.begin(), ".."); + } + } + VFSPath p2(parts); + p2.relative = true; + return p2; +} +VFSPath VFSPath::CollapseRelativeParents() const { + std::vector parts; + + for (auto item : this->path) { + if (item == "..") { + if (!parts.empty()) { + parts.erase(parts.end() - 1); } - #if defined(WIN32) - else if(c == '\\') - { - if(!curPath.empty()) - { - parts.push_back(curPath); - curPath = {}; - } - } - #endif - else - { - curPath.push_back(c); + } else if (item == ".") { + // do nothing but don't emit this + } else { + parts.push_back(item); + } + } + VFSPath newpath; + newpath.relative = relative; + newpath.path = parts; + return newpath; +} +VFSPath VFSPath::CurrentDirectoryAsRelative() { + VFSPath path; + path.relative = true; + return path; +} + +std::vector VFSPath::SplitPath(std::string path) { + std::vector parts; + std::string curPath = {}; + for (auto c : path) { + if (c == '/') { + if (!curPath.empty()) { + parts.push_back(curPath); + curPath = {}; } } - if(!curPath.empty()) - { - parts.push_back(curPath); - curPath = {}; - } - - return parts; - } - VFSPath::VFSPath() - { - this->relative=false; - } - VFSPath::VFSPath(std::vector p) - { - this->path = p; - } - - VFSPath VFSPath::ParseUriPath(std::string path) - { - std::string builder = {}; - VFSPath vpath; - vpath.relative=true; - - if(!path.empty() && path[0] == '/') vpath.relative=false; - - for(auto item : path) - { - if(item == '/') - { - if(!builder.empty()) - { - vpath.path.push_back(builder); - builder.clear(); - } - } - else { - builder.push_back(item); +#if defined(WIN32) + else if (c == '\\') { + if (!curPath.empty()) { + parts.push_back(curPath); + curPath = {}; } } - if(!builder.empty()) - { - vpath.path.push_back(builder); +#endif + else { + curPath.push_back(c); } - return vpath; + } + if (!curPath.empty()) { + parts.push_back(curPath); + curPath = {}; } - bool VFSPath::HasExtension() const - { - if(this->path.empty()) return false; - auto& str = this->path.back(); - auto index = str.find_last_of('.'); - if(index == std::string::npos) return false; - return true; - } - std::string VFSPath::GetExtension() const - { - if(this->path.empty()) return {}; - auto& str = this->path.back(); - auto index = str.find_last_of('.'); - if(index == std::string::npos) return {}; - return str.substr(index); - } - void VFSPath::ChangeExtension(std::string ext) - { - if(this->path.empty()) return; - auto& str = this->path.back(); - auto index = str.find_last_of('.'); - if(index != std::string::npos) - { - str = str.substr(0,index); - } - if(ext.empty()) return; - if(ext[0] != '.') - { - str += '.'; - str += ext; - } - else - { - str += ext; - } - } - void VFSPath::RemoveExtension() - { - ChangeExtension({}); - } + return parts; +} +VFSPath::VFSPath() { this->relative = false; } +VFSPath::VFSPath(std::vector p) { this->path = p; } - VFSPath::VFSPath(std::string str) - { - this->path = SplitPath(str); - this->relative=true; - if(!str.empty()) - { - if(str.front() == '/') this->relative=false; - #if defined(_WIN32) - if(str.front() == '\\') this->relative=false; - #endif - if(!this->path.empty()) - { - auto firstPartPath = this->path.front(); +VFSPath VFSPath::ParseUriPath(std::string path) { + std::string builder = {}; + VFSPath vpath; + vpath.relative = true; - if(!firstPartPath.empty() && firstPartPath.back() == ':') this->relative=false; + if (!path.empty() && path[0] == '/') + vpath.relative = false; + + for (auto item : path) { + if (item == '/') { + if (!builder.empty()) { + vpath.path.push_back(builder); + builder.clear(); } + } else { + builder.push_back(item); } } - void VFS::CreateDirectory(VFSPath path) - { - + if (!builder.empty()) { + vpath.path.push_back(builder); } - void VFS::DeleteDirectory(VFSPath path) - { + return vpath; +} +bool VFSPath::HasExtension() const { + if (this->path.empty()) + return false; + auto &str = this->path.back(); + auto index = str.find_last_of('.'); + if (index == std::string::npos) + return false; + return true; +} +std::string VFSPath::GetExtension() const { + if (this->path.empty()) + return {}; + auto &str = this->path.back(); + auto index = str.find_last_of('.'); + if (index == std::string::npos) + return {}; + return str.substr(index); +} +void VFSPath::ChangeExtension(std::string ext) { + if (this->path.empty()) + return; + auto &str = this->path.back(); + auto index = str.find_last_of('.'); + if (index != std::string::npos) { + str = str.substr(0, index); } - VFSPath::VFSPath(VFSPath p1, VFSPath p2) - { - this->relative = p1.relative; - this->path.insert(this->path.end(),p1.path.begin(),p1.path.end()); - this->path.insert(this->path.end(),p2.path.begin(),p2.path.end()); - } - VFSPath::VFSPath(VFSPath p1, std::string subpath) : VFSPath(p1, VFSPath(subpath)) - { - - } - VFSPath VFSPath::GetParent() const - { - std::vector paths; - if(this->path.empty()) return VFSPath(); - if(!this->relative && this->path.size() == 1 && !this->path[0].empty() && this->path[0].back() == ':') return *this; - - paths.insert(paths.begin(), this->path.begin(), this->path.end()-1); - auto res= VFSPath(paths); - res.relative = this->relative; - return res; - } - - std::string VFSPath::GetFileName() const - { - if(this->path.empty()) return ""; - return this->path.back(); - } - - std::string VFSPath::ToString() const - { - if(this->path.empty() && !this->relative) return "/"; - if(!this->relative && this->path.size() == 1 && !this->path[0].empty() && this->path[0].back() == ':') return this->path[0] + "/"; - bool first=true; - std::string p = {}; - for(auto item : this->path) - { - if(!(first && !item.empty() && item.back()==':') && !(first && this->relative)) - p.push_back('/'); - p.append(item); - first=false; - } - return p; - } - void VFS::DeleteFile(VFSPath path) - { - - } - void VFS::MoveFile(VFSPath src, VFSPath dest) - { - { - auto srcStrm = this->OpenFile(src, "rb"); - if(!srcStrm->CanRead()) return; - auto destStrm = this->OpenFile(dest, "wb"); - if(!destStrm->CanWrite()) return; - srcStrm->CopyTo(destStrm); - } - - VFS::DeleteFile(src); - } - VFS::~VFS() - { - - } - - void VFS::MoveDirectory(VFSPath src, VFSPath dest) - { - - for(auto item : EnumeratePaths(src)) - { - if(DirectoryExists(item)) - { - MoveDirectory(item, VFSPath(dest, item.GetFileName())); - } - else - { - MoveFile(item, VFSPath(dest, item.GetFileName())); - } - } - - DeleteDirectory(src); - } - void VFS::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) - { - - } - void VFS::CreateHardlink(VFSPath existingFile, VFSPath newName) - { - - } - - - void VFS::DeleteDirectoryRecurse(VFSPath path) - { - if(!DirectoryExists(path)) return; - - for(auto item : EnumeratePaths(path)) - { - if(DirectoryExists(item)) - { - DeleteDirectoryRecurse(item); - } - else - { - DeleteFile(item); - } - } - DeleteDirectory(path); - } - void VFS::GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess) - { - StatData data; - if(Stat(path, data)) - { - lastWrite = data.LastModified; - lastAccess = data.LastAccess; - } - } - void VFS::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess) - { - - } - bool VFS::StatVFS(VFSPath path, StatVFSData& data) - { - data.BlockSize = 512; - data.Blocks=10000000000000; - data.BlocksFree=10000000000000; - data.BlocksAvailable=10000000000000; - data.AvailableInodes=10000000000000; - data.TotalInodes=10000000000000; - data.FreeInodes=10000000000000; - data.FragmentSize=512; - data.Id = 85138; - data.Flags = 0; - data.MaxNameLength=255; - return true; - } - void VFS::Chmod(VFSPath path, uint32_t mode) { - - } - void VFS::Chown(VFSPath path, uint32_t uid, uint32_t gid) { - - } - void VFS::Close() { - - } - void VFS::Lock(VFSPath path) - { - - } - void VFS::Unlock(VFSPath path) - { - - } - - - - std::shared_ptr VFS::CreateWatcher(std::shared_ptr vfs,VFSPath path) - { - return std::make_shared(vfs,path); - } - void FSWatcher::SetEnabled(bool enabled) - { - if(this->enabled == enabled) return; - this->enabled = enabled; - this->SetEnabledImpl(enabled); - } - - bool FSWatcher::GetEnabled() - { - return this->enabled; - } - - void FSWatcher::SetEnabledImpl(bool enabled) - { - - } - - std::shared_ptr FSWatcher::GetFilesystem() - { - return this->vfs; - } - const VFSPath& FSWatcher::GetPath() - { - return this->path; - } - - FSWatcher::FSWatcher(std::shared_ptr vfs, VFSPath path): vfs(vfs), path(path) - { - - } - - std::shared_ptr FSWatcher::Create(std::shared_ptr vfs, VFSPath path) - { - return vfs->CreateWatcher(vfs,path); - } - - bool FSWatcherEvent::IsEvent(FSWatcherEventType e) - { - if(e == FSWatcherEventType::All) return this->type != FSWatcherEventType::None; - if(e == FSWatcherEventType::Moved) return ((uint32_t)this->type & (uint32_t)FSWatcherEventType::Moved) == (uint32_t)FSWatcherEventType::Moved; - if(e == FSWatcherEventType::Closed) return ((uint32_t)this->type & (uint32_t)FSWatcherEventType::Closed) != 0; - return (uint32_t)this->type & (uint32_t)e; - } - - std::string FSWatcherEvent::ToString() - { - if(IsEvent(FSWatcherEventType::Moved)) - { - return (this->isDir ? "Moved directory " : "Moved file ") + this->src.ToString() + " -> " + this->dest.ToString(); - } - else if(IsEvent(FSWatcherEventType::MoveOld)) - { - return (this->isDir ? "Move source directory " : "Move source file ") + this->src.ToString(); - } - else if(IsEvent(FSWatcherEventType::MoveNew)) - { - return (this->isDir ? "Move destination directory " : "Move destination file ") + this->src.ToString(); - } - else if(IsEvent(FSWatcherEventType::Accessed)) - { - return (this->isDir ? "Accessed directory " : "Accessed file ") + this->src.ToString(); - } - else if(IsEvent(FSWatcherEventType::AttributeChanged)) - { - return (this->isDir ? "Changed attr on directory " : "Changed attr on file ") + this->src.ToString(); - } - else if(IsEvent(FSWatcherEventType::Writen)) - { - return (this->isDir ? "Finished changing directory " : "Finished writing to file ") + this->src.ToString(); - } - else if(IsEvent(FSWatcherEventType::Read)) - { - return (this->isDir ? "Finished reading directory " : "Finished reading from file ") + this->src.ToString(); - } - else if(IsEvent(FSWatcherEventType::Created)) - { - return (this->isDir ? "Created directory " : "Created file ") + this->src.ToString(); - } - else if(IsEvent(FSWatcherEventType::Deleted)) - { - return (this->isDir ? "Deleted directory " : "Deleted file ") + this->src.ToString(); - } - else if(IsEvent(FSWatcherEventType::WatchEntryDeleted)) - { - - return (this->isDir ? "Deleted watched directory " : "Deleted watched file ") + this->src.ToString(); - } - else if(IsEvent(FSWatcherEventType::Modified)) - { - return (this->isDir ? "Modified directory " : "Modified file ") + this->src.ToString(); - } - else if(IsEvent(FSWatcherEventType::WatchEntryMoved)) - { - return (this->isDir ? "Moved watched directory " : "Moved watched file ") + this->src.ToString(); - } - else if(IsEvent(FSWatcherEventType::Opened)) - { - return (this->isDir ? "Opened directory " : "Opened file ") + this->src.ToString(); - } - - return ""; + if (ext.empty()) + return; + if (ext[0] != '.') { + str += '.'; + str += ext; + } else { + str += ext; } } +void VFSPath::RemoveExtension() { ChangeExtension({}); } + +VFSPath::VFSPath(std::string str) { + this->path = SplitPath(str); + this->relative = true; + if (!str.empty()) { + if (str.front() == '/') + this->relative = false; +#if defined(_WIN32) + if (str.front() == '\\') + this->relative = false; +#endif + if (!this->path.empty()) { + auto firstPartPath = this->path.front(); + + if (!firstPartPath.empty() && firstPartPath.back() == ':') + this->relative = false; + } + } +} +void VFS::CreateDirectory(VFSPath path) {} +void VFS::DeleteDirectory(VFSPath path) {} +VFSPath::VFSPath(VFSPath p1, VFSPath p2) { + this->relative = p1.relative; + this->path.insert(this->path.end(), p1.path.begin(), p1.path.end()); + this->path.insert(this->path.end(), p2.path.begin(), p2.path.end()); +} +VFSPath::VFSPath(VFSPath p1, std::string subpath) + : VFSPath(p1, VFSPath(subpath)) {} +VFSPath VFSPath::GetParent() const { + std::vector paths; + if (this->path.empty()) + return VFSPath(); + if (!this->relative && this->path.size() == 1 && !this->path[0].empty() && + this->path[0].back() == ':') + return *this; + + paths.insert(paths.begin(), this->path.begin(), this->path.end() - 1); + auto res = VFSPath(paths); + res.relative = this->relative; + return res; +} + +std::string VFSPath::GetFileName() const { + if (this->path.empty()) + return ""; + return this->path.back(); +} + +std::string VFSPath::ToString() const { + if (this->path.empty() && !this->relative) + return "/"; + if (!this->relative && this->path.size() == 1 && !this->path[0].empty() && + this->path[0].back() == ':') + return this->path[0] + "/"; + bool first = true; + std::string p = {}; + for (auto item : this->path) { + if (!(first && !item.empty() && item.back() == ':') && + !(first && this->relative)) + p.push_back('/'); + p.append(item); + first = false; + } + return p; +} +void VFS::DeleteFile(VFSPath path) {} +void VFS::MoveFile(VFSPath src, VFSPath dest) { + { + auto srcStrm = this->OpenFile(src, "rb"); + if (!srcStrm->CanRead()) + return; + auto destStrm = this->OpenFile(dest, "wb"); + if (!destStrm->CanWrite()) + return; + srcStrm->CopyTo(destStrm); + } + + VFS::DeleteFile(src); +} +VFS::~VFS() {} + +void VFS::MoveDirectory(VFSPath src, VFSPath dest) { + + for (auto item : EnumeratePaths(src)) { + if (DirectoryExists(item)) { + MoveDirectory(item, VFSPath(dest, item.GetFileName())); + } else { + MoveFile(item, VFSPath(dest, item.GetFileName())); + } + } + + DeleteDirectory(src); +} +void VFS::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile) {} +void VFS::CreateHardlink(VFSPath existingFile, VFSPath newName) {} + +void VFS::DeleteDirectoryRecurse(VFSPath path) { + if (!DirectoryExists(path)) + return; + + for (auto item : EnumeratePaths(path)) { + if (DirectoryExists(item)) { + DeleteDirectoryRecurse(item); + } else { + DeleteFile(item); + } + } + DeleteDirectory(path); +} +void VFS::GetDate(VFSPath path, Date::DateTime &lastWrite, + Date::DateTime &lastAccess) { + StatData data; + if (Stat(path, data)) { + lastWrite = data.LastModified; + lastAccess = data.LastAccess; + } +} +void VFS::SetDate(VFSPath path, Date::DateTime lastWrite, + Date::DateTime lastAccess) {} +bool VFS::StatVFS(VFSPath path, StatVFSData &data) { + data.BlockSize = 512; + data.Blocks = 10000000000000; + data.BlocksFree = 10000000000000; + data.BlocksAvailable = 10000000000000; + data.AvailableInodes = 10000000000000; + data.TotalInodes = 10000000000000; + data.FreeInodes = 10000000000000; + data.FragmentSize = 512; + data.Id = 85138; + data.Flags = 0; + data.MaxNameLength = 255; + return true; +} +void VFS::Chmod(VFSPath path, uint32_t mode) {} +void VFS::Chown(VFSPath path, uint32_t uid, uint32_t gid) {} +void VFS::Close() {} +void VFS::Lock(VFSPath path) {} +void VFS::Unlock(VFSPath path) {} + +std::shared_ptr VFS::CreateWatcher(std::shared_ptr vfs, + VFSPath path) { + return std::make_shared(vfs, path); +} +void FSWatcher::SetEnabled(bool enabled) { + if (this->enabled == enabled) + return; + this->enabled = enabled; + this->SetEnabledImpl(enabled); +} + +bool FSWatcher::GetEnabled() { return this->enabled; } + +void FSWatcher::SetEnabledImpl(bool enabled) {} + +std::shared_ptr FSWatcher::GetFilesystem() { return this->vfs; } +const VFSPath &FSWatcher::GetPath() { return this->path; } + +FSWatcher::FSWatcher(std::shared_ptr vfs, VFSPath path) + : vfs(vfs), path(path) {} + +std::shared_ptr FSWatcher::Create(std::shared_ptr vfs, + VFSPath path) { + return vfs->CreateWatcher(vfs, path); +} + +bool FSWatcherEvent::IsEvent(FSWatcherEventType e) { + if (e == FSWatcherEventType::All) + return this->type != FSWatcherEventType::None; + if (e == FSWatcherEventType::Moved) + return ((uint32_t)this->type & (uint32_t)FSWatcherEventType::Moved) == + (uint32_t)FSWatcherEventType::Moved; + if (e == FSWatcherEventType::Closed) + return ((uint32_t)this->type & (uint32_t)FSWatcherEventType::Closed) != + 0; + return (uint32_t)this->type & (uint32_t)e; +} + +std::string FSWatcherEvent::ToString() { + if (IsEvent(FSWatcherEventType::Moved)) { + return (this->isDir ? "Moved directory " : "Moved file ") + + this->src.ToString() + " -> " + this->dest.ToString(); + } else if (IsEvent(FSWatcherEventType::MoveOld)) { + return (this->isDir ? "Move source directory " : "Move source file ") + + this->src.ToString(); + } else if (IsEvent(FSWatcherEventType::MoveNew)) { + return (this->isDir ? "Move destination directory " + : "Move destination file ") + + this->src.ToString(); + } else if (IsEvent(FSWatcherEventType::Accessed)) { + return (this->isDir ? "Accessed directory " : "Accessed file ") + + this->src.ToString(); + } else if (IsEvent(FSWatcherEventType::AttributeChanged)) { + return (this->isDir ? "Changed attr on directory " + : "Changed attr on file ") + + this->src.ToString(); + } else if (IsEvent(FSWatcherEventType::Writen)) { + return (this->isDir ? "Finished changing directory " + : "Finished writing to file ") + + this->src.ToString(); + } else if (IsEvent(FSWatcherEventType::Read)) { + return (this->isDir ? "Finished reading directory " + : "Finished reading from file ") + + this->src.ToString(); + } else if (IsEvent(FSWatcherEventType::Created)) { + return (this->isDir ? "Created directory " : "Created file ") + + this->src.ToString(); + } else if (IsEvent(FSWatcherEventType::Deleted)) { + return (this->isDir ? "Deleted directory " : "Deleted file ") + + this->src.ToString(); + } else if (IsEvent(FSWatcherEventType::WatchEntryDeleted)) { + + return (this->isDir ? "Deleted watched directory " + : "Deleted watched file ") + + this->src.ToString(); + } else if (IsEvent(FSWatcherEventType::Modified)) { + return (this->isDir ? "Modified directory " : "Modified file ") + + this->src.ToString(); + } else if (IsEvent(FSWatcherEventType::WatchEntryMoved)) { + return (this->isDir ? "Moved watched directory " + : "Moved watched file ") + + this->src.ToString(); + } else if (IsEvent(FSWatcherEventType::Opened)) { + return (this->isDir ? "Opened directory " : "Opened file ") + + this->src.ToString(); + } + + return ""; +} +} // namespace Tesses::Framework::Filesystem diff --git a/src/HiddenField.cpp b/src/HiddenField.cpp index ae6c9e1..7668579 100644 --- a/src/HiddenField.cpp +++ b/src/HiddenField.cpp @@ -1,30 +1,36 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/HiddenField.hpp" -namespace Tesses::Framework -{ +namespace Tesses::Framework { -HiddenFieldData::~HiddenFieldData() -{ - -} +HiddenFieldData::~HiddenFieldData() {} - - -HiddenField::HiddenField() -{ - this->ptr = nullptr; -} -HiddenField::HiddenField(HiddenFieldData* data) -{ +HiddenField::HiddenField() { this->ptr = nullptr; } +HiddenField::HiddenField(HiddenFieldData *data) { this->ptr = data; } +void HiddenField::SetField(HiddenFieldData *data) { + if (this->ptr != nullptr) + delete this->ptr; this->ptr = data; } -void HiddenField::SetField(HiddenFieldData* data) -{ - if(this->ptr != nullptr) delete this->ptr; - this->ptr = data; +HiddenField::~HiddenField() { + if (this->ptr != nullptr) + delete this->ptr; } -HiddenField::~HiddenField() -{ - if(this->ptr != nullptr) delete this->ptr; -} -} \ No newline at end of file +} // namespace Tesses::Framework \ No newline at end of file diff --git a/src/Http/BasicAuthServer.cpp b/src/Http/BasicAuthServer.cpp index eeae1df..dea83c7 100644 --- a/src/Http/BasicAuthServer.cpp +++ b/src/Http/BasicAuthServer.cpp @@ -1,53 +1,73 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/BasicAuthServer.hpp" #include "TessesFramework/Crypto/Crypto.hpp" #include namespace Tesses::Framework::Http { - - BasicAuthServer::BasicAuthServer() - { - } - BasicAuthServer::BasicAuthServer(std::shared_ptr server, std::function auth,std::string realm) : server(server), authorization(auth), realm(realm) - { +BasicAuthServer::BasicAuthServer() {} +BasicAuthServer::BasicAuthServer( + std::shared_ptr server, + std::function auth, + std::string realm) + : server(server), authorization(auth), realm(realm) {} +bool BasicAuthServer::Handle(ServerContext &ctx) { + std::string www_authenticate = + "Basic realm=\"" + this->realm + "\", charset=\"UTF-8\""; + std::string user; + std::string pass; + if (!GetCreds(ctx, user, pass) || !this->authorization(user, pass)) { + ctx.responseHeaders.SetValue("WWW-Authenticate", www_authenticate); + ctx.statusCode = Unauthorized; + ctx.WithMimeType("text/html").SendText("

Unauthorized

"); + return true; + } - } - bool BasicAuthServer::Handle(ServerContext& ctx) - { - std::string www_authenticate = "Basic realm=\"" + this->realm + "\", charset=\"UTF-8\""; - std::string user; - std::string pass; - if(!GetCreds(ctx,user,pass) || !this->authorization(user,pass)) { - ctx.responseHeaders.SetValue("WWW-Authenticate",www_authenticate); - ctx.statusCode = Unauthorized; - ctx.WithMimeType("text/html").SendText("

Unauthorized

"); - return true; - } + if (this->server) + return this->server->Handle(ctx); + ctx.statusCode = InternalServerError; + ctx.WithMimeType("text/html") + .SendText("

Internal Server Error

\r\n

REASON: Internal " + "server not set on Basic Auth

"); + return true; +} - if(this->server) - return this->server->Handle(ctx); - ctx.statusCode = InternalServerError; - ctx.WithMimeType("text/html").SendText("

Internal Server Error

\r\n

REASON: Internal server not set on Basic Auth

"); - return true; - - } +bool BasicAuthServer::GetCreds(ServerContext &ctx, std::string &user, + std::string &pass) { + std::string auth; + if (!ctx.requestHeaders.TryGetFirst("Authorization", auth)) + return false; + auto security = HttpUtils::SplitString(auth, " ", 2); + if (security.size() < 2) + return false; + if (security[0] != "Basic") + return false; - bool BasicAuthServer::GetCreds(ServerContext& ctx, std::string& user, std::string& pass) - { - std::string auth; - if(!ctx.requestHeaders.TryGetFirst("Authorization", auth)) return false; - - auto security = HttpUtils::SplitString(auth," ",2); - if(security.size() < 2) return false; - if(security[0] != "Basic") return false; + auto decoded = Crypto::Base64_Decode(security[1]); - auto decoded = Crypto::Base64_Decode(security[1]); - - std::string decoded_str(decoded.begin(),decoded.end()); - security = HttpUtils::SplitString(decoded_str,":",2); - if(security.size() < 2) return false; - user = security[0]; - pass = security[1]; - return true; - } -} \ No newline at end of file + std::string decoded_str(decoded.begin(), decoded.end()); + security = HttpUtils::SplitString(decoded_str, ":", 2); + if (security.size() < 2) + return false; + user = security[0]; + pass = security[1]; + return true; +} +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/src/Http/CGIServer.cpp b/src/Http/CGIServer.cpp index ce90cfd..1cf67d9 100644 --- a/src/Http/CGIServer.cpp +++ b/src/Http/CGIServer.cpp @@ -1,135 +1,139 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/CGIServer.hpp" #include "TessesFramework/Filesystem/LocalFS.hpp" -#include "TessesFramework/Platform/Process.hpp" #include "TessesFramework/Http/BasicAuthServer.hpp" +#include "TessesFramework/Platform/Process.hpp" #include "TessesFramework/TextStreams/StreamReader.hpp" #include namespace Tesses::Framework::Http { - CGIServer::CGIServer(Tesses::Framework::Filesystem::VFSPath dir) - { - this->dir = dir; - } - bool CGIServer::Handle(ServerContext& ctx) - { - Tesses::Framework::Filesystem::VFSPath execPath = ctx.path; - execPath.relative=true; - CGIParams params; - params.document_root = this->document_root ? *this->document_root : this->dir; - params.adminEmail = this->adminEmail; - params.workingDirectory = this->workingDirectory; - params.program = this->dir / execPath.CollapseRelativeParents(); - - return ServeCGIRequest(ctx,params); - } - bool CGIServer::ServeCGIRequest(ServerContext& ctx, CGIParams& params) - { - using namespace Tesses::Framework::Filesystem; - auto program = params.program.MakeAbsolute(); - if(!LocalFS->FileExists(program)) return false; - Tesses::Framework::Platform::Process p; +CGIServer::CGIServer(Tesses::Framework::Filesystem::VFSPath dir) { + this->dir = dir; +} +bool CGIServer::Handle(ServerContext &ctx) { + Tesses::Framework::Filesystem::VFSPath execPath = ctx.path; + execPath.relative = true; + CGIParams params; + params.document_root = + this->document_root ? *this->document_root : this->dir; + params.adminEmail = this->adminEmail; + params.workingDirectory = this->workingDirectory; + params.program = this->dir / execPath.CollapseRelativeParents(); - Tesses::Framework::Filesystem::VFSPath p0=ctx.originalPath; - - p.env.emplace_back("SCRIPT_FILENAME",LocalFS->VFSPathToSystem(program)); - p.env.emplace_back("SCRIPT_NAME",p0.CollapseRelativeParents().ToString()); - if(ctx.encrypted) - p.env.emplace_back("HTTPS","on"); - - std::string query; - for(auto& item : ctx.queryParams.kvp) - { - for(auto& val : item.second) - { - if(!query.empty()) query += "&"; - - query += HttpUtils::UrlEncode(item.first); - query += "="; - query += HttpUtils::UrlEncode(val); - } - } - p.env.emplace_back("QUERY_STRING",query); - p.env.emplace_back("REQUEST_URI",ctx.GetOriginalPathWithQuery()); - p.env.emplace_back("REQUEST_METHOD",ctx.method); - p.env.emplace_back("REMOTE_HOST",ctx.ip); - p.env.emplace_back("REMOTE_ADDR",ctx.ip); - p.env.emplace_back("REMOTE_PORT",std::to_string(ctx.port)); - std::string user; - std::string pass; - if(BasicAuthServer::GetCreds(ctx,user,pass)) - p.env.emplace_back("REMOTE_USER",user); - p.env.emplace_back("SERVER_SOFTWARE","TessesFrameworkWebServer"); - p.env.emplace_back("SERVER_PORT",std::to_string(ctx.serverPort)); - p.env.emplace_back("GATEWAY_INTERFACE","CGI/1.1"); - p.env.emplace_back("SERVER_PROTOCOL",ctx.version); - - if(params.document_root) - p.env.emplace_back("DOCUMENT_ROOT",params.document_root->ToString()); - if(params.adminEmail) - p.env.emplace_back("SERVER_ADMIN",*params.adminEmail); - - for(auto& hdr : ctx.requestHeaders.kvp) - { - std::string hdr_name = HttpUtils::ToUpper(hdr.first); - if(hdr_name == "CONTENT-LENGTH") - { - if(!hdr.second.empty()) - p.env.emplace_back("CONTENT_LENGTH",hdr.second.front()); - } - else if(hdr_name == "CONTENT-TYPE") - { - if(!hdr.second.empty()) - p.env.emplace_back("CONTENT_LENGTH",hdr.second.front()); - } - else { - - if(!hdr.second.empty()) - p.env.emplace_back("HTTP_"+hdr.first,hdr.second.front()); - } - } - p.redirectStdIn=true; - p.redirectStdOut=true; - p.name = program.ToString(); - - - if(params.workingDirectory) - { - p.workingDirectory = params.workingDirectory->MakeAbsolute().ToString(); - } - - if(p.Start()) - { - auto strm = p.GetStdinStream(); - if(ctx.method != "GET") ctx.ReadStream(strm); - p.CloseStdInNow(); - auto stout = p.GetStdoutStream(); - Tesses::Framework::TextStreams::StreamReader reader(stout); - std::string line; - while(reader.ReadLineHttp(line)) - { - auto v = HttpUtils::SplitString(line,": ", 2); - if(v.size() == 2) - { - if(HttpUtils::ToLower(v[0]) == "status") - { - auto v2 = HttpUtils::SplitString(v[1]," ",2); - if(v2.empty()) - { - ctx.statusCode = StatusCode::InternalServerError; - throw std::runtime_error("Status response is empty"); - } - ctx.statusCode= (StatusCode)std::stoi(v2[0]); - } - else { - ctx.responseHeaders.AddValue(v[0],v[1]); - } - } - else throw std::runtime_error("Corrupted header: " + line); - line.clear(); - } - - ctx.SendStream(stout); - return true; - } + return ServeCGIRequest(ctx, params); +} +bool CGIServer::ServeCGIRequest(ServerContext &ctx, CGIParams ¶ms) { + using namespace Tesses::Framework::Filesystem; + auto program = params.program.MakeAbsolute(); + if (!LocalFS->FileExists(program)) return false; + Tesses::Framework::Platform::Process p; + + Tesses::Framework::Filesystem::VFSPath p0 = ctx.originalPath; + + p.env.emplace_back("SCRIPT_FILENAME", LocalFS->VFSPathToSystem(program)); + p.env.emplace_back("SCRIPT_NAME", p0.CollapseRelativeParents().ToString()); + if (ctx.encrypted) + p.env.emplace_back("HTTPS", "on"); + + std::string query; + for (auto &item : ctx.queryParams.kvp) { + for (auto &val : item.second) { + if (!query.empty()) + query += "&"; + + query += HttpUtils::UrlEncode(item.first); + query += "="; + query += HttpUtils::UrlEncode(val); + } } -} \ No newline at end of file + p.env.emplace_back("QUERY_STRING", query); + p.env.emplace_back("REQUEST_URI", ctx.GetOriginalPathWithQuery()); + p.env.emplace_back("REQUEST_METHOD", ctx.method); + p.env.emplace_back("REMOTE_HOST", ctx.ip); + p.env.emplace_back("REMOTE_ADDR", ctx.ip); + p.env.emplace_back("REMOTE_PORT", std::to_string(ctx.port)); + std::string user; + std::string pass; + if (BasicAuthServer::GetCreds(ctx, user, pass)) + p.env.emplace_back("REMOTE_USER", user); + p.env.emplace_back("SERVER_SOFTWARE", "TessesFrameworkWebServer"); + p.env.emplace_back("SERVER_PORT", std::to_string(ctx.serverPort)); + p.env.emplace_back("GATEWAY_INTERFACE", "CGI/1.1"); + p.env.emplace_back("SERVER_PROTOCOL", ctx.version); + + if (params.document_root) + p.env.emplace_back("DOCUMENT_ROOT", params.document_root->ToString()); + if (params.adminEmail) + p.env.emplace_back("SERVER_ADMIN", *params.adminEmail); + + for (auto &hdr : ctx.requestHeaders.kvp) { + std::string hdr_name = HttpUtils::ToUpper(hdr.first); + if (hdr_name == "CONTENT-LENGTH") { + if (!hdr.second.empty()) + p.env.emplace_back("CONTENT_LENGTH", hdr.second.front()); + } else if (hdr_name == "CONTENT-TYPE") { + if (!hdr.second.empty()) + p.env.emplace_back("CONTENT_LENGTH", hdr.second.front()); + } else { + + if (!hdr.second.empty()) + p.env.emplace_back("HTTP_" + hdr.first, hdr.second.front()); + } + } + p.redirectStdIn = true; + p.redirectStdOut = true; + p.name = program.ToString(); + + if (params.workingDirectory) { + p.workingDirectory = params.workingDirectory->MakeAbsolute().ToString(); + } + + if (p.Start()) { + auto strm = p.GetStdinStream(); + if (ctx.method != "GET") + ctx.ReadStream(strm); + p.CloseStdInNow(); + auto stout = p.GetStdoutStream(); + Tesses::Framework::TextStreams::StreamReader reader(stout); + std::string line; + while (reader.ReadLineHttp(line)) { + auto v = HttpUtils::SplitString(line, ": ", 2); + if (v.size() == 2) { + if (HttpUtils::ToLower(v[0]) == "status") { + auto v2 = HttpUtils::SplitString(v[1], " ", 2); + if (v2.empty()) { + ctx.statusCode = StatusCode::InternalServerError; + throw std::runtime_error("Status response is empty"); + } + ctx.statusCode = (StatusCode)std::stoi(v2[0]); + } else { + ctx.responseHeaders.AddValue(v[0], v[1]); + } + } else + throw std::runtime_error("Corrupted header: " + line); + line.clear(); + } + + ctx.SendStream(stout); + return true; + } + return false; +} +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/src/Http/CallbackServer.cpp b/src/Http/CallbackServer.cpp index 2123c4d..0b140df 100644 --- a/src/Http/CallbackServer.cpp +++ b/src/Http/CallbackServer.cpp @@ -1,22 +1,31 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/CallbackServer.hpp" -namespace Tesses::Framework::Http -{ - CallbackServer::CallbackServer(std::function cb) : CallbackServer(cb,[]()->void{}) - { - - } - CallbackServer::CallbackServer(std::function cb,std::function destroy) - { - this->cb = cb; - this->destroy=destroy; - } - bool CallbackServer::Handle(ServerContext& ctx) - { - return this->cb(ctx); - } - CallbackServer::~CallbackServer() - { - this->destroy(); - } -} \ No newline at end of file +namespace Tesses::Framework::Http { +CallbackServer::CallbackServer(std::function cb) + : CallbackServer(cb, []() -> void {}) {} +CallbackServer::CallbackServer(std::function cb, + std::function destroy) { + this->cb = cb; + this->destroy = destroy; +} +bool CallbackServer::Handle(ServerContext &ctx) { return this->cb(ctx); } +CallbackServer::~CallbackServer() { this->destroy(); } +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/src/Http/ChangeableServer.cpp b/src/Http/ChangeableServer.cpp index d3c24e1..af6eed9 100644 --- a/src/Http/ChangeableServer.cpp +++ b/src/Http/ChangeableServer.cpp @@ -1,22 +1,33 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/ChangeableServer.hpp" namespace Tesses::Framework::Http { -ChangeableServer::ChangeableServer() : ChangeableServer(nullptr) -{ - -} -ChangeableServer::ChangeableServer(std::shared_ptr original) -{ +ChangeableServer::ChangeableServer() : ChangeableServer(nullptr) {} +ChangeableServer::ChangeableServer(std::shared_ptr original) { this->server = original; } -bool ChangeableServer::Handle(ServerContext& ctx) -{ - if(this->server) this->server->Handle(ctx); +bool ChangeableServer::Handle(ServerContext &ctx) { + if (this->server) + this->server->Handle(ctx); return false; } -ChangeableServer::~ChangeableServer() -{ - -} -} \ No newline at end of file +ChangeableServer::~ChangeableServer() {} +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/src/Http/ContentDisposition.cpp b/src/Http/ContentDisposition.cpp index b8a3c44..c3d33ec 100644 --- a/src/Http/ContentDisposition.cpp +++ b/src/Http/ContentDisposition.cpp @@ -1,81 +1,81 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/ContentDisposition.hpp" #include "TessesFramework/Http/HttpUtils.hpp" #include -namespace Tesses::Framework::Http -{ - bool ContentDisposition::TryParse(std::string str, ContentDisposition& cd) - { - auto res = HttpUtils::SplitString(str,"; "); - - - if(res.empty()) return false; - cd.type = res[0]; - bool hasFileNameStar = false; - for(size_t i = 1; i < res.size(); i++) - { - auto res2 = HttpUtils::SplitString(res[i],"=",2); - if(res2.size() == 2) - { - if(res2[0] == "filename*") - { - //cd.filename = res2[1]; - //UTF-8'' - std::string p = res2[1]; - if(p.find("UTF-8''") == 0) - { - hasFileNameStar = true; - p = HttpUtils::UrlPathDecode(p.substr(7)); - cd.filename = p; - } - } - else if(res2[0] == "filename" && !hasFileNameStar) - { - std::string p = res2[1]; - if(!p.empty() && p[0] == '\"') - { - p = p.substr(1, p.size()-2); - } - - - p = HttpUtils::UrlPathDecode(p); - - +namespace Tesses::Framework::Http { +bool ContentDisposition::TryParse(std::string str, ContentDisposition &cd) { + auto res = HttpUtils::SplitString(str, "; "); + if (res.empty()) + return false; + cd.type = res[0]; + bool hasFileNameStar = false; + for (size_t i = 1; i < res.size(); i++) { + auto res2 = HttpUtils::SplitString(res[i], "=", 2); + if (res2.size() == 2) { + if (res2[0] == "filename*") { + // cd.filename = res2[1]; + // UTF-8'' + std::string p = res2[1]; + if (p.find("UTF-8''") == 0) { + hasFileNameStar = true; + p = HttpUtils::UrlPathDecode(p.substr(7)); cd.filename = p; } - else if(res2[0] == "name") - { - std::string p = res2[1]; - if(!p.empty() && p[0] == '\"') - { - p = p.substr(1, p.size()-2); - } - - cd.fieldName = HttpUtils::UrlPathDecode(p); - + } else if (res2[0] == "filename" && !hasFileNameStar) { + std::string p = res2[1]; + if (!p.empty() && p[0] == '\"') { + p = p.substr(1, p.size() - 2); } + + p = HttpUtils::UrlPathDecode(p); + + cd.filename = p; + } else if (res2[0] == "name") { + std::string p = res2[1]; + if (!p.empty() && p[0] == '\"') { + p = p.substr(1, p.size() - 2); + } + + cd.fieldName = HttpUtils::UrlPathDecode(p); } } - return true; } - + return true; +} - std::string ContentDisposition::ToString() - { - std::vector parts; +std::string ContentDisposition::ToString() { + std::vector parts; - parts.push_back(this->type); + parts.push_back(this->type); - if(!this->fieldName.empty()) - { - parts.push_back("name=\"" + HttpUtils::UrlPathEncode(this->fieldName,true) + "\""); - } - - if(!this->filename.empty()) - { - parts.push_back("filename=\"" + HttpUtils::UrlPathEncode(this->filename,true) + "\""); - } - - return HttpUtils::Join("; ", parts); + if (!this->fieldName.empty()) { + parts.push_back("name=\"" + + HttpUtils::UrlPathEncode(this->fieldName, true) + "\""); } -} \ No newline at end of file + + if (!this->filename.empty()) { + parts.push_back("filename=\"" + + HttpUtils::UrlPathEncode(this->filename, true) + "\""); + } + + return HttpUtils::Join("; ", parts); +} +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/src/Http/FileServer.cpp b/src/Http/FileServer.cpp index fb4d47f..203ca48 100644 --- a/src/Http/FileServer.cpp +++ b/src/Http/FileServer.cpp @@ -1,131 +1,135 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/FileServer.hpp" +#include "TessesFramework/Common.hpp" #include "TessesFramework/Filesystem/LocalFS.hpp" #include "TessesFramework/Filesystem/SubdirFilesystem.hpp" #include -#include "TessesFramework/Common.hpp" using LocalFilesystem = Tesses::Framework::Filesystem::LocalFilesystem; using SubdirFilesystem = Tesses::Framework::Filesystem::SubdirFilesystem; using VFSPath = Tesses::Framework::Filesystem::VFSPath; using VFS = Tesses::Framework::Filesystem::VFS; -namespace Tesses::Framework::Http -{ - FileServer::FileServer(std::filesystem::path path,bool allowListing,bool spa) : FileServer(path,allowListing,spa,{"index.html","default.html","index.htm","default.htm"}) - { +namespace Tesses::Framework::Http { +FileServer::FileServer(std::filesystem::path path, bool allowListing, bool spa) + : FileServer(path, allowListing, spa, + {"index.html", "default.html", "index.htm", "default.htm"}) {} +FileServer::FileServer(std::filesystem::path path, bool allowListing, bool spa, + std::vector defaultNames) { + std::shared_ptr sdfs = std::make_shared( + Tesses::Framework::Filesystem::LocalFS, + Tesses::Framework::Filesystem::LocalFS->SystemToVFSPath(path.string())); + this->vfs = sdfs; + this->spa = spa; - } - FileServer::FileServer(std::filesystem::path path,bool allowListing, bool spa, std::vector defaultNames) - { - std::shared_ptr sdfs=std::make_shared(Tesses::Framework::Filesystem::LocalFS,Tesses::Framework::Filesystem::LocalFS->SystemToVFSPath(path.string())); - this->vfs = sdfs; - this->spa = spa; - - this->allowListing = allowListing; - this->defaultNames = defaultNames; - } - FileServer::FileServer(std::shared_ptr fs, bool allowListing,bool spa) : FileServer(fs,allowListing,spa,{"index.html","default.html","index.htm","default.htm"}) - { - - } - FileServer::FileServer(std::shared_ptr fs, bool allowListing, bool spa, std::vector defaultNames) - { - this->vfs = fs; - this->allowListing = allowListing; - this->defaultNames = defaultNames; - this->spa = spa; - } - bool FileServer::SendFile(ServerContext& ctx,VFSPath path) - { - TF_LOG("File: " + path.ToString()); - auto strm = this->vfs->OpenFile(path,"rb"); - bool retVal = false; - if(strm != nullptr) - { - Date::DateTime lw,la; - this->vfs->GetDate(path,lw,la); - ctx.WithLastModified(lw).WithMimeType(HttpUtils::MimeType(path.GetFileName())).SendStream(strm); - retVal = true; - - } - return retVal; - } - - bool FileServer::Handle(ServerContext& ctx) - { - auto path = ((VFSPath)HttpUtils::UrlPathDecode(ctx.path)).CollapseRelativeParents(); - - - if(this->vfs->DirectoryExists(path)) - { - TF_LOG("Directory exists"); - for(auto f : defaultNames) - { - VFSPath p=path; - p = p / f; - TF_LOG("Trying " + p.ToString()); - TF_LOG("Before file exists"); - TF_LOG(this->vfs->FileExists(p)?"File Exists" : "File Does Not Exist"); - TF_LOG("After file exists"); - if(this->vfs->FileExists(p)) - return SendFile(ctx,p); - } - if(this->allowListing) - { - std::string p = HttpUtils::HtmlEncode(ctx.originalPath); - std::string html = "Index of "; - html.append(p); - html.append("

Index of "); - html.append(p); - html.append("


../\r\n");
-
-
-                for(auto item : vfs->EnumeratePaths(path))
-                {
-                    if(vfs->DirectoryExists(item))
-                    {
-                        //is dir
-                        std::string path = item.GetFileName();
-                        html.append("");
-                        html.append(HttpUtils::HtmlEncode(path) + "/");
-                        html.append("\r\n");
-                    }
-                    else
-                    {
-                        //is file
-                        std::string path = item.GetFileName();
-                        html.append("");
-                        html.append(HttpUtils::HtmlEncode(path));
-                        html.append("\r\n");
-                    }
-                }
-
-                html.append("

"); - - ctx.WithMimeType("text/html").SendText(html); - return true; - } - } - else if(this->vfs->FileExists(path)) - { - return SendFile(ctx,path); - } - else if(this->spa) - { - for(auto f : defaultNames) - { - VFSPath p(f); - p.relative=false; - if(this->vfs->FileExists(p)) - return SendFile(ctx,p); - } - } - return false; - } - FileServer::~FileServer() - { - - } + this->allowListing = allowListing; + this->defaultNames = defaultNames; } +FileServer::FileServer(std::shared_ptr fs, + bool allowListing, bool spa) + : FileServer(fs, allowListing, spa, + {"index.html", "default.html", "index.htm", "default.htm"}) {} +FileServer::FileServer(std::shared_ptr fs, + bool allowListing, bool spa, + std::vector defaultNames) { + this->vfs = fs; + this->allowListing = allowListing; + this->defaultNames = defaultNames; + this->spa = spa; +} +bool FileServer::SendFile(ServerContext &ctx, VFSPath path) { + TF_LOG("File: " + path.ToString()); + auto strm = this->vfs->OpenFile(path, "rb"); + bool retVal = false; + if (strm != nullptr) { + Date::DateTime lw, la; + this->vfs->GetDate(path, lw, la); + ctx.WithLastModified(lw) + .WithMimeType(HttpUtils::MimeType(path.GetFileName())) + .SendStream(strm); + retVal = true; + } + return retVal; +} + +bool FileServer::Handle(ServerContext &ctx) { + auto path = + ((VFSPath)HttpUtils::UrlPathDecode(ctx.path)).CollapseRelativeParents(); + + if (this->vfs->DirectoryExists(path)) { + TF_LOG("Directory exists"); + for (auto f : defaultNames) { + VFSPath p = path; + p = p / f; + TF_LOG("Trying " + p.ToString()); + TF_LOG("Before file exists"); + TF_LOG(this->vfs->FileExists(p) ? "File Exists" + : "File Does Not Exist"); + TF_LOG("After file exists"); + if (this->vfs->FileExists(p)) + return SendFile(ctx, p); + } + if (this->allowListing) { + std::string p = HttpUtils::HtmlEncode(ctx.originalPath); + std::string html = "Index of "; + html.append(p); + html.append("

Index of "); + html.append(p); + html.append("


../\r\n");
+
+            for (auto item : vfs->EnumeratePaths(path)) {
+                if (vfs->DirectoryExists(item)) {
+                    // is dir
+                    std::string path = item.GetFileName();
+                    html.append("");
+                    html.append(HttpUtils::HtmlEncode(path) + "/");
+                    html.append("\r\n");
+                } else {
+                    // is file
+                    std::string path = item.GetFileName();
+                    html.append("");
+                    html.append(HttpUtils::HtmlEncode(path));
+                    html.append("\r\n");
+                }
+            }
+
+            html.append("

"); + + ctx.WithMimeType("text/html").SendText(html); + return true; + } + } else if (this->vfs->FileExists(path)) { + return SendFile(ctx, path); + } else if (this->spa) { + for (auto f : defaultNames) { + VFSPath p(f); + p.relative = false; + if (this->vfs->FileExists(p)) + return SendFile(ctx, p); + } + } + return false; +} +FileServer::~FileServer() {} +} // namespace Tesses::Framework::Http diff --git a/src/Http/HttpClient.cpp b/src/Http/HttpClient.cpp index 31e3b37..951c57b 100644 --- a/src/Http/HttpClient.cpp +++ b/src/Http/HttpClient.cpp @@ -1,11 +1,29 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/HttpClient.hpp" #include "TessesFramework/Crypto/ClientTLSStream.hpp" #include "TessesFramework/Crypto/Crypto.hpp" -#include "TessesFramework/Streams/NetworkStream.hpp" -#include "TessesFramework/TextStreams/StreamWriter.hpp" -#include "TessesFramework/TextStreams/StreamReader.hpp" #include "TessesFramework/Http/HttpStream.hpp" #include "TessesFramework/Streams/BufferedStream.hpp" +#include "TessesFramework/Streams/NetworkStream.hpp" +#include "TessesFramework/TextStreams/StreamReader.hpp" +#include "TessesFramework/TextStreams/StreamWriter.hpp" #include "TessesFramework/Threading/Mutex.hpp" #include "TessesFramework/Threading/Thread.hpp" @@ -18,661 +36,634 @@ using StreamReader = Tesses::Framework::TextStreams::StreamReader; using StreamWriter = Tesses::Framework::TextStreams::StreamWriter; using BufferedStream = Tesses::Framework::Streams::BufferedStream; using HttpStream = Tesses::Framework::Http::HttpStream; -namespace Tesses::Framework::Http -{ - +namespace Tesses::Framework::Http { - void HttpRequestBody::HandleHeaders(HttpDictionary& dict) - { +void HttpRequestBody::HandleHeaders(HttpDictionary &dict) {} +HttpRequestBody::~HttpRequestBody() {} +TextHttpRequestBody::TextHttpRequestBody(std::string text, + std::string mimeType) { + this->text = text; + this->mimeType = mimeType; +} +void TextHttpRequestBody::HandleHeaders(HttpDictionary &dict) { + dict.SetValue("Content-Type", this->mimeType); + dict.SetValue("Content-Length", std::to_string(this->text.size())); +} +void TextHttpRequestBody::Write( + std::shared_ptr strm) { + strm->WriteBlock((const uint8_t *)this->text.c_str(), this->text.size()); +} +TextHttpRequestBody::~TextHttpRequestBody() {} + +StreamHttpRequestBody::StreamHttpRequestBody(std::shared_ptr strm, + std::string mimeType) { + this->strm = strm; + this->mimeType = mimeType; +} +void StreamHttpRequestBody::HandleHeaders(HttpDictionary &dict) { + dict.AddValue("Content-Type", this->mimeType); + auto len = this->strm->GetLength(); + if (len > -1) + dict.AddValue("Content-Length", std::to_string(len)); +} +void StreamHttpRequestBody::Write( + std::shared_ptr strm) { + this->strm->CopyTo(strm); +} +StreamHttpRequestBody::~StreamHttpRequestBody() {} +HttpRequest::HttpRequest() { + this->body = nullptr; + this->followRedirects = true; + this->ignoreSSLErrors = false; + this->trusted_root_cert_bundle = ""; + this->method = "GET"; + this->requestHeaders.SetValue("Connection", "close"); +} + +void HttpRequest::SendRequest( + std::shared_ptr strm) { + Uri uri; + if (!Uri::TryParse(this->url, uri)) + return; + + if (body != nullptr) { + body->HandleHeaders(requestHeaders); } - HttpRequestBody::~HttpRequestBody() - { - - } - TextHttpRequestBody::TextHttpRequestBody(std::string text, std::string mimeType) - { - this->text = text; - this->mimeType = mimeType; - } - void TextHttpRequestBody::HandleHeaders(HttpDictionary& dict) - { - dict.SetValue("Content-Type",this->mimeType); - dict.SetValue("Content-Length",std::to_string(this->text.size())); - } - void TextHttpRequestBody::Write(std::shared_ptr strm) - { - strm->WriteBlock((const uint8_t*)this->text.c_str(),this->text.size()); - } - TextHttpRequestBody::~TextHttpRequestBody() - { - + std::string request = method + " " + uri.GetPathAndQuery() + + " HTTP/1.1\r\nHost: " + uri.HostPort() + "\r\n"; + for (auto headers : requestHeaders.kvp) { + for (auto item : headers.second) { + request.append(headers.first); + request.append(": "); + request.append(item); + request.append("\r\n"); + } } - StreamHttpRequestBody::StreamHttpRequestBody(std::shared_ptr strm, std::string mimeType) - { - this->strm = strm; - this->mimeType = mimeType; - } - void StreamHttpRequestBody::HandleHeaders(HttpDictionary& dict) - { - dict.AddValue("Content-Type",this->mimeType); - auto len = this->strm->GetLength(); - if(len > -1) dict.AddValue("Content-Length",std::to_string(len)); + request.append("\r\n"); + StreamWriter writer(strm); + writer.Write(request); + + if (body != nullptr) { + body->Write(strm); } - void StreamHttpRequestBody::Write(std::shared_ptr strm) - { - this->strm->CopyTo(strm); - } - StreamHttpRequestBody::~StreamHttpRequestBody() - { - } - HttpRequest::HttpRequest() - { - this->body=nullptr; - this->followRedirects=true; - this->ignoreSSLErrors=false; - this->trusted_root_cert_bundle=""; - this->method = "GET"; - this->requestHeaders.SetValue("Connection","close"); +} +std::shared_ptr +HttpRequest::EstablishConnection(Uri uri, bool ignoreSSLErrors, + std::string trusted_root_cert_bundle) { + if (uri.scheme == "http:" || uri.scheme == "ws:") { + return std::make_shared(uri.host, uri.GetPort(), false, + false, false); + } else if (uri.scheme == "https:" || uri.scheme == "wss:") { + auto netStrm = std::make_shared(uri.host, uri.GetPort(), + false, false, false); + if (netStrm == nullptr) + return nullptr; + return std::make_shared( + netStrm, !ignoreSSLErrors, uri.host, trusted_root_cert_bundle); } - void HttpRequest::SendRequest(std::shared_ptr strm) - { - Uri uri; - if(!Uri::TryParse(this->url, uri)) return; - - - if(body != nullptr) - { - body->HandleHeaders(requestHeaders); + return nullptr; +} +std::shared_ptr +HttpRequest::EstablishUnixPathConnection(std::string unixPath, Uri uri, + bool ignoreSSLErrors, + std::string trusted_root_cert_bundle) { + if (uri.scheme == "http:" || uri.scheme == "ws:") { + return std::make_shared(unixPath, false); + } else if (uri.scheme == "https:" || uri.scheme == "wss:") { + auto netStrm = std::make_shared(unixPath, false); + if (netStrm == nullptr) + return nullptr; + return std::make_shared( + netStrm, !ignoreSSLErrors, uri.host, trusted_root_cert_bundle); + } + + return nullptr; +} +std::shared_ptr +HttpResponse::GetInternalStream() { + return this->handleStrm; +} + +HttpResponse::~HttpResponse() {} + +HttpResponse::HttpResponse(std::shared_ptr strm) { + this->handleStrm = nullptr; + StreamReader reader(strm); + std::string statusLine; + if (!reader.ReadLineHttp(statusLine)) + return; + auto statusLinesPart = HttpUtils::SplitString(statusLine, " ", 3); + if (statusLinesPart.size() >= 2) { + this->version = statusLinesPart[0]; + this->statusCode = (StatusCode)std::stoi(statusLinesPart[1]); + } + std::string line; + while (reader.ReadLineHttp(line)) { + if (line.empty()) + break; + auto v = HttpUtils::SplitString(line, ": ", 2); + if (v.size() != 2) { + return; + } + this->responseHeaders.AddValue(v[0], v[1]); + line.clear(); + } + this->handleStrm = strm; +} + +HttpResponse::HttpResponse(HttpRequest &req) { + this->handleStrm = nullptr; + + std::string url = req.url; + Uri uri; + while (Uri::TryParse(url, uri)) { + auto strm = + req.unixSocket.empty() + ? HttpRequest::EstablishConnection(uri, req.ignoreSSLErrors, + req.trusted_root_cert_bundle) + : HttpRequest::EstablishUnixPathConnection( + req.unixSocket, uri, req.ignoreSSLErrors, + req.trusted_root_cert_bundle); + if (strm == nullptr) + return; + auto reqHeaders = req.requestHeaders; + + if (req.body != nullptr) { + req.body->HandleHeaders(reqHeaders); } - std::string request = method + " " + uri.GetPathAndQuery() + " HTTP/1.1\r\nHost: " + uri.HostPort() + "\r\n"; - for(auto headers : requestHeaders.kvp) - { - for(auto item : headers.second) - { + std::string request = req.method + " " + uri.GetPathAndQuery() + + " HTTP/1.1\r\nHost: " + uri.HostPort() + "\r\n"; + + for (auto headers : reqHeaders.kvp) { + for (auto item : headers.second) { request.append(headers.first); request.append(": "); request.append(item); request.append("\r\n"); } - } request.append("\r\n"); - StreamWriter writer(strm); writer.Write(request); - if(body != nullptr) - { - body->Write(strm); + if (req.body != nullptr) { + req.body->Write(strm); } - } - std::shared_ptr HttpRequest::EstablishConnection(Uri uri, bool ignoreSSLErrors, std::string trusted_root_cert_bundle) - { - if(uri.scheme == "http:" || uri.scheme == "ws:") - { - return std::make_shared(uri.host,uri.GetPort(),false,false,false); - } - else if(uri.scheme == "https:" || uri.scheme == "wss:") - { - auto netStrm = std::make_shared(uri.host,uri.GetPort(),false,false,false); - if(netStrm == nullptr) return nullptr; - return std::make_shared(netStrm, !ignoreSSLErrors,uri.host, trusted_root_cert_bundle); - } - - return nullptr; - } - std::shared_ptr HttpRequest::EstablishUnixPathConnection(std::string unixPath,Uri uri, bool ignoreSSLErrors, std::string trusted_root_cert_bundle) - { - if(uri.scheme == "http:" || uri.scheme == "ws:") - { - return std::make_shared(unixPath,false); - } - else if(uri.scheme == "https:" || uri.scheme == "wss:") - { - auto netStrm = std::make_shared (unixPath,false); - if(netStrm == nullptr) return nullptr; - return std::make_shared(netStrm, !ignoreSSLErrors,uri.host, trusted_root_cert_bundle); - } - - return nullptr; - } - std::shared_ptr HttpResponse::GetInternalStream() - { - return this->handleStrm; - } - - HttpResponse::~HttpResponse() - { - - } - HttpResponse::HttpResponse(std::shared_ptr strm) - { - this->handleStrm=nullptr; StreamReader reader(strm); - std::string statusLine; - if(!reader.ReadLineHttp(statusLine)) return; - auto statusLinesPart = HttpUtils::SplitString(statusLine," ",3); - if(statusLinesPart.size() >= 2) - { - this->version = statusLinesPart[0]; - this->statusCode = (StatusCode)std::stoi(statusLinesPart[1]); - } - std::string line; - while(reader.ReadLineHttp(line)) - { - if(line.empty()) break; - auto v = HttpUtils::SplitString(line,": ", 2); - if(v.size() != 2) - { - return; - } - this->responseHeaders.AddValue(v[0],v[1]); - line.clear(); - } - this->handleStrm = strm; - } - - HttpResponse::HttpResponse(HttpRequest& req) - { - this->handleStrm = nullptr; - - std::string url = req.url; - Uri uri; - while(Uri::TryParse(url, uri)) - { - auto strm = req.unixSocket.empty() ? HttpRequest::EstablishConnection(uri, req.ignoreSSLErrors,req.trusted_root_cert_bundle) : HttpRequest::EstablishUnixPathConnection(req.unixSocket,uri, req.ignoreSSLErrors,req.trusted_root_cert_bundle); - if(strm == nullptr) return; - auto reqHeaders = req.requestHeaders; - - if(req.body != nullptr) - { - req.body->HandleHeaders(reqHeaders); - } - - std::string request = req.method + " " + uri.GetPathAndQuery() + " HTTP/1.1\r\nHost: " + uri.HostPort() + "\r\n"; - - for(auto headers : reqHeaders.kvp) - { - for(auto item : headers.second) - { - request.append(headers.first); - request.append(": "); - request.append(item); - request.append("\r\n"); - } - - } - - request.append("\r\n"); - - - StreamWriter writer(strm); - writer.Write(request); - - if(req.body != nullptr) - { - req.body->Write(strm); - } - - StreamReader reader(strm); - std::string statusLine; - if(!reader.ReadLineHttp(statusLine)) break; - auto statusLinesPart = HttpUtils::SplitString(statusLine," ",3); - if(statusLinesPart.size() >= 2) - { - this->version = statusLinesPart[0]; - this->statusCode = (StatusCode)std::stoi(statusLinesPart[1]); - } - std::string line; - while(reader.ReadLineHttp(line)) - { - if(line.empty()) break; - auto v = HttpUtils::SplitString(line,": ", 2); - if(v.size() != 2) - { - return; - } - this->responseHeaders.AddValue(v[0],v[1]); - line.clear(); - } - - std::string location; - Uri uri2; - - - if(req.followRedirects && (this->statusCode == MovedPermanently || this->statusCode == PermanentRedirect || this->statusCode == TemporaryRedirect || this->statusCode == SeeOther || this->statusCode == Found) && this->responseHeaders.TryGetFirst("Location",location) && uri.Relative(location, uri2)) - { - this->responseHeaders.Clear(); - - url = uri2.ToString(); - - - continue; - } - this->handleStrm = strm; + std::string statusLine; + if (!reader.ReadLineHttp(statusLine)) break; + auto statusLinesPart = HttpUtils::SplitString(statusLine, " ", 3); + if (statusLinesPart.size() >= 2) { + this->version = statusLinesPart[0]; + this->statusCode = (StatusCode)std::stoi(statusLinesPart[1]); } - } - std::string HttpResponse::ReadAsString() - { - auto strm = ReadAsStream(); - if(strm == nullptr) return {}; - StreamReader r(strm); - return r.ReadToEnd(); - } - void HttpResponse::CopyToStream(std::shared_ptr strm) - { - if(strm == nullptr) return; - auto src = ReadAsStream(); - if(src == nullptr) return; - src->CopyTo(strm); - - } - std::shared_ptr HttpResponse::ReadAsStream() - { - if(this->handleStrm == nullptr) return nullptr; - int64_t length = -1; - if(!this->responseHeaders.TryGetFirstInt("Content-Length",length)) - { - length = -1; - } - - return std::make_shared(this->handleStrm,length,true,version=="HTTP/1.1"); - } - - void DownloadUnixSocketToStreamSimple(std::string unixSocket,std::string url, std::shared_ptr strm) - { - if(strm == nullptr) throw std::runtime_error("strm is null"); - HttpRequest request; - request.url = url; - request.unixSocket = unixSocket; - request.followRedirects=true; - request.method = "GET"; - HttpResponse response(request); - if(response.statusCode < 200 || response.statusCode > 299) throw std::runtime_error("Status code does not indicate success: " + std::to_string(response.statusCode) + " " + HttpUtils::StatusCodeString(response.statusCode)); - response.CopyToStream(strm); - } - - - void DownloadUnixSocketToFileSimple(std::string unixSocket,std::string url, std::shared_ptr vfs, Tesses::Framework::Filesystem::VFSPath path) - { - if(vfs == nullptr) throw std::runtime_error("vfs is null"); - auto strm = vfs->OpenFile(path,"wb"); - if(strm == nullptr) throw std::runtime_error("strm is null"); - DownloadUnixSocketToStreamSimple(unixSocket,url,strm); - - } - - void DownloadUnixSocketToFileSimple(std::string unixSocket,std::string url, Tesses::Framework::Filesystem::VFSPath path) - { - DownloadUnixSocketToFileSimple(unixSocket,url,Tesses::Framework::Filesystem::LocalFS,path); - } - std::string DownloadUnixSocketToStringSimple(std::string unixSocket,std::string url) - { - HttpRequest request; - request.url = url; - request.unixSocket = unixSocket; - request.followRedirects=true; - request.method = "GET"; - HttpResponse response(request); - if(response.statusCode < 200 || response.statusCode > 299) throw std::runtime_error("Status code does not indicate success: " + std::to_string(response.statusCode) + " " + HttpUtils::StatusCodeString(response.statusCode)); - return response.ReadAsString(); - } - void DownloadToStreamSimple(std::string url, std::shared_ptr strm) - { - DownloadUnixSocketToStreamSimple("",url,strm); - } - - - void DownloadToFileSimple(std::string url, std::shared_ptr vfs, Tesses::Framework::Filesystem::VFSPath path) - { - DownloadUnixSocketToFileSimple("",url,vfs,path); - } - - void DownloadToFileSimple(std::string url, Tesses::Framework::Filesystem::VFSPath path) - { - DownloadUnixSocketToFileSimple("",url,path); - } - std::string DownloadToStringSimple(std::string url) - { - HttpRequest request; - request.url = url; - request.followRedirects=true; - request.method = "GET"; - HttpResponse response(request); - if(response.statusCode < 200 || response.statusCode > 299) throw std::runtime_error("Status code does not indicate success: " + std::to_string(response.statusCode) + " " + HttpUtils::StatusCodeString(response.statusCode)); - return response.ReadAsString(); - - } - bool WebSocketClientSuccessDefault(HttpDictionary& dict,bool v) - { - return true; - } - - - class WSClient { - public: - std::atomic closed; - Threading::Mutex mtx; - - std::shared_ptr conn; - std::shared_ptr strm; - void close() - { - mtx.Lock(); - this->closed=true; - uint8_t finField = 0b10000000 ; - uint8_t firstByte= finField | 0x8; - strm->WriteByte(firstByte); - strm->WriteByte(0); - - mtx.Unlock(); - - } - void write_len_bytes(uint64_t len) - { - if(len < 126) - { - strm->WriteByte((uint8_t)len | (uint8_t)128U); - } - else if(len < 65535) - { - uint8_t b[3]; - b[0] = 254; - b[1] = (uint8_t)(len >> 8); - b[2] = (uint8_t)len; - - strm->WriteBlock(b,sizeof(b)); - } - else { - - uint8_t b[9]; - b[0] = 255; - - b[1] = (uint8_t)(len >> 56); - b[2] = (uint8_t)(len >> 48); - b[3] = (uint8_t)(len >> 40); - b[4] = (uint8_t)(len >> 32); - b[5] = (uint8_t)(len >> 24); - b[6] = (uint8_t)(len >> 16); - b[7] = (uint8_t)(len >> 8); - b[8] = (uint8_t)len; - - strm->WriteBlock(b,sizeof(b)); - } - } - uint64_t get_long() - { - uint8_t buff[8]; - if(strm->ReadBlock(buff,sizeof(buff)) != sizeof(buff)) return 0; - - uint64_t v = 0; - v |= (uint64_t)buff[0] << 56; - v |= (uint64_t)buff[1] << 48; - v |= (uint64_t)buff[2] << 40; - v |= (uint64_t)buff[3] << 32; - v |= (uint64_t)buff[4] << 24; - v |= (uint64_t)buff[5] << 16; - v |= (uint64_t)buff[6] << 8; - v |= (uint64_t)buff[7]; - return v; - } - uint16_t get_short() - { - uint8_t buff[2]; - if(strm->ReadBlock(buff,sizeof(buff)) != sizeof(buff)) return 0; - - uint16_t v = 0; - v |= (uint16_t)buff[0] << 8; - v |= (uint16_t)buff[1]; - return v; - } - void send_msg(WebSocketMessage* msg) - { - mtx.Lock(); - - uint8_t opcode = msg->isBinary ? 0x2 : 0x1; - - size_t lengthLastByte = msg->data.size() % 4096; - size_t fullPackets = (msg->data.size() - lengthLastByte) / 4096; - size_t noPackets = lengthLastByte > 0 ? fullPackets+1 : fullPackets; - size_t offset = 0; - std::vector mask; - mask.resize(4); - for(size_t i = 0; i < noPackets; i++) - { - bool fin = i == noPackets - 1; - uint8_t finField = fin ? 0b10000000 : 0; - uint8_t opcode2 = i == 0 ? opcode : 0; - uint8_t firstByte = finField | (opcode2 & 0xF); - - size_t len = std::min((size_t)4096,msg->data.size()- offset); - - strm->WriteByte(firstByte); - write_len_bytes((uint64_t)len); - - Crypto::RandomBytes(mask,"Mask it"); - strm->WriteBlock(mask.data(),mask.size()); - for(size_t i = offset; i < offset+len; i++) - msg->data[i] ^= mask[i%4]; - strm->WriteBlock(msg->data.data() + offset,len); - for(size_t i = offset; i < offset+len; i++) - msg->data[i] ^= mask[i%4]; - offset += len; - } - mtx.Unlock(); - } - void ping_send(std::vector& pData) - { - mtx.Lock(); - - uint8_t finField = 0b10000000 ; - uint8_t firstByte= finField | 0x9; - strm->WriteByte(firstByte); - write_len_bytes((uint64_t)pData.size()); - std::vector mask; - mask.resize(4); - Crypto::RandomBytes(mask,"Mask it"); - strm->WriteBlock(mask.data(),mask.size()); - for(size_t i = 0; i < pData.size(); i++) - pData[i] ^= mask[i%4]; - strm->WriteBlock(pData.data(),pData.size()); - for(size_t i = 0; i < pData.size(); i++) - pData[i] ^= mask[i%4]; - mtx.Unlock(); - } - void pong_send(std::vector& pData) - { - mtx.Lock(); - - uint8_t finField = 0b10000000 ; - uint8_t firstByte= finField | 0xA; - strm->WriteByte(firstByte); - write_len_bytes((uint64_t)pData.size()); - strm->WriteBlock(pData.data(),pData.size()); - mtx.Unlock(); - } - bool read_packet(uint8_t len,std::vector& data) - { - - uint8_t realLen=len & 127; - bool masked=(len & 0b10000000) > 0; - uint64_t reallen2 = realLen >= 126 ? realLen > 126 ? get_long() : get_short() : realLen; - uint8_t mask[4]; - if(masked) - { - if(strm->ReadBlock(mask,sizeof(mask)) != sizeof(mask)) return false; - } - size_t offset = data.size(); - data.resize(offset+(size_t)reallen2); - if(data.size() < ((uint64_t)offset+reallen2)) return false; - if(strm->ReadBlock(data.data()+offset,(size_t)reallen2) != (size_t)reallen2) return false; - if(masked) - { - for(size_t i = 0; i < (size_t)reallen2; i++) - { - data[i+offset] ^= mask[i%4]; - } - } - return true; - } - - WSClient(std::shared_ptr strm,std::shared_ptr conn) - { - this->strm = strm; - this->conn = conn; - - } - void Start() - { - this->closed=false; - bool hasMessage =false; - - WebSocketMessage message; - message.isBinary=false; - message.data={}; - - - while(!strm->EndOfStream()) - { - - uint8_t frame_start[2]; - if(strm->ReadBlock(frame_start,2) != 2) return; - - - - uint8_t opcode = frame_start[0] & 0xF; - bool fin = (frame_start[0] & 0b10000000) > 0; - switch(opcode) - { - case 0x0: - if(!hasMessage) break; - read_packet(frame_start[1], message.data); - break; - case 0x1: - case 0x2: - hasMessage=true; - message.data = {}; - message.isBinary = opcode == 0x2; - - read_packet(frame_start[1], message.data); - break; - case 0x8: - if(!this->closed) this->close(); - this->conn->OnClose(true); - return; - case 0x9: - { - std::vector b; - read_packet(frame_start[1],b); - pong_send(b); - } - break; - case 0xA: - { - std::vector b; - read_packet(frame_start[1],b); - } - } - if(fin && hasMessage) - { - hasMessage=false; - this->conn->OnReceive(message); - message.data={}; - } - } - this->conn->OnClose(false); - } - }; - - - void WebSocketClient(std::string url, HttpDictionary& requestHeaders, std::shared_ptr wsc, std::function cb) - { - WebSocketUnixSocketClient("",url,requestHeaders,wsc,cb); - } - - void WebSocketUnixSocketClient(std::string unixSocket,std::string url, HttpDictionary& requestHeaders, std::shared_ptr wsc, std::function cb) - { - HttpRequest req; - req.url = url; - req.requestHeaders = requestHeaders; - req.followRedirects=true; - req.unixSocket = unixSocket; - - std::string hash = ""; - - if(Crypto::HaveCrypto()) - { - std::vector code; - code.resize(16); - Crypto::RandomBytes(code,"Tesses::Framework::Http::WebSocketClient"); - hash = Crypto::Base64_Encode(code); - } - - req.requestHeaders.SetValue("Sec-WebSocket-Key", hash); - req.requestHeaders.SetValue("Sec-WebSocket-Version","13"); - req.requestHeaders.SetValue("Upgrade","websocket"); - req.requestHeaders.SetValue("Connection","Upgrade"); - - HttpResponse resp(req); - - std::string accept="0uytbGS5rkQTa6saiHK4AQ=="; - - if(resp.statusCode != 101 || !resp.responseHeaders.TryGetFirst("Sec-WebSocket-Accept",accept) || !resp.responseHeaders.AnyEquals("Connection","Upgrade") || !resp.responseHeaders.AnyEquals("Upgrade","websocket")) - { - cb(resp.responseHeaders,false); - return; - } - - - if(Crypto::HaveCrypto()) - { - std::string txt=hash; - hash += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - if(Crypto::Base64_Encode(Crypto::Sha1::ComputeHash((const uint8_t*)hash.data(),hash.size())) != accept) - { - cb(resp.responseHeaders,false); + std::string line; + while (reader.ReadLineHttp(line)) { + if (line.empty()) + break; + auto v = HttpUtils::SplitString(line, ": ", 2); + if (v.size() != 2) { return; } + this->responseHeaders.AddValue(v[0], v[1]); + line.clear(); } - else if(accept != "Swjoe56alHg6mvKmKyiDd3tpNqc=") - { - cb(resp.responseHeaders,false); - return; - } - if(!cb(resp.responseHeaders,true)) - { - - return; - } - - WSClient clt(resp.GetInternalStream(),wsc); - - Threading::Thread thrd([&clt,wsc]()->void{ - try { - wsc->OnOpen([&clt](WebSocketMessage& msg)->void { - clt.send_msg(&msg); - },[&clt]()->void { - std::vector p = {(uint8_t)'p',(uint8_t)'i',(uint8_t)'n',(uint8_t)'g'}; - clt.ping_send(p); - },[&clt]()->void {clt.close();}); - }catch(...) { - } - }); - - - clt.Start(); - thrd.Join(); + std::string location; + Uri uri2; + + if (req.followRedirects && + (this->statusCode == MovedPermanently || + this->statusCode == PermanentRedirect || + this->statusCode == TemporaryRedirect || + this->statusCode == SeeOther || this->statusCode == Found) && + this->responseHeaders.TryGetFirst("Location", location) && + uri.Relative(location, uri2)) { + this->responseHeaders.Clear(); + + url = uri2.ToString(); + + continue; + } + this->handleStrm = strm; + break; } - } +std::string HttpResponse::ReadAsString() { + auto strm = ReadAsStream(); + if (strm == nullptr) + return {}; + StreamReader r(strm); + return r.ReadToEnd(); +} +void HttpResponse::CopyToStream(std::shared_ptr strm) { + if (strm == nullptr) + return; + auto src = ReadAsStream(); + if (src == nullptr) + return; + src->CopyTo(strm); +} +std::shared_ptr HttpResponse::ReadAsStream() { + if (this->handleStrm == nullptr) + return nullptr; + int64_t length = -1; + if (!this->responseHeaders.TryGetFirstInt("Content-Length", length)) { + length = -1; + } + + return std::make_shared(this->handleStrm, length, true, + version == "HTTP/1.1"); +} + +void DownloadUnixSocketToStreamSimple( + std::string unixSocket, std::string url, + std::shared_ptr strm) { + if (strm == nullptr) + throw std::runtime_error("strm is null"); + HttpRequest request; + request.url = url; + request.unixSocket = unixSocket; + request.followRedirects = true; + request.method = "GET"; + HttpResponse response(request); + if (response.statusCode < 200 || response.statusCode > 299) + throw std::runtime_error( + "Status code does not indicate success: " + + std::to_string(response.statusCode) + " " + + HttpUtils::StatusCodeString(response.statusCode)); + response.CopyToStream(strm); +} + +void DownloadUnixSocketToFileSimple( + std::string unixSocket, std::string url, + std::shared_ptr vfs, + Tesses::Framework::Filesystem::VFSPath path) { + if (vfs == nullptr) + throw std::runtime_error("vfs is null"); + auto strm = vfs->OpenFile(path, "wb"); + if (strm == nullptr) + throw std::runtime_error("strm is null"); + DownloadUnixSocketToStreamSimple(unixSocket, url, strm); +} + +void DownloadUnixSocketToFileSimple( + std::string unixSocket, std::string url, + Tesses::Framework::Filesystem::VFSPath path) { + DownloadUnixSocketToFileSimple( + unixSocket, url, Tesses::Framework::Filesystem::LocalFS, path); +} +std::string DownloadUnixSocketToStringSimple(std::string unixSocket, + std::string url) { + HttpRequest request; + request.url = url; + request.unixSocket = unixSocket; + request.followRedirects = true; + request.method = "GET"; + HttpResponse response(request); + if (response.statusCode < 200 || response.statusCode > 299) + throw std::runtime_error( + "Status code does not indicate success: " + + std::to_string(response.statusCode) + " " + + HttpUtils::StatusCodeString(response.statusCode)); + return response.ReadAsString(); +} +void DownloadToStreamSimple( + std::string url, std::shared_ptr strm) { + DownloadUnixSocketToStreamSimple("", url, strm); +} + +void DownloadToFileSimple( + std::string url, std::shared_ptr vfs, + Tesses::Framework::Filesystem::VFSPath path) { + DownloadUnixSocketToFileSimple("", url, vfs, path); +} + +void DownloadToFileSimple(std::string url, + Tesses::Framework::Filesystem::VFSPath path) { + DownloadUnixSocketToFileSimple("", url, path); +} +std::string DownloadToStringSimple(std::string url) { + HttpRequest request; + request.url = url; + request.followRedirects = true; + request.method = "GET"; + HttpResponse response(request); + if (response.statusCode < 200 || response.statusCode > 299) + throw std::runtime_error( + "Status code does not indicate success: " + + std::to_string(response.statusCode) + " " + + HttpUtils::StatusCodeString(response.statusCode)); + return response.ReadAsString(); +} +bool WebSocketClientSuccessDefault(HttpDictionary &dict, bool v) { + return true; +} + +class WSClient { + public: + std::atomic closed; + Threading::Mutex mtx; + + std::shared_ptr conn; + std::shared_ptr strm; + void close() { + mtx.Lock(); + this->closed = true; + uint8_t finField = 0b10000000; + uint8_t firstByte = finField | 0x8; + strm->WriteByte(firstByte); + strm->WriteByte(0); + + mtx.Unlock(); + } + void write_len_bytes(uint64_t len) { + if (len < 126) { + strm->WriteByte((uint8_t)len | (uint8_t)128U); + } else if (len < 65535) { + uint8_t b[3]; + b[0] = 254; + b[1] = (uint8_t)(len >> 8); + b[2] = (uint8_t)len; + + strm->WriteBlock(b, sizeof(b)); + } else { + + uint8_t b[9]; + b[0] = 255; + + b[1] = (uint8_t)(len >> 56); + b[2] = (uint8_t)(len >> 48); + b[3] = (uint8_t)(len >> 40); + b[4] = (uint8_t)(len >> 32); + b[5] = (uint8_t)(len >> 24); + b[6] = (uint8_t)(len >> 16); + b[7] = (uint8_t)(len >> 8); + b[8] = (uint8_t)len; + + strm->WriteBlock(b, sizeof(b)); + } + } + uint64_t get_long() { + uint8_t buff[8]; + if (strm->ReadBlock(buff, sizeof(buff)) != sizeof(buff)) + return 0; + + uint64_t v = 0; + v |= (uint64_t)buff[0] << 56; + v |= (uint64_t)buff[1] << 48; + v |= (uint64_t)buff[2] << 40; + v |= (uint64_t)buff[3] << 32; + v |= (uint64_t)buff[4] << 24; + v |= (uint64_t)buff[5] << 16; + v |= (uint64_t)buff[6] << 8; + v |= (uint64_t)buff[7]; + return v; + } + uint16_t get_short() { + uint8_t buff[2]; + if (strm->ReadBlock(buff, sizeof(buff)) != sizeof(buff)) + return 0; + + uint16_t v = 0; + v |= (uint16_t)buff[0] << 8; + v |= (uint16_t)buff[1]; + return v; + } + void send_msg(WebSocketMessage *msg) { + mtx.Lock(); + + uint8_t opcode = msg->isBinary ? 0x2 : 0x1; + + size_t lengthLastByte = msg->data.size() % 4096; + size_t fullPackets = (msg->data.size() - lengthLastByte) / 4096; + size_t noPackets = lengthLastByte > 0 ? fullPackets + 1 : fullPackets; + size_t offset = 0; + std::vector mask; + mask.resize(4); + for (size_t i = 0; i < noPackets; i++) { + bool fin = i == noPackets - 1; + uint8_t finField = fin ? 0b10000000 : 0; + uint8_t opcode2 = i == 0 ? opcode : 0; + uint8_t firstByte = finField | (opcode2 & 0xF); + + size_t len = std::min((size_t)4096, msg->data.size() - offset); + + strm->WriteByte(firstByte); + write_len_bytes((uint64_t)len); + + Crypto::RandomBytes(mask, "Mask it"); + strm->WriteBlock(mask.data(), mask.size()); + for (size_t i = offset; i < offset + len; i++) + msg->data[i] ^= mask[i % 4]; + strm->WriteBlock(msg->data.data() + offset, len); + for (size_t i = offset; i < offset + len; i++) + msg->data[i] ^= mask[i % 4]; + offset += len; + } + mtx.Unlock(); + } + void ping_send(std::vector &pData) { + mtx.Lock(); + + uint8_t finField = 0b10000000; + uint8_t firstByte = finField | 0x9; + strm->WriteByte(firstByte); + write_len_bytes((uint64_t)pData.size()); + std::vector mask; + mask.resize(4); + Crypto::RandomBytes(mask, "Mask it"); + strm->WriteBlock(mask.data(), mask.size()); + for (size_t i = 0; i < pData.size(); i++) + pData[i] ^= mask[i % 4]; + strm->WriteBlock(pData.data(), pData.size()); + for (size_t i = 0; i < pData.size(); i++) + pData[i] ^= mask[i % 4]; + mtx.Unlock(); + } + void pong_send(std::vector &pData) { + mtx.Lock(); + + uint8_t finField = 0b10000000; + uint8_t firstByte = finField | 0xA; + strm->WriteByte(firstByte); + write_len_bytes((uint64_t)pData.size()); + strm->WriteBlock(pData.data(), pData.size()); + mtx.Unlock(); + } + bool read_packet(uint8_t len, std::vector &data) { + + uint8_t realLen = len & 127; + bool masked = (len & 0b10000000) > 0; + uint64_t reallen2 = + realLen >= 126 ? realLen > 126 ? get_long() : get_short() : realLen; + uint8_t mask[4]; + if (masked) { + if (strm->ReadBlock(mask, sizeof(mask)) != sizeof(mask)) + return false; + } + size_t offset = data.size(); + data.resize(offset + (size_t)reallen2); + if (data.size() < ((uint64_t)offset + reallen2)) + return false; + if (strm->ReadBlock(data.data() + offset, (size_t)reallen2) != + (size_t)reallen2) + return false; + if (masked) { + for (size_t i = 0; i < (size_t)reallen2; i++) { + data[i + offset] ^= mask[i % 4]; + } + } + return true; + } + + WSClient(std::shared_ptr strm, + std::shared_ptr conn) { + this->strm = strm; + this->conn = conn; + } + void Start() { + this->closed = false; + bool hasMessage = false; + + WebSocketMessage message; + message.isBinary = false; + message.data = {}; + + while (!strm->EndOfStream()) { + + uint8_t frame_start[2]; + if (strm->ReadBlock(frame_start, 2) != 2) + return; + + uint8_t opcode = frame_start[0] & 0xF; + bool fin = (frame_start[0] & 0b10000000) > 0; + switch (opcode) { + case 0x0: + if (!hasMessage) + break; + read_packet(frame_start[1], message.data); + break; + case 0x1: + case 0x2: + hasMessage = true; + message.data = {}; + message.isBinary = opcode == 0x2; + + read_packet(frame_start[1], message.data); + break; + case 0x8: + if (!this->closed) + this->close(); + this->conn->OnClose(true); + return; + case 0x9: { + std::vector b; + read_packet(frame_start[1], b); + pong_send(b); + } break; + case 0xA: { + std::vector b; + read_packet(frame_start[1], b); + } + } + if (fin && hasMessage) { + hasMessage = false; + this->conn->OnReceive(message); + message.data = {}; + } + } + this->conn->OnClose(false); + } +}; + +void WebSocketClient(std::string url, HttpDictionary &requestHeaders, + std::shared_ptr wsc, + std::function cb) { + WebSocketUnixSocketClient("", url, requestHeaders, wsc, cb); +} + +void WebSocketUnixSocketClient(std::string unixSocket, std::string url, + HttpDictionary &requestHeaders, + std::shared_ptr wsc, + std::function cb) { + HttpRequest req; + req.url = url; + req.requestHeaders = requestHeaders; + req.followRedirects = true; + req.unixSocket = unixSocket; + + std::string hash = ""; + + if (Crypto::HaveCrypto()) { + std::vector code; + code.resize(16); + Crypto::RandomBytes(code, "Tesses::Framework::Http::WebSocketClient"); + hash = Crypto::Base64_Encode(code); + } + + req.requestHeaders.SetValue("Sec-WebSocket-Key", hash); + req.requestHeaders.SetValue("Sec-WebSocket-Version", "13"); + req.requestHeaders.SetValue("Upgrade", "websocket"); + req.requestHeaders.SetValue("Connection", "Upgrade"); + + HttpResponse resp(req); + + std::string accept = "0uytbGS5rkQTa6saiHK4AQ=="; + + if (resp.statusCode != 101 || + !resp.responseHeaders.TryGetFirst("Sec-WebSocket-Accept", accept) || + !resp.responseHeaders.AnyEquals("Connection", "Upgrade") || + !resp.responseHeaders.AnyEquals("Upgrade", "websocket")) { + cb(resp.responseHeaders, false); + return; + } + + if (Crypto::HaveCrypto()) { + std::string txt = hash; + hash += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + if (Crypto::Base64_Encode(Crypto::Sha1::ComputeHash( + (const uint8_t *)hash.data(), hash.size())) != accept) { + cb(resp.responseHeaders, false); + return; + } + } else if (accept != "Swjoe56alHg6mvKmKyiDd3tpNqc=") { + cb(resp.responseHeaders, false); + return; + } + if (!cb(resp.responseHeaders, true)) { + + return; + } + + WSClient clt(resp.GetInternalStream(), wsc); + + Threading::Thread thrd([&clt, wsc]() -> void { + try { + wsc->OnOpen( + [&clt](WebSocketMessage &msg) -> void { clt.send_msg(&msg); }, + [&clt]() -> void { + std::vector p = {(uint8_t)'p', (uint8_t)'i', + (uint8_t)'n', (uint8_t)'g'}; + clt.ping_send(p); + }, + [&clt]() -> void { clt.close(); }); + } catch (...) { + } + }); + + clt.Start(); + thrd.Join(); +} + +} // namespace Tesses::Framework::Http diff --git a/src/Http/HttpServer.cpp b/src/Http/HttpServer.cpp index 9438c1b..c90dd47 100644 --- a/src/Http/HttpServer.cpp +++ b/src/Http/HttpServer.cpp @@ -1,17 +1,35 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/HttpServer.hpp" +#include "TessesFramework/Common.hpp" +#include "TessesFramework/Crypto/Crypto.hpp" +#include "TessesFramework/Filesystem/VFS.hpp" +#include "TessesFramework/Filesystem/VFSFix.hpp" +#include "TessesFramework/Http/ContentDisposition.hpp" +#include "TessesFramework/Http/HttpStream.hpp" +#include "TessesFramework/Streams/BufferedStream.hpp" #include "TessesFramework/Streams/FileStream.hpp" #include "TessesFramework/Streams/MemoryStream.hpp" -#include "TessesFramework/TextStreams/StreamWriter.hpp" -#include "TessesFramework/TextStreams/StreamReader.hpp" -#include "TessesFramework/Http/ContentDisposition.hpp" -#include "TessesFramework/Streams/BufferedStream.hpp" -#include "TessesFramework/Http/HttpStream.hpp" -#include "TessesFramework/Crypto/Crypto.hpp" -#include "TessesFramework/Threading/Mutex.hpp" -#include "TessesFramework/Common.hpp" #include "TessesFramework/TextStreams/StdIOWriter.hpp" -#include "TessesFramework/Filesystem/VFSFix.hpp" -#include "TessesFramework/Filesystem/VFS.hpp" +#include "TessesFramework/TextStreams/StreamReader.hpp" +#include "TessesFramework/TextStreams/StreamWriter.hpp" +#include "TessesFramework/Threading/Mutex.hpp" #include #include @@ -25,1205 +43,1141 @@ using BufferedStream = Tesses::Framework::Streams::BufferedStream; using namespace Tesses::Framework::TextStreams; -namespace Tesses::Framework::Http +namespace Tesses::Framework::Http { + +void ServerSentEvents::SendEventRaw(const std::string &evt) { + this->mtx.Lock(); + for (auto &item : this->strms) { + try { + StreamWriter writer(item); + writer.newline = "\r\n"; + writer.WriteLine(evt); + + } catch (...) { + } + } + this->mtx.Unlock(); +} + +void ServerSentEvents::SendId(const std::string &id) { + SendCustomEvent("id", id); +} + +void ServerSentEvents::SendData(const std::string &message) { + SendCustomEvent("data", message); +} +void ServerSentEvents::SendComment(const std::string &comment) { + SendCustomEvent("", comment); +} +void ServerSentEvents::SendCustomEvent(const std::string &etype, + const std::string &message) { + std::string text = etype + ": "; + for (auto item : message) { + if (item == '\r') + continue; + if (item == '\n') { + text += "\r\n" + etype + ": "; + } else + text += item; + } + + SendEventRaw(text); +} +void ServerSentEvents::SendData(const std::string &message, + const std::string &mtype) { + std::string text = "event: " + mtype + "\r\ndata: "; + for (auto item : message) { + if (item == '\r') + continue; + if (item == '\n') { + text += "\r\ndata: "; + } else + text += item; + } + + SendEventRaw(text); +} +void ServerSentEvents::SendRetry(uint32_t ms) { + SendCustomEvent("retry", std::to_string(ms)); +} +void ServerSentEvents::SendRetry(std::chrono::milliseconds ms) { + SendCustomEvent("retry", std::to_string(ms.count())); +} +void ServerSentEvents::SendRetry(Tesses::Framework::Date::TimeSpan ts) { + SendRetry((uint32_t)ts.TotalSeconds() * 1000); +} + +class WSServer { + public: + Threading::Mutex mtx; + ServerContext *ctx; + std::shared_ptr conn; + std::shared_ptr strm; + std::atomic_bool hasInit; + std::atomic closed; + void close() { + mtx.Lock(); + closed = true; + uint8_t finField = 0b10000000; + uint8_t firstByte = finField | 0x8; + strm->WriteByte(firstByte); + strm->WriteByte(0); + + this->strm = nullptr; + mtx.Unlock(); + this->conn->OnClose(true); + } + void write_len_bytes(uint64_t len) { + if (len < 126) { + strm->WriteByte((uint8_t)len); + } else if (len < 65535) { + uint8_t b[3]; + b[0] = 126; + b[1] = (uint8_t)(len >> 8); + b[2] = (uint8_t)len; + + strm->WriteBlock(b, sizeof(b)); + } else { + + uint8_t b[9]; + b[0] = 127; + + b[1] = (uint8_t)(len >> 56); + b[2] = (uint8_t)(len >> 48); + b[3] = (uint8_t)(len >> 40); + b[4] = (uint8_t)(len >> 32); + b[5] = (uint8_t)(len >> 24); + b[6] = (uint8_t)(len >> 16); + b[7] = (uint8_t)(len >> 8); + b[8] = (uint8_t)len; + + strm->WriteBlock(b, sizeof(b)); + } + } + uint64_t get_long() { + uint8_t buff[8]; + if (strm->ReadBlock(buff, sizeof(buff)) != sizeof(buff)) + return 0; + + uint64_t v = 0; + v |= (uint64_t)buff[0] << 56; + v |= (uint64_t)buff[1] << 48; + v |= (uint64_t)buff[2] << 40; + v |= (uint64_t)buff[3] << 32; + v |= (uint64_t)buff[4] << 24; + v |= (uint64_t)buff[5] << 16; + v |= (uint64_t)buff[6] << 8; + v |= (uint64_t)buff[7]; + return v; + } + uint16_t get_short() { + uint8_t buff[2]; + if (strm->ReadBlock(buff, sizeof(buff)) != sizeof(buff)) + return 0; + + uint16_t v = 0; + v |= (uint16_t)buff[0] << 8; + v |= (uint16_t)buff[1]; + return v; + } + void send_msg(WebSocketMessage *msg) { + while (!hasInit) + ; + mtx.Lock(); + + uint8_t opcode = msg->isBinary ? 0x2 : 0x1; + + size_t lengthLastByte = msg->data.size() % 4096; + size_t fullPackets = (msg->data.size() - lengthLastByte) / 4096; + size_t noPackets = lengthLastByte > 0 ? fullPackets + 1 : fullPackets; + size_t offset = 0; + for (size_t i = 0; i < noPackets; i++) { + bool fin = i == noPackets - 1; + uint8_t finField = fin ? 0b10000000 : 0; + uint8_t opcode2 = i == 0 ? opcode : 0; + uint8_t firstByte = finField | (opcode2 & 0xF); + + size_t len = std::min((size_t)4096, msg->data.size() - offset); + + strm->WriteByte(firstByte); + write_len_bytes((uint64_t)len); + strm->WriteBlock(msg->data.data() + offset, len); + offset += len; + } + mtx.Unlock(); + } + void ping_send(std::vector &pData) { + + mtx.Lock(); + + uint8_t finField = 0b10000000; + uint8_t firstByte = finField | 0x9; + strm->WriteByte(firstByte); + write_len_bytes((uint64_t)pData.size()); + strm->WriteBlock(pData.data(), pData.size()); + mtx.Unlock(); + } + void pong_send(std::vector &pData) { + mtx.Lock(); + + uint8_t finField = 0b10000000; + uint8_t firstByte = finField | 0xA; + strm->WriteByte(firstByte); + write_len_bytes((uint64_t)pData.size()); + strm->WriteBlock(pData.data(), pData.size()); + mtx.Unlock(); + } + bool read_packet(uint8_t len, std::vector &data) { + + uint8_t realLen = len & 127; + bool masked = (len & 0b10000000) > 0; + uint64_t reallen2 = + realLen >= 126 ? realLen > 126 ? get_long() : get_short() : realLen; + uint8_t mask[4]; + if (masked) { + if (strm->ReadBlock(mask, sizeof(mask)) != sizeof(mask)) + return false; + } + size_t offset = data.size(); + data.resize(offset + (size_t)reallen2); + if (data.size() < ((uint64_t)offset + reallen2)) + return false; + if (strm->ReadBlock(data.data() + offset, (size_t)reallen2) != + (size_t)reallen2) + return false; + if (masked) { + for (size_t i = 0; i < (size_t)reallen2; i++) { + data[i + offset] ^= mask[i % 4]; + } + } + return true; + } + + WSServer(ServerContext *ctx, std::shared_ptr conn) { + this->ctx = ctx; + this->conn = conn; + this->strm = this->ctx->GetStream(); + this->hasInit = false; + } + void Start() { + this->closed = false; + std::string key; + if (ctx->requestHeaders.TryGetFirst("Sec-WebSocket-Key", key) && + !key.empty()) { + key.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + auto res = Crypto::Sha1::ComputeHash((const uint8_t *)key.c_str(), + key.size()); + + if (res.empty()) + return; + key = Crypto::Base64_Encode(res); + + } else { + + return; + } + + if (!ctx->requestHeaders.AnyEquals("Upgrade", "websocket")) { + + return; + } + if (!ctx->requestHeaders.AnyEquals("Sec-WebSocket-Version", "13")) { + + return; + } + ctx->statusCode = StatusCode::SwitchingProtocols; + ctx->responseHeaders.SetValue("Connection", "Upgrade"); + ctx->responseHeaders.SetValue("Upgrade", "websocket"); + ctx->responseHeaders.SetValue("Sec-WebSocket-Accept", key); + + ctx->WriteHeaders(); + bool hasMessage = false; + + WebSocketMessage message; + message.isBinary = false; + message.data = {}; + hasInit = true; + + while (!strm->EndOfStream()) { + + uint8_t frame_start[2]; + if (strm->ReadBlock(frame_start, 2) != 2) + return; + + uint8_t opcode = frame_start[0] & 0xF; + bool fin = (frame_start[0] & 0b10000000) > 0; + switch (opcode) { + case 0x0: + if (!hasMessage) + break; + read_packet(frame_start[1], message.data); + break; + case 0x1: + case 0x2: + hasMessage = true; + message.data = {}; + message.isBinary = opcode == 0x2; + + read_packet(frame_start[1], message.data); + break; + case 0x8: + + if (!this->closed) + this->close(); + this->conn->OnClose(true); + return; + case 0x9: { + std::vector b; + read_packet(frame_start[1], b); + pong_send(b); + } break; + case 0xA: { + std::vector b; + read_packet(frame_start[1], b); + } + } + if (fin && hasMessage) { + hasMessage = false; + this->conn->OnReceive(message); + message.data = {}; + } + } + this->conn->OnClose(false); + } +}; + +/* +static int _header_field(multipart_parser* p, const char *at, size_t length) { - - void ServerSentEvents::SendEventRaw(const std::string& evt) + UploadData* data = static_cast(multipart_parser_get_data(p)); + data->currentHeaderKey = std::string(at,length); + return 0; +} +static int _header_value(multipart_parser* p, const char *at, size_t length) +{ + UploadData* data = static_cast(multipart_parser_get_data(p)); + data->currentHeaders.AddValue(data->currentHeaderKey, +std::string(at,length)); std::cout << data->currentHeaderKey << ": " << +std::string(at,length) << std::endl; return 0; +} +static int _part_begin(multipart_parser* p) +{ + UploadData* data = static_cast(multipart_parser_get_data(p)); + std::string cd0; + ContentDisposition cd1; + std::string ct; + if(!data->currentHeaders.TryGetFirst("Content-Type",ct)) + ct = "application/octet-stream"; + if(data->currentHeaders.TryGetFirst("Content-Disposition", cd0) && +ContentDisposition::TryParse(cd0,cd1)) { - this->mtx.Lock(); - for(auto& item : this->strms) + if(cd1.filename.empty()) { - try { - StreamWriter writer(item); - writer.newline = "\r\n"; - writer.WriteLine(evt); - - - }catch(...) { - - } + data->isFile=false; + data->key = cd1.fieldName; + data->currentBody = new MemoryStream(true); + } + else + { + data->isFile = true; + data->currentBody = data->cb(ct, cd1.filename, cd1.fieldName); + } + } + return 0; +} +static int _part_end(multipart_parser* p) +{ + UploadData* data = static_cast(multipart_parser_get_data(p)); + if(data->currentBody == nullptr) return 0; + if(data->isFile) + { + delete data->currentBody; + data->currentBody = nullptr; + } + else + { + MemoryStream* ms = dynamic_cast(data->currentBody); + if(ms != nullptr) + { + ms->Seek(0, SeekOrigin::Begin); + auto& buff = ms->GetBuffer(); + data->ctx->queryParams.AddValue(data->key, +std::string(buff.begin(),buff.end())); + } + delete data->currentBody; + data->currentBody=nullptr; + } + data->currentHeaders.Clear(); + return 0; +}*/ + +std::string ServerContext::GetUrlWithQuery() { + if (this->queryParams.kvp.empty()) + return this->path; + return this->path + "?" + HttpUtils::QueryParamsEncode(this->queryParams); +} +std::string ServerContext::GetOriginalPathWithQuery() { + if (this->queryParams.kvp.empty()) + return this->originalPath; + return this->originalPath + "?" + + HttpUtils::QueryParamsEncode(this->queryParams); +} +void ServerContext::ReadStream(std::shared_ptr strm) { + if (strm == nullptr) + return; + auto strm2 = this->OpenRequestStream(); + if (strm2 != nullptr) { + strm2->CopyTo(strm); + } +} + +void ServerContext::SendServerSentEvents( + std::shared_ptr sse) { + this->responseHeaders.SetValue("X-Accel-Buffering", "no"); + this->responseHeaders.SetValue("Content-Type", "text/event-stream"); + this->responseHeaders.SetValue("Cache-Control", "no-cache"); + auto strm = this->OpenResponseStream(); + if (strm == nullptr || this->method == "HEAD") + return; + sse->mtx.Lock(); + sse->strms.push_back(strm); + sse->mtx.Unlock(); + while (!this->strm->EndOfStream()) { + TF_Sleep(10); + } + sse->mtx.Lock(); + for (auto index = sse->strms.begin(); index != sse->strms.end(); index++) { + if (*index == strm) { + sse->strms.erase(index); + break; } - this->mtx.Unlock(); } - void ServerSentEvents::SendId(const std::string& id) - { - SendCustomEvent("id",id); + sse->mtx.Unlock(); +} + +std::string ServerContext::ReadString() { + if (strm == nullptr) + return {}; + auto strm2 = this->OpenRequestStream(); + if (strm2 != nullptr) { + TextStreams::StreamReader reader(strm2); + return reader.ReadToEnd(); } - - void ServerSentEvents::SendData(const std::string& message) - { - SendCustomEvent("data", message); - } - void ServerSentEvents::SendComment(const std::string& comment) - { - SendCustomEvent("", comment); - } - void ServerSentEvents::SendCustomEvent(const std::string& etype, const std::string& message) - { - std::string text = etype + ": "; - for(auto item : message) - { - if(item == '\r') continue; - if(item == '\n') { - text += "\r\n" + etype + ": "; - } - else text += item; - } - - SendEventRaw(text); - } - void ServerSentEvents::SendData(const std::string& message, const std::string& mtype) - { - std::string text = "event: " + mtype + "\r\ndata: "; - for(auto item : message) - { - if(item == '\r') continue; - if(item == '\n') { - text += "\r\ndata: "; - } - else text += item; - } - - SendEventRaw(text); - } - void ServerSentEvents::SendRetry(uint32_t ms) - { - SendCustomEvent("retry", std::to_string(ms)); - } - void ServerSentEvents::SendRetry(std::chrono::milliseconds ms) - { - SendCustomEvent("retry", std::to_string(ms.count())); - } - void ServerSentEvents::SendRetry(Tesses::Framework::Date::TimeSpan ts) - { - SendRetry((uint32_t)ts.TotalSeconds() * 1000); - } - - class WSServer - { - public: - Threading::Mutex mtx; - ServerContext* ctx; - std::shared_ptr conn; - std::shared_ptr strm; - std::atomic_bool hasInit; - std::atomic closed; - void close() - { - mtx.Lock(); - closed=true; - uint8_t finField = 0b10000000 ; - uint8_t firstByte= finField | 0x8; - strm->WriteByte(firstByte); - strm->WriteByte(0); - - this->strm = nullptr; - mtx.Unlock(); - this->conn->OnClose(true); - } - void write_len_bytes(uint64_t len) - { - if(len < 126) - { - strm->WriteByte((uint8_t)len); - } - else if(len < 65535) - { - uint8_t b[3]; - b[0] = 126; - b[1] = (uint8_t)(len >> 8); - b[2] = (uint8_t)len; - - strm->WriteBlock(b,sizeof(b)); - } - else { - - uint8_t b[9]; - b[0] = 127; - - b[1] = (uint8_t)(len >> 56); - b[2] = (uint8_t)(len >> 48); - b[3] = (uint8_t)(len >> 40); - b[4] = (uint8_t)(len >> 32); - b[5] = (uint8_t)(len >> 24); - b[6] = (uint8_t)(len >> 16); - b[7] = (uint8_t)(len >> 8); - b[8] = (uint8_t)len; - - strm->WriteBlock(b,sizeof(b)); - } - } - uint64_t get_long() - { - uint8_t buff[8]; - if(strm->ReadBlock(buff,sizeof(buff)) != sizeof(buff)) return 0; - - uint64_t v = 0; - v |= (uint64_t)buff[0] << 56; - v |= (uint64_t)buff[1] << 48; - v |= (uint64_t)buff[2] << 40; - v |= (uint64_t)buff[3] << 32; - v |= (uint64_t)buff[4] << 24; - v |= (uint64_t)buff[5] << 16; - v |= (uint64_t)buff[6] << 8; - v |= (uint64_t)buff[7]; - return v; - } - uint16_t get_short() - { - uint8_t buff[2]; - if(strm->ReadBlock(buff,sizeof(buff)) != sizeof(buff)) return 0; - - uint16_t v = 0; - v |= (uint16_t)buff[0] << 8; - v |= (uint16_t)buff[1]; - return v; - } - void send_msg(WebSocketMessage* msg) - { - while(!hasInit); - mtx.Lock(); - - uint8_t opcode = msg->isBinary ? 0x2 : 0x1; - - size_t lengthLastByte = msg->data.size() % 4096; - size_t fullPackets = (msg->data.size() - lengthLastByte) / 4096; - size_t noPackets = lengthLastByte > 0 ? fullPackets+1 : fullPackets; - size_t offset = 0; - for(size_t i = 0; i < noPackets; i++) - { - bool fin = i == noPackets - 1; - uint8_t finField = fin ? 0b10000000 : 0; - uint8_t opcode2 = i == 0 ? opcode : 0; - uint8_t firstByte = finField | (opcode2 & 0xF); - - size_t len = std::min((size_t)4096,msg->data.size()- offset); - - strm->WriteByte(firstByte); - write_len_bytes((uint64_t)len); - strm->WriteBlock(msg->data.data() + offset,len); - offset += len; - } - mtx.Unlock(); - } - void ping_send(std::vector& pData) - { - - mtx.Lock(); - - uint8_t finField = 0b10000000 ; - uint8_t firstByte= finField | 0x9; - strm->WriteByte(firstByte); - write_len_bytes((uint64_t)pData.size()); - strm->WriteBlock(pData.data(),pData.size()); - mtx.Unlock(); - } - void pong_send(std::vector& pData) - { - mtx.Lock(); - - uint8_t finField = 0b10000000 ; - uint8_t firstByte= finField | 0xA; - strm->WriteByte(firstByte); - write_len_bytes((uint64_t)pData.size()); - strm->WriteBlock(pData.data(),pData.size()); - mtx.Unlock(); - } - bool read_packet(uint8_t len,std::vector& data) - { - - uint8_t realLen=len & 127; - bool masked=(len & 0b10000000) > 0; - uint64_t reallen2 = realLen >= 126 ? realLen > 126 ? get_long() : get_short() : realLen; - uint8_t mask[4]; - if(masked) - { - if(strm->ReadBlock(mask,sizeof(mask)) != sizeof(mask)) return false; - } - size_t offset = data.size(); - data.resize(offset+(size_t)reallen2); - if(data.size() < ((uint64_t)offset+reallen2)) return false; - if(strm->ReadBlock(data.data()+offset,(size_t)reallen2) != (size_t)reallen2) return false; - if(masked) - { - for(size_t i = 0; i < (size_t)reallen2; i++) - { - data[i+offset] ^= mask[i%4]; - } - } + return {}; +} +bool ServerContext::NeedToParseFormData() { + std::string ct; + if (this->requestHeaders.TryGetFirst("Content-Type", ct)) { + if (ct.find("multipart/form-data") == 0) { return true; } - - WSServer(ServerContext* ctx,std::shared_ptr conn) - { - this->ctx = ctx; - this->conn = conn; - this->strm = this->ctx->GetStream(); - this->hasInit=false; - - - } - void Start() - { - this->closed=false; - std::string key; - if(ctx->requestHeaders.TryGetFirst("Sec-WebSocket-Key", key) && !key.empty()) - { - key.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); - auto res = Crypto::Sha1::ComputeHash((const uint8_t*)key.c_str(),key.size()); - - if(res.empty()) return; - key = Crypto::Base64_Encode(res); - - }else { - - return; - } - - if(!ctx->requestHeaders.AnyEquals("Upgrade","websocket")) - { - - return; - } - if(!ctx->requestHeaders.AnyEquals("Sec-WebSocket-Version","13")) - { - - return; - } - ctx->statusCode = StatusCode::SwitchingProtocols; - ctx->responseHeaders.SetValue("Connection","Upgrade"); - ctx->responseHeaders.SetValue("Upgrade","websocket"); - ctx->responseHeaders.SetValue("Sec-WebSocket-Accept",key); - - ctx->WriteHeaders(); - bool hasMessage =false; - - WebSocketMessage message; - message.isBinary=false; - message.data={}; - hasInit=true; - - while( !strm->EndOfStream()) - { - - uint8_t frame_start[2]; - if( strm->ReadBlock(frame_start,2) != 2) return; - - - uint8_t opcode = frame_start[0] & 0xF; - bool fin = (frame_start[0] & 0b10000000) > 0; - switch(opcode) - { - case 0x0: - if(!hasMessage) break; - read_packet(frame_start[1], message.data); - break; - case 0x1: - case 0x2: - hasMessage=true; - message.data = {}; - message.isBinary = opcode == 0x2; - - read_packet(frame_start[1], message.data); - break; - case 0x8: - - if(!this->closed) this->close(); - this->conn->OnClose(true); - return; - case 0x9: - { - std::vector b; - read_packet(frame_start[1],b); - pong_send(b); - } - break; - case 0xA: - { - std::vector b; - read_packet(frame_start[1],b); - } - } - if(fin && hasMessage) - { - hasMessage=false; - this->conn->OnReceive(message); - message.data={}; - } - } - this->conn->OnClose(false); - } - }; - - /* - static int _header_field(multipart_parser* p, const char *at, size_t length) - { - UploadData* data = static_cast(multipart_parser_get_data(p)); - data->currentHeaderKey = std::string(at,length); - return 0; } - static int _header_value(multipart_parser* p, const char *at, size_t length) - { - UploadData* data = static_cast(multipart_parser_get_data(p)); - data->currentHeaders.AddValue(data->currentHeaderKey, std::string(at,length)); - std::cout << data->currentHeaderKey << ": " << std::string(at,length) << std::endl; - return 0; - } - static int _part_begin(multipart_parser* p) - { - UploadData* data = static_cast(multipart_parser_get_data(p)); - std::string cd0; - ContentDisposition cd1; - std::string ct; - if(!data->currentHeaders.TryGetFirst("Content-Type",ct)) - ct = "application/octet-stream"; - if(data->currentHeaders.TryGetFirst("Content-Disposition", cd0) && ContentDisposition::TryParse(cd0,cd1)) - { - if(cd1.filename.empty()) - { - data->isFile=false; - data->key = cd1.fieldName; - data->currentBody = new MemoryStream(true); - } - else - { - data->isFile = true; - data->currentBody = data->cb(ct, cd1.filename, cd1.fieldName); - } - } - return 0; - } - static int _part_end(multipart_parser* p) - { - UploadData* data = static_cast(multipart_parser_get_data(p)); - if(data->currentBody == nullptr) return 0; - if(data->isFile) - { - delete data->currentBody; - data->currentBody = nullptr; - } - else - { - MemoryStream* ms = dynamic_cast(data->currentBody); - if(ms != nullptr) - { - ms->Seek(0, SeekOrigin::Begin); - auto& buff = ms->GetBuffer(); - data->ctx->queryParams.AddValue(data->key, std::string(buff.begin(),buff.end())); - } - delete data->currentBody; - data->currentBody=nullptr; - } - data->currentHeaders.Clear(); - return 0; - }*/ - - std::string ServerContext::GetUrlWithQuery() - { - if(this->queryParams.kvp.empty()) return this->path; - return this->path + "?" + HttpUtils::QueryParamsEncode(this->queryParams); - } - std::string ServerContext::GetOriginalPathWithQuery() - { - if(this->queryParams.kvp.empty()) return this->originalPath; - return this->originalPath + "?" + HttpUtils::QueryParamsEncode(this->queryParams); - } - void ServerContext::ReadStream(std::shared_ptr strm) - { - if(strm == nullptr) return; - auto strm2 = this->OpenRequestStream(); - if(strm2 != nullptr) - { - strm2->CopyTo(strm); - } - } - - - void ServerContext::SendServerSentEvents(std::shared_ptr sse) - { - this->responseHeaders.SetValue("X-Accel-Buffering","no"); - this->responseHeaders.SetValue("Content-Type","text/event-stream"); - this->responseHeaders.SetValue("Cache-Control","no-cache"); - auto strm = this->OpenResponseStream(); - if(strm == nullptr || this->method == "HEAD") return; - sse->mtx.Lock(); - sse->strms.push_back(strm); - sse->mtx.Unlock(); - while(!this->strm->EndOfStream()) - { - TF_Sleep(10); - } - sse->mtx.Lock(); - for(auto index = sse->strms.begin(); index != sse->strms.end(); index++) - { - if(*index == strm) - { - sse->strms.erase(index); - break; - } - } - - sse->mtx.Unlock(); - } - - std::string ServerContext::ReadString() - { - if(strm == nullptr) return {}; - auto strm2 = this->OpenRequestStream(); - if(strm2 != nullptr) - { - TextStreams::StreamReader reader(strm2); - return reader.ReadToEnd(); - } - return {}; - } - bool ServerContext::NeedToParseFormData() - { - std::string ct; - if(this->requestHeaders.TryGetFirst("Content-Type",ct)) - { - if(ct.find("multipart/form-data") == 0) - { - return true; - } - } - return false; - } - static bool parseUntillBoundaryEnd(std::shared_ptr src, std::shared_ptr dest, std::string boundary) - { - bool hasMore=true; - std::vector checkBuffer(boundary.size()); - - int b; - size_t i = 0; - size_t i2 = 0; - - uint8_t buffer[1024]; - size_t offsetInMem = 0; - - while((b = src->ReadByte()) != -1) - { - if(i == boundary.size()) - { - if (b == '-') - { - i2++; - if(i2 == 2) hasMore=false; - } - if (b == '\n') break; - else if(b != '-') - i2 = 0; - continue; - } - i2=0; - - if (b == boundary[i]) //start filling the check buffer - { - checkBuffer[i] = (uint8_t)b; - i++; - } - else - { - size_t idx = 0; - while (idx < i) //write the buffer data to stream - { - - if(offsetInMem >= sizeof(buffer)) - { - dest->Write(buffer, sizeof(buffer)); - offsetInMem=0; - - } - - buffer[offsetInMem++] = checkBuffer[idx]; - - idx++; - } - - i = 0; - - if(offsetInMem >= sizeof(buffer)) - { - dest->Write(buffer, sizeof(buffer)); - offsetInMem=0; - - } - - buffer[offsetInMem++] = (uint8_t)b; - } - } - - if(offsetInMem > 0) - { - dest->Write(buffer,offsetInMem); - } - return hasMore; - } - - static bool parseSection(ServerContext* ctx, std::string boundary, std::function(std::string mime, std::string filename, std::string name)> cb) - { - HttpDictionary req; - StreamReader reader(ctx->GetStream()); - std::string line; - while(reader.ReadLineHttp(line)) - { - auto v = HttpUtils::SplitString(line,": ", 2); - if(v.size() == 2) - req.AddValue(v[0],v[1]); - line.clear(); - } - - std::string cd0; - ContentDisposition cd1; - std::string ct; - - if(!req.TryGetFirst("Content-Type",ct)) - ct = "application/octet-stream"; - if(req.TryGetFirst("Content-Disposition", cd0) && ContentDisposition::TryParse(cd0,cd1)) - { - if(cd1.filename.empty()) - { - std::shared_ptr ms=std::make_shared(true); - - bool retVal = parseUntillBoundaryEnd(ctx->GetStream(),ms,boundary); - auto& buff = ms->GetBuffer(); - - ctx->queryParams.AddValue(cd1.fieldName, std::string(buff.begin(),buff.end())); - - return retVal; - - } - else - { - auto strm = cb(ct, cd1.filename, cd1.fieldName); - if(strm == nullptr) strm = std::make_shared(); - bool retVal = parseUntillBoundaryEnd(ctx->GetStream(),strm,boundary); - - return retVal; - } - } - return false; - } - - void ServerContext::ParseFormData(std::function(std::string mime, std::string filename, std::string name)> cb) - { - std::string ct; - if(this->requestHeaders.TryGetFirst("Content-Type",ct)) - { - if(ct.find("multipart/form-data") != 0) - { - throw std::runtime_error("Not form data"); - } - auto res = ct.find("boundary="); - if(res == std::string::npos) return; - ct = "--" + ct.substr(res+9); - } - std::shared_ptr nullStrm=std::make_shared(); - parseUntillBoundaryEnd(this->strm,nullStrm, ct); - - while(parseSection(this, ct, cb)); - } - - HttpServer::HttpServer(std::shared_ptr tcpServer, std::shared_ptr http, bool showIPs, bool debug) - { - this->server = tcpServer; - this->http = http; - - this->showIPs = showIPs; - this->thrd=nullptr; - this->showARTL = showIPs; - this->debug = debug; - } - - - HttpServer::HttpServer(uint16_t port, std::shared_ptr http, bool showIPs, bool debug) : HttpServer(std::make_shared(port,10),http,showIPs, debug) - { - - } - - HttpServer::HttpServer(std::string unixPath, std::shared_ptr http, bool debug) : HttpServer(std::make_shared(unixPath,10),http,false, debug) - { - this->showARTL=true; - } - - - uint16_t HttpServer::GetPort() - { - if(server != nullptr) - return server->GetPort(); - return 0; - } - std::shared_ptr ServerContext::OpenResponseStream() - { - if(sent) return nullptr; - int64_t length = -1; - if(!this->responseHeaders.TryGetFirstInt("Content-Length",length)) - length = -1; - - if(this->version == "HTTP/1.1" && length == -1) - this->responseHeaders.SetValue("Transfer-Encoding","chunked"); - - this->WriteHeaders(); - auto strm = std::make_shared(this->strm,length,false,version == "HTTP/1.1"); - if(method == "HEAD") - { - strm->Close(); - } - return strm; - } - std::shared_ptr ServerContext::OpenRequestStream() - { - int64_t length = -1; - if(!this->requestHeaders.TryGetFirstInt("Content-Length",length)) - length = -1; - return std::make_shared(this->strm,length,true,version == "HTTP/1.1"); - } - void HttpServer::StartAccepting() - { - fflush(stdout); - if(http == nullptr || server == nullptr) return; - auto svr=this->server; - auto serverPort = this->server->GetPort(); - auto http = this->http; - TF_LOG("Before Creating Thread"); - bool debug = this->debug; - thrd = new Threading::Thread([svr,http,serverPort,debug]()->void { - while(TF_IsRunning()) - { - TF_LOG("after TF_IsRunning"); - std::string ip; - uint16_t port; - auto sock =svr->GetStream(ip,port); - - TF_LOG("New Host IP: " + ip + ":" + std::to_string(port)); - - if(sock == nullptr) - { - std::cout << "STREAM ERROR" << std::endl; - return; - } - TF_LOG("Before entering socket thread"); - - Threading::Thread thrd2([sock,http,ip,port,serverPort,debug]()->void { - TF_LOG("In thread to process"); - HttpServer::Process(sock,http,ip,port,serverPort,false, debug); - TF_LOG("In thread after process"); - - }); - TF_LOG("Before attach"); - thrd2.Detach(); - TF_LOG("After attach"); - } - }); - if(this->showIPs) - { - TF_LOG("Before printing interfaces"); - - StdOut() << "\x1B[34mInterfaces:" << NewLine(); - for(auto _ip : NetworkStream::GetIPs()) - { - StdOut() << "\x1B[32m" - << _ip.first << ": " - << "\x1B[35mhttp://" - << _ip.second << ":" << (uint64_t)this->GetPort() << "/" << NewLine(); - } - - - } - if(this->showARTL) - { - if(!svr->IsValid()) std::cout << "\x1B[31mError, we failed to bind or something\x1B[39m\n" << std::endl; - StdOut() << "\x1B[31mAlmost Ready to Listen\x1B[39m" << NewLine(); - } - - TF_LOG("After printing interfaces"); - } - HttpServer::~HttpServer() - { - auto port = this->GetPort(); - this->server->Close(); - TF_ConnectToSelf(port); - if(this->thrd != nullptr) - { - this->thrd->Join(); - delete this->thrd; - } - - } - IHttpServer::~IHttpServer() - { - - } - ServerContext::ServerContext(std::shared_ptr strm, bool debug) - { - this->statusCode = OK; - this->strm = strm; - this->debug = debug; - this->sent = false; - this->queryParams.SetCaseSensitive(true); - this->pathArguments.SetCaseSensitive(true); - this->responseHeaders.AddValue("Server","TessesFrameworkWebServer"); - } - std::shared_ptr ServerContext::GetStream() - { - return this->strm; - } - void ServerContext::SendBytes(std::vector buff) - { - std::shared_ptr strm=std::make_shared(false); - strm->GetBuffer() = buff; - SendStream(strm); - } - ServerContext& ServerContext::WithLastModified(Date::DateTime dt) - { - this->responseHeaders.SetValue("Last-Modified",dt); - return *this; - } - - void ServerContext::SendText(std::string text) - { - std::shared_ptr strm=std::make_shared(false); - - auto& buff= strm->GetBuffer(); - buff.insert(buff.end(),text.begin(),text.end()); - SendStream(strm); - } - void ServerContext::SendErrorPage(bool showPath) - { - if(sent) return; - std::string errorHtml = showPath ? ("File " + HttpUtils::HtmlEncode(this->originalPath) + " " + HttpUtils::StatusCodeString(this->statusCode) + "

" + std::to_string((int)this->statusCode) + " " + HttpUtils::StatusCodeString(this->statusCode) + "

" + HttpUtils::HtmlEncode(this->originalPath) + "

") : ( - "" + std::to_string(this->statusCode) + " " + HttpUtils::StatusCodeString(this->statusCode) + "

" + std::to_string((int)this->statusCode) + " " + HttpUtils::StatusCodeString(this->statusCode) + "

" - ); - - WithMimeType("text/html").SendText(errorHtml); - } - - ServerContext::~ServerContext() - { - for(auto item : this->data) - { - delete item.second; - } - } - ServerContextData::~ServerContextData() - { - - } - void ServerContext::SendStream(std::shared_ptr strm) - { - if(sent) return; - if(!strm->CanRead()) throw std::runtime_error("Cannot read from stream"); - if(strm->EndOfStream()) throw std::runtime_error("End of stream"); - if(strm->CanSeek()) - { - int64_t len=strm->GetLength(); - std::string range={}; - if(this->requestHeaders.TryGetFirst("Range",range)) - { - auto res = HttpUtils::SplitString(range,"=",2); - if(res.size() == 2) - { - if(res[0] != "bytes") - { - this->statusCode = BadRequest; - this->WriteHeaders(); - return; - } - res = HttpUtils::SplitString(res[1],", ",2); - if(res.size() != 1) - { - this->statusCode = BadRequest; - this->WriteHeaders(); - return; - } - auto dash=HttpUtils::SplitString(res[0],"-",2); - int64_t begin = 0; - int64_t end = -1; - - if(dash.size() == 1 && res[0].find_first_of('-') != std::string::npos) - { - //NUMBER- - begin = std::stoll(dash[0]); - } - else if(dash.size() == 2) - { - //NUMBER-NUMBER - //or - //-NUMBER - - if(dash[0].size() > 0) - { - //NUMBER-NUMBER - begin = std::stoll(dash[0]); - end = std::stoll(dash[1]); - } - else - { - //-NUMBER - end = std::stoll(dash[1]); - } - } - else - { - this->statusCode = BadRequest; - this->WriteHeaders(); - return; - } - - if(end == -1) - { - end = len-1; - } - - if(end > len-1) - { - this->statusCode = RangeNotSatisfiable; - this->WithSingleHeader("Content-Range","bytes */" + std::to_string(len)); - this->WriteHeaders(); - return; - } - - if(begin >= end) { - this->statusCode = RangeNotSatisfiable; - this->WithSingleHeader("Content-Range","bytes */" + std::to_string(len)); - this->WriteHeaders(); - return; - } - - int64_t myLen = (end - begin)+1; - - this->WithSingleHeader("Accept-Ranges","bytes"); - this->WithSingleHeader("Content-Length",std::to_string(myLen)); - this->WithSingleHeader("Content-Range","bytes " + std::to_string(begin) + "-" + std::to_string(end) + "/" + std::to_string(len)); - this->statusCode = PartialContent; - this->WriteHeaders(); - if(this->method != "HEAD") - { - strm->Seek(begin,SeekOrigin::Begin); - - uint8_t buffer[1024]; - - size_t read=0; - - do { - read = sizeof(buffer); - myLen = (end - begin)+1; - if(myLen < read) read = (size_t)myLen; - if(read == 0) break; - read = strm->Read(buffer,read); - if(read == 0) break; - this->strm->WriteBlock(buffer,read); - - begin += read; - } while(read > 0 && !this->strm->EndOfStream()); - } - - } - else - { - this->statusCode = BadRequest; - this->SendErrorPage(true); - return; - } - } - else - { - if(len > -1) - { - this->WithSingleHeader("Accept-Range","bytes"); - this->WithSingleHeader("Content-Length",std::to_string(len)); - this->WriteHeaders(); - if(this->method != "HEAD") - strm->CopyTo(this->strm); - } - } - - } - else - { - - auto chunkedStream = this->OpenResponseStream(); - - if(method != "HEAD") - strm->CopyTo(chunkedStream); - - - } - } - ServerContext& ServerContext::WithHeader(std::string key, std::string value) - { - this->responseHeaders.AddValue(key, value); - return *this; - } - ServerContext& ServerContext::WithSingleHeader(std::string key, std::string value) - { - this->responseHeaders.SetValue(key, value); - return *this; - } - ServerContext& ServerContext::WithMimeType(std::string mime) - { - this->responseHeaders.SetValue("Content-Type",mime); - return *this; - } - ServerContext& ServerContext::WithContentDisposition(std::string filename, bool isInline) - { - ContentDisposition cd; - cd.type = isInline ? "inline" : "attachment"; - cd.filename = filename; - - //std::string cd; - //cd = (isInline ? "inline; filename*=UTF-8''" : "attachment; filename*=UTF-8''") + HttpUtils::UrlPathEncode(filename); - this->responseHeaders.SetValue("Content-Disposition",cd.ToString()); - return *this; - } - ServerContext& ServerContext::WithLocationHeader(std::string url) - { - return WithSingleHeader("Location", this->MakeAbsolute(url)); - } - ServerContext& ServerContext::WithLocationHeader(std::string url,StatusCode sc) - { - this->statusCode = sc; - return WithSingleHeader("Location", this->MakeAbsolute(url)); - } - void ServerContext::SendRedirect(std::string url) - { - WithLocationHeader(url); - this->WriteHeaders(); - } - void ServerContext::SendRedirect(std::string url,StatusCode sc) - { - WithLocationHeader(url,sc); - this->WriteHeaders(); - } - - void ServerContext::SendNotFound() - { - if(sent) return; - statusCode = StatusCode::NotFound; - SendErrorPage(true); - } - void ServerContext::SendBadRequest() - { - if(sent) return; - statusCode = StatusCode::BadRequest; - SendErrorPage(false); - } - ServerContext& ServerContext::WithStatusCode(StatusCode code) - { - this->statusCode = code; - return *this; - } - void ServerContext::SendException(std::exception& ex) - { - if(this->debug) - { - - this->WithMimeType("text/html").WithStatusCode(StatusCode::InternalServerError).SendText(" Internal Server Error at " + HttpUtils::HtmlEncode(this->originalPath) + "

Internal Server Error at " + HttpUtils::HtmlEncode(this->originalPath) + "

what(): " + HttpUtils::HtmlEncode(ex.what()) + "

"); - } - else { - this->WithStatusCode(StatusCode::InternalServerError).SendErrorPage(true); - } - } - - ServerContext& ServerContext::WithHeaderIntercepter(std::function cb) - { - this->headerhandlers.push(cb); - return *this; - } - - ServerContext& ServerContext::WriteHeaders() - { - if(this->sent) return *this; - while(!this->headerhandlers.empty()) - { - auto header = this->headerhandlers.front(); - this->headerhandlers.pop(); - if(header(*this)) { - break; - } - } - if(this->sent) return *this; - this->sent = true; - - StreamWriter writer(this->strm); - writer.newline = "\r\n"; - writer.WriteLine("HTTP/1.1 " + std::to_string((int)statusCode) + " " + HttpUtils::StatusCodeString(statusCode)); - for(auto& hdr : responseHeaders.kvp) - { - auto& key = hdr.first; - for(auto& val : hdr.second) - { - writer.WriteLine(key + ": " + val); - } - } - writer.WriteLine(); - - return *this; - } - void HttpServer::Process(std::shared_ptr strm, std::shared_ptr server, std::string ip, uint16_t port,uint16_t serverPort, bool encrypted, bool debug) - { - TF_LOG("In process"); - std::shared_ptr bStrm = std::make_shared(strm); - StreamReader reader(bStrm); - ServerContext ctx(bStrm); - ctx.ip = ip; - ctx.port = port; - ctx.encrypted = encrypted; - ctx.serverPort = serverPort; - try{ - bool firstLine = true; - std::string line; - while(reader.ReadLineHttp(line)) - { - if(firstLine) - { - auto v = HttpUtils::SplitString(line, " ", 3); - if(v.size() != 3) { - ctx.statusCode = BadRequest; - ctx.WithMimeType("text/plain").SendText("First line is not 3 elements"); - return; - } - ctx.method = v[0]; - auto pp = HttpUtils::SplitString(v[1],"?", 2); - pp.resize(2); - - ctx.originalPath = pp[0]; - ctx.path = ctx.originalPath; - - TF_LOG(ctx.method + " with path " + ctx.path); - - auto queryPart = pp[1]; - if(!queryPart.empty()) - { - HttpUtils::QueryParamsDecode(ctx.queryParams, queryPart); - } - - ctx.version = v[2]; - - } - else - { - auto v = HttpUtils::SplitString(line,": ", 2); - if(v.size() != 2) { - ctx.statusCode = BadRequest; - ctx.WithMimeType("text/plain").SendText("Header line is not 2 elements"); - return; - } - - ctx.requestHeaders.AddValue(v[0],v[1]); - - } - line.clear(); - firstLine=false; - } - - if(firstLine) return; - - std::string type; - int64_t length; - - if(ctx.requestHeaders.TryGetFirst("Content-Type",type) && type == "application/x-www-form-urlencoded" && ctx.requestHeaders.TryGetFirstInt("Content-Length",length)) - { - size_t len = (size_t)length; - std::vector buffer(len); - len = bStrm->ReadBlock(buffer.data(),len); - std::string query((const char*)buffer.data(),len); - HttpUtils::QueryParamsDecode(ctx.queryParams, query); - } - - if(!server->Handle(ctx)) - { - if((int)ctx.statusCode < 400) - ctx.SendNotFound(); - else - ctx.SendErrorPage(true); - } - } - - catch(std::exception& ex) - { - ctx.SendException(ex); - } - catch(std::string& ex) - { - std::runtime_error re(ex); - ctx.SendException(re); - } - catch(const char* ex) - { - std::runtime_error re(ex); - ctx.SendException(re); - } - catch(...) - { - std::runtime_error ex("An unknown error occurred"); - ctx.SendException(ex); - } - - - - - } - - bool ServerContext::Debug() - { - return this->debug; - } - - ServerContext& ServerContext::WithDebug(bool debug) - { - this->debug = debug; - return *this; - } - - WebSocketConnection::~WebSocketConnection() - { - - } - void ServerContext::StartWebSocketSession(std::function,std::function,std::function)> onOpen, std::function onReceive, std::function onClose) - { - std::shared_ptr wsc = std::make_shared(onOpen,onReceive,onClose); - StartWebSocketSession(wsc); - } - - void ServerContext::StartWebSocketSession(std::shared_ptr connection) - { - WSServer svr(this,connection); - Threading::Thread thrd([&svr,&connection]()->void{ - try { - connection->OnOpen([&svr](WebSocketMessage& msg)->void { - svr.send_msg(&msg); - },[&svr]()->void { - std::vector p = {(uint8_t)'p',(uint8_t)'i',(uint8_t)'n',(uint8_t)'g'}; - svr.ping_send(p); - },[&svr]()->void{svr.close();}); - }catch(...) { - - } - }); - - svr.Start(); - thrd.Join(); - } - std::string ServerContext::GetServerRoot() - { - if(this->originalPath == this->path) return "/"; - Tesses::Framework::Filesystem::VFSPath originalPath=this->originalPath; - - Tesses::Framework::Filesystem::VFSPath path=this->path; - if(originalPath.path.size() <= path.path.size()) return "/"; - - originalPath.path.resize(originalPath.path.size() - path.path.size()); - return originalPath.ToString(); - } - std::string ServerContext::MakeAbsolute(std::string path) - { - if(path.find("http://") == 0 || path.find("https://") == 0 || path.find("/") == 0) return path; - Tesses::Framework::Filesystem::VFSPath path2 = GetServerRoot(); - path2 = path2 / path; - return path2.CollapseRelativeParents().ToString(); - } - - + return false; } +static bool +parseUntillBoundaryEnd(std::shared_ptr src, + std::shared_ptr dest, + std::string boundary) { + bool hasMore = true; + std::vector checkBuffer(boundary.size()); + + int b; + size_t i = 0; + size_t i2 = 0; + + uint8_t buffer[1024]; + size_t offsetInMem = 0; + + while ((b = src->ReadByte()) != -1) { + if (i == boundary.size()) { + if (b == '-') { + i2++; + if (i2 == 2) + hasMore = false; + } + if (b == '\n') + break; + else if (b != '-') + i2 = 0; + continue; + } + i2 = 0; + + if (b == boundary[i]) // start filling the check buffer + { + checkBuffer[i] = (uint8_t)b; + i++; + } else { + size_t idx = 0; + while (idx < i) // write the buffer data to stream + { + + if (offsetInMem >= sizeof(buffer)) { + dest->Write(buffer, sizeof(buffer)); + offsetInMem = 0; + } + + buffer[offsetInMem++] = checkBuffer[idx]; + + idx++; + } + + i = 0; + + if (offsetInMem >= sizeof(buffer)) { + dest->Write(buffer, sizeof(buffer)); + offsetInMem = 0; + } + + buffer[offsetInMem++] = (uint8_t)b; + } + } + + if (offsetInMem > 0) { + dest->Write(buffer, offsetInMem); + } + return hasMore; +} + +static bool +parseSection(ServerContext *ctx, std::string boundary, + std::function( + std::string mime, std::string filename, std::string name)> + cb) { + HttpDictionary req; + StreamReader reader(ctx->GetStream()); + std::string line; + while (reader.ReadLineHttp(line)) { + auto v = HttpUtils::SplitString(line, ": ", 2); + if (v.size() == 2) + req.AddValue(v[0], v[1]); + line.clear(); + } + + std::string cd0; + ContentDisposition cd1; + std::string ct; + + if (!req.TryGetFirst("Content-Type", ct)) + ct = "application/octet-stream"; + if (req.TryGetFirst("Content-Disposition", cd0) && + ContentDisposition::TryParse(cd0, cd1)) { + if (cd1.filename.empty()) { + std::shared_ptr ms = + std::make_shared(true); + + bool retVal = + parseUntillBoundaryEnd(ctx->GetStream(), ms, boundary); + auto &buff = ms->GetBuffer(); + + ctx->queryParams.AddValue(cd1.fieldName, + std::string(buff.begin(), buff.end())); + + return retVal; + + } else { + auto strm = cb(ct, cd1.filename, cd1.fieldName); + if (strm == nullptr) + strm = std::make_shared(); + bool retVal = + parseUntillBoundaryEnd(ctx->GetStream(), strm, boundary); + + return retVal; + } + } + return false; +} + +void ServerContext::ParseFormData( + std::function( + std::string mime, std::string filename, std::string name)> + cb) { + std::string ct; + if (this->requestHeaders.TryGetFirst("Content-Type", ct)) { + if (ct.find("multipart/form-data") != 0) { + throw std::runtime_error("Not form data"); + } + auto res = ct.find("boundary="); + if (res == std::string::npos) + return; + ct = "--" + ct.substr(res + 9); + } + std::shared_ptr nullStrm = std::make_shared(); + parseUntillBoundaryEnd(this->strm, nullStrm, ct); + + while (parseSection(this, ct, cb)) + ; +} + +HttpServer::HttpServer( + std::shared_ptr tcpServer, + std::shared_ptr http, bool showIPs, bool debug) { + this->server = tcpServer; + this->http = http; + + this->showIPs = showIPs; + this->thrd = nullptr; + this->showARTL = showIPs; + this->debug = debug; +} + +HttpServer::HttpServer(uint16_t port, std::shared_ptr http, + bool showIPs, bool debug) + : HttpServer(std::make_shared(port, 10), http, showIPs, debug) {} + +HttpServer::HttpServer(std::string unixPath, std::shared_ptr http, + bool debug) + : HttpServer(std::make_shared(unixPath, 10), http, false, + debug) { + this->showARTL = true; +} + +uint16_t HttpServer::GetPort() { + if (server != nullptr) + return server->GetPort(); + return 0; +} +std::shared_ptr ServerContext::OpenResponseStream() { + if (sent) + return nullptr; + int64_t length = -1; + if (!this->responseHeaders.TryGetFirstInt("Content-Length", length)) + length = -1; + + if (this->version == "HTTP/1.1" && length == -1) + this->responseHeaders.SetValue("Transfer-Encoding", "chunked"); + + this->WriteHeaders(); + auto strm = std::make_shared(this->strm, length, false, + version == "HTTP/1.1"); + if (method == "HEAD") { + strm->Close(); + } + return strm; +} +std::shared_ptr ServerContext::OpenRequestStream() { + int64_t length = -1; + if (!this->requestHeaders.TryGetFirstInt("Content-Length", length)) + length = -1; + return std::make_shared(this->strm, length, true, + version == "HTTP/1.1"); +} +void HttpServer::StartAccepting() { + fflush(stdout); + if (http == nullptr || server == nullptr) + return; + auto svr = this->server; + auto serverPort = this->server->GetPort(); + auto http = this->http; + TF_LOG("Before Creating Thread"); + bool debug = this->debug; + thrd = new Threading::Thread([svr, http, serverPort, debug]() -> void { + while (TF_IsRunning()) { + TF_LOG("after TF_IsRunning"); + std::string ip; + uint16_t port; + auto sock = svr->GetStream(ip, port); + + TF_LOG("New Host IP: " + ip + ":" + std::to_string(port)); + + if (sock == nullptr) { + std::cout << "STREAM ERROR" << std::endl; + return; + } + TF_LOG("Before entering socket thread"); + + Threading::Thread thrd2( + [sock, http, ip, port, serverPort, debug]() -> void { + TF_LOG("In thread to process"); + HttpServer::Process(sock, http, ip, port, serverPort, false, + debug); + TF_LOG("In thread after process"); + }); + TF_LOG("Before attach"); + thrd2.Detach(); + TF_LOG("After attach"); + } + }); + if (this->showIPs) { + TF_LOG("Before printing interfaces"); + + StdOut() << "\x1B[34mInterfaces:" << NewLine(); + for (auto _ip : NetworkStream::GetIPs()) { + StdOut() << "\x1B[32m" << _ip.first << ": " + << "\x1B[35mhttp://" << _ip.second << ":" + << (uint64_t)this->GetPort() << "/" << NewLine(); + } + } + if (this->showARTL) { + if (!svr->IsValid()) + StdOut() + << "\x1B[31mError, we failed to bind or something\x1B[39m" << NewLine(); + StdOut() << "\x1B[31mAlmost Ready to Listen\x1B[39m" << NewLine(); + } + + TF_LOG("After printing interfaces"); +} +HttpServer::~HttpServer() { + auto port = this->GetPort(); + this->server->Close(); + TF_ConnectToSelf(port); + if (this->thrd != nullptr) { + this->thrd->Join(); + delete this->thrd; + } +} +IHttpServer::~IHttpServer() {} +ServerContext::ServerContext(std::shared_ptr strm, bool debug) { + this->statusCode = OK; + this->strm = strm; + this->debug = debug; + this->sent = false; + this->queryParams.SetCaseSensitive(true); + this->pathArguments.SetCaseSensitive(true); + this->responseHeaders.AddValue("Server", "TessesFrameworkWebServer"); +} +std::shared_ptr ServerContext::GetStream() { return this->strm; } +void ServerContext::SendBytes(std::vector buff) { + std::shared_ptr strm = std::make_shared(false); + strm->GetBuffer() = buff; + SendStream(strm); +} +ServerContext &ServerContext::WithLastModified(Date::DateTime dt) { + this->responseHeaders.SetValue("Last-Modified", dt); + return *this; +} + +void ServerContext::SendText(std::string text) { + std::shared_ptr strm = std::make_shared(false); + + auto &buff = strm->GetBuffer(); + buff.insert(buff.end(), text.begin(), text.end()); + SendStream(strm); +} +void ServerContext::SendErrorPage(bool showPath) { + if (sent) + return; + std::string errorHtml = + showPath ? ("File " + + HttpUtils::HtmlEncode(this->originalPath) + " " + + HttpUtils::StatusCodeString(this->statusCode) + + "

" + + std::to_string((int)this->statusCode) + " " + + HttpUtils::StatusCodeString(this->statusCode) + + "

" + HttpUtils::HtmlEncode(this->originalPath) + + "

") + : ("" + std::to_string(this->statusCode) + + " " + HttpUtils::StatusCodeString(this->statusCode) + + "

" + + std::to_string((int)this->statusCode) + " " + + HttpUtils::StatusCodeString(this->statusCode) + + "

"); + + WithMimeType("text/html").SendText(errorHtml); +} + +ServerContext::~ServerContext() { + for (auto item : this->data) { + delete item.second; + } +} +ServerContextData::~ServerContextData() {} +void ServerContext::SendStream(std::shared_ptr strm) { + if (sent) + return; + if (!strm->CanRead()) + throw std::runtime_error("Cannot read from stream"); + if (strm->EndOfStream()) + throw std::runtime_error("End of stream"); + if (strm->CanSeek()) { + int64_t len = strm->GetLength(); + std::string range = {}; + if (this->requestHeaders.TryGetFirst("Range", range)) { + auto res = HttpUtils::SplitString(range, "=", 2); + if (res.size() == 2) { + if (res[0] != "bytes") { + this->statusCode = BadRequest; + this->WriteHeaders(); + return; + } + res = HttpUtils::SplitString(res[1], ", ", 2); + if (res.size() != 1) { + this->statusCode = BadRequest; + this->WriteHeaders(); + return; + } + auto dash = HttpUtils::SplitString(res[0], "-", 2); + int64_t begin = 0; + int64_t end = -1; + + if (dash.size() == 1 && + res[0].find_first_of('-') != std::string::npos) { + // NUMBER- + begin = std::stoll(dash[0]); + } else if (dash.size() == 2) { + // NUMBER-NUMBER + // or + //-NUMBER + + if (dash[0].size() > 0) { + // NUMBER-NUMBER + begin = std::stoll(dash[0]); + end = std::stoll(dash[1]); + } else { + //-NUMBER + end = std::stoll(dash[1]); + } + } else { + this->statusCode = BadRequest; + this->WriteHeaders(); + return; + } + + if (end == -1) { + end = len - 1; + } + + if (end > len - 1) { + this->statusCode = RangeNotSatisfiable; + this->WithSingleHeader("Content-Range", + "bytes */" + std::to_string(len)); + this->WriteHeaders(); + return; + } + + if (begin >= end) { + this->statusCode = RangeNotSatisfiable; + this->WithSingleHeader("Content-Range", + "bytes */" + std::to_string(len)); + this->WriteHeaders(); + return; + } + + int64_t myLen = (end - begin) + 1; + + this->WithSingleHeader("Accept-Ranges", "bytes"); + this->WithSingleHeader("Content-Length", std::to_string(myLen)); + this->WithSingleHeader("Content-Range", + "bytes " + std::to_string(begin) + "-" + + std::to_string(end) + "/" + + std::to_string(len)); + this->statusCode = PartialContent; + this->WriteHeaders(); + if (this->method != "HEAD") { + strm->Seek(begin, SeekOrigin::Begin); + + uint8_t buffer[1024]; + + size_t read = 0; + + do { + read = sizeof(buffer); + myLen = (end - begin) + 1; + if (myLen < read) + read = (size_t)myLen; + if (read == 0) + break; + read = strm->Read(buffer, read); + if (read == 0) + break; + this->strm->WriteBlock(buffer, read); + + begin += read; + } while (read > 0 && !this->strm->EndOfStream()); + } + + } else { + this->statusCode = BadRequest; + this->SendErrorPage(true); + return; + } + } else { + if (len > -1) { + this->WithSingleHeader("Accept-Range", "bytes"); + this->WithSingleHeader("Content-Length", std::to_string(len)); + this->WriteHeaders(); + if (this->method != "HEAD") + strm->CopyTo(this->strm); + } + } + + } else { + + auto chunkedStream = this->OpenResponseStream(); + + if (method != "HEAD") + strm->CopyTo(chunkedStream); + } +} +ServerContext &ServerContext::WithHeader(std::string key, std::string value) { + this->responseHeaders.AddValue(key, value); + return *this; +} +ServerContext &ServerContext::WithSingleHeader(std::string key, + std::string value) { + this->responseHeaders.SetValue(key, value); + return *this; +} +ServerContext &ServerContext::WithMimeType(std::string mime) { + this->responseHeaders.SetValue("Content-Type", mime); + return *this; +} +ServerContext &ServerContext::WithContentDisposition(std::string filename, + bool isInline) { + ContentDisposition cd; + cd.type = isInline ? "inline" : "attachment"; + cd.filename = filename; + + // std::string cd; + // cd = (isInline ? "inline; filename*=UTF-8''" : "attachment; + // filename*=UTF-8''") + HttpUtils::UrlPathEncode(filename); + this->responseHeaders.SetValue("Content-Disposition", cd.ToString()); + return *this; +} +ServerContext &ServerContext::WithLocationHeader(std::string url) { + return WithSingleHeader("Location", this->MakeAbsolute(url)); +} +ServerContext &ServerContext::WithLocationHeader(std::string url, + StatusCode sc) { + this->statusCode = sc; + return WithSingleHeader("Location", this->MakeAbsolute(url)); +} +void ServerContext::SendRedirect(std::string url) { + WithLocationHeader(url); + this->WriteHeaders(); +} +void ServerContext::SendRedirect(std::string url, StatusCode sc) { + WithLocationHeader(url, sc); + this->WriteHeaders(); +} + +void ServerContext::SendNotFound() { + if (sent) + return; + statusCode = StatusCode::NotFound; + SendErrorPage(true); +} +void ServerContext::SendBadRequest() { + if (sent) + return; + statusCode = StatusCode::BadRequest; + SendErrorPage(false); +} +ServerContext &ServerContext::WithStatusCode(StatusCode code) { + this->statusCode = code; + return *this; +} +void ServerContext::SendException(std::exception &ex) { + if (this->debug) { + + this->WithMimeType("text/html") + .WithStatusCode(StatusCode::InternalServerError) + .SendText(" Internal Server Error at " + + HttpUtils::HtmlEncode(this->originalPath) + + "

Internal Server Error at " + + HttpUtils::HtmlEncode(this->originalPath) + + "

what(): " + HttpUtils::HtmlEncode(ex.what()) + + "

"); + } else { + this->WithStatusCode(StatusCode::InternalServerError) + .SendErrorPage(true); + } +} + +ServerContext & +ServerContext::WithHeaderIntercepter(std::function cb) { + this->headerhandlers.push(cb); + return *this; +} + +ServerContext &ServerContext::WriteHeaders() { + if (this->sent) + return *this; + while (!this->headerhandlers.empty()) { + auto header = this->headerhandlers.front(); + this->headerhandlers.pop(); + if (header(*this)) { + break; + } + } + if (this->sent) + return *this; + this->sent = true; + + StreamWriter writer(this->strm); + writer.newline = "\r\n"; + writer.WriteLine("HTTP/1.1 " + std::to_string((int)statusCode) + " " + + HttpUtils::StatusCodeString(statusCode)); + for (auto &hdr : responseHeaders.kvp) { + auto &key = hdr.first; + for (auto &val : hdr.second) { + writer.WriteLine(key + ": " + val); + } + } + writer.WriteLine(); + + return *this; +} +void HttpServer::Process(std::shared_ptr strm, + std::shared_ptr server, std::string ip, + uint16_t port, uint16_t serverPort, bool encrypted, + bool debug) { + TF_LOG("In process"); + std::shared_ptr bStrm = + std::make_shared(strm); + StreamReader reader(bStrm); + ServerContext ctx(bStrm); + ctx.ip = ip; + ctx.port = port; + ctx.encrypted = encrypted; + ctx.serverPort = serverPort; + try { + bool firstLine = true; + std::string line; + while (reader.ReadLineHttp(line)) { + if (firstLine) { + auto v = HttpUtils::SplitString(line, " ", 3); + if (v.size() != 3) { + ctx.statusCode = BadRequest; + ctx.WithMimeType("text/plain") + .SendText("First line is not 3 elements"); + return; + } + ctx.method = v[0]; + auto pp = HttpUtils::SplitString(v[1], "?", 2); + pp.resize(2); + + ctx.originalPath = pp[0]; + ctx.path = ctx.originalPath; + + TF_LOG(ctx.method + " with path " + ctx.path); + + auto queryPart = pp[1]; + if (!queryPart.empty()) { + HttpUtils::QueryParamsDecode(ctx.queryParams, queryPart); + } + + ctx.version = v[2]; + + } else { + auto v = HttpUtils::SplitString(line, ": ", 2); + if (v.size() != 2) { + ctx.statusCode = BadRequest; + ctx.WithMimeType("text/plain") + .SendText("Header line is not 2 elements"); + return; + } + + ctx.requestHeaders.AddValue(v[0], v[1]); + } + line.clear(); + firstLine = false; + } + + if (firstLine) + return; + + std::string type; + int64_t length; + + if (ctx.requestHeaders.TryGetFirst("Content-Type", type) && + type == "application/x-www-form-urlencoded" && + ctx.requestHeaders.TryGetFirstInt("Content-Length", length)) { + size_t len = (size_t)length; + std::vector buffer(len); + len = bStrm->ReadBlock(buffer.data(), len); + std::string query((const char *)buffer.data(), len); + HttpUtils::QueryParamsDecode(ctx.queryParams, query); + } + + if (!server->Handle(ctx)) { + if ((int)ctx.statusCode < 400) + ctx.SendNotFound(); + else + ctx.SendErrorPage(true); + } + } + + catch (std::exception &ex) { + ctx.SendException(ex); + } catch (std::string &ex) { + std::runtime_error re(ex); + ctx.SendException(re); + } catch (const char *ex) { + std::runtime_error re(ex); + ctx.SendException(re); + } catch (...) { + std::runtime_error ex("An unknown error occurred"); + ctx.SendException(ex); + } +} + +bool ServerContext::Debug() { return this->debug; } + +ServerContext &ServerContext::WithDebug(bool debug) { + this->debug = debug; + return *this; +} + +WebSocketConnection::~WebSocketConnection() {} +void ServerContext::StartWebSocketSession( + std::function, + std::function, std::function)> + onOpen, + std::function onReceive, + std::function onClose) { + std::shared_ptr wsc = + std::make_shared(onOpen, onReceive, + onClose); + StartWebSocketSession(wsc); +} + +void ServerContext::StartWebSocketSession( + std::shared_ptr connection) { + WSServer svr(this, connection); + Threading::Thread thrd([&svr, &connection]() -> void { + try { + connection->OnOpen( + [&svr](WebSocketMessage &msg) -> void { svr.send_msg(&msg); }, + [&svr]() -> void { + std::vector p = {(uint8_t)'p', (uint8_t)'i', + (uint8_t)'n', (uint8_t)'g'}; + svr.ping_send(p); + }, + [&svr]() -> void { svr.close(); }); + } catch (...) { + } + }); + + svr.Start(); + thrd.Join(); +} +std::string ServerContext::GetServerRoot() { + if (this->originalPath == this->path) + return "/"; + Tesses::Framework::Filesystem::VFSPath originalPath = this->originalPath; + + Tesses::Framework::Filesystem::VFSPath path = this->path; + if (originalPath.path.size() <= path.path.size()) + return "/"; + + originalPath.path.resize(originalPath.path.size() - path.path.size()); + return originalPath.ToString(); +} +std::string ServerContext::MakeAbsolute(std::string path) { + if (path.find("http://") == 0 || path.find("https://") == 0 || + path.find("/") == 0) + return path; + Tesses::Framework::Filesystem::VFSPath path2 = GetServerRoot(); + path2 = path2 / path; + return path2.CollapseRelativeParents().ToString(); +} + +} // namespace Tesses::Framework::Http diff --git a/src/Http/HttpStream.cpp b/src/Http/HttpStream.cpp index eb80d8a..2d7bdd2 100644 --- a/src/Http/HttpStream.cpp +++ b/src/Http/HttpStream.cpp @@ -1,189 +1,182 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/HttpStream.hpp" -#include "TessesFramework/TextStreams/StreamWriter.hpp" #include "TessesFramework/TextStreams/StreamReader.hpp" -#include +#include "TessesFramework/TextStreams/StreamWriter.hpp" #include +#include using StreamWriter = Tesses::Framework::TextStreams::StreamWriter; using StreamReader = Tesses::Framework::TextStreams::StreamReader; -namespace Tesses::Framework::Http -{ - HttpStream::HttpStream(std::shared_ptr strm, int64_t length, bool recv, bool http1_1) - { - this->strm = strm; - this->length = length; - this->recv = recv; - this->http1_1 = http1_1; - this->offset = 0; - this->read = 0; - this->position = 0; - this->done=false; - } - bool HttpStream::CanRead() - { - if(this->done) return false; - if(!this->recv) return false; - if(this->offset < this->read) return true; - return this->strm->CanRead(); - } - bool HttpStream::CanWrite() - { - if(this->done) return false; - if(this->recv) return false; - return this->strm->CanWrite(); - } - bool HttpStream::EndOfStream() - { - if(this->done) return true; - if(!this->recv) return true; - if(this->offset < this->read) return false; - return this->strm->EndOfStream(); - } - int64_t HttpStream::GetLength() - { - return this->length; - } - int64_t HttpStream::GetPosition() - { - return this->position; - } - size_t HttpStream::Read(uint8_t* buff, size_t len) - { - if(this->done) return 0; - if(!this->recv) return 0; - if(this->length == 0) return 0; - if(this->length > 0) - { - - len = std::min((size_t)(this->length - this->position), len); - - if(len > 0) - len = this->strm->Read(buff,len); - this->position += len; - return len; - } - else - { - if(this->http1_1) - { - if(this->offset < this->read) - { - - len = std::min((size_t)(this->read - this->offset), len); - if(len > 0) - len = this->strm->Read(buff,len); - this->offset += len; - this->position += len; - if(this->offset >= this->read) - { - StreamReader reader(this->strm); - reader.ReadLine(); - } - return len; - } - else - { +namespace Tesses::Framework::Http { +HttpStream::HttpStream(std::shared_ptr strm, + int64_t length, bool recv, bool http1_1) { + this->strm = strm; + this->length = length; + this->recv = recv; + this->http1_1 = http1_1; + this->offset = 0; + this->read = 0; + this->position = 0; + this->done = false; +} +bool HttpStream::CanRead() { + if (this->done) + return false; + if (!this->recv) + return false; + if (this->offset < this->read) + return true; + return this->strm->CanRead(); +} +bool HttpStream::CanWrite() { + if (this->done) + return false; + if (this->recv) + return false; + return this->strm->CanWrite(); +} +bool HttpStream::EndOfStream() { + if (this->done) + return true; + if (this->offset < this->read) + return false; + return this->strm->EndOfStream(); +} +int64_t HttpStream::GetLength() { return this->length; } +int64_t HttpStream::GetPosition() { return this->position; } +size_t HttpStream::Read(uint8_t *buff, size_t len) { + if (this->done) + return 0; + if (!this->recv) + return 0; + if (this->length == 0) + return 0; + if (this->length > 0) { + + len = std::min((size_t)(this->length - this->position), len); + + if (len > 0) + len = this->strm->Read(buff, len); + this->position += len; + return len; + } else { + if (this->http1_1) { + if (this->offset < this->read) { + + len = std::min((size_t)(this->read - this->offset), len); + if (len > 0) + len = this->strm->Read(buff, len); + this->offset += len; + this->position += len; + if (this->offset >= this->read) { StreamReader reader(this->strm); - std::string line = reader.ReadLine(); - if(!line.empty()) - { - this->read = std::stoull(line, NULL, 16); - - - if(this->read == 0) - { - reader.ReadLine(); - this->done=true; - return 0; - } - else - { - this->offset=0; - - len = std::min((size_t)(this->read - this->offset), len); - if(len > 0) - len = this->strm->Read(buff,len); - this->offset += len; - this->position += len; - return len; - } - } - return 0; + reader.ReadLine(); } - - } - else - { - return this->strm->Read(buff,len); - } - } - } - size_t HttpStream::Write(const uint8_t* buff, size_t len) - { - if(this->done) return 0; - if(this->recv) return 0; - if(this->length == 0) return 0; - if(this->length > 0) - { - - len = std::min((size_t)(this->length - this->position), len); - - if(len > 0) - len = this->strm->Write(buff,len); - this->position += len; - return len; - } - else - { - if(len == 0) return 0; - if(this->http1_1) - { - std::stringstream strm; - strm << std::hex << len; - - StreamWriter writer(this->strm); - writer.newline = "\r\n"; - writer.WriteLine(strm.str()); - - this->strm->WriteBlock(buff, len); - - writer.WriteLine(); return len; - } - else - { - return this->strm->Write(buff,len); - } - } - } - void HttpStream::Close() - { - if(this->length == -1 && this->http1_1 && !done && !this->recv) - { - this->done=true; - try { - - StreamWriter writer(this->strm); - writer.newline = "\r\n"; - writer.WriteLine("0"); - writer.WriteLine(); - }catch(...){ + } else { + StreamReader reader(this->strm); + std::string line = reader.ReadLine(); + if (!line.empty()) { + this->read = std::stoull(line, NULL, 16); + if (this->read == 0) { + reader.ReadLine(); + this->done = true; + return 0; + } else { + this->offset = 0; + + len = + std::min((size_t)(this->read - this->offset), len); + if (len > 0) + len = this->strm->Read(buff, len); + this->offset += len; + this->position += len; + return len; + } + } + return 0; } + + } else { + return this->strm->Read(buff, len); } } - HttpStream::~HttpStream() - { - if(this->length == -1 && this->http1_1 && !done && !this->recv) - { - try { - +} +size_t HttpStream::Write(const uint8_t *buff, size_t len) { + if (this->done) + return 0; + if (this->recv) + return 0; + if (this->length == 0) + return 0; + if (this->length > 0) { + + len = std::min((size_t)(this->length - this->position), len); + + if (len > 0) + len = this->strm->Write(buff, len); + this->position += len; + return len; + } else { + if (len == 0) + return 0; + if (this->http1_1) { + std::stringstream strm; + strm << std::hex << len; + + StreamWriter writer(this->strm); + writer.newline = "\r\n"; + writer.WriteLine(strm.str()); + + this->strm->WriteBlock(buff, len); + + writer.WriteLine(); + return len; + } else { + return this->strm->Write(buff, len); + } + } +} +void HttpStream::Close() { + if (this->length == -1 && this->http1_1 && !done && !this->recv) { + this->done = true; + try { + StreamWriter writer(this->strm); writer.newline = "\r\n"; writer.WriteLine("0"); writer.WriteLine(); - }catch(...) { - - } + } catch (...) { } } -} \ No newline at end of file +} +HttpStream::~HttpStream() { + if (this->length == -1 && this->http1_1 && !done && !this->recv) { + try { + + StreamWriter writer(this->strm); + writer.newline = "\r\n"; + writer.WriteLine("0"); + writer.WriteLine(); + } catch (...) { + } + } +} +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/src/Http/HttpUtils.cpp b/src/Http/HttpUtils.cpp index 684297f..732e756 100644 --- a/src/Http/HttpUtils.cpp +++ b/src/Http/HttpUtils.cpp @@ -1,1040 +1,933 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/HttpUtils.hpp" #include "TessesFramework/Filesystem/VFS.hpp" -#include #include +#include #include using VFSPath = Tesses::Framework::Filesystem::VFSPath; namespace Tesses::Framework::Http { - - bool Uri::Relative(std::string url, Uri& uri) - { - auto index = url.find_first_of("//"); - if(index != std::string::npos) - { - if(Uri::TryParse(url,uri)) - { - if(index == 0) - uri.scheme = this->scheme; - return true; - } - } - else if(!url.empty()) - { - if(url[0] == '/') - { - - auto thirdPart = HttpUtils::SplitString(url,"#",2); - if(thirdPart.empty()) return false; - if(thirdPart.size() == 2) - { - uri.hash=thirdPart[1]; - } - - auto fourthPart = HttpUtils::SplitString(thirdPart[1],"?",2); - - VFSPath p = fourthPart[0]; - uri.path = p.CollapseRelativeParents().ToString(); //this should be safe - if(fourthPart.size() == 2) - { - HttpUtils::QueryParamsDecode(uri.query, fourthPart[1]); - } - } - else - { - auto thirdPart = HttpUtils::SplitString(url,"#",2); - if(thirdPart.empty()) return false; - if(thirdPart.size() == 2) - { - uri.hash=thirdPart[1]; - } - - - auto fourthPart = HttpUtils::SplitString(thirdPart[1],"?",2); - - VFSPath p = VFSPath(this->path,fourthPart[0]); - uri.path = p.CollapseRelativeParents().ToString(); //this should be safe - if(fourthPart.size() == 2) - { - HttpUtils::QueryParamsDecode(uri.query, fourthPart[1]); - } - } - uri.scheme = this->scheme; - uri.host = this->host; - uri.port = this->port; +bool Uri::Relative(std::string url, Uri &uri) { + auto index = url.find_first_of("//"); + if (index != std::string::npos) { + if (Uri::TryParse(url, uri)) { + if (index == 0) + uri.scheme = this->scheme; return true; } - return false; - } - std::string Uri::HostPort() - { - if(this->port != 0) return this->host + ":" + std::to_string(this->port); - return this->host; - } - uint16_t Uri::GetPort() - { - if(this->port != 0) return this->port; + } else if (!url.empty()) { + if (url[0] == '/') { - if(this->scheme == "http:" || this->scheme == "ws:") - return 80; - if(this->scheme == "https:" || this->scheme == "wss:") - return 443; - if(this->scheme == "sftp:") - return 22; - if(this->scheme == "ftp:") - return 21; - if(this->scheme == "tftp:") - return 69; - return 0; - } - bool Uri::TryParse(std::string url, Uri& uri) - { - uri.scheme = ""; - uri.port=0; - auto firstPart = HttpUtils::SplitString(url,"//",2); - if(firstPart.size() == 2) - - uri.scheme=firstPart[0]; - else if(firstPart.empty()) - return false; - - auto secondPart = HttpUtils::SplitString(firstPart.size() == 2 ? firstPart[1] : firstPart[0] ,"/",2); - - if(secondPart.size() == 1) - { - uri.path = "/"; - } - else if(secondPart.size() == 2) - { - auto thirdPart = HttpUtils::SplitString(secondPart[1],"#",2); - if(thirdPart.empty()) return false; - if(thirdPart.size() == 2) - { - uri.hash=thirdPart[1]; - } - - - auto fourthPart = HttpUtils::SplitString(thirdPart[0],"?",2); - uri.path = "/" + fourthPart[0]; //this should be safe - if(fourthPart.size() == 2) - { - HttpUtils::QueryParamsDecode(uri.query, fourthPart[1]); - } - } - else - { + auto thirdPart = HttpUtils::SplitString(url, "#", 2); + if (thirdPart.empty()) return false; + if (thirdPart.size() == 2) { + uri.hash = thirdPart[1]; } - - auto hostPortPart = HttpUtils::SplitString(secondPart[0],":",2); - if(hostPortPart.empty()) return false; - if(hostPortPart.size() == 2) - { - uri.port = (uint16_t)std::stoul(hostPortPart[1]); + auto fourthPart = HttpUtils::SplitString(thirdPart[1], "?", 2); + + VFSPath p = fourthPart[0]; + uri.path = + p.CollapseRelativeParents().ToString(); // this should be safe + if (fourthPart.size() == 2) { + HttpUtils::QueryParamsDecode(uri.query, fourthPart[1]); } - uri.host = hostPortPart[0]; - + } else { + auto thirdPart = HttpUtils::SplitString(url, "#", 2); + if (thirdPart.empty()) + return false; + if (thirdPart.size() == 2) { + uri.hash = thirdPart[1]; + } + + auto fourthPart = HttpUtils::SplitString(thirdPart[1], "?", 2); + + VFSPath p = VFSPath(this->path, fourthPart[0]); + uri.path = + p.CollapseRelativeParents().ToString(); // this should be safe + if (fourthPart.size() == 2) { + HttpUtils::QueryParamsDecode(uri.query, fourthPart[1]); + } + } + uri.scheme = this->scheme; + uri.host = this->host; + uri.port = this->port; return true; } - Uri::Uri() - { - this->query.SetCaseSensitive(true); - } - std::string Uri::GetPathAndQuery() - { - return this->path + this->GetQuery(); - } - std::string Uri::GetQuery() - { - if(this->query.kvp.empty()) return ""; - std::string queryStr = "?"; - queryStr.append(HttpUtils::QueryParamsEncode(query)); - return queryStr; - } + return false; +} +std::string Uri::HostPort() { + if (this->port != 0) + return this->host + ":" + std::to_string(this->port); + return this->host; +} +uint16_t Uri::GetPort() { + if (this->port != 0) + return this->port; - std::string Uri::ToString() - { - std::string uri = this->scheme; - uri.append("//"); - uri.append(this->host); - if(this->port > 0) - { - uri.push_back(':'); - uri.append(std::to_string(this->port)); + if (this->scheme == "http:" || this->scheme == "ws:") + return 80; + if (this->scheme == "https:" || this->scheme == "wss:") + return 443; + if (this->scheme == "sftp:") + return 22; + if (this->scheme == "ftp:") + return 21; + if (this->scheme == "tftp:") + return 69; + return 0; +} +bool Uri::TryParse(std::string url, Uri &uri) { + uri.scheme = ""; + uri.port = 0; + auto firstPart = HttpUtils::SplitString(url, "//", 2); + if (firstPart.size() == 2) + + uri.scheme = firstPart[0]; + else if (firstPart.empty()) + return false; + + auto secondPart = HttpUtils::SplitString( + firstPart.size() == 2 ? firstPart[1] : firstPart[0], "/", 2); + + if (secondPart.size() == 1) { + uri.path = "/"; + } else if (secondPart.size() == 2) { + auto thirdPart = HttpUtils::SplitString(secondPart[1], "#", 2); + if (thirdPart.empty()) + return false; + if (thirdPart.size() == 2) { + uri.hash = thirdPart[1]; } - uri.append(this->GetPathAndQuery()); - return uri; - } - std::string HttpUtils::Replace(std::string text, std::string find, std::string replace) - { - std::string dest; - while(text.length() > 0) - { - std::size_t index= text.find(find); - - - if(index == std::string::npos) - { - dest.append(text); - break; - } - else - { - std::string left = text.substr(0,index); - - text = text.substr(index+find.size()); - dest.append(left); - dest.append(replace); - - } + auto fourthPart = HttpUtils::SplitString(thirdPart[0], "?", 2); + uri.path = "/" + fourthPart[0]; // this should be safe + if (fourthPart.size() == 2) { + HttpUtils::QueryParamsDecode(uri.query, fourthPart[1]); } - return dest; + } else { + return false; } - std::string HttpUtils::LeftPad(std::string text, int count, char c) - { - std::stringstream strm(std::ios_base::out); - strm << std::setfill(c) << std::setw(count) << text; - return strm.str(); + + auto hostPortPart = HttpUtils::SplitString(secondPart[0], ":", 2); + + if (hostPortPart.empty()) + return false; + if (hostPortPart.size() == 2) { + uri.port = (uint16_t)std::stoul(hostPortPart[1]); } - char HttpUtils::NibbleToHex(uint8_t b, bool isUppercase) - { - if(isUppercase) - { - b %= 16; - if(b >= 0 && b <= 9) - return b + '0'; - if(b >= 10 && b <= 15) - return b + ('A' - 10); - return 0; + uri.host = hostPortPart[0]; + + return true; +} +Uri::Uri() { this->query.SetCaseSensitive(true); } +std::string Uri::GetPathAndQuery() { return this->path + this->GetQuery(); } +std::string Uri::GetQuery() { + if (this->query.kvp.empty()) + return ""; + std::string queryStr = "?"; + queryStr.append(HttpUtils::QueryParamsEncode(query)); + return queryStr; +} + +std::string Uri::ToString() { + std::string uri = this->scheme; + uri.append("//"); + uri.append(this->host); + if (this->port > 0) { + uri.push_back(':'); + uri.append(std::to_string(this->port)); + } + uri.append(this->GetPathAndQuery()); + return uri; +} +std::string HttpUtils::Replace(std::string text, std::string find, + std::string replace) { + std::string dest; + while (text.length() > 0) { + std::size_t index = text.find(find); + + if (index == std::string::npos) { + dest.append(text); + break; + } else { + std::string left = text.substr(0, index); + + text = text.substr(index + find.size()); + dest.append(left); + dest.append(replace); } - return NibbleToHex(b); } - char HttpUtils::NibbleToHex(uint8_t b) - { + return dest; +} + +std::string HttpUtils::LeftPad(std::string text, int count, char c) { + if(text.size() >= (size_t)count) return text; + + text.insert(text.begin(), (size_t)count - text.size(), c); + return text; +} +char HttpUtils::NibbleToHex(uint8_t b, bool isUppercase) { + if (isUppercase) { b %= 16; - if(b >= 0 && b <= 9) + if (b >= 0 && b <= 9) return b + '0'; - if(b >= 10 && b <= 15) - return b + ('a' - 10); + if (b >= 10 && b <= 15) + return b + ('A' - 10); return 0; } - uint8_t HttpUtils::HexToNibble(char c) - { - if(c >= '0' && c <= '9') - return (uint8_t)(c - '0'); - - if(c >= 'A' && c <= 'F') - return (uint8_t)(c - 55); - - if(c >= 'a' && c <= 'f') - return (uint8_t)(c - 87); + return NibbleToHex(b); +} +char HttpUtils::NibbleToHex(uint8_t b) { + b %= 16; + if (b >= 0 && b <= 9) + return b + '0'; + if (b >= 10 && b <= 15) + return b + ('a' - 10); + return 0; +} +uint8_t HttpUtils::HexToNibble(char c) { + if (c >= '0' && c <= '9') + return (uint8_t)(c - '0'); - return 0; + if (c >= 'A' && c <= 'F') + return (uint8_t)(c - 55); + + if (c >= 'a' && c <= 'f') + return (uint8_t)(c - 87); + + return 0; +} + +std::string HttpUtils::MimeType(std::filesystem::path p) { + std::string ext = p.extension().string(); + if (ext == ".html" || ext == ".htm") { + return "text/html"; + } + if (ext == ".txt" || ext == ".log" || ext == ".twss") { + return "text/plain"; + } + if (ext == ".woff") { + return "application/x-font-woff"; + } + if (ext == ".vtt") { + return "text/vtt"; + } + if (ext == ".svg") { + return "image/svg+xml"; + } + if (ext == ".webp") { + return "image/webp"; + } + if (ext == ".vcf") { + return "text/v-card"; + } + if (ext == ".rss" || ext == ".xml" || ext == ".atom" || ext == ".rdf") { + return "application/xml"; + } + if (ext == ".js") { + return "text/javascript"; + } + if (ext == ".json") { + return "application/json"; + } + if (ext == ".wasm") { + return "application/wasm"; + } + if (ext == ".png") { + return "image/png"; + } + if (ext == ".jpg" || ext == ".jpeg") { + return "image/jpeg"; + } + if (ext == ".css") { + return "text/css"; + } + if (ext == ".gif") { + return "image/gif"; + } + if (ext == ".mp4") { + return "video/mp4"; + } + if (ext == ".mov") { + return "video/quicktime"; + } + if (ext == ".m4a") { + return "audio/mp4"; + } + if (ext == ".webm") { + return "video/webm"; + } + if (ext == ".webmanifest") { + return "application/manifest+json"; + } + if (ext == ".ico") { + return "image/x-icon"; } - std::string HttpUtils::MimeType(std::filesystem::path p) - { - std::string ext = p.extension().string(); - if(ext == ".html" || ext == ".htm") - { - return "text/html"; - } - if(ext == ".txt" || ext == ".log" || ext == ".twss") - { - return "text/plain"; - } - if(ext == ".woff") - { - return "application/x-font-woff"; - } - if(ext == ".vtt") - { - return "text/vtt"; - } - if(ext == ".svg") - { - return "image/svg+xml"; - } - if(ext == ".webp") - { - return "image/webp"; - } - if(ext == ".vcf") - { - return "text/v-card"; - } - if(ext == ".rss" || ext == ".xml" || ext == ".atom" || ext == ".rdf") - { - return "application/xml"; - } - if(ext == ".js") - { - return "text/javascript"; - } - if(ext == ".json") - { - return "application/json"; - } - if(ext == ".wasm") - { - return "application/wasm"; - } - if(ext == ".png") - { - return "image/png"; - } - if(ext == ".jpg" || ext == ".jpeg") - { - return "image/jpeg"; - } - if(ext == ".css") - { - return "text/css"; - } - if(ext == ".gif") - { - return "image/gif"; - } - if(ext == ".mp4") - { - return "video/mp4"; - } - if(ext == ".mov") - { - return "video/quicktime"; - } - if(ext == ".m4a") - { - return "audio/mp4"; - } - if(ext == ".webm") - { - return "video/webm"; - } - if(ext == ".webmanifest") - { - return "application/manifest+json"; - } - if(ext == ".ico") - { - return "image/x-icon"; - } - - return "application/octet-stream"; - } - bool HttpUtils::Invalid(char c) - { - //just do windows because it is the strictist when it comes to windows, mac and linux - if(c >= 0 && c < 32) return true; - if(c == 127) return true; - if(c == '\\') return true; - if(c == '*') return true; - if(c == '/') return true; - if(c == '|') return true; - if(c == ':') return true; - if(c == '<') return true; - if(c == '>') return true; - if(c == '\"') return true; - if(c == '?') return true; - return false; - } - std::string HttpUtils::Sanitise(std::string text) - { - std::string myStr={}; - for(auto item : text) - { - if(Invalid(item)) continue; - myStr.push_back(item); - } - return myStr; - } - - void HttpUtils::QueryParamsDecode(HttpDictionary& dict,std::string query) - { - for(auto item : SplitString(query,"&")) - { - std::vector ss=SplitString(item,"=",2); - if(ss.size() >= 1) - { - std::string value = {}; - if(ss.size() == 2) - { - value = UrlDecode(ss[1]); - } - dict.AddValue(UrlDecode(ss[0]),value); - } - } - } - std::string HttpUtils::Join(std::string joinStr, std::vector ents) - { - std::string str={}; - bool first=true; - - for(auto item : ents) - { - if(!first) str.append(joinStr); - str.append(item); - first=false; - } - return str; - } - std::string HttpUtils::QueryParamsEncode(HttpDictionary& dict) - { - std::string s={}; - bool first = true; - for(auto item : dict.kvp) - { - for(auto item2 : item.second) - { - if(!first) - { - s.push_back('&'); - } - s.insert(s.size(),UrlEncode(item.first)); - s.push_back('='); - s.insert(s.size(),UrlEncode(item2)); - first=false; - } - } - return s; - } - - std::string HttpUtils::UrlDecode(std::string v) - { - std::string s = {}; - - for(size_t i = 0;i= 'A' && item <= 'Z') - s.push_back(item); - else if(item >= 'a' && item <= 'z') - s.push_back(item); - else if(item >= '0' && item <= '9') - s.push_back(item); - else if(item == '-' || item == '_' || item == '.' || item == '~' || item == '/') - s.push_back(item); - else - { - if(item != ' ' || !ignoreSpace) - { - s.push_back('%'); - s.push_back(NibbleToHex((item >> 4) & 0xF)); - s.push_back(NibbleToHex((item) & 0xF)); - } - else - { - s.push_back(' '); - } - } - } - return s; - } - std::string HttpUtils::UrlPathDecode(std::string v) - { - std::string s = {}; - - for(size_t i = 0;i= 'A' && item <= 'Z') - s.push_back(item); - else if(item >= 'a' && item <= 'z') - s.push_back(item); - else if(item >= '0' && item <= '9') - s.push_back(item); - else if(item == '-' || item == '_' || item == '.' || item == '~') - s.push_back(item); - else - { - s.push_back('%'); - s.push_back(NibbleToHex((item >> 4) & 0xF)); - s.push_back(NibbleToHex((item) & 0xF)); - } - } - return s; - } - - std::vector HttpUtils::SplitString(std::string text, std::string delimiter,std::size_t maxCnt) - { - std::vector strs; - std::size_t i = 1; - while(text.length() > 0) - { - if(i == maxCnt) - { - strs.push_back(text); - break; - } - std::size_t index= text.find(delimiter); - - - - if(index == std::string::npos) - { - strs.push_back(text); - break; - } - else - { - std::string left = text.substr(0,index); - - text = text.substr(index+delimiter.size()); - - strs.push_back(left); - - } - i++; - } - return strs; - } - std::string HttpUtils::HtmlDecodeOnlyEntityNumber(std::string v) - { - std::string buff={}; - int state = 0; - uint64_t n=0; - for(auto item : v) - { - switch(state) - { - case 0: - if(item == '&') state=1; - else buff.push_back(item); - break; - case 1: - if(item == '#') {state = 2; n=0;} - else {state=0; buff.push_back('&'); buff.push_back(item); } - break; - case 2: - if(item == ';') { - state = 0; - if(n <= 0x7F) - { - buff.push_back((char)n); - } - else if(n >= 0x80 && n <= 0x7FF) - { - uint8_t high = 0b11000000 | ((uint8_t)(n >> 6) & 0b00011111); - uint8_t low = 0b10000000 | ((uint8_t)(n) & 0b00111111); - buff.push_back((char)high); - buff.push_back((char)low); - } - else if(n >= 0x800 && n <= 0xFFFF) - { - uint8_t high = 0b11100000 | ((uint8_t)(n >> 12) & 0b00001111); - uint8_t low = 0b10000000 | ((uint8_t)(n >> 6) & 0b00111111); - uint8_t lowest = 0b10000000 | ((uint8_t)(n) & 0b00111111); - buff.push_back((char)high); - buff.push_back((char)low); - buff.push_back((char)lowest); - } - else if(n >= 0x010000 && n <= 0x10FFFF) - { - uint8_t highest = 0b11110000 | ((uint8_t)(n >> 18) & 0b00000111); - uint8_t high = 0b10000000 | ((uint8_t)(n >> 12) & 0b00111111); - uint8_t low = 0b10000000 | ((uint8_t)(n >> 6) & 0b00111111); - uint8_t lowest = 0b10000000 | ((uint8_t)(n) & 0b00111111); - buff.push_back((char)highest); - - buff.push_back((char)high); - buff.push_back((char)low); - buff.push_back((char)lowest); - } - } - else if(item >= '0' && item <= '9') - { - n *= 10; - n += item - '0'; - } - } - } - return buff; - } - std::string HttpUtils::HtmlP(std::string text) - { - std::string newText = ""; - std::string builder = ""; - - auto flush = [&]()->void { - if(!builder.empty()) - { - if(builder.find("http://") == 0 || builder.find("https://") == 0 || builder.find("ftp://") == 0 || builder.find("ftps://") == 0 || builder.find("magnet:") == 0 || builder.find("btmh:") == 0) - { - newText += "" + HttpUtils::HtmlEncode(builder) + ""; - } - else if(builder.find("mailto:") == 0) - { - newText += "" + HttpUtils::HtmlEncode(builder.substr(7)) + ""; - } - else if(builder.find("tel:") == 0) - { - newText += "" + HttpUtils::HtmlEncode(builder.substr(4)) + ""; - } - else { - newText += HttpUtils::HtmlEncode(builder); - } - builder = ""; - } - }; - - - for(auto item : text) - { - switch(item) - { - case ' ': - flush(); - newText += " "; - break; - case '\n': - flush(); - newText += "
"; - break; - case '\t': - flush(); - newText += "&tab;"; - break; - case '\r': - flush(); - break; - default: - builder += item; - break; - } - } - flush(); - - return newText; - } - std::string HttpUtils::HtmlEncode(std::string html) - { - std::string myHtml = {}; - for(auto item : html) - { - if(item == '\"') - { - myHtml.append("""); - } - else if(item == '\'') - { - myHtml.append("'"); - } - else if(item == '&') - { - myHtml.append("&"); - } - else if(item == '<') - { - myHtml.append("<"); - } - else if(item == '>') - { - myHtml.append(">"); - } - else - { - myHtml.push_back(item); - } - } - return myHtml; - } - std::string HttpUtils::StatusCodeString(StatusCode code) - { - switch(code) - { - case StatusCode::Continue: - return "Continue"; - case StatusCode::SwitchingProtocols: - return "Switching Protocols"; - case StatusCode::Processing: - return "Processing"; - case StatusCode::EarlyHints: - return "Early Hints"; - case StatusCode::OK: - return "OK"; - case StatusCode::Created: - return "Created"; - case StatusCode::Accepted: - return "Accepted"; - case StatusCode::NonAuthoritativeInformation: - return "Non-Authoritative Information"; - case StatusCode::NoContent: - return "No Content"; - case StatusCode::ResetContent: - return "Reset Content"; - case StatusCode::PartialContent: - return "PartialContent"; - case StatusCode::MultiStatus: - return "Multi-Status"; - case StatusCode::AlreadyReported: - return "Already Reported"; - case StatusCode::IMUsed: - return "IM Used"; - case StatusCode::MultipleChoices: - return "Multiple Choices"; - case StatusCode::MovedPermanently: - return "Moved Permanently"; - case StatusCode::Found: - return "Found"; - case StatusCode::SeeOther: - return "See Other"; - case StatusCode::NotModified: - return "Not Modified"; - case StatusCode::UseProxy: - return "Use Proxy"; - case StatusCode::TemporaryRedirect: - return "Temporary Redirect"; - case StatusCode::PermanentRedirect: - return "Permanent Redirect"; - case StatusCode::BadRequest: - return "Bad Request"; - case StatusCode::Unauthorized: - return "Unauthorized"; - case StatusCode::PaymentRequired: - return "Payment Required"; - case StatusCode::Forbidden: - return "Forbidden"; - case StatusCode::NotFound: - return "Not Found"; - case StatusCode::MethodNotAllowed: - return "Method Not Allowed"; - case StatusCode::NotAcceptable: - return "Not Acceptable"; - case StatusCode::ProxyAuthenticationRequired: - return "Proxy Authentication Required"; - case StatusCode::RequestTimeout: - return "Request Timeout"; - case StatusCode::Conflict: - return "Conflict"; - case StatusCode::Gone: - return "Gone"; - case StatusCode::LengthRequired: - return "Length Required"; - case StatusCode::PreconditionFailed: - return "Precondition Failed"; - case StatusCode::PayloadTooLarge: - return "Payload Too Large"; - case StatusCode::URITooLong: - return "URI Too Long"; - case StatusCode::UnsupportedMediaType: - return "Unsupported Media Type"; - case StatusCode::RangeNotSatisfiable: - return "Range Not Satisfiable"; - case StatusCode::ExpectationFailed: - return "Expectation Failed"; - case StatusCode::ImATeapot: - return "I'm a teapot"; - case StatusCode::MisdirectedRequest: - return "Misdirected Request"; - case StatusCode::UnprocessableContent: - return "Unprocessable Content"; - case StatusCode::Locked: - return "Locked"; - case StatusCode::FailedDependency: - return "Failed Dependency"; - case StatusCode::TooEarly: - return "Too Early"; - case StatusCode::UpgradeRequired: - return "Upgrade Required"; - case StatusCode::PreconditionRequired: - return "Precondition Required"; - case StatusCode::TooManyRequests: - return "Too Many Requests"; - case StatusCode::RequestHeaderFieldsTooLarge: - return "Request Header Fields Too Large"; - case StatusCode::UnavailableForLegalReasons: - return "Unavailable For Legal Reasons"; - case StatusCode::InternalServerError: - return "Internal Server Error"; - case StatusCode::NotImplemented: - return "Not Implemented"; - case StatusCode::ServiceUnavailable: - return "Service Unavailable"; - case StatusCode::GatewayTimeout: - return "Gateway Timeout"; - case StatusCode::HTTPVersionNotSupported: - return "HTTP Version Not Supported"; - case StatusCode::VariantAlsoNegotiates: - return "Variant Also Negotiates"; - case StatusCode::InsufficientStorage: - return "Insufficient Storage"; - case StatusCode::LoopDetected: - return "Loop Detected"; - case StatusCode::NotExtended: - return "Not Extended"; - case StatusCode::NetworkAuthenticationRequired: - return "Network Authentication Required"; - default: - return ""; - } - } - CaseInsensitiveLess::CaseInsensitiveLess(const CaseInsensitiveLess& str) - { - this->caseSensitive = str.caseSensitive; - this->offset = this; - } - CaseInsensitiveLess::CaseInsensitiveLess() - { - this->caseSensitive=false; - this->offset = this; - } - void HttpDictionary::SetCaseSensitive(bool isCaseSensitive) - { - this->kvp.key_comp().offset->caseSensitive=isCaseSensitive; - } - HttpDictionary::HttpDictionary(bool isCaseSensitive) - { - this->SetCaseSensitive(isCaseSensitive); - } - HttpDictionary::HttpDictionary() : HttpDictionary(false) - { - - } - - bool HttpDictionary::AnyEquals(std::string key, std::string value) - { - if(this->kvp.count(key) > 0) - for(auto v : this->kvp[key]) if(v == value) return true; - // for(auto v : item.second) if(v == value) return true; - return false; - } - - void HttpDictionary::Clear() - { - kvp.clear(); - } - void HttpDictionary::Clear(std::string key, bool kvpExistsAfter) - { - if(kvpExistsAfter) - { - kvp[key].clear(); - } - else - { - if(kvp.count(key) == 0) return; - kvp[key].clear(); - kvp.erase(key); - } - } - void HttpDictionary::SetValue(std::string key, std::string value) - { - kvp[key] = {value}; - } - void HttpDictionary::SetValue(std::string key, int64_t value) - { - kvp[key] = {std::to_string(value)}; - } - void HttpDictionary::SetValue(std::string key, double value) - { - kvp[key] = {std::to_string(value)}; - } - void HttpDictionary::SetValue(std::string key, Date::DateTime value) - { - kvp[key] = {value.ToHttpDate()}; - } - void HttpDictionary::SetValue(std::string key, std::vector value) - { - kvp[key] = value; - } - void HttpDictionary::AddValue(std::string key, std::string value) - { - kvp[key].push_back(value); - } - void HttpDictionary::AddValue(std::string key, int64_t value) - { - kvp[key].push_back(std::to_string(value)); - } - - void HttpDictionary::AddValue(std::string key, double value) - { - kvp[key].push_back(std::to_string(value)); - } - - void HttpDictionary::AddValue(std::string key, Date::DateTime value) - { - kvp[key].push_back(value.ToHttpDate()); - } - void HttpDictionary::AddValue(std::string key, std::vector value) - { - auto& ls = kvp[key]; - ls.insert(ls.end(), value.begin(), value.end()); - } - bool HttpDictionary::TryGetFirst(std::string key, std::string& value) - { - if(kvp.count(key) == 0) return false; - auto& ls = kvp[key]; - if(ls.empty()) return false; - value = ls.front(); - + return "application/octet-stream"; +} +bool HttpUtils::Invalid(char c) { + // just do windows because it is the strictist when it comes to windows, mac + // and linux + if (c >= 0 && c < 32) return true; - } - - bool HttpDictionary::TryGetFirstInt(std::string key, int64_t& value) - { - std::string val; - if(!TryGetFirst(key,val)) return false; - try{ - size_t off = 0; - auto v = std::stoll(val,&off); - if(off != val.size()) return false; - value = v; - } - catch(std::exception& ex) - { - return false; - } + if (c == 127) return true; - } - bool HttpDictionary::TryGetFirstDate(std::string key, Date::DateTime& dt) - { - std::string val; - if(!TryGetFirst(key,val)) return false; - return Date::DateTime::TryParseHttpDate(val,dt); - } - bool HttpDictionary::TryGetFirstDouble(std::string key, double& value) - { - std::string val; - if(!TryGetFirst(key,val)) return false; - try{ - size_t off = 0; - auto v = std::stod(val,&off); - if(off != val.size()) return false; - value = v; - } - catch(std::exception& ex) - { - return false; - } + if (c == '\\') return true; + if (c == '*') + return true; + if (c == '/') + return true; + if (c == '|') + return true; + if (c == ':') + return true; + if (c == '<') + return true; + if (c == '>') + return true; + if (c == '\"') + return true; + if (c == '?') + return true; + return false; +} +std::string HttpUtils::Sanitise(std::string text) { + std::string myStr = {}; + for (auto item : text) { + if (Invalid(item)) + continue; + myStr.push_back(item); } - bool CaseInsensitiveLess::operator() (const std::string& s1, const std::string& s2) const { - if(this->caseSensitive) return s1 == s2; - return HttpUtils::ToLower(s1) < HttpUtils::ToLower(s2); - - } + return myStr; +} - std::string HttpUtils::ToLower(std::string str) - { - std::string str1(str.length(),' '); - std::transform(str.begin(), str.end(), str1.begin(), tolower); - return str1; - } - - std::string HttpUtils::ToUpper(std::string str) - { - std::string str1(str.length(),' '); - std::transform(str.begin(), str.end(), str1.begin(), toupper); - return str1; - } - - bool HttpDictionary::GetFirstBoolean(std::string key) - { - std::string val; - if(!TryGetFirst(key,val)) return false; - return val == "true" || val == "on"; - } - std::string HttpUtils::BytesToHex(const std::vector& data) - { - std::string text; - BytesToHex(text,data); - return text; - } - std::string HttpUtils::BytesToHex(const std::vector& data, bool isUpper) - { - std::string text; - BytesToHex(text,data, isUpper); - return text; - } - void HttpUtils::BytesToHex(std::string& text,const std::vector& data) - { - if(data.empty()) { - text.clear(); - return; - } - text.resize(data.size()*2); - for(size_t i = 0; i < data.size(); i++) - { - text[i*2] = NibbleToHex(data[i] >> 4); - text[i*2+1] += NibbleToHex(data[i]); - } - } - - void HttpUtils::BytesToHex(std::string& text,const std::vector& data, bool isUpper) - { - if(data.empty()) { - text.clear(); - return; - } - text.resize(data.size()*2); - for(size_t i = 0; i < data.size(); i++) - { - text[i*2] = NibbleToHex(data[i] >> 4, isUpper); - text[i*2+1] += NibbleToHex(data[i], isUpper); - } - } - std::vector HttpUtils::HexToBytes(const std::string& text) - { - std::vector data; - HexToBytes(data,text); - return data; - } - void HttpUtils::HexToBytes(std::vector& data,const std::string& text) - { - if(text.empty()) { data.clear(); return;} - if(text.size() % 2 != 0) throw std::runtime_error("Text length is not even"); - - - data.resize(text.size()/2); - for(size_t i = 0; i < text.size(); i+=2) - { - uint8_t b = HexToNibble(text[i]) << 4; - b |= HexToNibble(text[i+1]); - data[i/2] = b; +void HttpUtils::QueryParamsDecode(HttpDictionary &dict, std::string query) { + for (auto item : SplitString(query, "&")) { + std::vector ss = SplitString(item, "=", 2); + if (ss.size() >= 1) { + std::string value = {}; + if (ss.size() == 2) { + value = UrlDecode(ss[1]); + } + dict.AddValue(UrlDecode(ss[0]), value); } } } +std::string HttpUtils::Join(std::string joinStr, + std::vector ents) { + std::string str = {}; + bool first = true; + + for (auto item : ents) { + if (!first) + str.append(joinStr); + str.append(item); + first = false; + } + return str; +} +std::string HttpUtils::QueryParamsEncode(HttpDictionary &dict) { + std::string s = {}; + bool first = true; + for (auto item : dict.kvp) { + for (auto item2 : item.second) { + if (!first) { + s.push_back('&'); + } + s.insert(s.size(), UrlEncode(item.first)); + s.push_back('='); + s.insert(s.size(), UrlEncode(item2)); + first = false; + } + } + return s; +} + +std::string HttpUtils::UrlDecode(std::string v) { + std::string s = {}; + + for (size_t i = 0; i < v.size(); i++) { + if (v[i] == '+') + s.push_back(' '); + else if (v[i] == '%') { + i++; + uint8_t n = HexToNibble(v[i]) << 4; + i++; + n |= HexToNibble(v[i]); + s.push_back((char)n); + } else + s.push_back(v[i]); + } + return s; +} +std::string HttpUtils::UrlPathEncode(std::string v, bool ignoreSpace) { + std::string s = {}; + + for (auto item : v) { + if (item >= 'A' && item <= 'Z') + s.push_back(item); + else if (item >= 'a' && item <= 'z') + s.push_back(item); + else if (item >= '0' && item <= '9') + s.push_back(item); + else if (item == '-' || item == '_' || item == '.' || item == '~' || + item == '/') + s.push_back(item); + else { + if (item != ' ' || !ignoreSpace) { + s.push_back('%'); + s.push_back(NibbleToHex((item >> 4) & 0xF)); + s.push_back(NibbleToHex((item) & 0xF)); + } else { + s.push_back(' '); + } + } + } + return s; +} +std::string HttpUtils::UrlPathDecode(std::string v) { + std::string s = {}; + + for (size_t i = 0; i < v.size(); i++) { + if (v[i] == '%') { + i++; + uint8_t n = HexToNibble(v[i]) << 4; + i++; + n |= HexToNibble(v[i]); + s.push_back((char)n); + } else + s.push_back(v[i]); + } + return s; +} + +std::string HttpUtils::UrlEncode(std::string v) { + std::string s = {}; + + for (auto item : v) { + if (item == ' ') + s.push_back('+'); + else if (item >= 'A' && item <= 'Z') + s.push_back(item); + else if (item >= 'a' && item <= 'z') + s.push_back(item); + else if (item >= '0' && item <= '9') + s.push_back(item); + else if (item == '-' || item == '_' || item == '.' || item == '~') + s.push_back(item); + else { + s.push_back('%'); + s.push_back(NibbleToHex((item >> 4) & 0xF)); + s.push_back(NibbleToHex((item) & 0xF)); + } + } + return s; +} + +std::vector HttpUtils::SplitString(std::string text, + std::string delimiter, + std::size_t maxCnt) { + std::vector strs; + std::size_t i = 1; + while (text.length() > 0) { + if (i == maxCnt) { + strs.push_back(text); + break; + } + std::size_t index = text.find(delimiter); + + if (index == std::string::npos) { + strs.push_back(text); + break; + } else { + std::string left = text.substr(0, index); + + text = text.substr(index + delimiter.size()); + + strs.push_back(left); + } + i++; + } + return strs; +} +std::string HttpUtils::HtmlDecodeOnlyEntityNumber(std::string v) { + std::string buff = {}; + int state = 0; + uint64_t n = 0; + for (auto item : v) { + switch (state) { + case 0: + if (item == '&') + state = 1; + else + buff.push_back(item); + break; + case 1: + if (item == '#') { + state = 2; + n = 0; + } else { + state = 0; + buff.push_back('&'); + buff.push_back(item); + } + break; + case 2: + if (item == ';') { + state = 0; + if (n <= 0x7F) { + buff.push_back((char)n); + } else if (n >= 0x80 && n <= 0x7FF) { + uint8_t high = + 0b11000000 | ((uint8_t)(n >> 6) & 0b00011111); + uint8_t low = 0b10000000 | ((uint8_t)(n) & 0b00111111); + buff.push_back((char)high); + buff.push_back((char)low); + } else if (n >= 0x800 && n <= 0xFFFF) { + uint8_t high = + 0b11100000 | ((uint8_t)(n >> 12) & 0b00001111); + uint8_t low = 0b10000000 | ((uint8_t)(n >> 6) & 0b00111111); + uint8_t lowest = 0b10000000 | ((uint8_t)(n) & 0b00111111); + buff.push_back((char)high); + buff.push_back((char)low); + buff.push_back((char)lowest); + } else if (n >= 0x010000 && n <= 0x10FFFF) { + uint8_t highest = + 0b11110000 | ((uint8_t)(n >> 18) & 0b00000111); + uint8_t high = + 0b10000000 | ((uint8_t)(n >> 12) & 0b00111111); + uint8_t low = 0b10000000 | ((uint8_t)(n >> 6) & 0b00111111); + uint8_t lowest = 0b10000000 | ((uint8_t)(n) & 0b00111111); + buff.push_back((char)highest); + + buff.push_back((char)high); + buff.push_back((char)low); + buff.push_back((char)lowest); + } + } else if (item >= '0' && item <= '9') { + n *= 10; + n += item - '0'; + } + } + } + return buff; +} +std::string HttpUtils::HtmlP(std::string text) { + std::string newText = ""; + std::string builder = ""; + + auto flush = [&]() -> void { + if (!builder.empty()) { + if (builder.find("http://") == 0 || builder.find("https://") == 0 || + builder.find("ftp://") == 0 || builder.find("ftps://") == 0 || + builder.find("magnet:") == 0 || builder.find("btmh:") == 0) { + newText += "" + HttpUtils::HtmlEncode(builder) + ""; + } else if (builder.find("mailto:") == 0) { + newText += "" + HttpUtils::HtmlEncode(builder.substr(7)) + + ""; + } else if (builder.find("tel:") == 0) { + newText += "" + HttpUtils::HtmlEncode(builder.substr(4)) + + ""; + } else { + newText += HttpUtils::HtmlEncode(builder); + } + builder = ""; + } + }; + + for (auto item : text) { + switch (item) { + case ' ': + flush(); + newText += " "; + break; + case '\n': + flush(); + newText += "
"; + break; + case '\t': + flush(); + newText += "&tab;"; + break; + case '\r': + flush(); + break; + default: + builder += item; + break; + } + } + flush(); + + return newText; +} +std::string HttpUtils::HtmlEncode(std::string html) { + std::string myHtml = {}; + for (auto item : html) { + if (item == '\"') { + myHtml.append("""); + } else if (item == '\'') { + myHtml.append("'"); + } else if (item == '&') { + myHtml.append("&"); + } else if (item == '<') { + myHtml.append("<"); + } else if (item == '>') { + myHtml.append(">"); + } else { + myHtml.push_back(item); + } + } + return myHtml; +} +std::string HttpUtils::StatusCodeString(StatusCode code) { + switch (code) { + case StatusCode::Continue: + return "Continue"; + case StatusCode::SwitchingProtocols: + return "Switching Protocols"; + case StatusCode::Processing: + return "Processing"; + case StatusCode::EarlyHints: + return "Early Hints"; + case StatusCode::OK: + return "OK"; + case StatusCode::Created: + return "Created"; + case StatusCode::Accepted: + return "Accepted"; + case StatusCode::NonAuthoritativeInformation: + return "Non-Authoritative Information"; + case StatusCode::NoContent: + return "No Content"; + case StatusCode::ResetContent: + return "Reset Content"; + case StatusCode::PartialContent: + return "PartialContent"; + case StatusCode::MultiStatus: + return "Multi-Status"; + case StatusCode::AlreadyReported: + return "Already Reported"; + case StatusCode::IMUsed: + return "IM Used"; + case StatusCode::MultipleChoices: + return "Multiple Choices"; + case StatusCode::MovedPermanently: + return "Moved Permanently"; + case StatusCode::Found: + return "Found"; + case StatusCode::SeeOther: + return "See Other"; + case StatusCode::NotModified: + return "Not Modified"; + case StatusCode::UseProxy: + return "Use Proxy"; + case StatusCode::TemporaryRedirect: + return "Temporary Redirect"; + case StatusCode::PermanentRedirect: + return "Permanent Redirect"; + case StatusCode::BadRequest: + return "Bad Request"; + case StatusCode::Unauthorized: + return "Unauthorized"; + case StatusCode::PaymentRequired: + return "Payment Required"; + case StatusCode::Forbidden: + return "Forbidden"; + case StatusCode::NotFound: + return "Not Found"; + case StatusCode::MethodNotAllowed: + return "Method Not Allowed"; + case StatusCode::NotAcceptable: + return "Not Acceptable"; + case StatusCode::ProxyAuthenticationRequired: + return "Proxy Authentication Required"; + case StatusCode::RequestTimeout: + return "Request Timeout"; + case StatusCode::Conflict: + return "Conflict"; + case StatusCode::Gone: + return "Gone"; + case StatusCode::LengthRequired: + return "Length Required"; + case StatusCode::PreconditionFailed: + return "Precondition Failed"; + case StatusCode::PayloadTooLarge: + return "Payload Too Large"; + case StatusCode::URITooLong: + return "URI Too Long"; + case StatusCode::UnsupportedMediaType: + return "Unsupported Media Type"; + case StatusCode::RangeNotSatisfiable: + return "Range Not Satisfiable"; + case StatusCode::ExpectationFailed: + return "Expectation Failed"; + case StatusCode::ImATeapot: + return "I'm a teapot"; + case StatusCode::MisdirectedRequest: + return "Misdirected Request"; + case StatusCode::UnprocessableContent: + return "Unprocessable Content"; + case StatusCode::Locked: + return "Locked"; + case StatusCode::FailedDependency: + return "Failed Dependency"; + case StatusCode::TooEarly: + return "Too Early"; + case StatusCode::UpgradeRequired: + return "Upgrade Required"; + case StatusCode::PreconditionRequired: + return "Precondition Required"; + case StatusCode::TooManyRequests: + return "Too Many Requests"; + case StatusCode::RequestHeaderFieldsTooLarge: + return "Request Header Fields Too Large"; + case StatusCode::UnavailableForLegalReasons: + return "Unavailable For Legal Reasons"; + case StatusCode::InternalServerError: + return "Internal Server Error"; + case StatusCode::NotImplemented: + return "Not Implemented"; + case StatusCode::ServiceUnavailable: + return "Service Unavailable"; + case StatusCode::GatewayTimeout: + return "Gateway Timeout"; + case StatusCode::HTTPVersionNotSupported: + return "HTTP Version Not Supported"; + case StatusCode::VariantAlsoNegotiates: + return "Variant Also Negotiates"; + case StatusCode::InsufficientStorage: + return "Insufficient Storage"; + case StatusCode::LoopDetected: + return "Loop Detected"; + case StatusCode::NotExtended: + return "Not Extended"; + case StatusCode::NetworkAuthenticationRequired: + return "Network Authentication Required"; + default: + return ""; + } +} +CaseInsensitiveLess::CaseInsensitiveLess(const CaseInsensitiveLess &str) { + this->caseSensitive = str.caseSensitive; + this->offset = this; +} +CaseInsensitiveLess::CaseInsensitiveLess() { + this->caseSensitive = false; + this->offset = this; +} +void HttpDictionary::SetCaseSensitive(bool isCaseSensitive) { + this->kvp.key_comp().offset->caseSensitive = isCaseSensitive; +} +HttpDictionary::HttpDictionary(bool isCaseSensitive) { + this->SetCaseSensitive(isCaseSensitive); +} +HttpDictionary::HttpDictionary() : HttpDictionary(false) {} + +bool HttpDictionary::AnyEquals(std::string key, std::string value) { + if (this->kvp.count(key) > 0) + for (auto v : this->kvp[key]) + if (v == value) + return true; + // for(auto v : item.second) if(v == value) return true; + return false; +} + +void HttpDictionary::Clear() { kvp.clear(); } +void HttpDictionary::Clear(std::string key, bool kvpExistsAfter) { + if (kvpExistsAfter) { + kvp[key].clear(); + } else { + if (kvp.count(key) == 0) + return; + kvp[key].clear(); + kvp.erase(key); + } +} +void HttpDictionary::SetValue(std::string key, std::string value) { + kvp[key] = {value}; +} +void HttpDictionary::SetValue(std::string key, int64_t value) { + kvp[key] = {std::to_string(value)}; +} +void HttpDictionary::SetValue(std::string key, double value) { + kvp[key] = {std::to_string(value)}; +} +void HttpDictionary::SetValue(std::string key, Date::DateTime value) { + kvp[key] = {value.ToHttpDate()}; +} +void HttpDictionary::SetValue(std::string key, std::vector value) { + kvp[key] = value; +} +void HttpDictionary::AddValue(std::string key, std::string value) { + kvp[key].push_back(value); +} +void HttpDictionary::AddValue(std::string key, int64_t value) { + kvp[key].push_back(std::to_string(value)); +} + +void HttpDictionary::AddValue(std::string key, double value) { + kvp[key].push_back(std::to_string(value)); +} + +void HttpDictionary::AddValue(std::string key, Date::DateTime value) { + kvp[key].push_back(value.ToHttpDate()); +} +void HttpDictionary::AddValue(std::string key, std::vector value) { + auto &ls = kvp[key]; + ls.insert(ls.end(), value.begin(), value.end()); +} +bool HttpDictionary::TryGetFirst(std::string key, std::string &value) { + if (kvp.count(key) == 0) + return false; + auto &ls = kvp[key]; + if (ls.empty()) + return false; + value = ls.front(); + + return true; +} + +bool HttpDictionary::TryGetFirstInt(std::string key, int64_t &value) { + std::string val; + if (!TryGetFirst(key, val)) + return false; + try { + size_t off = 0; + auto v = std::stoll(val, &off); + if (off != val.size()) + return false; + value = v; + } catch (std::exception &ex) { + return false; + } + return true; +} +bool HttpDictionary::TryGetFirstDate(std::string key, Date::DateTime &dt) { + std::string val; + if (!TryGetFirst(key, val)) + return false; + return Date::DateTime::TryParseHttpDate(val, dt); +} +bool HttpDictionary::TryGetFirstDouble(std::string key, double &value) { + std::string val; + if (!TryGetFirst(key, val)) + return false; + try { + size_t off = 0; + auto v = std::stod(val, &off); + if (off != val.size()) + return false; + value = v; + } catch (std::exception &ex) { + return false; + } + return true; +} +bool CaseInsensitiveLess::operator()(const std::string &s1, + const std::string &s2) const { + if (this->caseSensitive) + return s1 == s2; + return HttpUtils::ToLower(s1) < HttpUtils::ToLower(s2); +} + +std::string HttpUtils::ToLower(std::string str) { + std::string str1(str.length(), ' '); + std::transform(str.begin(), str.end(), str1.begin(), tolower); + return str1; +} + +std::string HttpUtils::ToUpper(std::string str) { + std::string str1(str.length(), ' '); + std::transform(str.begin(), str.end(), str1.begin(), toupper); + return str1; +} + +bool HttpDictionary::GetFirstBoolean(std::string key) { + std::string val; + if (!TryGetFirst(key, val)) + return false; + return val == "true" || val == "on"; +} +std::string HttpUtils::BytesToHex(const std::vector &data) { + std::string text; + BytesToHex(text, data); + return text; +} +std::string HttpUtils::BytesToHex(const std::vector &data, + bool isUpper) { + std::string text; + BytesToHex(text, data, isUpper); + return text; +} +void HttpUtils::BytesToHex(std::string &text, + const std::vector &data) { + if (data.empty()) { + text.clear(); + return; + } + text.resize(data.size() * 2); + for (size_t i = 0; i < data.size(); i++) { + text[i * 2] = NibbleToHex(data[i] >> 4); + text[i * 2 + 1] += NibbleToHex(data[i]); + } +} + +void HttpUtils::BytesToHex(std::string &text, const std::vector &data, + bool isUpper) { + if (data.empty()) { + text.clear(); + return; + } + text.resize(data.size() * 2); + for (size_t i = 0; i < data.size(); i++) { + text[i * 2] = NibbleToHex(data[i] >> 4, isUpper); + text[i * 2 + 1] += NibbleToHex(data[i], isUpper); + } +} +std::vector HttpUtils::HexToBytes(const std::string &text) { + std::vector data; + HexToBytes(data, text); + return data; +} +void HttpUtils::HexToBytes(std::vector &data, + const std::string &text) { + if (text.empty()) { + data.clear(); + return; + } + if (text.size() % 2 != 0) + throw std::runtime_error("Text length is not even"); + + data.resize(text.size() / 2); + for (size_t i = 0; i < text.size(); i += 2) { + uint8_t b = HexToNibble(text[i]) << 4; + b |= HexToNibble(text[i + 1]); + data[i / 2] = b; + } +} +} // namespace Tesses::Framework::Http diff --git a/src/Http/MountableServer.cpp b/src/Http/MountableServer.cpp index 331d43c..13e76c1 100644 --- a/src/Http/MountableServer.cpp +++ b/src/Http/MountableServer.cpp @@ -1,62 +1,72 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/MountableServer.hpp" namespace Tesses::Framework::Http { -std::string MountableServer::Subpath(Filesystem::VFSPath fullPath, Filesystem::VFSPath offsetPath) -{ - if(fullPath.path.size() < offsetPath.path.size()) return {}; //this shouldn't happen but here just in case +std::string MountableServer::Subpath(Filesystem::VFSPath fullPath, + Filesystem::VFSPath offsetPath) { + if (fullPath.path.size() < offsetPath.path.size()) + return {}; // this shouldn't happen but here just in case Filesystem::VFSPath p; - p.relative=false; - - for(size_t i = offsetPath.path.size(); i < fullPath.path.size(); i++) - { + p.relative = false; + + for (size_t i = offsetPath.path.size(); i < fullPath.path.size(); i++) { p.path.push_back(fullPath.path[i]); } return p.ToString(); } -bool MountableServer::StartsWith(Filesystem::VFSPath fullPath, Filesystem::VFSPath offsetPath) -{ - if(fullPath.path.size() < offsetPath.path.size()) return false; - for(size_t i = 0; i < offsetPath.path.size(); i++) - { - if(fullPath.path[i] != offsetPath.path[i]) return false; +bool MountableServer::StartsWith(Filesystem::VFSPath fullPath, + Filesystem::VFSPath offsetPath) { + if (fullPath.path.size() < offsetPath.path.size()) + return false; + for (size_t i = 0; i < offsetPath.path.size(); i++) { + if (fullPath.path[i] != offsetPath.path[i]) + return false; } return true; } -MountableServer::MountableServer() : MountableServer(nullptr) -{ - -} -MountableServer::MountableServer(std::shared_ptr root) -{ +MountableServer::MountableServer() : MountableServer(nullptr) {} +MountableServer::MountableServer(std::shared_ptr root) { this->root = root; } -void MountableServer::Mount(std::string path, std::shared_ptr server) -{ - this->servers.insert(this->servers.begin(), std::pair>(path, server)); +void MountableServer::Mount(std::string path, + std::shared_ptr server) { + this->servers.insert( + this->servers.begin(), + std::pair>(path, server)); } -void MountableServer::Unmount(std::string path) -{ - for(auto i = this->servers.begin(); i != this->servers.end(); i++) - { - auto& item = *i; - if(item.first == path) - { +void MountableServer::Unmount(std::string path) { + for (auto i = this->servers.begin(); i != this->servers.end(); i++) { + auto &item = *i; + if (item.first == path) { this->servers.erase(i); return; } } } -bool MountableServer::Handle(ServerContext& ctx) -{ +bool MountableServer::Handle(ServerContext &ctx) { std::string oldPath = ctx.path; - for(auto item : this->servers) - { - if(StartsWith(oldPath, item.first)) - { + for (auto item : this->servers) { + if (StartsWith(oldPath, item.first)) { ctx.path = Subpath(oldPath, item.first); - if(item.second->Handle(ctx)) - { + if (item.second->Handle(ctx)) { ctx.path = oldPath; return true; } @@ -64,11 +74,10 @@ bool MountableServer::Handle(ServerContext& ctx) break; } } - ctx.path=oldPath; - if(this->root && this->root->Handle(ctx)) return true; + ctx.path = oldPath; + if (this->root && this->root->Handle(ctx)) + return true; return false; } -MountableServer::~MountableServer() -{ -} -} +MountableServer::~MountableServer() {} +} // namespace Tesses::Framework::Http diff --git a/src/Http/RouteServer.cpp b/src/Http/RouteServer.cpp index d9fb6a4..222d04d 100644 --- a/src/Http/RouteServer.cpp +++ b/src/Http/RouteServer.cpp @@ -1,97 +1,104 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/RouteServer.hpp" -namespace Tesses::Framework::Http -{ - - RouteServer::RouteServerRoute::RouteServerRoute(std::string route, std::string method, ServerRequestHandler handler) : method(method), handler(handler) - { - auto path = Tesses::Framework::Filesystem::VFSPath::ParseUriPath(route); - for(auto item : path.path) - { - if(item.size() > 2 && item[0] == '{' && item[item.size()-1] == '}') - { - this->parts.emplace_back( item.substr(1,item.size()-2),true); - } - else { - this->parts.emplace_back(item,false); - } +namespace Tesses::Framework::Http { + +RouteServer::RouteServerRoute::RouteServerRoute(std::string route, + std::string method, + ServerRequestHandler handler) + : method(method), handler(handler) { + auto path = Tesses::Framework::Filesystem::VFSPath::ParseUriPath(route); + for (auto item : path.path) { + if (item.size() > 2 && item[0] == '{' && item[item.size() - 1] == '}') { + this->parts.emplace_back(item.substr(1, item.size() - 2), true); + } else { + this->parts.emplace_back(item, false); } } - bool RouteServer::RouteServerRoute::Equals(Tesses::Framework::Filesystem::VFSPath& path, HttpDictionary& args) - { - if(path.path.size() != this->parts.size()) return false; - - - for(size_t i = 0; i < this->parts.size(); i++) - { - auto& part = this->parts[i]; - if(part.second) - args.SetValue(part.first, Tesses::Framework::Http::HttpUtils::UrlPathDecode(path.path[i])); - else if(part.first != path.path[i]) return false; - - } - - - return true; - } - - RouteServer::RouteServer(std::shared_ptr root) : root(root) - { - - } - - void RouteServer::Add(std::string method, std::string pattern, ServerRequestHandler handler) - { - this->routes.emplace_back(pattern,method,handler); - } - - bool RouteServer::Handle(ServerContext& ctx) - { - auto pathArgs = ctx.pathArguments; - auto path = Tesses::Framework::Filesystem::VFSPath::ParseUriPath(ctx.path); - for(auto& svr : this->routes) - { - if(svr.method != ctx.method && !((svr.method == "GET" && ctx.method == "HEAD") || (svr.method == "HEAD" && ctx.method == "GET"))) continue; - ctx.pathArguments = pathArgs; - if(svr.Equals(path, ctx.pathArguments) && svr.handler && svr.handler(ctx)) - return true; - - } - ctx.pathArguments = pathArgs; - - if(this->root) - return this->root->Handle(ctx); +} +bool RouteServer::RouteServerRoute::Equals( + Tesses::Framework::Filesystem::VFSPath &path, HttpDictionary &args) { + if (path.path.size() != this->parts.size()) return false; + + for (size_t i = 0; i < this->parts.size(); i++) { + auto &part = this->parts[i]; + if (part.second) + args.SetValue(part.first, + Tesses::Framework::Http::HttpUtils::UrlPathDecode( + path.path[i])); + else if (part.first != path.path[i]) + return false; } - void RouteServer::Get(std::string pattern, ServerRequestHandler handler) - { - Add("GET",pattern,handler); - } - void RouteServer::Post(std::string pattern, ServerRequestHandler handler) - { - Add("POST",pattern,handler); - } - void RouteServer::Put(std::string pattern, ServerRequestHandler handler) - { - Add("PUT",pattern,handler); - } - void RouteServer::Patch(std::string pattern, ServerRequestHandler handler) - { - Add("PATCH",pattern,handler); - } + return true; +} - void RouteServer::Delete(std::string pattern, ServerRequestHandler handler) - { - Add("DELETE",pattern,handler); - } +RouteServer::RouteServer(std::shared_ptr root) : root(root) {} - void RouteServer::Trace(std::string pattern, ServerRequestHandler handler) - { - Add("TRACE",pattern,handler); - } - void RouteServer::Options(std::string pattern, ServerRequestHandler handler) - { - Add("OPTIONS",pattern,handler); - } -} \ No newline at end of file +void RouteServer::Add(std::string method, std::string pattern, + ServerRequestHandler handler) { + this->routes.emplace_back(pattern, method, handler); +} + +bool RouteServer::Handle(ServerContext &ctx) { + auto pathArgs = ctx.pathArguments; + auto path = Tesses::Framework::Filesystem::VFSPath::ParseUriPath(ctx.path); + for (auto &svr : this->routes) { + if (svr.method != ctx.method && + !((svr.method == "GET" && ctx.method == "HEAD") || + (svr.method == "HEAD" && ctx.method == "GET"))) + continue; + ctx.pathArguments = pathArgs; + if (svr.Equals(path, ctx.pathArguments) && svr.handler && + svr.handler(ctx)) + return true; + } + ctx.pathArguments = pathArgs; + + if (this->root) + return this->root->Handle(ctx); + return false; +} + +void RouteServer::Get(std::string pattern, ServerRequestHandler handler) { + Add("GET", pattern, handler); +} +void RouteServer::Post(std::string pattern, ServerRequestHandler handler) { + Add("POST", pattern, handler); +} +void RouteServer::Put(std::string pattern, ServerRequestHandler handler) { + Add("PUT", pattern, handler); +} +void RouteServer::Patch(std::string pattern, ServerRequestHandler handler) { + Add("PATCH", pattern, handler); +} + +void RouteServer::Delete(std::string pattern, ServerRequestHandler handler) { + Add("DELETE", pattern, handler); +} + +void RouteServer::Trace(std::string pattern, ServerRequestHandler handler) { + Add("TRACE", pattern, handler); +} +void RouteServer::Options(std::string pattern, ServerRequestHandler handler) { + Add("OPTIONS", pattern, handler); +} +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/src/Http/WebSocket.cpp b/src/Http/WebSocket.cpp index 7bc6a3c..08a50df 100644 --- a/src/Http/WebSocket.cpp +++ b/src/Http/WebSocket.cpp @@ -1,67 +1,79 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Http/WebSocket.hpp" -namespace Tesses::Framework::Http -{ +namespace Tesses::Framework::Http { - CallbackWebSocketConnection::CallbackWebSocketConnection() - { +CallbackWebSocketConnection::CallbackWebSocketConnection() {} +CallbackWebSocketConnection::CallbackWebSocketConnection( + std::function, + std::function, std::function)> + onOpen, + std::function onReceive, + std::function onClose) { + this->onOpen = onOpen; + this->onReceive = onReceive; + this->onClose = onClose; +} - } - CallbackWebSocketConnection::CallbackWebSocketConnection(std::function,std::function,std::function)> onOpen, std::function onReceive, std::function onClose) - { - this->onOpen = onOpen; - this->onReceive = onReceive; - this->onClose = onClose; - } +void CallbackWebSocketConnection::OnOpen( + std::function sendMessage, + std::function ping, std::function closeConnection) { + if (this->onOpen) + this->onOpen(sendMessage, ping, closeConnection); +} +void CallbackWebSocketConnection::OnReceive(WebSocketMessage &message) { + if (this->onReceive) + this->onReceive(message); +} +void CallbackWebSocketConnection::OnClose(bool clean) { + if (this->onClose) + this->onClose(clean); +} - void CallbackWebSocketConnection::OnOpen(std::function sendMessage, std::function ping, std::function closeConnection) - { - if(this->onOpen) - this->onOpen(sendMessage,ping,closeConnection); - } - void CallbackWebSocketConnection::OnReceive(WebSocketMessage& message) - { - if(this->onReceive) - this->onReceive(message); - } - void CallbackWebSocketConnection::OnClose(bool clean) - { - if(this->onClose) - this->onClose(clean); - } +WebSocketMessage::WebSocketMessage() { + this->isBinary = false; + this->data = {}; +} +WebSocketMessage::WebSocketMessage(std::vector data) { + this->isBinary = true; + this->data = data; +} +WebSocketMessage::WebSocketMessage(const void *data, size_t len) { + this->isBinary = true; + this->data = {}; + this->data.insert(this->data.end(), (uint8_t *)data, + ((uint8_t *)data) + len); +} +WebSocketMessage::WebSocketMessage(std::string message) { + this->isBinary = false; + this->data = {}; + this->data.insert(this->data.end(), message.begin(), message.end()); +} +std::string WebSocketMessage::ToString() { + std::string str = {}; + str.insert(str.end(), this->data.begin(), this->data.end()); + return str; +} - - WebSocketMessage::WebSocketMessage() - { - this->isBinary=false; - this->data={}; - } - WebSocketMessage::WebSocketMessage(std::vector data) - { - this->isBinary = true; - this->data = data; - } - WebSocketMessage::WebSocketMessage(const void* data, size_t len) - { - this->isBinary=true; - this->data={}; - this->data.insert(this->data.end(),(uint8_t*)data,((uint8_t*)data)+len); - } - WebSocketMessage::WebSocketMessage(std::string message) - { - this->isBinary=false; - this->data={}; - this->data.insert(this->data.end(),message.begin(), message.end()); - } - std::string WebSocketMessage::ToString() - { - std::string str = {}; - str.insert(str.end(),this->data.begin(),this->data.end()); - return str; - } - - void SendWebSocketMessage(std::function cb, std::string text) - { - WebSocketMessage message(text); - cb(message); - } -} \ No newline at end of file +void SendWebSocketMessage(std::function cb, + std::string text) { + WebSocketMessage message(text); + cb(message); +} +} // namespace Tesses::Framework::Http \ No newline at end of file diff --git a/src/Mail/Smtp.cpp b/src/Mail/Smtp.cpp index 80ef567..7aaf2de 100644 --- a/src/Mail/Smtp.cpp +++ b/src/Mail/Smtp.cpp @@ -1,149 +1,159 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Mail/Smtp.hpp" #include "TessesFramework/Crypto/Crypto.hpp" +#include "TessesFramework/Http/HttpUtils.hpp" #include "TessesFramework/Streams/MemoryStream.hpp" #include "TessesFramework/TextStreams/StreamWriter.hpp" -#include "TessesFramework/Http/HttpUtils.hpp" -namespace Tesses::Framework::Mail -{ - static void SMTP_ATTACHMENT_WRITE(std::string& myStr, std::shared_ptr strm) - { - std::string txt = Tesses::Framework::Crypto::Base64_Encode(strm->GetBuffer()); - bool first=true; - size_t read; - size_t offset = 0; - do { - if(!first) myStr.append("\r\n"); - - read = std::min(72, txt.size()-offset); - - if(read > 0) - myStr.insert(myStr.end(),txt.begin()+offset,txt.begin()+offset+read); - - offset+=read; - first=false; - } while(read > 0); +namespace Tesses::Framework::Mail { +static void SMTP_ATTACHMENT_WRITE( + std::string &myStr, + std::shared_ptr strm) { + std::string txt = + Tesses::Framework::Crypto::Base64_Encode(strm->GetBuffer()); + bool first = true; + size_t read; + size_t offset = 0; + do { + if (!first) + myStr.append("\r\n"); - } + read = std::min(72, txt.size() - offset); - SMTPBody::~SMTPBody() - { + if (read > 0) + myStr.insert(myStr.end(), txt.begin() + offset, + txt.begin() + offset + read); - } + offset += read; + first = false; + } while (read > 0); +} - SMTPStringBody::SMTPStringBody() - { - - } - SMTPStringBody::SMTPStringBody(std::string text, std::string mimeType) - { - this->text = text; - this->mimeType=mimeType; - } - void SMTPStringBody::Write(std::shared_ptr strm) - { - strm->WriteBlock((const uint8_t*)this->text.c_str(),this->text.size()); - } - SMTPStreamBody::SMTPStreamBody(std::string mimeType,std::shared_ptr strm) - { - this->mimeType = mimeType; - this->stream = strm; - } - - void SMTPStreamBody::Write(std::shared_ptr strm) - { - this->stream->Seek(0L,Tesses::Framework::Streams::SeekOrigin::Begin); - this->stream->CopyTo(strm); - } - SMTPStreamBody::~SMTPStreamBody() - { - } - SMTPClient::SMTPClient(std::shared_ptr stream) - { - this->strm = stream; - this->body = nullptr; - } - - void SMTPClient::Send() - { - std::string emailHeaders = "EHLO "; - emailHeaders.append(this->domain); - emailHeaders.append("\r\nAUTH LOGIN\r\n"); - std::vector data; - data.insert(data.begin(), this->username.begin(),this->username.end()); +SMTPBody::~SMTPBody() {} - emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(data)); - emailHeaders.append("\r\n"); - data.clear(); - data.insert(data.begin(),this->password.begin(),this->password.end()); - emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(data)); - emailHeaders.append("\r\n"); - emailHeaders.append("MAIL FROM:<"); - - emailHeaders.append(this->from); - emailHeaders.append(">\r\n"); - - emailHeaders.append("RCPT TO:<"); - emailHeaders.append(to); - emailHeaders.append(">\r\n"); - - emailHeaders.append("DATA\r\n"); - std::string boundary = "joel&<<94292025209248"; - emailHeaders.append("From: "); - emailHeaders.append(this->from_name); - emailHeaders.append(" <"); - emailHeaders.append(this->from); - emailHeaders.append(">\r\nSubject: "); - emailHeaders.append(this->subject); - emailHeaders.append("\r\n"); - emailHeaders.append("Content-Type: multipart/mixed; boundary="); +SMTPStringBody::SMTPStringBody() {} +SMTPStringBody::SMTPStringBody(std::string text, std::string mimeType) { + this->text = text; + this->mimeType = mimeType; +} +void SMTPStringBody::Write( + std::shared_ptr strm) { + strm->WriteBlock((const uint8_t *)this->text.c_str(), this->text.size()); +} +SMTPStreamBody::SMTPStreamBody( + std::string mimeType, + std::shared_ptr strm) { + this->mimeType = mimeType; + this->stream = strm; +} + +void SMTPStreamBody::Write( + std::shared_ptr strm) { + this->stream->Seek(0L, Tesses::Framework::Streams::SeekOrigin::Begin); + this->stream->CopyTo(strm); +} +SMTPStreamBody::~SMTPStreamBody() {} +SMTPClient::SMTPClient( + std::shared_ptr stream) { + this->strm = stream; + this->body = nullptr; +} + +void SMTPClient::Send() { + std::string emailHeaders = "EHLO "; + emailHeaders.append(this->domain); + emailHeaders.append("\r\nAUTH LOGIN\r\n"); + std::vector data; + data.insert(data.begin(), this->username.begin(), this->username.end()); + + emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(data)); + emailHeaders.append("\r\n"); + data.clear(); + data.insert(data.begin(), this->password.begin(), this->password.end()); + emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(data)); + emailHeaders.append("\r\n"); + emailHeaders.append("MAIL FROM:<"); + + emailHeaders.append(this->from); + emailHeaders.append(">\r\n"); + + emailHeaders.append("RCPT TO:<"); + emailHeaders.append(to); + emailHeaders.append(">\r\n"); + + emailHeaders.append("DATA\r\n"); + std::string boundary = "joel&<<94292025209248"; + emailHeaders.append("From: "); + emailHeaders.append(this->from_name); + emailHeaders.append(" <"); + emailHeaders.append(this->from); + emailHeaders.append(">\r\nSubject: "); + emailHeaders.append(this->subject); + emailHeaders.append("\r\n"); + emailHeaders.append("Content-Type: multipart/mixed; boundary="); + emailHeaders.append(boundary); + emailHeaders.append("\r\n\r\n--"); + emailHeaders.append(boundary); + emailHeaders.append("\r\nContent-Type: "); + emailHeaders.append(this->body->mimeType); + emailHeaders.append("; charset=utf-8\r\n\r\n"); + Tesses::Framework::TextStreams::StreamWriter writer(this->strm); + writer.Write(emailHeaders); + this->body->Write(this->strm); + + if (this->attachments.empty()) { + emailHeaders = "\r\n--"; emailHeaders.append(boundary); - emailHeaders.append("\r\n\r\n--"); - emailHeaders.append(boundary); - emailHeaders.append("\r\nContent-Type: "); - emailHeaders.append(this->body->mimeType); - emailHeaders.append("; charset=utf-8\r\n\r\n"); - Tesses::Framework::TextStreams::StreamWriter writer(this->strm); + emailHeaders.append("--\r\n.\r\n"); writer.Write(emailHeaders); - this->body->Write(this->strm); - - if(this->attachments.empty()) - { - emailHeaders="\r\n--"; + } else { + for (auto item : this->attachments) { + emailHeaders = "\r\n--"; emailHeaders.append(boundary); - emailHeaders.append("--\r\n.\r\n"); + emailHeaders.append("\r\n"); + emailHeaders.append("Content-Type: "); + emailHeaders.append(item.second->mimeType); + emailHeaders.append("; name=\""); + std::string name = + Tesses::Framework::Http::HttpUtils::UrlPathEncode(item.first, + true); + emailHeaders.append(name); + emailHeaders.append( + "\"\r\nContent-Transfer-Encoding: " + "base64\r\nContent-Disposition: attachment; filename=\""); + emailHeaders.append(name); + emailHeaders.append("\"\r\n\r\n"); + std::shared_ptr ms = + std::make_shared( + true); + item.second->Write(ms); + SMTP_ATTACHMENT_WRITE(emailHeaders, ms); + // emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(ms.GetBuffer())); writer.Write(emailHeaders); } - else - { - for(auto item : this->attachments) - { - emailHeaders="\r\n--"; - emailHeaders.append(boundary); - emailHeaders.append("\r\n"); - emailHeaders.append("Content-Type: "); - emailHeaders.append(item.second->mimeType); - emailHeaders.append("; name=\""); - std::string name = Tesses::Framework::Http::HttpUtils::UrlPathEncode(item.first,true); - emailHeaders.append(name); - emailHeaders.append("\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\""); - emailHeaders.append(name); - emailHeaders.append("\"\r\n\r\n"); - std::shared_ptr ms = std::make_shared(true); - item.second->Write(ms); - SMTP_ATTACHMENT_WRITE(emailHeaders,ms); - //emailHeaders.append(Tesses::Framework::Crypto::Base64_Encode(ms.GetBuffer())); - writer.Write(emailHeaders); - } - emailHeaders="\r\n--"; - emailHeaders.append(boundary); - emailHeaders.append("--\r\n.\r\n"); - writer.Write(emailHeaders); - } + emailHeaders = "\r\n--"; + emailHeaders.append(boundary); + emailHeaders.append("--\r\n.\r\n"); + writer.Write(emailHeaders); } +} - SMTPClient::~SMTPClient() - { - - } -} \ No newline at end of file +SMTPClient::~SMTPClient() {} +} // namespace Tesses::Framework::Mail \ No newline at end of file diff --git a/src/Platform/Environment.cpp b/src/Platform/Environment.cpp index cf487b0..cfc6ccb 100644 --- a/src/Platform/Environment.cpp +++ b/src/Platform/Environment.cpp @@ -1,338 +1,338 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Platform/Environment.hpp" #include "TessesFramework/Http/HttpUtils.hpp" #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) #include "sago/platform_folders.h" #endif #if defined(_WIN32) -#include #include "TessesFramework/Filesystem/VFSFix.hpp" #include "TessesFramework/Text/StringConverter.hpp" +#include using namespace Tesses::Framework::Text::StringConverter; #endif - #if !defined(_WIN32) - extern char** environ; - #endif +#if !defined(_WIN32) +extern char **environ; +#endif using namespace Tesses::Framework::Filesystem; -namespace Tesses::Framework::Platform::Environment -{ - #if defined(_WIN32) - const char EnvPathSeperator=';'; - #else - const char EnvPathSeperator=':'; - #endif - PortableAppConfig portable_config; +namespace Tesses::Framework::Platform::Environment { +#if defined(_WIN32) +const char EnvPathSeperator = ';'; +#else +const char EnvPathSeperator = ':'; +#endif +PortableAppConfig portable_config; - namespace SpecialFolders - { - VFSPath GetTemp() - { +namespace SpecialFolders { +VFSPath GetTemp() { - if(portable_config.temp) - return *portable_config.temp; - return std::filesystem::temp_directory_path().string(); - } - VFSPath GetHomeFolder() - { + if (portable_config.temp) + return *portable_config.temp; + return std::filesystem::temp_directory_path().string(); +} +VFSPath GetHomeFolder() { - if(portable_config.user) - return *portable_config.user; + if (portable_config.user) + return *portable_config.user; - #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) - return sago::getHomeDir(); - #elif defined(__EMSCRIPTEN__) - return (std::string)"/home/web_user"; - #elif defined(__ANDROID__) - return (std::string)"/sdcard/TF_User"; - #else - return (std::string)"/TF_User"; - #endif - } - VFSPath GetDownloads() - { - if(portable_config.downloads) - return *portable_config.downloads; +#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) + return sago::getHomeDir(); +#elif defined(__EMSCRIPTEN__) + return (std::string) "/home/web_user"; +#elif defined(__ANDROID__) + return (std::string) "/sdcard/TF_User"; +#else + return (std::string) "/TF_User"; +#endif +} +VFSPath GetDownloads() { + if (portable_config.downloads) + return *portable_config.downloads; - #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) - return sago::getDownloadFolder(); - #elif defined(__ANDROID__) - return (std::string)"/sdcard/Download"; - #else - return GetHomeFolder() / "Downloads"; - #endif - } - VFSPath GetMusic() - { +#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) + return sago::getDownloadFolder(); +#elif defined(__ANDROID__) + return (std::string) "/sdcard/Download"; +#else + return GetHomeFolder() / "Downloads"; +#endif +} +VFSPath GetMusic() { - if(portable_config.music) - return *portable_config.music; - #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) - return sago::getMusicFolder(); - #elif defined(__ANDROID__) - return (std::string)"/sdcard/Music"; - #else - return GetHomeFolder() / "Music"; - #endif - } - VFSPath GetPictures() - { + if (portable_config.music) + return *portable_config.music; +#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) + return sago::getMusicFolder(); +#elif defined(__ANDROID__) + return (std::string) "/sdcard/Music"; +#else + return GetHomeFolder() / "Music"; +#endif +} +VFSPath GetPictures() { - if(portable_config.pictures) - return *portable_config.pictures; - #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) - return sago::getPicturesFolder(); - #elif defined(__ANDROID__) - return (std::string)"/sdcard/Pictures"; - #else - return GetHomeFolder() / "Pictures"; - #endif - } - VFSPath GetVideos() - { - if(portable_config.videos) - return *portable_config.videos; - #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) - return sago::getVideoFolder(); - #elif defined(__ANDROID__) - return (std::string)"/sdcard/Movies"; - #else - return GetHomeFolder() / "Videos"; - #endif - } - VFSPath GetDocuments() - { + if (portable_config.pictures) + return *portable_config.pictures; +#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) + return sago::getPicturesFolder(); +#elif defined(__ANDROID__) + return (std::string) "/sdcard/Pictures"; +#else + return GetHomeFolder() / "Pictures"; +#endif +} +VFSPath GetVideos() { + if (portable_config.videos) + return *portable_config.videos; +#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) + return sago::getVideoFolder(); +#elif defined(__ANDROID__) + return (std::string) "/sdcard/Movies"; +#else + return GetHomeFolder() / "Videos"; +#endif +} +VFSPath GetDocuments() { - if(portable_config.documents) - return *portable_config.documents; - #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) - return sago::getDocumentsFolder(); - #elif defined(__ANDROID__) - return (std::string)"/sdcard/Documents"; - #else - return GetHomeFolder() / "Documents"; - #endif - } - VFSPath GetConfig() - { + if (portable_config.documents) + return *portable_config.documents; +#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) + return sago::getDocumentsFolder(); +#elif defined(__ANDROID__) + return (std::string) "/sdcard/Documents"; +#else + return GetHomeFolder() / "Documents"; +#endif +} +VFSPath GetConfig() { - if(portable_config.config) - return *portable_config.config; - #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) - return sago::getConfigHome(); - #else - return GetHomeFolder() / "Config"; - #endif - } - VFSPath GetDesktop() - { - if(portable_config.desktop) - return *portable_config.desktop; - #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) - return sago::getDesktopFolder(); - #else - return GetHomeFolder() / "Desktop"; - #endif - } - VFSPath GetState() - { + if (portable_config.config) + return *portable_config.config; +#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) + return sago::getConfigHome(); +#else + return GetHomeFolder() / "Config"; +#endif +} +VFSPath GetDesktop() { + if (portable_config.desktop) + return *portable_config.desktop; +#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) + return sago::getDesktopFolder(); +#else + return GetHomeFolder() / "Desktop"; +#endif +} +VFSPath GetState() { - if(portable_config.state) - return *portable_config.state; - #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) - return sago::getStateDir(); - #else - return GetHomeFolder() / "State"; - #endif - } - VFSPath GetCache() - { + if (portable_config.state) + return *portable_config.state; +#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) + return sago::getStateDir(); +#else + return GetHomeFolder() / "State"; +#endif +} +VFSPath GetCache() { - if(portable_config.cache) - return *portable_config.cache; - #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) - return sago::getCacheDir(); - #else - return GetHomeFolder() / "Cache"; - #endif - } - VFSPath GetData() - { + if (portable_config.cache) + return *portable_config.cache; +#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) + return sago::getCacheDir(); +#else + return GetHomeFolder() / "Cache"; +#endif +} +VFSPath GetData() { - if(portable_config.data) - return *portable_config.data; - #if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) - return sago::getDataHome(); - #else - return GetHomeFolder() / "Data"; - #endif - } - } + if (portable_config.data) + return *portable_config.data; +#if defined(TESSESFRAMEWORK_ENABLE_PLATFORMFOLDERS) && !defined(SAGO_DISABLE) + return sago::getDataHome(); +#else + return GetHomeFolder() / "Data"; +#endif +} +} // namespace SpecialFolders - VFSPath GetRealExecutablePath(VFSPath realPath) - { - using namespace Tesses::Framework::Http; +VFSPath GetRealExecutablePath(VFSPath realPath) { + using namespace Tesses::Framework::Http; - - if(!realPath.relative) return realPath; - if(LocalFS->FileExists(realPath)) return realPath.MakeAbsolute(); - const char* path = std::getenv("PATH"); - #if defined(_WIN32) - const char* pathext = std::getenv("PATHEXT"); - auto pext = HttpUtils::SplitString(pathext,";"); - pext.push_back({}); - auto pathParts = HttpUtils::SplitString(path,";"); - for(auto item : pathParts) - { - - auto newPath = LocalFS->SystemToVFSPath(item) / realPath; - for(auto item2 : pext) - { - auto newPathExt = newPath + item2; - if(LocalFS->FileExists(newPathExt)) return newPathExt; - } - if(LocalFS->FileExists(newPath)) return newPath; - } + if (!realPath.relative) return realPath; - #else - - auto pathParts = HttpUtils::SplitString(path,":"); - for(auto item : pathParts) - { - auto newPath = LocalFS->SystemToVFSPath(item) / realPath; - if(LocalFS->FileExists(newPath)) return newPath; - } + if (LocalFS->FileExists(realPath)) return realPath.MakeAbsolute(); - #endif - } + const char *path = std::getenv("PATH"); +#if defined(_WIN32) + const char *pathext = std::getenv("PATHEXT"); + auto pext = HttpUtils::SplitString(pathext, ";"); + pext.push_back({}); + auto pathParts = HttpUtils::SplitString(path, ";"); + for (auto item : pathParts) { - std::optional GetVariable(std::string name) - { - auto res = std::getenv(name.c_str()); - if(res == nullptr) return std::nullopt; - std::string value = res; - return value; - } - void SetVariable(std::string name, std::optional var) - { - if (var) - #if defined(_WIN32) - { - std::u16string nameu16 = {}; - - std::u16string varu16 = {}; - - UTF16::FromUTF8(nameu16, name); - UTF16::FromUTF8(varu16, var.value()); - SetEnvironmentVariableW((LPCWSTR)nameu16.c_str(),(LPCWSTR)varu16.c_str()); + auto newPath = LocalFS->SystemToVFSPath(item) / realPath; + for (auto item2 : pext) { + auto newPathExt = newPath + item2; + if (LocalFS->FileExists(newPathExt)) + return newPathExt; } - #else - setenv(name.c_str(), var->c_str(),1); - #endif - else - #if defined(_WIN32) - { - std::u16string nameu16 = {}; + if (LocalFS->FileExists(newPath)) + return newPath; + } + return realPath; +#else - UTF16::FromUTF8(nameu16, name); + auto pathParts = HttpUtils::SplitString(path, ":"); + for (auto item : pathParts) { + auto newPath = LocalFS->SystemToVFSPath(item) / realPath; + if (LocalFS->FileExists(newPath)) + return newPath; + } + return realPath.MakeAbsolute(); +#endif +} - SetEnvironmentVariableW((LPCWSTR)nameu16.c_str(),NULL); - } - #else +std::optional GetVariable(std::string name) { + auto res = std::getenv(name.c_str()); + if (res == nullptr) + return std::nullopt; + std::string value = res; + return value; +} +void SetVariable(std::string name, std::optional var) { + if (var) +#if defined(_WIN32) + { + std::u16string nameu16 = {}; + + std::u16string varu16 = {}; + + UTF16::FromUTF8(nameu16, name); + UTF16::FromUTF8(varu16, var.value()); + SetEnvironmentVariableW((LPCWSTR)nameu16.c_str(), + (LPCWSTR)varu16.c_str()); + } +#else + setenv(name.c_str(), var->c_str(), 1); +#endif + else +#if defined(_WIN32) + { + std::u16string nameu16 = {}; + + UTF16::FromUTF8(nameu16, name); + + SetEnvironmentVariableW((LPCWSTR)nameu16.c_str(), NULL); + } +#else unsetenv(name.c_str()); - #endif - +#endif +} + +void GetEnvironmentVariables( + std::vector> &env) { +#if defined(_WIN32) + auto environ0 = GetEnvironmentStringsW(); + auto envthing = environ0; + while (*envthing) { + std::u16string str = (const char16_t *)envthing; + std::string stru8; + UTF8::FromUTF16(stru8, str); + auto items = Http::HttpUtils::SplitString(stru8, "=", 2); + if (items.size() == 2) { + + env.push_back( + std::pair(items[0], items[1])); + } else if (items.size() == 1) { + env.push_back(std::pair(items[0], "")); + } + envthing += str.size() + 1; } + FreeEnvironmentStringsW(environ0); +#else + for (char **envthing = environ; *envthing != NULL; envthing++) { + // if(*envthing == NULL) break; + auto items = Http::HttpUtils::SplitString(*envthing, "=", 2); + if (items.size() == 2) { - - void GetEnvironmentVariables(std::vector>& env) - { - #if defined(_WIN32) - auto environ0 = GetEnvironmentStringsW(); - auto envthing = environ0; - while(*envthing) - { - std::u16string str = (const char16_t*)envthing; - std::string stru8; - UTF8::FromUTF16(stru8, str); - auto items = Http::HttpUtils::SplitString(stru8, "=", 2); - if(items.size() == 2) - { - - env.push_back(std::pair(items[0],items[1])); - } - else if(items.size() == 1) - { - env.push_back(std::pair(items[0],"")); - } - envthing += str.size() + 1; - } - FreeEnvironmentStringsW(environ0); - #else - for(char** envthing = environ; *envthing != NULL; envthing++) - { - //if(*envthing == NULL) break; - auto items = Http::HttpUtils::SplitString(*envthing,"=",2); - if(items.size() == 2) - { - - env.push_back(std::pair(items[0],items[1])); - } - else if(items.size() == 1) - { - env.push_back(std::pair(items[0],"")); - } - - } - #endif + env.push_back( + std::pair(items[0], items[1])); + } else if (items.size() == 1) { + env.push_back(std::pair(items[0], "")); + } } - std::string GetPlatform() - { - #if defined(__ANDROID__) - return "Android"; - #endif - #if defined(__SWITCH__) - return "Nintendo Switch"; - #endif - #if defined(__PS2__) - return "PlayStation 2"; - #endif - #if defined(GEKKO) - #if defined(HW_RVL) - return "Nintendo Wii"; - #endif - return "Nintendo Gamecube"; - #endif - #if defined(WIN32) || defined(_WIN32) - return "Windows"; - #endif - #if defined(linux) - return "Linux"; - #endif +#endif +} +std::string GetPlatform() { +#if defined(__ANDROID__) + return "Android"; +#endif +#if defined(__SWITCH__) + return "Nintendo Switch"; +#endif +#if defined(__PS2__) + return "PlayStation 2"; +#endif +#if defined(GEKKO) +#if defined(HW_RVL) + return "Nintendo Wii"; +#endif + return "Nintendo Gamecube"; +#endif +#if defined(WIN32) || defined(_WIN32) + return "Windows"; +#endif +#if defined(__FreeBSD__) + return "FreeBSD"; +#endif +#if defined(__NetBSD__) + return "NetBSD"; +#endif +#if defined(__linux__) + return "Linux"; +#endif - #ifdef __APPLE__ - #include "TargetConditionals.h" - #if TARGET_OS_MAC - return "MacOS"; - #endif - #if TARGET_OS_IOS - return "iOS"; - #endif - #if TARGET_OS_TV - return "Apple TV"; - #endif +#ifdef __APPLE__ +#include "TargetConditionals.h" +#if TARGET_OS_MAC + return "MacOS"; +#endif +#if TARGET_OS_IOS + return "iOS"; +#endif +#if TARGET_OS_TV + return "Apple TV"; +#endif - #if TARGET_OS_WATCH - return "Apple Watch"; - #endif +#if TARGET_OS_WATCH + return "Apple Watch"; +#endif - #if __EMSCRIPTEN__ - return "WebAssembly"; - #endif +#if __EMSCRIPTEN__ + return "WebAssembly"; +#endif return "Unknown Apple Device"; #endif - return "Unknown"; - } -} \ No newline at end of file + return "Unknown"; +} +} // namespace Tesses::Framework::Platform::Environment \ No newline at end of file diff --git a/src/Platform/Process.cpp b/src/Platform/Process.cpp index c2fc1ec..b4686da 100644 --- a/src/Platform/Process.cpp +++ b/src/Platform/Process.cpp @@ -1,3 +1,21 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Platform/Process.hpp" #include "TessesFramework/Http/HttpUtils.hpp" #include "TessesFramework/Platform/Environment.hpp" @@ -10,711 +28,667 @@ extern "C" { #include "TessesFramework/Filesystem/VFSFix.hpp" #include "TessesFramework/Text/StringConverter.hpp" using namespace Tesses::Framework::Text::StringConverter; -static void escape_windows_args(std::string& str, std::vector args) -{ +static void escape_windows_args(std::string &str, + std::vector args) { bool first = true; - for (auto item : args) - { - if (first) - { + for (auto item : args) { + if (first) { str.push_back('\"'); first = false; - } - else { + } else { str.append(" \""); } - for (auto c : item) - { - if (c == '"') str.append("\\\""); - else str.push_back(c); + for (auto c : item) { + if (c == '"') + str.append("\\\""); + else + str.push_back(c); } str.push_back('\"'); } } - - #else -#include #include - +#include #endif - - namespace Tesses::Framework::Platform { - class ProcessData : public HiddenFieldData { - public: - //TODO: Implement for WIN32 - #if defined(_WIN32) - STARTUPINFOW si; - PROCESS_INFORMATION pi; +class ProcessData : public HiddenFieldData { + public: +// TODO: Implement for WIN32 +#if defined(_WIN32) + STARTUPINFOW si; + PROCESS_INFORMATION pi; - HANDLE stdin_strm; - HANDLE stdout_strm; - HANDLE stderr_strm; + HANDLE stdin_strm; + HANDLE stdout_strm; + HANDLE stderr_strm; - static int __stdcall KillGraceFully(HWND hndle, LPARAM arg) - { - auto pd=static_cast((void*)arg); - DWORD curProc; - GetWindowThreadProcessId(hndle, &curProc); - if (curProc == pd->pi.dwProcessId) - { - PostMessage(hndle, WM_CLOSE, 0, 0); + static int __stdcall KillGraceFully(HWND hndle, LPARAM arg) { + auto pd = static_cast((void *)arg); + DWORD curProc; + GetWindowThreadProcessId(hndle, &curProc); + if (curProc == pd->pi.dwProcessId) { + PostMessage(hndle, WM_CLOSE, 0, 0); - return true; - } - return false; - } - - - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - #else - int stdin_strm; - int stdout_strm; - int stderr_strm; - pid_t pid; - #endif - - - ProcessData() { - //TODO: Implement for WIN32 - #if defined(_WIN32) - stdin_strm = NULL; - stdout_strm = NULL; - stderr_strm = NULL; - - - - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - - #else - this->stdin_strm=-1; - this->stdout_strm = -1; - this->stderr_strm=-1; - #endif - } - }; - class ProcessStream : public Tesses::Framework::Streams::Stream { - #if defined(_WIN32) - HANDLE strm; - bool writing; - bool eos; - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - #else - int strm; - - bool writing; - bool eos; - #endif - public: - #if defined(_WIN32) - ProcessStream(HANDLE strm, bool writing) - { - this->strm = strm; - this->writing = writing; - this->eos = false; - } - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - #else - ProcessStream(int strm, bool writing) - { - this->strm = strm; - this->writing = writing; - this->eos=false; - } - #endif - bool EndOfStream() - { - //TODO: Implement for WIN32 - #if defined(_WIN32) - return this->strm == NULL || eos; - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - return true; - #else - return this->strm < 0 || eos; - #endif - } - bool CanRead() - { - //TODO: Implement for WIN32 - #if defined(_WIN32) - return !writing && this->strm != NULL; - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - return false; - #else - return !writing && this->strm > -1; - #endif - } - bool CanWrite() - { - //TODO: Implement for WIN32 - #if defined(_WIN32) - - return writing && this->strm != NULL; - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - return false; - #else - return writing && this->strm > -1; - #endif - } - - size_t Read(uint8_t* buff, size_t sz) - { - //TODO: Implement for WIN32 - #if defined(_WIN32) - if (this->strm == NULL || this->eos && writing) return 0; - - DWORD dataR = (DWORD)sz; - if (!ReadFile(this->strm, buff, dataR, &dataR, NULL)) - return 0; - if (dataR == 0) { - this->eos = true; - } - return (size_t)dataR; - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - return 0; - #else - - if(this->strm < 0 || this->eos && writing) return 0; - - auto r = read(this->strm,buff,sz); - if(r == -1) return 0; - if(r == 0 && sz != 0) { this->eos=true; return 0;} - return (size_t)r; - #endif - } - - size_t Write(const uint8_t* buff, size_t sz) - { - //TODO: Implement for WIN32 - #if defined(_WIN32) - if (this->strm == NULL || !writing) return 0; - DWORD dataW=(DWORD)sz; - if (!WriteFile(this->strm, buff, dataW, &dataW, NULL)) - return 0; - - return (size_t)dataW; - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - return 0; - #else - if(this->strm < 0 || !writing) return 0; - auto r = write(this->strm,buff,sz); - if(r == -1) return 0; - return (size_t)r; - #endif - } - - }; - - - Process::Process() : Process("",{},true) - { - - } - Process::Process(std::string name, std::vector args,bool includeThisEnv) : Process(name,args,std::vector>(),includeThisEnv) - { - - } - Process::Process(std::string name, std::vector args, std::vector> env,bool includeThisEnv) - { - this->name = name; - this->args = args; - this->env = env; - this->includeThisEnv = includeThisEnv; - this->hidden.AllocField(); - } - bool Process::HasExited() - { - if (this->exited) return true; - ProcessData* p = this->hidden.GetField(); - - #if defined(_WIN32) - if (WaitForSingleObject(p->pi.hProcess, 0) == WAIT_OBJECT_0) - { - DWORD ec = 0; - GetExitCodeProcess(p->pi.hProcess,&ec); - this->exitCode = (int)ec; - this->exited = true; return true; } - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - return true; - #else - int r; - if (waitpid(this->hidden.GetField()->pid, &r, WNOHANG) != -1) - { - this->exited = true; - this->exitCode = r; - return r; - } - #endif return false; } - Process::Process(std::string name, std::vector args, std::vector env,bool includeThisEnv) : Process(name,args,std::vector>(),includeThisEnv) - { - this->env.resize(env.size()); - for(size_t i =0; i < env.size(); i++) - { - auto res=Http::HttpUtils::SplitString(env[i],"=",2); - if(res.size() == 2) - { - this->env[i].first = res[0]; - this->env[i].second = res[1]; - } - else if(res.size() == 1) - { - this->env[i].first = res[0]; - this->env[i].second = ""; - } - } +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) +#else + int stdin_strm; + int stdout_strm; + int stderr_strm; + pid_t pid; +#endif + + ProcessData() { +// TODO: Implement for WIN32 +#if defined(_WIN32) + stdin_strm = NULL; + stdout_strm = NULL; + stderr_strm = NULL; + +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + +#else + this->stdin_strm = -1; + this->stdout_strm = -1; + this->stderr_strm = -1; +#endif } - void Process::CloseStdInNow() - { - ProcessData* p = this->hidden.GetField(); - - #if defined(_WIN32) - if (p->stdin_strm != NULL) - { - CloseHandle(p->stdin_strm); - p->stdin_strm = NULL; - } - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - - #else - if (p->stdin_strm > -1) - { - close(p->stdin_strm); - p->stdin_strm = -1; - } - #endif +}; +class ProcessStream : public Tesses::Framework::Streams::Stream { +#if defined(_WIN32) + HANDLE strm; + bool writing; + bool eos; +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) +#else + int strm; + bool writing; + bool eos; +#endif + public: +#if defined(_WIN32) + ProcessStream(HANDLE strm, bool writing) { + this->strm = strm; + this->writing = writing; + this->eos = false; } - Process::~Process() - { - - ProcessData* p = this->hidden.GetField(); - - #if defined(_WIN32) - if(!this->exited) - Kill(SIGTERM); - if (p->stdin_strm != NULL) - CloseHandle(p->stdin_strm); +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) +#else + ProcessStream(int strm, bool writing) { + this->strm = strm; + this->writing = writing; + this->eos = false; + } +#endif + bool EndOfStream() { +// TODO: Implement for WIN32 +#if defined(_WIN32) + return this->strm == NULL || eos; +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + return true; +#else + return this->strm < 0 || eos; +#endif + } + bool CanRead() { +// TODO: Implement for WIN32 +#if defined(_WIN32) + return !writing && this->strm != NULL; +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + return false; +#else + return !writing && this->strm > -1; +#endif + } + bool CanWrite() { +// TODO: Implement for WIN32 +#if defined(_WIN32) - if (p->stdout_strm != NULL) - CloseHandle(p->stdout_strm); - - if (p->stderr_strm != NULL) - CloseHandle(p->stderr_strm); - - - CloseHandle(p->pi.hProcess); - CloseHandle(p->pi.hThread); - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - - #else - if (!this->exited) - { - Kill(SIGTERM); - WaitForExit(); - } - - if (p->stdin_strm != -1) - close(p->stdin_strm); - if (p->stdout_strm != -1) - close(p->stdout_strm); - if (p->stderr_strm != -1) - close(p->stderr_strm); - #endif + return writing && this->strm != NULL; +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + return false; +#else + return writing && this->strm > -1; +#endif } - + size_t Read(uint8_t *buff, size_t sz) { +// TODO: Implement for WIN32 +#if defined(_WIN32) + if (this->strm == NULL || this->eos && writing) + return 0; - bool Process::Start() - { - ProcessData* p = - this->hidden.GetField(); - std::vector> envs; - - if(this->includeThisEnv) - Environment::GetEnvironmentVariables(envs); - - for(auto itemNew : this->env) - { - bool has=false; - for(auto& item : envs) - { - if(item.first == itemNew.first) - { - - item.second = itemNew.second; - has=true; - break; - } - } - if(!has) envs.push_back(itemNew); + DWORD dataR = (DWORD)sz; + if (!ReadFile(this->strm, buff, dataR, &dataR, NULL)) + return 0; + if (dataR == 0) { + this->eos = true; } + return (size_t)dataR; +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + return 0; +#else - #if defined(_WIN32) + if (this->strm < 0 || this->eos && writing) + return 0; + auto r = read(this->strm, buff, sz); - ZeroMemory(&p->si, sizeof(p->si)); - p->si.cb = sizeof(p->si); - ZeroMemory(&p->pi, sizeof(p->pi)); - std::u16string u16_name; - std::u16string u16_args; - std::string args; - escape_windows_args(args,this->args); - UTF16::FromUTF8(u16_name,this->name); - UTF16::FromUTF8(u16_args, args); - std::u16string env = {}; - - for (auto envItem : envs) - { - auto partOld = envItem.first + "=" + envItem.second; - std::u16string part = {}; - - UTF16::FromUTF8(part,partOld); - env.append(part); - env.push_back(0); + if (r == -1) + return 0; + if (r == 0 && sz != 0) { + this->eos = true; + return 0; } - env.push_back(0); + return (size_t)r; +#endif + } - std::u16string workDir = {}; + size_t Write(const uint8_t *buff, size_t sz) { +// TODO: Implement for WIN32 +#if defined(_WIN32) + if (this->strm == NULL || !writing) + return 0; + DWORD dataW = (DWORD)sz; + if (!WriteFile(this->strm, buff, dataW, &dataW, NULL)) + return 0; - if (!this->workingDirectory.empty()) - UTF16::FromUTF8(workDir, this->workingDirectory); - - SECURITY_ATTRIBUTES attr; - attr.nLength = sizeof(attr); - attr.lpSecurityDescriptor = NULL; - attr.bInheritHandle = true; - p->si.hStdInput = NULL; - p->si.hStdOutput = NULL; + return (size_t)dataW; +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + return 0; +#else + if (this->strm < 0 || !writing) + return 0; + auto r = write(this->strm, buff, sz); + if (r == -1) + return 0; + return (size_t)r; +#endif + } +}; - p->si.hStdError = NULL; +Process::Process() : Process("", {}, true) {} +Process::Process(std::string name, std::vector args, + bool includeThisEnv) + : Process(name, args, std::vector>(), + includeThisEnv) {} +Process::Process(std::string name, std::vector args, + std::vector> env, + bool includeThisEnv) { + this->name = name; + this->args = args; + this->env = env; + this->includeThisEnv = includeThisEnv; + this->hidden.AllocField(); +} +bool Process::HasExited() { + if (this->exited) + return true; + ProcessData *p = this->hidden.GetField(); +#if defined(_WIN32) + if (WaitForSingleObject(p->pi.hProcess, 0) == WAIT_OBJECT_0) { + DWORD ec = 0; + GetExitCodeProcess(p->pi.hProcess, &ec); + this->exitCode = (int)ec; + this->exited = true; + return true; + } +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + return true; +#else + int r; + if (waitpid(this->hidden.GetField()->pid, &r, WNOHANG) != + -1) { + this->exited = true; + this->exitCode = r; + return r; + } +#endif + return false; +} +Process::Process(std::string name, std::vector args, + std::vector env, bool includeThisEnv) + : Process(name, args, std::vector>(), + includeThisEnv) { + this->env.resize(env.size()); + for (size_t i = 0; i < env.size(); i++) { + auto res = Http::HttpUtils::SplitString(env[i], "=", 2); + if (res.size() == 2) { + this->env[i].first = res[0]; + this->env[i].second = res[1]; + } else if (res.size() == 1) { + + this->env[i].first = res[0]; + this->env[i].second = ""; + } + } +} +void Process::CloseStdInNow() { + ProcessData *p = this->hidden.GetField(); + +#if defined(_WIN32) + if (p->stdin_strm != NULL) { + CloseHandle(p->stdin_strm); p->stdin_strm = NULL; - p->stdout_strm = NULL; - p->stderr_strm = NULL; - if (this->redirectStdIn || this->redirectStdOut || this->redirectStdErr) - { - p->si.dwFlags |= STARTF_USESTDHANDLES; - if (!this->redirectStdIn) - { - p->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - } - if (!this->redirectStdOut) - { - - p->si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); - } - if (!this->redirectStdOut) - { - p->si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + } +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + +#else + if (p->stdin_strm > -1) { + close(p->stdin_strm); + p->stdin_strm = -1; + } +#endif +} +Process::~Process() { + + ProcessData *p = this->hidden.GetField(); + +#if defined(_WIN32) + if (!this->exited) + Kill(SIGTERM); + if (p->stdin_strm != NULL) + CloseHandle(p->stdin_strm); + + if (p->stdout_strm != NULL) + CloseHandle(p->stdout_strm); + + if (p->stderr_strm != NULL) + CloseHandle(p->stderr_strm); + + CloseHandle(p->pi.hProcess); + CloseHandle(p->pi.hThread); +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + +#else + if (!this->exited) { + Kill(SIGTERM); + WaitForExit(); + } + + if (p->stdin_strm != -1) + close(p->stdin_strm); + if (p->stdout_strm != -1) + close(p->stdout_strm); + if (p->stderr_strm != -1) + close(p->stderr_strm); +#endif +} + +bool Process::Start() { + ProcessData *p = this->hidden.GetField(); + std::vector> envs; + + if (this->includeThisEnv) + Environment::GetEnvironmentVariables(envs); + + for (auto itemNew : this->env) { + bool has = false; + for (auto &item : envs) { + if (item.first == itemNew.first) { + + item.second = itemNew.second; + has = true; + break; } } + if (!has) + envs.push_back(itemNew); + } - if (this->redirectStdIn) - { - if (!CreatePipe(&p->si.hStdInput, &p->stdin_strm, &attr,0)) return false; +#if defined(_WIN32) - SetHandleInformation(p->stdin_strm, HANDLE_FLAG_INHERIT, 0); + ZeroMemory(&p->si, sizeof(p->si)); + p->si.cb = sizeof(p->si); + ZeroMemory(&p->pi, sizeof(p->pi)); + std::u16string u16_name; + std::u16string u16_args; + std::string args; + escape_windows_args(args, this->args); + UTF16::FromUTF8(u16_name, this->name); + UTF16::FromUTF8(u16_args, args); + std::u16string env = {}; + + for (auto envItem : envs) { + auto partOld = envItem.first + "=" + envItem.second; + std::u16string part = {}; + + UTF16::FromUTF8(part, partOld); + env.append(part); + env.push_back(0); + } + env.push_back(0); + + std::u16string workDir = {}; + + if (!this->workingDirectory.empty()) + UTF16::FromUTF8(workDir, this->workingDirectory); + + SECURITY_ATTRIBUTES attr; + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = NULL; + attr.bInheritHandle = true; + p->si.hStdInput = NULL; + p->si.hStdOutput = NULL; + + p->si.hStdError = NULL; + + p->stdin_strm = NULL; + p->stdout_strm = NULL; + p->stderr_strm = NULL; + if (this->redirectStdIn || this->redirectStdOut || this->redirectStdErr) { + p->si.dwFlags |= STARTF_USESTDHANDLES; + if (!this->redirectStdIn) { + p->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); } + if (!this->redirectStdOut) { - if (this->redirectStdOut) - { - if (!CreatePipe(&p->stdout_strm, &p->si.hStdOutput, &attr, 0)) - { - if (this->redirectStdIn) - { - CloseHandle(p->stdin_strm); - CloseHandle(p->si.hStdInput); - } - return false; - } - - SetHandleInformation(p->stdout_strm, HANDLE_FLAG_INHERIT, 0); + p->si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); } - if (this->redirectStdErr) - { - if (!CreatePipe(&p->stderr_strm, &p->si.hStdError, &attr, 0)) - { - if (this->redirectStdIn) - { - CloseHandle(p->stdin_strm); - CloseHandle(p->si.hStdInput); - } - if (this->redirectStdOut) - { - CloseHandle(p->stdout_strm); - CloseHandle(p->si.hStdOutput); - } - return false; - } - - SetHandleInformation(p->stderr_strm, HANDLE_FLAG_INHERIT, 0); + if (!this->redirectStdOut) { + p->si.hStdError = GetStdHandle(STD_ERROR_HANDLE); } - - if (!CreateProcessW((LPCWSTR)u16_name.c_str(), (LPWSTR)u16_args.data(), NULL, NULL, (this->redirectStdIn || this->redirectStdOut || this->redirectStdErr), CREATE_UNICODE_ENVIRONMENT, (LPVOID)env.c_str(), workDir.empty() ? (LPCWSTR)NULL : (LPCWSTR)workDir.c_str(), &(p->si), &(p->pi))) - { - if (this->redirectStdIn) - { + } + + if (this->redirectStdIn) { + if (!CreatePipe(&p->si.hStdInput, &p->stdin_strm, &attr, 0)) + return false; + + SetHandleInformation(p->stdin_strm, HANDLE_FLAG_INHERIT, 0); + } + + if (this->redirectStdOut) { + if (!CreatePipe(&p->stdout_strm, &p->si.hStdOutput, &attr, 0)) { + if (this->redirectStdIn) { CloseHandle(p->stdin_strm); CloseHandle(p->si.hStdInput); } - if (this->redirectStdOut) - { + return false; + } + + SetHandleInformation(p->stdout_strm, HANDLE_FLAG_INHERIT, 0); + } + if (this->redirectStdErr) { + if (!CreatePipe(&p->stderr_strm, &p->si.hStdError, &attr, 0)) { + if (this->redirectStdIn) { + CloseHandle(p->stdin_strm); + CloseHandle(p->si.hStdInput); + } + if (this->redirectStdOut) { CloseHandle(p->stdout_strm); CloseHandle(p->si.hStdOutput); } - if (this->redirectStdErr) - { - CloseHandle(p->stderr_strm); - CloseHandle(p->si.hStdError); - } return false; } - /* - BOOL + SetHandleInformation(p->stderr_strm, HANDLE_FLAG_INHERIT, 0); + } + + if (!CreateProcessW( + (LPCWSTR)u16_name.c_str(), (LPWSTR)u16_args.data(), NULL, NULL, + (this->redirectStdIn || this->redirectStdOut || + this->redirectStdErr), + CREATE_UNICODE_ENVIRONMENT, (LPVOID)env.c_str(), + workDir.empty() ? (LPCWSTR)NULL : (LPCWSTR)workDir.c_str(), + &(p->si), &(p->pi))) { + if (this->redirectStdIn) { + CloseHandle(p->stdin_strm); + CloseHandle(p->si.hStdInput); + } + if (this->redirectStdOut) { + CloseHandle(p->stdout_strm); + CloseHandle(p->si.hStdOutput); + } + if (this->redirectStdErr) { + CloseHandle(p->stderr_strm); + CloseHandle(p->si.hStdError); + } + return false; + } + + /* + BOOL WINAPI CreateProcessW( - _In_opt_ LPCWSTR lpApplicationName, - _Inout_opt_ LPWSTR lpCommandLine, - _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ BOOL bInheritHandles, - _In_ DWORD dwCreationFlags, - _In_opt_ LPVOID lpEnvironment, - _In_opt_ LPCWSTR lpCurrentDirectory, - _In_ LPSTARTUPINFOW lpStartupInfo, - _Out_ LPPROCESS_INFORMATION lpProcessInformation - ); - */ - return true; - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - - return false; - #else +_In_opt_ LPCWSTR lpApplicationName, +_Inout_opt_ LPWSTR lpCommandLine, +_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, +_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, +_In_ BOOL bInheritHandles, +_In_ DWORD dwCreationFlags, +_In_opt_ LPVOID lpEnvironment, +_In_opt_ LPCWSTR lpCurrentDirectory, +_In_ LPSTARTUPINFOW lpStartupInfo, +_Out_ LPPROCESS_INFORMATION lpProcessInformation +); +*/ + return true; +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - int strm_stdin[2]; - int strm_stdout[2]; - int strm_stderr[2]; + return false; +#else - if(this->redirectStdIn) - { - if(pipe(strm_stdin) == -1) return false; - p->stdin_strm = strm_stdin[1]; - } - if(this->redirectStdOut) - { - if(pipe(strm_stdout) == -1) - { - if(this->redirectStdIn) - { - close(strm_stdin[0]); - close(strm_stdin[1]); - } - return false; - } + int strm_stdin[2]; + int strm_stdout[2]; + int strm_stderr[2]; - p->stdout_strm = strm_stdout[0]; - } - if(this->redirectStdErr) - { - if(pipe(strm_stderr) == -1) - { - if(this->redirectStdIn) - { - close(strm_stdin[0]); - close(strm_stdin[1]); - } - if(this->redirectStdOut) - { - close(strm_stdout[0]); - close(strm_stdout[1]); - } - return false; - } - - p->stderr_strm = strm_stderr[0]; - } - - auto pid=fork(); - if(pid == -1) { - if(this->redirectStdIn) - { + if (this->redirectStdIn) { + if (pipe(strm_stdin) == -1) + return false; + p->stdin_strm = strm_stdin[1]; + } + if (this->redirectStdOut) { + if (pipe(strm_stdout) == -1) { + if (this->redirectStdIn) { close(strm_stdin[0]); close(strm_stdin[1]); } - - if(this->redirectStdOut) - { - close(strm_stdout[0]); - close(strm_stdout[1]); - } - - if(this->redirectStdErr) - { - close(strm_stderr[0]); - close(strm_stderr[1]); - } return false; } - if(pid == 0) - { - std::vector env2; - env2.resize(envs.size()); - - for(size_t i = 0; i < envs.size(); i++) - { - env2[i] = envs[i].first + "=" + envs[i].second; - } - - char** argv = new char*[args.size()+1]; - argv[args.size()]=NULL; - char** envp = new char*[env2.size()+1]; - envp[env2.size()]=NULL; - - for(size_t i = 0; i < args.size();i++) - { - argv[i] = (char*)args[i].c_str(); - } - for(size_t i = 0; i < env2.size();i++) - { - envp[i] = (char*)env2[i].c_str(); - } - if(this->redirectStdIn) - { - dup2(strm_stdin[0],0); + p->stdout_strm = strm_stdout[0]; + } + if (this->redirectStdErr) { + if (pipe(strm_stderr) == -1) { + if (this->redirectStdIn) { close(strm_stdin[0]); close(strm_stdin[1]); } - - if(this->redirectStdOut) - { - dup2(strm_stdout[1],1); + if (this->redirectStdOut) { close(strm_stdout[0]); close(strm_stdout[1]); } - - if(this->redirectStdErr) - { - dup2(strm_stderr[1],2); - close(strm_stderr[0]); - close(strm_stderr[1]); - } - if(!this->workingDirectory.empty()) - Tesses::Framework::Filesystem::VFSPath::SetAbsoluteCurrentDirectory(this->workingDirectory); - execve(this->name.c_str(),argv,envp); - exit(1); + return false; } - p->pid = pid; - if(this->redirectStdIn) - close(strm_stdin[0]); - if(this->redirectStdOut) - close(strm_stdout[1]); - if(this->redirectStdErr) - close(strm_stderr[1]); - return true; - #endif + + p->stderr_strm = strm_stderr[0]; } - + auto pid = fork(); + if (pid == -1) { + if (this->redirectStdIn) { + close(strm_stdin[0]); + close(strm_stdin[1]); + } - void Process::Kill(int signal) - { - #if defined(_WIN32) - if (signal != SIGKILL && signal != SIGTERM) std::cout << "WARN: We terminated the process" << std::endl; + if (this->redirectStdOut) { + close(strm_stdout[0]); + close(strm_stdout[1]); + } - if (signal == SIGTERM) - { - auto win = this->hidden.GetField(); - if (EnumWindows(ProcessData::KillGraceFully, (LPARAM)(void*)win)) - { - if (WaitForSingleObject(win->pi.hProcess, 60000) != WAIT_OBJECT_0) - { - PostThreadMessage(win->pi.dwThreadId, WM_QUIT, 0, 0); - } - } - else { + if (this->redirectStdErr) { + close(strm_stderr[0]); + close(strm_stderr[1]); + } + return false; + } + if (pid == 0) { + std::vector env2; + env2.resize(envs.size()); + + for (size_t i = 0; i < envs.size(); i++) { + env2[i] = envs[i].first + "=" + envs[i].second; + } + + char **argv = new char *[args.size() + 1]; + argv[args.size()] = NULL; + char **envp = new char *[env2.size() + 1]; + envp[env2.size()] = NULL; + + for (size_t i = 0; i < args.size(); i++) { + argv[i] = (char *)args[i].c_str(); + } + for (size_t i = 0; i < env2.size(); i++) { + envp[i] = (char *)env2[i].c_str(); + } + if (this->redirectStdIn) { + dup2(strm_stdin[0], 0); + close(strm_stdin[0]); + close(strm_stdin[1]); + } + + if (this->redirectStdOut) { + dup2(strm_stdout[1], 1); + close(strm_stdout[0]); + close(strm_stdout[1]); + } + + if (this->redirectStdErr) { + dup2(strm_stderr[1], 2); + close(strm_stderr[0]); + close(strm_stderr[1]); + } + if (!this->workingDirectory.empty()) + Tesses::Framework::Filesystem::VFSPath::SetAbsoluteCurrentDirectory( + this->workingDirectory); + execve(this->name.c_str(), argv, envp); + exit(1); + } + p->pid = pid; + if (this->redirectStdIn) + close(strm_stdin[0]); + if (this->redirectStdOut) + close(strm_stdout[1]); + if (this->redirectStdErr) + close(strm_stderr[1]); + return true; +#endif +} + +void Process::Kill(int signal) { +#if defined(_WIN32) + if (signal != SIGKILL && signal != SIGTERM) + std::cout << "WARN: We terminated the process" << std::endl; + + if (signal == SIGTERM) { + auto win = this->hidden.GetField(); + if (EnumWindows(ProcessData::KillGraceFully, (LPARAM)(void *)win)) { + if (WaitForSingleObject(win->pi.hProcess, 60000) != WAIT_OBJECT_0) { PostThreadMessage(win->pi.dwThreadId, WM_QUIT, 0, 0); } + } else { + PostThreadMessage(win->pi.dwThreadId, WM_QUIT, 0, 0); } - else - TerminateProcess(this->hidden.GetField()->pi.hProcess,-1); - - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - - #else - kill(this->hidden.GetField()->pid,signal); - #endif - } + } else + TerminateProcess(this->hidden.GetField()->pi.hProcess, + -1); - int Process::WaitForExit() - { - if (this->exited) return this->exitCode; - #if defined(_WIN32) - auto p = this->hidden.GetField(); - WaitForSingleObject(p->pi.hProcess, INFINITE); - DWORD ret=0; - GetExitCodeProcess(p->pi.hThread, &ret); - this->exitCode = (int)ret; - this->exited = true; - return (int)ret; - - #elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - - return -1; - #else - int r; - if (waitpid(this->hidden.GetField()->pid, &r, 0) != -1) - { - this->exited = true; - this->exitCode = r; - return r; - } - return -1; - #endif - } - - std::shared_ptr Process::GetStdinStream() - { - if (this->exited) return nullptr; - #if defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - - return nullptr; - #else - return std::make_shared(this->hidden.GetField()->stdin_strm,true); - #endif - } - std::shared_ptr Process::GetStdoutStream() - { - - if (this->exited) return nullptr; - #if defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - return nullptr; - #else - return std::make_shared(this->hidden.GetField()->stdout_strm,false); - #endif - } - std::shared_ptr Process::GetStderrStream() - { - - if (this->exited) return nullptr; - #if defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || !defined(TESSESFRAMEWORK_ENABLE_PROCESS) - return nullptr; - #else - return std::make_shared(this->hidden.GetField()->stderr_strm,false); - #endif - } +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) +#else + kill(this->hidden.GetField()->pid, signal); +#endif } + +int Process::WaitForExit() { + if (this->exited) + return this->exitCode; +#if defined(_WIN32) + auto p = this->hidden.GetField(); + WaitForSingleObject(p->pi.hProcess, INFINITE); + DWORD ret = 0; + GetExitCodeProcess(p->pi.hThread, &ret); + this->exitCode = (int)ret; + this->exited = true; + return (int)ret; + +#elif defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + + return -1; +#else + int r; + if (waitpid(this->hidden.GetField()->pid, &r, 0) != -1) { + this->exited = true; + this->exitCode = r; + return r; + } + return -1; +#endif +} + +std::shared_ptr Process::GetStdinStream() { + if (this->exited) + return nullptr; +#if defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + + return nullptr; +#else + return std::make_shared( + this->hidden.GetField()->stdin_strm, true); +#endif +} +std::shared_ptr Process::GetStdoutStream() { + + if (this->exited) + return nullptr; +#if defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + return nullptr; +#else + return std::make_shared( + this->hidden.GetField()->stdout_strm, false); +#endif +} +std::shared_ptr Process::GetStderrStream() { + + if (this->exited) + return nullptr; +#if defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__) || \ + !defined(TESSESFRAMEWORK_ENABLE_PROCESS) + return nullptr; +#else + return std::make_shared( + this->hidden.GetField()->stderr_strm, false); +#endif +} + +} // namespace Tesses::Framework::Platform diff --git a/src/Random.cpp b/src/Random.cpp index b117e77..1946334 100644 --- a/src/Random.cpp +++ b/src/Random.cpp @@ -1,31 +1,34 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Random.hpp" namespace Tesses::Framework { - Random::Random() : Random((uint64_t)time(NULL)) - { +Random::Random() : Random((uint64_t)time(NULL)) {} +Random::Random(uint64_t seed) : num(seed) {} +uint32_t Random::Next(uint32_t max) { return (uint32_t)Next(0, (int32_t)max); } +int32_t Random::Next(int32_t min, int32_t max) { + uint32_t number = (uint32_t)(Next() >> 31); + int32_t range = max - min; - } - Random::Random(uint64_t seed) : num(seed) - { - - } - uint32_t Random::Next(uint32_t max) - { - return (uint32_t)Next(0,(int32_t)max); - } - int32_t Random::Next(int32_t min, int32_t max) - { - uint32_t number = (uint32_t)(Next() >> 31); - int32_t range = max-min; - - return (uint32_t)((((double)number / (double)0xFFFFFFFF) * (double)range)+min); - } - uint64_t Random::Next() - { - return num = 6364136223846793005 * num + 1; - } - uint8_t Random::NextByte() - { - return (uint8_t)Next(0,256); - } -} \ No newline at end of file + return (uint32_t)((((double)number / (double)0xFFFFFFFF) * (double)range) + + min); +} +uint64_t Random::Next() { return num = 6364136223846793005 * num + 1; } +uint8_t Random::NextByte() { return (uint8_t)Next(0, 256); } +} // namespace Tesses::Framework \ No newline at end of file diff --git a/src/Serialization/Bencode.cpp b/src/Serialization/Bencode.cpp index c8129bb..7cd1695 100644 --- a/src/Serialization/Bencode.cpp +++ b/src/Serialization/Bencode.cpp @@ -1,253 +1,232 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Serialization/Bencode.hpp" namespace Tesses::Framework::Serialization::Bencode { - BeToken BeDictionary::GetValue(BeString key) const - { - for(auto item : this->tokens) - if(item.first == key) return item.second; - return BeUndefined(); - } - void BeDictionary::SetValue(BeString key, BeToken value) - { - if(std::holds_alternative(value)) - { - for(auto idx = this->tokens.begin(); idx != this->tokens.end(); idx++) - { - if(idx->first == key) { - this->tokens.erase(idx); - break; - } - } - } - else - { - for(auto& item : this->tokens) - { - if(item.first == key) - { - item.second = value; - return; - } - } - this->tokens.emplace_back(key,value); - } - } - - BeString::BeString() - { - - } - BeString::BeString(const std::string& text) - { - this->data.insert(this->data.end(),text.cbegin(),text.cend()); - } - BeString::BeString(const char* text) - { - size_t len = strlen(text); - this->data.insert(this->data.end(),text,text+len); - } - BeString::BeString(const std::vector& data) : data(data) - { - - } - - BeString::operator std::string() const - { - return std::string(data.cbegin(),data.cend()); - } - - - bool operator==(const BeString& lStr, const BeString& rStr) - { - return lStr.data == rStr.data; - } - bool operator==(const BeString& lStr, const std::string& rStr) - { - if(lStr.data.size() != rStr.size()) return false; - return std::equal(lStr.data.cbegin(),lStr.data.cend(),rStr.cbegin()); - } - bool operator==(const std::string& lStr, const BeString& rStr) - { - if(lStr.size() != rStr.data.size()) return false; - return std::equal(lStr.cbegin(),lStr.cend(),rStr.data.cbegin()); - } - bool operator==(const BeString& lStr, const char* rStr) - { - size_t len = strlen(rStr); - if(lStr.data.size() != len) return false; - return std::equal(lStr.data.cbegin(),lStr.data.cend(),rStr); - } - bool operator==(const char* lStr, const BeString& rStr) - { - size_t len = strlen(lStr); - if(rStr.data.size() != len) return false; - - return std::equal(lStr,lStr+len,rStr.data.cbegin()); - } - - bool operator!=(const BeString& lStr, const BeString& rStr) - { - return !(lStr == rStr); - } - bool operator!=(const BeString& lStr, const std::string& rStr) - { - return !(lStr == rStr); - } - bool operator!=(const std::string& lStr, const BeString& rStr) - { - return !(lStr == rStr); - } - bool operator!=(const BeString& lStr, const char* rStr) - { - return !(lStr == rStr); - } - bool operator!=(const char* lStr, const BeString& rStr) - { - return !(lStr == rStr); - } - - void Bencode::Save(std::shared_ptr strm,const BeToken& value) - { - if(std::holds_alternative(value)) - { - auto& array = std::get(value); - strm->WriteByte((uint8_t)'l'); - for(auto& item : array.tokens) - { - Save(strm,item); - } - strm->WriteByte((uint8_t)'e'); - } - else if(std::holds_alternative(value)) - { - auto& dict = std::get(value); - strm->WriteByte((uint8_t)'d'); - for(auto& item : dict.tokens) - { - Save(strm,item.first); - Save(strm,item.second); - } - strm->WriteByte((uint8_t)'e'); - } - else if(std::holds_alternative(value)) - { - auto& str = std::get(value); - std::string prefix = std::to_string(str.data.size()) + ":"; - strm->WriteBlock((const uint8_t*)prefix.data(),prefix.size()); - strm->WriteBlock(str.data.data(),str.data.size()); - } - else if(std::holds_alternative(value)) - { - int64_t val = std::get(value); - std::string str = "i" + std::to_string(val) + "e"; - strm->WriteBlock((const uint8_t*)str.data(),str.size()); - } - } - BeToken Bencode::Load(std::shared_ptr strm) - { - auto chr = strm->ReadByte(); - switch(chr) - { - case 'i': - { - std::string no; - while(true) { - chr = strm->ReadByte(); - if(chr == -1) throw std::out_of_range("End of file"); - if(chr == 'e') break; - no.push_back((char)chr); - } - return std::stoll(no); - } +BeToken BeDictionary::GetValue(BeString key) const { + for (auto item : this->tokens) + if (item.first == key) + return item.second; + return BeUndefined(); +} +void BeDictionary::SetValue(BeString key, BeToken value) { + if (std::holds_alternative(value)) { + for (auto idx = this->tokens.begin(); idx != this->tokens.end(); + idx++) { + if (idx->first == key) { + this->tokens.erase(idx); break; - case 'd': - { - BeDictionary dict; - while(true) - { - auto key = Load(strm); - if(std::holds_alternative(key)) break; - if(!std::holds_alternative(key)) throw std::runtime_error("Key must be a string"); - auto value = Load(strm); - if(std::holds_alternative(key)) throw std::runtime_error("Incomplete dictionary entry"); - dict.tokens.emplace_back(std::get(key),value); - } - return dict; } - break; - case 'l': - { - BeArray array; - while(true) - { - auto tkn = Load(strm); - if(std::holds_alternative(tkn)) break; - array.tokens.push_back(tkn); - } - return array; + } + } else { + for (auto &item : this->tokens) { + if (item.first == key) { + item.second = value; + return; } - break; - case 'e': - return BeUndefined(); - case -1: + } + this->tokens.emplace_back(key, value); + } +} + +BeString::BeString() {} +BeString::BeString(const std::string &text) { + this->data.insert(this->data.end(), text.cbegin(), text.cend()); +} +BeString::BeString(const char *text) { + size_t len = strlen(text); + this->data.insert(this->data.end(), text, text + len); +} +BeString::BeString(const std::vector &data) : data(data) {} + +BeString::operator std::string() const { + return std::string(data.cbegin(), data.cend()); +} + +bool operator==(const BeString &lStr, const BeString &rStr) { + return lStr.data == rStr.data; +} +bool operator==(const BeString &lStr, const std::string &rStr) { + if (lStr.data.size() != rStr.size()) + return false; + return std::equal(lStr.data.cbegin(), lStr.data.cend(), rStr.cbegin()); +} +bool operator==(const std::string &lStr, const BeString &rStr) { + if (lStr.size() != rStr.data.size()) + return false; + return std::equal(lStr.cbegin(), lStr.cend(), rStr.data.cbegin()); +} +bool operator==(const BeString &lStr, const char *rStr) { + size_t len = strlen(rStr); + if (lStr.data.size() != len) + return false; + return std::equal(lStr.data.cbegin(), lStr.data.cend(), rStr); +} +bool operator==(const char *lStr, const BeString &rStr) { + size_t len = strlen(lStr); + if (rStr.data.size() != len) + return false; + + return std::equal(lStr, lStr + len, rStr.data.cbegin()); +} + +bool operator!=(const BeString &lStr, const BeString &rStr) { + return !(lStr == rStr); +} +bool operator!=(const BeString &lStr, const std::string &rStr) { + return !(lStr == rStr); +} +bool operator!=(const std::string &lStr, const BeString &rStr) { + return !(lStr == rStr); +} +bool operator!=(const BeString &lStr, const char *rStr) { + return !(lStr == rStr); +} +bool operator!=(const char *lStr, const BeString &rStr) { + return !(lStr == rStr); +} + +void Bencode::Save(std::shared_ptr strm, + const BeToken &value) { + if (std::holds_alternative(value)) { + auto &array = std::get(value); + strm->WriteByte((uint8_t)'l'); + for (auto &item : array.tokens) { + Save(strm, item); + } + strm->WriteByte((uint8_t)'e'); + } else if (std::holds_alternative(value)) { + auto &dict = std::get(value); + strm->WriteByte((uint8_t)'d'); + for (auto &item : dict.tokens) { + Save(strm, item.first); + Save(strm, item.second); + } + strm->WriteByte((uint8_t)'e'); + } else if (std::holds_alternative(value)) { + auto &str = std::get(value); + std::string prefix = std::to_string(str.data.size()) + ":"; + strm->WriteBlock((const uint8_t *)prefix.data(), prefix.size()); + strm->WriteBlock(str.data.data(), str.data.size()); + } else if (std::holds_alternative(value)) { + int64_t val = std::get(value); + std::string str = "i" + std::to_string(val) + "e"; + strm->WriteBlock((const uint8_t *)str.data(), str.size()); + } +} +BeToken +Bencode::Load(std::shared_ptr strm) { + auto chr = strm->ReadByte(); + switch (chr) { + case 'i': { + std::string no; + while (true) { + chr = strm->ReadByte(); + if (chr == -1) throw std::out_of_range("End of file"); - default: - { - std::string no({(char)chr}); - while(true) { - chr = strm->ReadByte(); - if(chr == -1) throw std::out_of_range("End of file"); - if(chr == ':') break; - no.push_back((char)chr); - } - auto len = std::stoll(no); - if(len < 0) throw std::out_of_range("Less than zero byte string"); - BeString str; - str.data.resize((size_t)len); - - size_t result = strm->ReadBlock(str.data.data(),str.data.size()); - if(result != str.data.size() || result != (size_t)len) throw std::out_of_range("Didn't read entire string"); - return str; - - - } + if (chr == 'e') break; + no.push_back((char)chr); } + return std::stoll(no); + } break; + case 'd': { + BeDictionary dict; + while (true) { + auto key = Load(strm); + if (std::holds_alternative(key)) + break; + if (!std::holds_alternative(key)) + throw std::runtime_error("Key must be a string"); + auto value = Load(strm); + if (std::holds_alternative(key)) + throw std::runtime_error("Incomplete dictionary entry"); + dict.tokens.emplace_back(std::get(key), value); + } + return dict; + } break; + case 'l': { + BeArray array; + while (true) { + auto tkn = Load(strm); + if (std::holds_alternative(tkn)) + break; + array.tokens.push_back(tkn); + } + return array; + } break; + case 'e': + return BeUndefined(); + case -1: + throw std::out_of_range("End of file"); + default: { + std::string no({(char)chr}); + while (true) { + chr = strm->ReadByte(); + if (chr == -1) + throw std::out_of_range("End of file"); + if (chr == ':') + break; + no.push_back((char)chr); + } + auto len = std::stoll(no); + if (len < 0) + throw std::out_of_range("Less than zero byte string"); + BeString str; + str.data.resize((size_t)len); + + size_t result = strm->ReadBlock(str.data.data(), str.data.size()); + if (result != str.data.size() || result != (size_t)len) + throw std::out_of_range("Didn't read entire string"); + return str; + + } break; } - Json::JToken Bencode::ToJson(const BeToken& tkn) - { - if(std::holds_alternative(tkn)) - { - auto& dict = std::get(tkn); - Json::JObject o; - for(auto& itm : dict.tokens) - { - o.SetValue(itm.first,ToJson(itm.second)); - } - return o; +} +Json::JToken Bencode::ToJson(const BeToken &tkn) { + if (std::holds_alternative(tkn)) { + auto &dict = std::get(tkn); + Json::JObject o; + for (auto &itm : dict.tokens) { + o.SetValue(itm.first, ToJson(itm.second)); } - if(std::holds_alternative(tkn)) - { - auto& array = std::get(tkn); - Json::JArray a; - for(auto& itm : array.tokens) - { - a.Add(ToJson(itm)); - } - return a; - } - if(std::holds_alternative(tkn)) - { - return (std::string)std::get(tkn); - } - if(std::holds_alternative(tkn)) - { - return std::get(tkn); - } - return Json::JUndefined(); + return o; } - void Bencode::Print(std::shared_ptr writer, BeToken tkn) - { - writer->WriteLine(Json::Json::Encode(ToJson(tkn),true)); + if (std::holds_alternative(tkn)) { + auto &array = std::get(tkn); + Json::JArray a; + for (auto &itm : array.tokens) { + a.Add(ToJson(itm)); + } + return a; } -} \ No newline at end of file + if (std::holds_alternative(tkn)) { + return (std::string)std::get(tkn); + } + if (std::holds_alternative(tkn)) { + return std::get(tkn); + } + return Json::JUndefined(); +} +void Bencode::Print( + std::shared_ptr writer, + BeToken tkn) { + writer->WriteLine(Json::Json::Encode(ToJson(tkn), true)); +} +} // namespace Tesses::Framework::Serialization::Bencode \ No newline at end of file diff --git a/src/Serialization/BitConverter.cpp b/src/Serialization/BitConverter.cpp index 37e832c..eb545b1 100644 --- a/src/Serialization/BitConverter.cpp +++ b/src/Serialization/BitConverter.cpp @@ -1,319 +1,284 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Serialization/BitConverter.hpp" -namespace Tesses::Framework::Serialization -{ - double BitConverter::ToDoubleBits(uint64_t v) - { - static_assert(sizeof(double) == sizeof(uint64_t), "double is not the same size as uint64_t"); - double dest=0; - memcpy(&dest,&v, sizeof(uint64_t)); - return dest; - } - uint64_t BitConverter::ToUintBits(double v) - { - //as static_assert is compile time we don't need it here - uint64_t dest = 0; - memcpy(&dest,&v, sizeof(uint64_t)); - return dest; - } - float BitConverter::ToFloatBits(uint32_t v) - { - static_assert(sizeof(float) == sizeof(uint32_t), "float is not the same size as uint32_t"); - float dest=0; - memcpy(&dest,&v, sizeof(uint32_t)); - return dest; - } - uint32_t BitConverter::ToUint32Bits(float v) - { - //as static_assert is compile time we don't need it here - uint32_t dest = 0; - memcpy(&dest,&v, sizeof(uint32_t)); - return dest; - } - double BitConverter::ToDoubleBE(uint8_t& b) - { - return ToDoubleBits(ToUint64BE(b)); - } - float BitConverter::ToFloatBE(uint8_t& b) - { - return ToFloatBits(ToUint32BE(b)); - } - uint64_t BitConverter::ToUint64BE(uint8_t& b) - { - uint8_t* b2 = &b; - uint64_t v = 0; - v |= ((uint64_t)b2[0] << 56); - v |= ((uint64_t)b2[1] << 48); - v |= ((uint64_t)b2[2] << 40); - v |= ((uint64_t)b2[3] << 32); - v |= ((uint64_t)b2[4] << 24); - v |= ((uint64_t)b2[5] << 16); - v |= ((uint64_t)b2[6] << 8); - v |= (uint64_t)b2[7]; - return v; - } - uint32_t BitConverter::ToUint32BE(uint8_t& b) - { - uint8_t* b2 = &b; - uint32_t v = 0; - - v |= ((uint32_t)b2[0] << 24); - v |= ((uint32_t)b2[1] << 16); - v |= ((uint32_t)b2[2] << 8); - v |= (uint32_t)b2[3]; - return v; - } - uint16_t BitConverter::ToUint16BE(uint8_t& b) - { - uint8_t* b2 = &b; - uint16_t v = 0; - - - v |= ((uint16_t)b2[0] << 8); - v |= (uint16_t)b2[1]; - return v; - } - double BitConverter::ToDoubleLE(uint8_t& b) - { - return ToDoubleBits(ToUint64LE(b)); - } - float BitConverter::ToFloatLE(uint8_t& b) - { - return ToFloatBits(ToUint32LE(b)); - } - uint64_t BitConverter::ToUint64LE(uint8_t& b) - { - uint8_t* b2 = &b; - uint64_t v = 0; - v |= (uint64_t)b2[0]; - v |= ((uint64_t)b2[1] << 8); - v |= ((uint64_t)b2[2] << 16); - v |= ((uint64_t)b2[3] << 24); - v |= ((uint64_t)b2[4] << 32); - v |= ((uint64_t)b2[5] << 40); - v |= ((uint64_t)b2[6] << 48); - v |= ((uint64_t)b2[7] << 56); - - return v; - } - uint32_t BitConverter::ToUint32LE(uint8_t& b) - { - uint8_t* b2 = &b; - uint32_t v = 0; - v |= (uint32_t)b2[0]; - v |= ((uint32_t)b2[1] << 8); - v |= ((uint32_t)b2[2] << 16); - v |= ((uint32_t)b2[3] << 24); - - return v; - } - uint16_t BitConverter::ToUint16LE(uint8_t& b) - { - uint8_t* b2 = &b; - uint16_t v = 0; - - v |= (uint16_t)b2[0]; - v |= ((uint16_t)b2[1] << 8); - - return v; - } - void BitConverter::FromDoubleBE(uint8_t& b, double v) - { - FromUint64BE(b,ToUintBits(v)); - } - void BitConverter::FromUint64BE(uint8_t& b, uint64_t v) - { - uint8_t* b2 = &b; - b2[0] = (uint8_t)(v >> 56); - b2[1] = (uint8_t)(v >> 48); - b2[2] = (uint8_t)(v >> 40); - b2[3] = (uint8_t)(v >> 32); - b2[4] = (uint8_t)(v >> 24); - b2[5] = (uint8_t)(v >> 16); - b2[6] = (uint8_t)(v >> 8); - b2[7] = (uint8_t)v; - } - void BitConverter::FromUint32BE(uint8_t& b, uint32_t v) - { - uint8_t* b2 = &b; - - b2[0] = (uint8_t)(v >> 24); - b2[1] = (uint8_t)(v >> 16); - b2[2] = (uint8_t)(v >> 8); - b2[3] = (uint8_t)v; - } - void BitConverter::FromUint16BE(uint8_t& b, uint16_t v) - { - uint8_t* b2 = &b; - - b2[0] = (uint8_t)(v >> 8); - b2[1] = (uint8_t)v; - } +namespace Tesses::Framework::Serialization { +double BitConverter::ToDoubleBits(uint64_t v) { + static_assert(sizeof(double) == sizeof(uint64_t), + "double is not the same size as uint64_t"); + double dest = 0; + memcpy(&dest, &v, sizeof(uint64_t)); + return dest; +} +uint64_t BitConverter::ToUintBits(double v) { + // as static_assert is compile time we don't need it here + uint64_t dest = 0; + memcpy(&dest, &v, sizeof(uint64_t)); + return dest; +} +float BitConverter::ToFloatBits(uint32_t v) { + static_assert(sizeof(float) == sizeof(uint32_t), + "float is not the same size as uint32_t"); + float dest = 0; + memcpy(&dest, &v, sizeof(uint32_t)); + return dest; +} +uint32_t BitConverter::ToUint32Bits(float v) { + // as static_assert is compile time we don't need it here + uint32_t dest = 0; + memcpy(&dest, &v, sizeof(uint32_t)); + return dest; +} +double BitConverter::ToDoubleBE(uint8_t &b) { + return ToDoubleBits(ToUint64BE(b)); +} +float BitConverter::ToFloatBE(uint8_t &b) { return ToFloatBits(ToUint32BE(b)); } +uint64_t BitConverter::ToUint64BE(uint8_t &b) { + uint8_t *b2 = &b; + uint64_t v = 0; + v |= ((uint64_t)b2[0] << 56); + v |= ((uint64_t)b2[1] << 48); + v |= ((uint64_t)b2[2] << 40); + v |= ((uint64_t)b2[3] << 32); + v |= ((uint64_t)b2[4] << 24); + v |= ((uint64_t)b2[5] << 16); + v |= ((uint64_t)b2[6] << 8); + v |= (uint64_t)b2[7]; + return v; +} +uint32_t BitConverter::ToUint32BE(uint8_t &b) { + uint8_t *b2 = &b; + uint32_t v = 0; - void BitConverter::FromDoubleLE(uint8_t& b, double v) - { - FromUint64BE(b,ToUintBits(v)); - } - void BitConverter::FromFloatLE(uint8_t& b, float v) - { - FromUint32LE(b,ToUint32Bits(v)); - } - void BitConverter::FromFloatBE(uint8_t& b, float v) - { - FromUint32BE(b,ToUint32Bits(v)); - } - void BitConverter::FromUint64LE(uint8_t& b, uint64_t v) - { - uint8_t* b2 = &b; - b2[0] = (uint8_t)v; - b2[1] = (uint8_t)(v >> 8); - b2[2] = (uint8_t)(v >> 16); - b2[3] = (uint8_t)(v >> 24); - b2[4] = (uint8_t)(v >> 32); - b2[5] = (uint8_t)(v >> 40); - b2[6] = (uint8_t)(v >> 48); - b2[7] = (uint8_t)(v >> 56); - - } - void BitConverter::FromUint32LE(uint8_t& b, uint32_t v) - { - uint8_t* b2 = &b; - - b2[0] = (uint8_t)v; - b2[1] = (uint8_t)(v >> 8); - b2[2] = (uint8_t)(v >> 16); - b2[3] = (uint8_t)(v >> 24); - - } - void BitConverter::FromUint16LE(uint8_t& b, uint16_t v) - { - uint8_t* b2 = &b; - + v |= ((uint32_t)b2[0] << 24); + v |= ((uint32_t)b2[1] << 16); + v |= ((uint32_t)b2[2] << 8); + v |= (uint32_t)b2[3]; + return v; +} +uint16_t BitConverter::ToUint16BE(uint8_t &b) { + uint8_t *b2 = &b; + uint16_t v = 0; - b2[0] = (uint8_t)v; - b2[1] = (uint8_t)(v >> 8); - } - void BitConverter::FromUuid(uint8_t& b, const Uuid& uuid) - { - uint8_t* b2 = &b; - FromUint32BE(b2[0], uuid.time_low); - FromUint16BE(b2[4], uuid.time_mid); - FromUint16BE(b2[6], uuid.time_hi_and_version); - b2[8] = uuid.clock_seq_hi_and_reserved; - b2[9] = uuid.clock_seq_low; - for(size_t i = 0; i < 6; i++) - b2[i+10] = uuid.node[i]; - - } - + v |= ((uint16_t)b2[0] << 8); + v |= (uint16_t)b2[1]; + return v; +} +double BitConverter::ToDoubleLE(uint8_t &b) { + return ToDoubleBits(ToUint64LE(b)); +} +float BitConverter::ToFloatLE(uint8_t &b) { return ToFloatBits(ToUint32LE(b)); } +uint64_t BitConverter::ToUint64LE(uint8_t &b) { + uint8_t *b2 = &b; + uint64_t v = 0; + v |= (uint64_t)b2[0]; + v |= ((uint64_t)b2[1] << 8); + v |= ((uint64_t)b2[2] << 16); + v |= ((uint64_t)b2[3] << 24); + v |= ((uint64_t)b2[4] << 32); + v |= ((uint64_t)b2[5] << 40); + v |= ((uint64_t)b2[6] << 48); + v |= ((uint64_t)b2[7] << 56); - Uuid BitConverter::ToUuid(uint8_t& b) - { - Uuid uuid; - BitConverter::ToUuid(b,uuid); - return uuid; - } - + return v; +} +uint32_t BitConverter::ToUint32LE(uint8_t &b) { + uint8_t *b2 = &b; + uint32_t v = 0; + v |= (uint32_t)b2[0]; + v |= ((uint32_t)b2[1] << 8); + v |= ((uint32_t)b2[2] << 16); + v |= ((uint32_t)b2[3] << 24); - void BitConverter::ToUuid(uint8_t& b, Uuid& uuid) - { - uint8_t* b2 = &b; - uuid.time_low = ToUint32BE(b2[0]); + return v; +} +uint16_t BitConverter::ToUint16LE(uint8_t &b) { + uint8_t *b2 = &b; + uint16_t v = 0; - uuid.time_mid = ToUint16BE(b2[4]); + v |= (uint16_t)b2[0]; + v |= ((uint16_t)b2[1] << 8); - uuid.time_hi_and_version = ToUint16BE(b2[6]); - - uuid.clock_seq_hi_and_reserved = b2[8]; - uuid.clock_seq_low = b2[9]; - for(size_t i = 0; i < 6; i++) - uuid.node[i]= b2[i+10]; + return v; +} +void BitConverter::FromDoubleBE(uint8_t &b, double v) { + FromUint64BE(b, ToUintBits(v)); +} +void BitConverter::FromUint64BE(uint8_t &b, uint64_t v) { + uint8_t *b2 = &b; + b2[0] = (uint8_t)(v >> 56); + b2[1] = (uint8_t)(v >> 48); + b2[2] = (uint8_t)(v >> 40); + b2[3] = (uint8_t)(v >> 32); + b2[4] = (uint8_t)(v >> 24); + b2[5] = (uint8_t)(v >> 16); + b2[6] = (uint8_t)(v >> 8); + b2[7] = (uint8_t)v; +} +void BitConverter::FromUint32BE(uint8_t &b, uint32_t v) { + uint8_t *b2 = &b; - } + b2[0] = (uint8_t)(v >> 24); + b2[1] = (uint8_t)(v >> 16); + b2[2] = (uint8_t)(v >> 8); + b2[3] = (uint8_t)v; +} +void BitConverter::FromUint16BE(uint8_t &b, uint16_t v) { + uint8_t *b2 = &b; - int64_t BitConverter::ToSint64BE(uint8_t& b) - { - uint64_t src = ToUint64BE(b); - int64_t dest = 0; - memcpy(&dest,&src,sizeof(uint64_t)); - return dest; - } - int64_t BitConverter::ToSint64LE(uint8_t& b) - { - uint64_t src = ToUint64LE(b); - int64_t dest = 0; - memcpy(&dest,&src,sizeof(uint64_t)); - return dest; - } + b2[0] = (uint8_t)(v >> 8); + b2[1] = (uint8_t)v; +} +void BitConverter::FromDoubleLE(uint8_t &b, double v) { + FromUint64BE(b, ToUintBits(v)); +} +void BitConverter::FromFloatLE(uint8_t &b, float v) { + FromUint32LE(b, ToUint32Bits(v)); +} +void BitConverter::FromFloatBE(uint8_t &b, float v) { + FromUint32BE(b, ToUint32Bits(v)); +} +void BitConverter::FromUint64LE(uint8_t &b, uint64_t v) { + uint8_t *b2 = &b; + b2[0] = (uint8_t)v; + b2[1] = (uint8_t)(v >> 8); + b2[2] = (uint8_t)(v >> 16); + b2[3] = (uint8_t)(v >> 24); + b2[4] = (uint8_t)(v >> 32); + b2[5] = (uint8_t)(v >> 40); + b2[6] = (uint8_t)(v >> 48); + b2[7] = (uint8_t)(v >> 56); +} +void BitConverter::FromUint32LE(uint8_t &b, uint32_t v) { + uint8_t *b2 = &b; - int32_t BitConverter::ToSint32BE(uint8_t& b) - { - uint32_t src = ToUint32BE(b); - int32_t dest = 0; - memcpy(&dest,&src,sizeof(uint32_t)); - return dest; - } - int32_t BitConverter::ToSint32LE(uint8_t& b) - { - uint32_t src = ToUint32LE(b); - int32_t dest = 0; - memcpy(&dest,&src,sizeof(uint32_t)); - return dest; - } + b2[0] = (uint8_t)v; + b2[1] = (uint8_t)(v >> 8); + b2[2] = (uint8_t)(v >> 16); + b2[3] = (uint8_t)(v >> 24); +} +void BitConverter::FromUint16LE(uint8_t &b, uint16_t v) { + uint8_t *b2 = &b; + b2[0] = (uint8_t)v; + b2[1] = (uint8_t)(v >> 8); +} +void BitConverter::FromUuid(uint8_t &b, const Uuid &uuid) { + uint8_t *b2 = &b; + FromUint32BE(b2[0], uuid.time_low); + FromUint16BE(b2[4], uuid.time_mid); + FromUint16BE(b2[6], uuid.time_hi_and_version); + b2[8] = uuid.clock_seq_hi_and_reserved; + b2[9] = uuid.clock_seq_low; + for (size_t i = 0; i < 6; i++) + b2[i + 10] = uuid.node[i]; +} - int16_t BitConverter::ToSint16BE(uint8_t& b) - { - uint16_t src = ToUint16BE(b); - int16_t dest = 0; - memcpy(&dest,&src,sizeof(uint16_t)); - return dest; - } - int16_t BitConverter::ToSint16LE(uint8_t& b) - { - uint16_t src = ToUint16LE(b); - int16_t dest = 0; - memcpy(&dest,&src,sizeof(uint16_t)); - return dest; - } - +Uuid BitConverter::ToUuid(uint8_t &b) { + Uuid uuid; + BitConverter::ToUuid(b, uuid); + return uuid; +} - void BitConverter::FromSint64BE(uint8_t& b, int64_t v) - { - uint64_t dest = 0; - memcpy(&dest,&v,sizeof(uint64_t)); - FromUint64BE(b, dest); - } - void BitConverter::FromSint32BE(uint8_t& b, int32_t v) - { - uint32_t dest = 0; - memcpy(&dest,&v,sizeof(uint32_t)); - FromUint32BE(b, dest); - } - void BitConverter::FromSint16BE(uint8_t& b, int16_t v) - { - uint16_t dest = 0; - memcpy(&dest,&v,sizeof(uint16_t)); - FromUint16BE(b, dest); - } - - void BitConverter::FromSint64LE(uint8_t& b, int64_t v) - { - uint64_t dest = 0; - memcpy(&dest,&v,sizeof(uint64_t)); - FromUint64LE(b, dest); - } - void BitConverter::FromSint32LE(uint8_t& b, int32_t v) - { - uint32_t dest = 0; - memcpy(&dest,&v,sizeof(uint32_t)); - FromUint32LE(b, dest); - } - void BitConverter::FromSint16LE(uint8_t& b, int16_t v) - { - uint16_t dest = 0; - memcpy(&dest,&v,sizeof(uint16_t)); - FromUint16LE(b, dest); - } -} \ No newline at end of file +void BitConverter::ToUuid(uint8_t &b, Uuid &uuid) { + uint8_t *b2 = &b; + uuid.time_low = ToUint32BE(b2[0]); + + uuid.time_mid = ToUint16BE(b2[4]); + + uuid.time_hi_and_version = ToUint16BE(b2[6]); + + uuid.clock_seq_hi_and_reserved = b2[8]; + uuid.clock_seq_low = b2[9]; + for (size_t i = 0; i < 6; i++) + uuid.node[i] = b2[i + 10]; +} + +int64_t BitConverter::ToSint64BE(uint8_t &b) { + uint64_t src = ToUint64BE(b); + int64_t dest = 0; + memcpy(&dest, &src, sizeof(uint64_t)); + return dest; +} +int64_t BitConverter::ToSint64LE(uint8_t &b) { + uint64_t src = ToUint64LE(b); + int64_t dest = 0; + memcpy(&dest, &src, sizeof(uint64_t)); + return dest; +} + +int32_t BitConverter::ToSint32BE(uint8_t &b) { + uint32_t src = ToUint32BE(b); + int32_t dest = 0; + memcpy(&dest, &src, sizeof(uint32_t)); + return dest; +} +int32_t BitConverter::ToSint32LE(uint8_t &b) { + uint32_t src = ToUint32LE(b); + int32_t dest = 0; + memcpy(&dest, &src, sizeof(uint32_t)); + return dest; +} + +int16_t BitConverter::ToSint16BE(uint8_t &b) { + uint16_t src = ToUint16BE(b); + int16_t dest = 0; + memcpy(&dest, &src, sizeof(uint16_t)); + return dest; +} +int16_t BitConverter::ToSint16LE(uint8_t &b) { + uint16_t src = ToUint16LE(b); + int16_t dest = 0; + memcpy(&dest, &src, sizeof(uint16_t)); + return dest; +} + +void BitConverter::FromSint64BE(uint8_t &b, int64_t v) { + uint64_t dest = 0; + memcpy(&dest, &v, sizeof(uint64_t)); + FromUint64BE(b, dest); +} +void BitConverter::FromSint32BE(uint8_t &b, int32_t v) { + uint32_t dest = 0; + memcpy(&dest, &v, sizeof(uint32_t)); + FromUint32BE(b, dest); +} +void BitConverter::FromSint16BE(uint8_t &b, int16_t v) { + uint16_t dest = 0; + memcpy(&dest, &v, sizeof(uint16_t)); + FromUint16BE(b, dest); +} + +void BitConverter::FromSint64LE(uint8_t &b, int64_t v) { + uint64_t dest = 0; + memcpy(&dest, &v, sizeof(uint64_t)); + FromUint64LE(b, dest); +} +void BitConverter::FromSint32LE(uint8_t &b, int32_t v) { + uint32_t dest = 0; + memcpy(&dest, &v, sizeof(uint32_t)); + FromUint32LE(b, dest); +} +void BitConverter::FromSint16LE(uint8_t &b, int16_t v) { + uint16_t dest = 0; + memcpy(&dest, &v, sizeof(uint16_t)); + FromUint16LE(b, dest); +} +} // namespace Tesses::Framework::Serialization \ No newline at end of file diff --git a/src/Serialization/Json.cpp b/src/Serialization/Json.cpp index da75822..9020aa6 100644 --- a/src/Serialization/Json.cpp +++ b/src/Serialization/Json.cpp @@ -1,901 +1,746 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Serialization/Json.hpp" #include "TessesFramework/Http/HttpUtils.hpp" #include #include "TessesFramework/Common.hpp" using namespace Tesses::Framework::Http; -namespace Tesses::Framework::Serialization::Json -{ - void JObject::Remove(std::string key) - { - map.erase(key); - } - void JObject::SetValue(std::string key, JToken item) - { - if(!std::holds_alternative(item)) - map[key] = item; - else - Remove(key); - } - JToken JObject::GetValue(std::string key) - { - if(map.count(key)>0) - return map[key]; - else - return JUndefined(); - } - std::map& JObject::GetMap() - { - return map; - } - std::map::iterator JObject::begin() - { - return map.begin(); - } - std::map::iterator JObject::end() - { - return map.end(); - } - JArray::JArray() - { - - } - JArray::JArray(std::initializer_list tokens) - { - this->items = tokens; - } - std::vector& JArray::GetVector() - { - return items; - } - void JArray::Add(JToken item) - { - if(!std::holds_alternative(item)) - items.push_back(item); - } - void JArray::RemoveAt(size_t index) - { - if(index < items.size()) - items.erase(items.begin()+index); - } - size_t JArray::Count() - { - return items.size(); - } - void JArray::SetAt(size_t index, JToken item) - { - if(index < items.size() && !std::holds_alternative(item)) - items[index] = item; - } - JToken JArray::GetAt(size_t index) - { - if(index < items.size()) - return items.at(index); +namespace Tesses::Framework::Serialization::Json { +void JObject::Remove(std::string key) { map.erase(key); } +void JObject::SetValue(std::string key, JToken item) { + if (!std::holds_alternative(item)) + map[key] = item; + else + Remove(key); +} +JToken JObject::GetValue(std::string key) { + if (map.count(key) > 0) + return map[key]; + else return JUndefined(); - } - void JArray::Clear() - { - items.clear(); - } +} +std::map &JObject::GetMap() { return map; } +std::map::iterator JObject::begin() { return map.begin(); } +std::map::iterator JObject::end() { return map.end(); } +JArray::JArray() {} +JArray::JArray(std::initializer_list tokens) { this->items = tokens; } +std::vector &JArray::GetVector() { return items; } +void JArray::Add(JToken item) { + if (!std::holds_alternative(item)) + items.push_back(item); +} +void JArray::RemoveAt(size_t index) { + if (index < items.size()) + items.erase(items.begin() + index); +} +size_t JArray::Count() { return items.size(); } +void JArray::SetAt(size_t index, JToken item) { + if (index < items.size() && !std::holds_alternative(item)) + items[index] = item; +} +JToken JArray::GetAt(size_t index) { + if (index < items.size()) + return items.at(index); + return JUndefined(); +} +void JArray::Clear() { items.clear(); } - std::vector::iterator JArray::begin() - { - return items.begin(); - } - std::vector::iterator JArray::end() - { - return items.end(); - } - JOItem::JOItem() - { +std::vector::iterator JArray::begin() { return items.begin(); } +std::vector::iterator JArray::end() { return items.end(); } +JOItem::JOItem() {} +JOItem::JOItem(std::string key, JToken value) { + this->key = key; + this->value = value; +} - } - JOItem::JOItem(std::string key, JToken value) - { - this->key = key; - this->value = value; - } +JObject::JObject() {} +JObject::JObject(std::initializer_list items) { + for (auto item : items) + this->map[item.key] = item.value; +} - JObject::JObject() - { - - } - JObject::JObject(std::initializer_list items) - { - for(auto item : items) - this->map[item.key] = item.value; - } - - std::string Json::tab(std::string txt) - { - std::string newStr="\t"; - for(auto item : txt) - { - if(item == '\n') - { - newStr.append("\n\t"); - } - else - { - newStr.push_back(item); - } +std::string Json::tab(std::string txt) { + std::string newStr = "\t"; + for (auto item : txt) { + if (item == '\n') { + newStr.append("\n\t"); + } else { + newStr.push_back(item); } - return newStr; } - - JToken Json::Decode(std::string str) - { - std::vector> tokens; + return newStr; +} - std::string buff={}; +JToken Json::Decode(std::string str) { + std::vector> tokens; - std::function flush = [&buff,&tokens]()->void { - if(!buff.empty()) - { - tokens.push_back(std::pair(buff,false)); - buff={}; - } - }; + std::string buff = {}; - for(size_t i = 0; i < str.size(); i++) - { - switch(str[i]) - { - case '\"': - { - flush(); + std::function flush = [&buff, &tokens]() -> void { + if (!buff.empty()) { + tokens.push_back(std::pair(buff, false)); + buff = {}; + } + }; + + for (size_t i = 0; i < str.size(); i++) { + switch (str[i]) { + case '\"': { + flush(); + i++; + std::string str2 = {}; + for (; i < str.size() && str[i] != '\"'; i++) { + if (str[i] == '\\') { i++; - std::string str2={}; - for(; i < str.size() && str[i] != '\"'; i++) - { - if(str[i] == '\\') - { + if (i < str.size()) { + if (str[i] == 'n') { + str2.push_back('\n'); + } else if (str[i] == 'r') { + str2.push_back('\r'); + } else if (str[i] == 't') { + str2.push_back('\t'); + } else if (str[i] == 'f') { + str2.push_back('\f'); + } else if (str[i] == 'b') { + str2.push_back('\b'); + } else if (str[i] == 'u') { i++; - if(i < str.size()) - { - if(str[i] == 'n') - { - str2.push_back('\n'); + // we need to get four of these + uint16_t v = 0; + if (i + 4 <= str.size()) { + for (size_t i2 = 0; i2 < 4; i2++, i++) { + v |= HttpUtils::HexToNibble(str[i]) + << ((3 - i2) * 4); } - else if(str[i] == 'r') - { - str2.push_back('\r'); - } - else if(str[i] == 't') - { - str2.push_back('\t'); - } - else if(str[i] == 'f') - { - str2.push_back('\f'); - } - else if(str[i] == 'b') - { - str2.push_back('\b'); - } - else if(str[i] == 'u') - { - i++; - //we need to get four of these - uint16_t v = 0; - if(i + 4 <= str.size()) - { - for(size_t i2 = 0; i2 < 4; i2++,i++) - { - v |= HttpUtils::HexToNibble(str[i]) << ((3-i2) * 4); + + uint32_t v2 = v; + + if ((v & 0xFC00) == 0xD800) { + + v2 = (v & 0x03FF) << 10; + if (i + 6 <= str.size() && + str.substr(i, 2) == "\\u") { + i += 2; + v = 0; + + for (size_t i2 = 0; i2 < 4; i2++, i++) { + v |= HttpUtils::HexToNibble(str[i]) + << ((3 - i2) * 4); } - - - uint32_t v2 = v; - - if((v & 0xFC00) == 0xD800) - { - - v2 = (v & 0x03FF) << 10; - if(i + 6 <= str.size() && str.substr(i,2) == "\\u") - { - i+=2; - v=0; - - for(size_t i2 = 0; i2 < 4; i2++, i++) - { - v |= HttpUtils::HexToNibble(str[i]) << ((3-i2) * 4); - } - if((v & 0xFC00) != 0xDC00) - { - throw std::runtime_error("Not a lower utf-16 surrogate pair."); - } - - - v2 |= (v & 0x03FF); - - v2 += 0x10000; - } - else - { - - throw std::runtime_error("Could not read lower utf-16 surrogate pair."); - } - if(v2 <= 0x7F) - { - str2.push_back((char)v2); - } - else if(v2 >= 0x80 && v2 <= 0x7FF) - { - uint8_t high = 0b11000000 | ((v2 >> 6) & 0b00011111); - uint8_t low = 0b10000000 | (v2 & 0b00111111); - str2.push_back((char)high); - str2.push_back((char)low); - } - else if(v2 >= 0x800 && v2 <= 0xFFFF) - { - uint8_t highest = 0b11100000 | ((v2 >> 12) & 0b00001111); - uint8_t high = 0b10000000 | ((v2 >> 6) & 0b00111111); - uint8_t low = 0b10000000 | (v2 & 0b00111111); - str2.push_back((char)highest); - str2.push_back((char)high); - str2.push_back((char)low); - } - else if(v2 >= 0x010000 && v2 <= 0x10FFFF) - { - uint8_t highest = 0b11110000 | ((v2 >> 18) & 0b00000111); - uint8_t high = 0b10000000 | ((v2 >> 12) & 0b00111111); - uint8_t low = 0b10000000 | ((v2 >> 6) & 0b00111111); - uint8_t lowest = 0b10000000 | (v2 & 0b00111111); - str2.push_back((char)highest); - str2.push_back((char)high); - str2.push_back((char)low); - str2.push_back((char)lowest); - } + if ((v & 0xFC00) != 0xDC00) { + throw std::runtime_error( + "Not a lower utf-16 surrogate " + "pair."); } - + v2 |= (v & 0x03FF); + + v2 += 0x10000; + } else { + + throw std::runtime_error( + "Could not read lower utf-16 " + "surrogate pair."); + } + if (v2 <= 0x7F) { + str2.push_back((char)v2); + } else if (v2 >= 0x80 && v2 <= 0x7FF) { + uint8_t high = 0b11000000 | + ((v2 >> 6) & 0b00011111); + uint8_t low = + 0b10000000 | (v2 & 0b00111111); + str2.push_back((char)high); + str2.push_back((char)low); + } else if (v2 >= 0x800 && v2 <= 0xFFFF) { + uint8_t highest = + 0b11100000 | + ((v2 >> 12) & 0b00001111); + uint8_t high = 0b10000000 | + ((v2 >> 6) & 0b00111111); + uint8_t low = + 0b10000000 | (v2 & 0b00111111); + str2.push_back((char)highest); + str2.push_back((char)high); + str2.push_back((char)low); + } else if (v2 >= 0x010000 && + v2 <= 0x10FFFF) { + uint8_t highest = + 0b11110000 | + ((v2 >> 18) & 0b00000111); + uint8_t high = + 0b10000000 | + ((v2 >> 12) & 0b00111111); + uint8_t low = 0b10000000 | + ((v2 >> 6) & 0b00111111); + uint8_t lowest = + 0b10000000 | (v2 & 0b00111111); + str2.push_back((char)highest); + str2.push_back((char)high); + str2.push_back((char)low); + str2.push_back((char)lowest); } } - else - { - str2.push_back(str[i]); - } } - else break; + } else { + str2.push_back(str[i]); } - else - { - str2.push_back(str[i]); - } - } - tokens.push_back(std::pair(str2,true)); + } else + break; + } else { + str2.push_back(str[i]); } - break; - case ',': - case '[': - case ']': - case '(': - case ')': - case '{': - case '}': - case ':': - flush(); - tokens.push_back(std::pair(str.substr(i,1),false)); - break; - case ' ': - case '\n': - case '\r': - case '\t': - flush(); - break; - default: - buff.push_back(str[i]); - break; - } + tokens.push_back(std::pair(str2, true)); + } break; + case ',': + case '[': + case ']': + case '(': + case ')': + case '{': + case '}': + case ':': + flush(); + tokens.push_back( + std::pair(str.substr(i, 1), false)); + break; + case ' ': + case '\n': + case '\r': + case '\t': + flush(); + break; + default: + buff.push_back(str[i]); + break; } + } - flush(); - if(tokens.size() == 0) return JUndefined(); + flush(); + if (tokens.size() == 0) + return JUndefined(); - size_t tokenIndex=0; + size_t tokenIndex = 0; - std::function()> pop_token=[&tokens,&tokenIndex]()->std::pair { - if(tokenIndex >= tokens.size()) throw std::runtime_error("Json tokens out of bounds."); - return tokens[tokenIndex++]; - }; + std::function()> pop_token = + [&tokens, &tokenIndex]() -> std::pair { + if (tokenIndex >= tokens.size()) + throw std::runtime_error("Json tokens out of bounds."); + return tokens[tokenIndex++]; + }; - std::function read_token; - read_token= [&pop_token,&read_token,&tokenIndex,&tokens]()->JToken{ - auto tkn = pop_token(); - if(tkn.second) - { - return tkn.first; - } - else - { - if(tkn.first == "{") - { - - JObject obj; - while(true) - { - begin_dict: + std::function read_token; + read_token = [&pop_token, &read_token, &tokenIndex, &tokens]() -> JToken { + auto tkn = pop_token(); + if (tkn.second) { + return tkn.first; + } else { + if (tkn.first == "{") { + + JObject obj; + while (true) { + begin_dict: + tkn = pop_token(); + if (tkn.second) { + std::string key = tkn.first; tkn = pop_token(); - if(tkn.second) - { - std::string key = tkn.first; - tkn = pop_token(); - if(tkn.second || tkn.first != ":") - { - throw std::runtime_error("Invalid JSON key value seperator."); - } - - auto value = read_token(); - obj.SetValue(key,value); + if (tkn.second || tkn.first != ":") { + throw std::runtime_error( + "Invalid JSON key value seperator."); } - else if(tkn.first == ",") - { - goto begin_dict; - } - else if(tkn.first == "}") - { + + auto value = read_token(); + obj.SetValue(key, value); + } else if (tkn.first == ",") { + goto begin_dict; + } else if (tkn.first == "}") { + break; + } else { + + throw std::runtime_error("Invalid JSON object."); + } + } + return obj; + } else if (tkn.first == "[") { + JArray arr; + while (tokenIndex < tokens.size()) { + auto tkn = tokens[tokenIndex]; + if (!tkn.second) { + if (tkn.first == ",") + tokenIndex++; + if (tkn.first == "]") { + tokenIndex++; break; - } - else - { - - throw std::runtime_error("Invalid JSON object."); } } - return obj; - } - else if(tkn.first == "[") - { - JArray arr; - while(tokenIndex < tokens.size()) - { - auto tkn = tokens[tokenIndex]; - if(!tkn.second) - { - if(tkn.first == ",") tokenIndex++; - if(tkn.first == "]") {tokenIndex++; break;} - } - arr.Add(read_token()); - } - return arr; + arr.Add(read_token()); } - else if(tkn.first == "true") - { - return true; + return arr; + } else if (tkn.first == "true") { + return true; + } else if (tkn.first == "false") { + return false; + } else if (tkn.first == "null") { + return nullptr; + } else { + // number + if (tkn.first.find(".") != std::string::npos) { + return std::stod(tkn.first); + } else { + return (int64_t)std::stoll(tkn.first); } - else if(tkn.first == "false") - { - return false; - } - else if(tkn.first == "null") - { - return nullptr; - } - else { - //number - if(tkn.first.find(".") != std::string::npos) - { - return std::stod(tkn.first); - } - else - { - return (int64_t)std::stoll(tkn.first); - } - } - } - }; + } + }; - return read_token(); + return read_token(); +} +std::string Json::Encode(JToken tkn, bool indent) { + int64_t i64; + double f64; + std::string str; + bool b; + JArray ls; + JObject dict; + if (std::holds_alternative(tkn)) + return "null"; + if (TryGetJToken(tkn, b)) + return b ? "true" : "false"; + if (TryGetJToken(tkn, f64)) { + auto str = std::to_string(f64); + + if (str.find(".") != std::string::npos) { + size_t i; + for (i = str.size() - 1; str[i] != '.'; i--) { + if (str[i] != '0') { + i++; + break; + } + } + return str.substr(0, i); + } + return str; } - std::string Json::Encode(JToken tkn, bool indent) - { - int64_t i64; - double f64; - std::string str; - bool b; - JArray ls; - JObject dict; - if(std::holds_alternative(tkn)) return "null"; - if(TryGetJToken(tkn,b)) return b ? "true" : "false"; - if(TryGetJToken(tkn,f64)) - { - auto str = std::to_string(f64); - - if(str.find(".") != std::string::npos) - { - size_t i; - for(i = str.size()-1; str[i] != '.'; i--) - { - if(str[i] != '0') - { - i++; - break; - } - } - return str.substr(0,i); - } - return str; - } - if(TryGetJToken(tkn,i64)) return std::to_string(i64); - if(TryGetJToken(tkn,str)) - { - std::string str2 = "\""; - for(auto c : str) - { - if(c == '\"') - { - str2.append("\\\""); - } - else if(c == '\n') - { - str2.append("\\n"); - } - else if(c == '\t') - { - str2.append("\\t"); - } - else if(c == '\r') - { - str2.append("\\r"); - } - else if(c == '/') - { - str2.append("\\/"); - } - else if(c == '\\') - { - str2.append("\\\\"); - } - else if(c == '\f') - { - str2.append("\\f"); - } - else if(c == '\b') - { - str2.append("\\b"); - } - else if((c >= 0 && c < 32) || c == 127 ) - { - str2.append("\\u00"); - uint8_t c2 = (uint8_t)c; + if (TryGetJToken(tkn, i64)) + return std::to_string(i64); + if (TryGetJToken(tkn, str)) { + std::string str2 = "\""; + for (auto c : str) { + if (c == '\"') { + str2.append("\\\""); + } else if (c == '\n') { + str2.append("\\n"); + } else if (c == '\t') { + str2.append("\\t"); + } else if (c == '\r') { + str2.append("\\r"); + } else if (c == '/') { + str2.append("\\/"); + } else if (c == '\\') { + str2.append("\\\\"); + } else if (c == '\f') { + str2.append("\\f"); + } else if (c == '\b') { + str2.append("\\b"); + } else if ((c >= 0 && c < 32) || c == 127) { + str2.append("\\u00"); + uint8_t c2 = (uint8_t)c; - str2.push_back(HttpUtils::NibbleToHex((c2 >> 4) & 0x0F)); - str2.push_back(HttpUtils::NibbleToHex(c2 & 0x0F)); - } - else - { - str2.push_back(c); - } + str2.push_back(HttpUtils::NibbleToHex((c2 >> 4) & 0x0F)); + str2.push_back(HttpUtils::NibbleToHex(c2 & 0x0F)); + } else { + str2.push_back(c); } - str2.push_back('\"'); - return str2; } - if(TryGetJToken(tkn,dict)) - { - - std::string str="{"; - bool first=true; - for(auto item : dict) - { - if(!first) { - str.push_back(','); - } - if(indent) { - str.append("\n"); - str.append(tab(Encode(item.first,true) + ": " + Encode(item.second,true))); - } - else - { - str.append(Encode(item.first,false)+":"+Encode(item.second,false)); - } - first=false; - } - if(indent) str.append("\n"); - str.push_back('}'); - return str; - } - if(TryGetJToken(tkn,ls)) - { - std::string str="["; - bool first=true; - for(auto item : ls) - { - - if(!first) { - str.push_back(','); - } - if(indent) { - str.append("\n"); - str.append(tab(Encode(item,true))); - } - else - { - str.append(Encode(item,false)); - } - first=false; - } - if(indent) str.append("\n"); - str.push_back(']'); - return str; - } - return ""; + str2.push_back('\"'); + return str2; } + if (TryGetJToken(tkn, dict)) { - JArray Json::DocDecode(std::string str) - { - JArray data; - std::string builder = ""; - bool inSplit = false; - size_t i=0; - auto flush_builder = [&]()->void{ - if(builder.empty()) return; - data.Add(builder); - builder.clear(); - }; - auto consume_white_space = [&]()->void { - for(; i < str.size(); i++) - { - switch(str[i]) - { - case '\t': - case '\r': - case '\n': - case ' ': - break; - default: - return; - } + std::string str = "{"; + bool first = true; + for (auto item : dict) { + if (!first) { + str.push_back(','); } - }; - auto is_keyword_of_and_consume = [&](std::string text)->bool { - if(i + text.size() <= str.size()) { - if(str.substr(i,text.size()) == text) - { - i+=text.size(); - return true; - } + if (indent) { + str.append("\n"); + str.append(tab(Encode(item.first, true) + ": " + + Encode(item.second, true))); + } else { + str.append(Encode(item.first, false) + ":" + + Encode(item.second, false)); } - return false; - }; - std::function json_decode_token; - json_decode_token = [&]() -> JToken { - if(i >= str.size()) return JUndefined(); - if(is_keyword_of_and_consume("true")) return true; - if(is_keyword_of_and_consume("false")) return false; - if(is_keyword_of_and_consume("null")) return nullptr; - - + first = false; + } + if (indent) + str.append("\n"); + str.push_back('}'); + return str; + } + if (TryGetJToken(tkn, ls)) { + std::string str = "["; + bool first = true; + for (auto item : ls) { - if(str[i] == '[') - { - i++; - JArray array; - //array - while(i < str.size()) - { - consume_white_space(); - if(i < str.size() && str[i] ==',') - { - i++; - } - - consume_white_space(); - if(i < str.size() && str[i] ==']') - { - i++; - return array; - } - auto val = json_decode_token(); - array.Add(val); - } - return array; + if (!first) { + str.push_back(','); } - if(str[i] == '{') - { - //dictionary - i++; - JObject dict; - while(i < str.size()) - { - consume_white_space(); - if(i < str.size() && str[i] ==',') - { - i++; - } - - consume_white_space(); - if(i < str.size() && str[i] =='}') - { - i++; - return dict; - } - auto key = json_decode_token(); - consume_white_space(); - if(i < str.size() && str[i] ==':') - { - i++; - } - consume_white_space(); - auto value = json_decode_token(); - std::string keyStr; - if(TryGetJToken(key,keyStr) && !std::holds_alternative(value)) - { - dict.SetValue(keyStr,value); - } - } - return dict; + if (indent) { + str.append("\n"); + str.append(tab(Encode(item, true))); + } else { + str.append(Encode(item, false)); } - if(str[i] == '\"') - { - i++; - std::string str2 = ""; - //string - for(; i < str.size() && str[i] != '\"'; i++) - { - if(str[i] == '\\') - { - i++; - if(i < str.size()) - { - if(str[i] == 'n') - { - str2.push_back('\n'); - } - else if(str[i] == 'r') - { - str2.push_back('\r'); - } - else if(str[i] == 't') - { - str2.push_back('\t'); - } - else if(str[i] == 'f') - { - str2.push_back('\f'); - } - else if(str[i] == 'b') - { - str2.push_back('\b'); - } - else if(str[i] == 'u') - { - i++; - //we need to get four of these - uint16_t v = 0; - if(i + 4 <= str.size()) - { - for(size_t i2 = 0; i2 < 4; i2++,i++) - { - v |= HttpUtils::HexToNibble(str[i]) << ((3-i2) * 4); - } - + first = false; + } + if (indent) + str.append("\n"); + str.push_back(']'); + return str; + } + return ""; +} - uint32_t v2 = v; - - if((v & 0xFC00) == 0xD800) - { - - v2 = (v & 0x03FF) << 10; - if(i + 6 <= str.size() && str.substr(i,2) == "\\u") - { - i+=2; - v=0; - - for(size_t i2 = 0; i2 < 4; i2++, i++) - { - v |= HttpUtils::HexToNibble(str[i]) << ((3-i2) * 4); - } - if((v & 0xFC00) != 0xDC00) - { - throw std::runtime_error("Not a lower utf-16 surrogate pair."); - } - - - v2 |= (v & 0x03FF); - - v2 += 0x10000; - } - else - { - - throw std::runtime_error("Could not read lower utf-16 surrogate pair."); - } - if(v2 <= 0x7F) - { - str2.push_back((char)v2); - } - else if(v2 >= 0x80 && v2 <= 0x7FF) - { - uint8_t high = 0b11000000 | ((v2 >> 6) & 0b00011111); - uint8_t low = 0b10000000 | (v2 & 0b00111111); - str2.push_back((char)high); - str2.push_back((char)low); - } - else if(v2 >= 0x800 && v2 <= 0xFFFF) - { - uint8_t highest = 0b11100000 | ((v2 >> 12) & 0b00001111); - uint8_t high = 0b10000000 | ((v2 >> 6) & 0b00111111); - uint8_t low = 0b10000000 | (v2 & 0b00111111); - str2.push_back((char)highest); - str2.push_back((char)high); - str2.push_back((char)low); - } - else if(v2 >= 0x010000 && v2 <= 0x10FFFF) - { - uint8_t highest = 0b11110000 | ((v2 >> 18) & 0b00000111); - uint8_t high = 0b10000000 | ((v2 >> 12) & 0b00111111); - uint8_t low = 0b10000000 | ((v2 >> 6) & 0b00111111); - uint8_t lowest = 0b10000000 | (v2 & 0b00111111); - str2.push_back((char)highest); - str2.push_back((char)high); - str2.push_back((char)low); - str2.push_back((char)lowest); - } - } - - - } - } - else - { - str2.push_back(str[i]); - } - } - else break; - } - else - { - str2.push_back(str[i]); - } - } - i++; - return str2; +JArray Json::DocDecode(std::string str) { + JArray data; + std::string builder = ""; + bool inSplit = false; + size_t i = 0; + auto flush_builder = [&]() -> void { + if (builder.empty()) + return; + data.Add(builder); + builder.clear(); + }; + auto consume_white_space = [&]() -> void { + for (; i < str.size(); i++) { + switch (str[i]) { + case '\t': + case '\r': + case '\n': + case ' ': + break; + default: + return; } - if((str[i] >= '0' && str[i] <= '9' ) || str[i] == '-') - { - //number - std::string noStr = ""; - bool hasPt = false; - bool hasExponent=false; - bool hasNeg = false; - for(;i= '0' && str[i] <= '9') - { - noStr += str[i]; - } - else break; - } - if(noStr.find(".") != std::string::npos) - { - return std::stod(noStr); - } - else - { - return (int64_t)std::stoll(noStr); - } + } + }; + auto is_keyword_of_and_consume = [&](std::string text) -> bool { + if (i + text.size() <= str.size()) { + if (str.substr(i, text.size()) == text) { + i += text.size(); + return true; } + } + return false; + }; + std::function json_decode_token; + json_decode_token = [&]() -> JToken { + if (i >= str.size()) return JUndefined(); - }; + if (is_keyword_of_and_consume("true")) + return true; + if (is_keyword_of_and_consume("false")) + return false; + if (is_keyword_of_and_consume("null")) + return nullptr; - - for(; i < str.size(); i++) - { - if(inSplit) - { + if (str[i] == '[') { + i++; + JArray array; + // array + while (i < str.size()) { consume_white_space(); - std::string key = ""; - for(; i < str.size() && str[i] != ' '; i++) - { - key += str[i]; + if (i < str.size() && str[i] == ',') { + i++; + } + + consume_white_space(); + if (i < str.size() && str[i] == ']') { + i++; + return array; + } + auto val = json_decode_token(); + array.Add(val); + } + return array; + } + if (str[i] == '{') { + // dictionary + i++; + JObject dict; + while (i < str.size()) { + consume_white_space(); + if (i < str.size() && str[i] == ',') { + i++; + } + + consume_white_space(); + if (i < str.size() && str[i] == '}') { + i++; + return dict; + } + auto key = json_decode_token(); + consume_white_space(); + if (i < str.size() && str[i] == ':') { + i++; } consume_white_space(); auto value = json_decode_token(); - if(!std::holds_alternative(value)) - { - data.Add(JObject({ - JOItem("key",key), - JOItem("value", value) - })); + std::string keyStr; + if (TryGetJToken(key, keyStr) && + !std::holds_alternative(value)) { + dict.SetValue(keyStr, value); } - inSplit=false; - i--; } - else { - if(str[i] == '@') - { - if(i+1= 0x80 && v2 <= 0x7FF) { + uint8_t high = 0b11000000 | + ((v2 >> 6) & 0b00011111); + uint8_t low = + 0b10000000 | (v2 & 0b00111111); + str2.push_back((char)high); + str2.push_back((char)low); + } else if (v2 >= 0x800 && v2 <= 0xFFFF) { + uint8_t highest = + 0b11100000 | + ((v2 >> 12) & 0b00001111); + uint8_t high = 0b10000000 | + ((v2 >> 6) & 0b00111111); + uint8_t low = + 0b10000000 | (v2 & 0b00111111); + str2.push_back((char)highest); + str2.push_back((char)high); + str2.push_back((char)low); + } else if (v2 >= 0x010000 && + v2 <= 0x10FFFF) { + uint8_t highest = + 0b11110000 | + ((v2 >> 18) & 0b00000111); + uint8_t high = + 0b10000000 | + ((v2 >> 12) & 0b00111111); + uint8_t low = 0b10000000 | + ((v2 >> 6) & 0b00111111); + uint8_t lowest = + 0b10000000 | (v2 & 0b00111111); + str2.push_back((char)highest); + str2.push_back((char)high); + str2.push_back((char)low); + str2.push_back((char)lowest); + } + } + } + } else { + str2.push_back(str[i]); + } + } else + break; + } else { + str2.push_back(str[i]); + } + } + i++; + return str2; + } + if ((str[i] >= '0' && str[i] <= '9') || str[i] == '-') { + // number + std::string noStr = ""; + bool hasPt = false; + bool hasExponent = false; + bool hasNeg = false; + for (; i < str.size(); i++) { + if (str[i] == 'E' || str[i] == 'e') { + if (hasExponent) + break; + hasExponent = true; + hasNeg = false; + noStr += str[i]; + } else if (str[i] == '.') { + if (hasPt) + break; + hasPt = true; + noStr += str[i]; + } else if (str[i] == '-') { + if (hasNeg) + break; + hasNeg = true; + noStr += str[i]; + } else if (str[i] == '+') { + if (!hasExponent) + break; + if (hasNeg) + break; + hasNeg = true; + noStr += str[i]; + } else if (str[i] >= '0' && str[i] <= '9') { + noStr += str[i]; + } else + break; + } + if (noStr.find(".") != std::string::npos) { + return std::stod(noStr); + } else { + return (int64_t)std::stoll(noStr); + } + } + return JUndefined(); + }; + + for (; i < str.size(); i++) { + if (inSplit) { + consume_white_space(); + std::string key = ""; + for (; i < str.size() && str[i] != ' '; i++) { + key += str[i]; + } + consume_white_space(); + auto value = json_decode_token(); + if (!std::holds_alternative(value)) { + data.Add(JObject({JOItem("key", key), JOItem("value", value)})); + } + inSplit = false; + i--; + } else { + if (str[i] == '@') { + if (i + 1 < str.size() && str[i + 1] == '@') { + i++; + builder.push_back('@'); continue; } - builder.push_back(str[i]); + inSplit = true; + flush_builder(); + continue; } + builder.push_back(str[i]); } - flush_builder(); - return data; - } - std::string Json::DocEncode(JArray array,bool indent) - { - /* - The beautiful text - @name "John" - @list ["A","B"] - @number 42 - @dict {"a": 5, "b": true} - */ - - /* - [ - "\tThe beautiful text\n\t", - { - "key": "name", - "value": ["A","B"] - }, - "\n\t", - { - "key": "number", - "value": 42 - }, - "\n\t", - { - "key": "dict", - "value": {"a": 5, "b": true} - }, - "\n" - ] - */ - std::string text = ""; - - for(auto& item : array.GetVector()) - { - std::string str; - JObject obj; - if(TryGetJToken(item,str)) text += HttpUtils::Replace(str,"@","@@"); - if(TryGetJToken(item,obj)) - { - auto value = obj.GetValue("value"); - if(obj.TryGetValueAsType("key", str) && !std::holds_alternative(value)) - { - text += "@" + str + " " + Json::Encode(value,indent); - } - } - } - - return text; } + flush_builder(); + return data; } +std::string Json::DocEncode(JArray array, bool indent) { + /* + The beautiful text + @name "John" + @list ["A","B"] + @number 42 + @dict {"a": 5, "b": true} + */ + + /* + [ + "\tThe beautiful text\n\t", + { + "key": "name", + "value": ["A","B"] + }, + "\n\t", + { + "key": "number", + "value": 42 + }, + "\n\t", + { + "key": "dict", + "value": {"a": 5, "b": true} + }, + "\n" + ] + */ + std::string text = ""; + + for (auto &item : array.GetVector()) { + std::string str; + JObject obj; + if (TryGetJToken(item, str)) + text += HttpUtils::Replace(str, "@", "@@"); + if (TryGetJToken(item, obj)) { + auto value = obj.GetValue("value"); + if (obj.TryGetValueAsType("key", str) && + !std::holds_alternative(value)) { + text += "@" + str + " " + Json::Encode(value, indent); + } + } + } + + return text; +} +} // namespace Tesses::Framework::Serialization::Json diff --git a/src/Serialization/SQLite.cpp b/src/Serialization/SQLite.cpp index 8126859..43bc95c 100644 --- a/src/Serialization/SQLite.cpp +++ b/src/Serialization/SQLite.cpp @@ -1,82 +1,102 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Serialization/SQLite.hpp" #if defined(TESSESFRAMEWORK_ENABLE_SQLITE) #include "sqlite/sqlite3-mod.h" #endif namespace Tesses::Framework::Serialization { - int SQLiteDatabase::collector(void* user, int count,char** vals, char** keys) - { - auto list = static_cast>>>*>(user); - std::vector>> d; - for(int i = 0; i < count; i++) - { - std::string key = keys[i] == nullptr ? "" : keys[i]; - std::optional value = vals[i] == nullptr ? std::nullopt : (std::optional)vals[i]; - - d.push_back(std::pair>(key,value)); - } - list->push_back(d); - return 0; - } - - SQLiteDatabase::SQLiteDatabase(Tesses::Framework::Filesystem::VFSPath path) - { - #if defined(TESSESFRAMEWORK_ENABLE_SQLITE) - auto name = path.ToString(); - sqlite3* sqlite; - int rc =sqlite3_open(name.c_str(),&sqlite); - if(rc) - { - std::string error = sqlite3_errmsg(sqlite); - throw std::runtime_error(error); - } - this->data = static_cast(sqlite); - #endif - } - std::string SQLiteDatabase::Escape(std::string text) - { - std::string myStr = "\'"; - for(auto c : text) - { - if(c == '\'') myStr += "\'\'"; - else - myStr += c; - } - myStr += '\''; - return myStr; - } - bool SQLiteDatabase::IsEnabled() - { - #if defined(TESSESFRAMEWORK_ENABLE_SQLITE) - return true; - #endif - return false; - } - std::vector>>> SQLiteDatabase::Exec(std::string statement) - { - std::vector>>> items; - - Exec(statement,items); - - return items; - } - void SQLiteDatabase::Exec(std::string statement,std::vector>>>& items) - { +int SQLiteDatabase::collector(void *user, int count, char **vals, char **keys) { + auto list = static_cast>>> *>( + user); + std::vector>> d; + for (int i = 0; i < count; i++) { + std::string key = keys[i] == nullptr ? "" : keys[i]; + std::optional value = + vals[i] == nullptr ? std::nullopt + : (std::optional)vals[i]; - #if defined(TESSESFRAMEWORK_ENABLE_SQLITE) - char* err; - int res = sqlite3_exec(static_cast(this->data),statement.c_str(),SQLiteDatabase::collector,&items,&err); - if(res != SQLITE_OK) - { - std::string errstr = err == nullptr ? "" : err; - sqlite3_free(err); - throw std::runtime_error(errstr); - } - #endif - } - SQLiteDatabase::~SQLiteDatabase() - { - #if defined(TESSESFRAMEWORK_ENABLE_SQLITE) - sqlite3_close(static_cast(this->data)); - #endif + d.push_back( + std::pair>(key, value)); } + list->push_back(d); + return 0; } + +SQLiteDatabase::SQLiteDatabase(Tesses::Framework::Filesystem::VFSPath path) { +#if defined(TESSESFRAMEWORK_ENABLE_SQLITE) + auto name = path.ToString(); + sqlite3 *sqlite; + int rc = sqlite3_open(name.c_str(), &sqlite); + if (rc) { + std::string error = sqlite3_errmsg(sqlite); + throw std::runtime_error(error); + } + this->data = static_cast(sqlite); +#endif +} +std::string SQLiteDatabase::Escape(std::string text) { + std::string myStr = "\'"; + for (auto c : text) { + if (c == '\'') + myStr += "\'\'"; + else + myStr += c; + } + myStr += '\''; + return myStr; +} +bool SQLiteDatabase::IsEnabled() { +#if defined(TESSESFRAMEWORK_ENABLE_SQLITE) + return true; +#endif + return false; +} +std::vector>>> +SQLiteDatabase::Exec(std::string statement) { + std::vector>>> + items; + + Exec(statement, items); + + return items; +} +void SQLiteDatabase::Exec( + std::string statement, + std::vector>>> + &items) { + +#if defined(TESSESFRAMEWORK_ENABLE_SQLITE) + char *err; + int res = + sqlite3_exec(static_cast(this->data), statement.c_str(), + SQLiteDatabase::collector, &items, &err); + if (res != SQLITE_OK) { + std::string errstr = err == nullptr ? "" : err; + sqlite3_free(err); + throw std::runtime_error(errstr); + } +#endif +} +SQLiteDatabase::~SQLiteDatabase() { +#if defined(TESSESFRAMEWORK_ENABLE_SQLITE) + sqlite3_close(static_cast(this->data)); +#endif +} +} // namespace Tesses::Framework::Serialization diff --git a/src/Streams/BufferedStream.cpp b/src/Streams/BufferedStream.cpp index fe7e135..dd7ac1a 100644 --- a/src/Streams/BufferedStream.cpp +++ b/src/Streams/BufferedStream.cpp @@ -1,66 +1,69 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Streams/BufferedStream.hpp" namespace Tesses::Framework::Streams { - BufferedStream::BufferedStream(std::shared_ptr strm, size_t bufferSize) - { - this->strm = strm; - this->bufferSize = bufferSize; - this->buffer = new uint8_t[bufferSize]; - this->read = 0; +BufferedStream::BufferedStream(std::shared_ptr strm, + size_t bufferSize) { + this->strm = strm; + this->bufferSize = bufferSize; + this->buffer = new uint8_t[bufferSize]; + this->read = 0; + this->offset = 0; +} + +bool BufferedStream::EndOfStream() { + if (this->offset < this->read) + return false; + return this->strm->EndOfStream(); +} +bool BufferedStream::CanRead() { + if (this->offset < this->read) + return true; + return this->strm->CanRead(); +} +bool BufferedStream::CanWrite() { return this->strm->CanWrite(); } +size_t BufferedStream::Read(uint8_t *buff, size_t sz) { + if (this->offset < this->read) { + sz = std::min(sz, this->read - this->offset); + + memcpy(buff, this->buffer + this->offset, sz); + this->offset += sz; + return sz; + } + if (sz < this->bufferSize) { + this->read = this->strm->Read(this->buffer, this->bufferSize); this->offset = 0; - } - - bool BufferedStream::EndOfStream() - { - if(this->offset < this->read) return false; - return this->strm->EndOfStream(); - } - bool BufferedStream::CanRead() - { - if(this->offset < this->read) return true; - return this->strm->CanRead(); - } - bool BufferedStream::CanWrite() - { - return this->strm->CanWrite(); - } - size_t BufferedStream::Read(uint8_t* buff, size_t sz) - { - if(this->offset < this->read) - { - sz = std::min(sz,this->read-this->offset); - - memcpy(buff, this->buffer+this->offset, sz); - this->offset+=sz; - return sz; - } - if(sz < this->bufferSize) - { - this->read = this->strm->Read(this->buffer, this->bufferSize); - this->offset=0; - - sz = std::min(sz,this->read-this->offset); - - memcpy(buff, this->buffer+this->offset, sz); - this->offset+=sz; - return sz; - } - else - { - return this->strm->Read(buff, sz); - } - } - size_t BufferedStream::Write(const uint8_t* buff, size_t sz) - { - return this->strm->Write(buff,sz); - } - BufferedStream::~BufferedStream() - { - delete buffer; - } + sz = std::min(sz, this->read - this->offset); - void BufferedStream::Close() - { - this->strm->Close(); + memcpy(buff, this->buffer + this->offset, sz); + this->offset += sz; + return sz; + } else { + return this->strm->Read(buff, sz); } -} \ No newline at end of file +} +size_t BufferedStream::Write(const uint8_t *buff, size_t sz) { + return this->strm->Write(buff, sz); +} + +BufferedStream::~BufferedStream() { delete buffer; } + +void BufferedStream::Close() { this->strm->Close(); } +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/src/Streams/ByteReader.cpp b/src/Streams/ByteReader.cpp index 94447c8..15e350b 100644 --- a/src/Streams/ByteReader.cpp +++ b/src/Streams/ByteReader.cpp @@ -1,169 +1,165 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Streams/ByteReader.hpp" #include "TessesFramework/Serialization/BitConverter.hpp" -namespace Tesses::Framework::Streams -{ - std::shared_ptr ByteReader::GetStream() - { - return this->strm; - } - ByteReader::ByteReader(std::shared_ptr strm) - { - this->strm = strm; - } - - uint8_t ByteReader::ReadU8() - { - auto r = this->strm->ReadByte(); - if(r < 0) throw std::runtime_error("End of file"); - return (uint8_t)r; - } - uint16_t ByteReader::ReadU16BE() - { - uint8_t data[2]; - if(this->strm->ReadBlock(data,2) != 2) throw std::runtime_error("End of file"); - uint16_t n = 0; - n |= (uint16_t)data[0] << 8; - n |= (uint16_t)data[1]; +namespace Tesses::Framework::Streams { +std::shared_ptr ByteReader::GetStream() { return this->strm; } +ByteReader::ByteReader(std::shared_ptr strm) { this->strm = strm; } - return n; - } - uint16_t ByteReader::ReadU16LE() - { - uint8_t data[2]; - if(this->strm->ReadBlock(data,2) != 2) throw std::runtime_error("End of file"); - uint16_t n = 0; - n |= (uint16_t)data[0]; - n |= (uint16_t)data[1] << 8; - +uint8_t ByteReader::ReadU8() { + auto r = this->strm->ReadByte(); + if (r < 0) + throw std::runtime_error("End of file"); + return (uint8_t)r; +} +uint16_t ByteReader::ReadU16BE() { + uint8_t data[2]; + if (this->strm->ReadBlock(data, 2) != 2) + throw std::runtime_error("End of file"); + uint16_t n = 0; + n |= (uint16_t)data[0] << 8; + n |= (uint16_t)data[1]; - return n; - } - uint32_t ByteReader::ReadU32BE() - { + return n; +} +uint16_t ByteReader::ReadU16LE() { + uint8_t data[2]; + if (this->strm->ReadBlock(data, 2) != 2) + throw std::runtime_error("End of file"); + uint16_t n = 0; + n |= (uint16_t)data[0]; + n |= (uint16_t)data[1] << 8; - uint8_t data[4]; - if(this->strm->ReadBlock(data,4) != 4) throw std::runtime_error("End of file"); - uint32_t n = 0; - n |= (uint32_t)data[0] << 24; - n |= (uint32_t)data[1] << 16; - n |= (uint32_t)data[2] << 8; - n |= (uint32_t)data[3]; + return n; +} +uint32_t ByteReader::ReadU32BE() { - return n; - } - uint32_t ByteReader::ReadU32LE() - { - uint8_t data[4]; - if(this->strm->ReadBlock(data,4) != 4) throw std::runtime_error("End of file"); - uint32_t n = 0; - n |= (uint32_t)data[0]; - n |= (uint32_t)data[1] << 8; - n |= (uint32_t)data[2] << 16; - n |= (uint32_t)data[3] << 24; - return n; - } - uint64_t ByteReader::ReadU64BE() - { - uint8_t data[8]; - if(this->strm->ReadBlock(data,8) != 8) throw std::runtime_error("End of file"); - uint64_t n = 0; - n |= (uint64_t)data[0] << 56; - n |= (uint64_t)data[1] << 48; - n |= (uint64_t)data[2] << 40; - n |= (uint64_t)data[3] << 32; - n |= (uint64_t)data[4] << 24; - n |= (uint64_t)data[5] << 16; - n |= (uint64_t)data[6] << 8; - n |= (uint64_t)data[7]; + uint8_t data[4]; + if (this->strm->ReadBlock(data, 4) != 4) + throw std::runtime_error("End of file"); + uint32_t n = 0; + n |= (uint32_t)data[0] << 24; + n |= (uint32_t)data[1] << 16; + n |= (uint32_t)data[2] << 8; + n |= (uint32_t)data[3]; - return n; - } - uint64_t ByteReader::ReadU64LE() - { - uint8_t data[8]; - if(this->strm->ReadBlock(data,8) != 8) throw std::runtime_error("End of file"); - uint64_t n = 0; - n |= (uint64_t)data[0]; - n |= (uint64_t)data[1] << 8; - n |= (uint64_t)data[2] << 16; - n |= (uint64_t)data[3] << 24; - n |= (uint64_t)data[4] << 32; - n |= (uint64_t)data[5] << 40; - n |= (uint64_t)data[6] << 48; - n |= (uint64_t)data[7] << 56; - + return n; +} +uint32_t ByteReader::ReadU32LE() { + uint8_t data[4]; + if (this->strm->ReadBlock(data, 4) != 4) + throw std::runtime_error("End of file"); + uint32_t n = 0; + n |= (uint32_t)data[0]; + n |= (uint32_t)data[1] << 8; + n |= (uint32_t)data[2] << 16; + n |= (uint32_t)data[3] << 24; + return n; +} +uint64_t ByteReader::ReadU64BE() { + uint8_t data[8]; + if (this->strm->ReadBlock(data, 8) != 8) + throw std::runtime_error("End of file"); + uint64_t n = 0; + n |= (uint64_t)data[0] << 56; + n |= (uint64_t)data[1] << 48; + n |= (uint64_t)data[2] << 40; + n |= (uint64_t)data[3] << 32; + n |= (uint64_t)data[4] << 24; + n |= (uint64_t)data[5] << 16; + n |= (uint64_t)data[6] << 8; + n |= (uint64_t)data[7]; - return n; - } - int8_t ByteReader::ReadI8() - { - auto v=ReadU8(); - return *(int8_t*)&v; - } - int16_t ByteReader::ReadI16BE() - { - auto v=ReadU16BE(); - return *(int16_t*)&v; - } - int16_t ByteReader::ReadI16LE() - { - auto v=ReadU16BE(); - return *(int16_t*)&v; - } - int32_t ByteReader::ReadI32BE() - { - auto v=ReadU32BE(); - return *(int32_t*)&v; - } - int32_t ByteReader::ReadI32LE() - { - auto v=ReadU32LE(); - return *(int32_t*)&v; - } - int64_t ByteReader::ReadI64BE() - { - auto v=ReadU64BE(); - return *(int64_t*)&v; - } - int64_t ByteReader::ReadI64LE() - { - auto v=ReadU64LE(); - return *(int64_t*)&v; - } - float ByteReader::ReadF32BE() - { - auto v=ReadU32BE(); - return *(float*)&v; - } - float ByteReader::ReadF32LE() - { - auto v=ReadU32LE(); - return *(float*)&v; - } - double ByteReader::ReadF64BE() - { - auto v=ReadU64BE(); - return *(double*)&v; - } - double ByteReader::ReadF64LE() - { - auto v=ReadU64LE(); - return *(double*)&v; - } - - Uuid ByteReader::ReadUuid() - { - uint8_t data[16]; - if(this->strm->ReadBlock(data, 16) != 16) - throw std::runtime_error("End of file"); - return Serialization::BitConverter::ToUuid(data[0]); - } - void ByteReader::ReadUuid(Uuid& uuid) - { - uint8_t data[16]; - if(this->strm->ReadBlock(data, 16) != 16) - throw std::runtime_error("End of file"); - Serialization::BitConverter::ToUuid(data[0],uuid); - } - -} \ No newline at end of file + return n; +} +uint64_t ByteReader::ReadU64LE() { + uint8_t data[8]; + if (this->strm->ReadBlock(data, 8) != 8) + throw std::runtime_error("End of file"); + uint64_t n = 0; + n |= (uint64_t)data[0]; + n |= (uint64_t)data[1] << 8; + n |= (uint64_t)data[2] << 16; + n |= (uint64_t)data[3] << 24; + n |= (uint64_t)data[4] << 32; + n |= (uint64_t)data[5] << 40; + n |= (uint64_t)data[6] << 48; + n |= (uint64_t)data[7] << 56; + + return n; +} +int8_t ByteReader::ReadI8() { + auto v = ReadU8(); + return *(int8_t *)&v; +} +int16_t ByteReader::ReadI16BE() { + auto v = ReadU16BE(); + return *(int16_t *)&v; +} +int16_t ByteReader::ReadI16LE() { + auto v = ReadU16BE(); + return *(int16_t *)&v; +} +int32_t ByteReader::ReadI32BE() { + auto v = ReadU32BE(); + return *(int32_t *)&v; +} +int32_t ByteReader::ReadI32LE() { + auto v = ReadU32LE(); + return *(int32_t *)&v; +} +int64_t ByteReader::ReadI64BE() { + auto v = ReadU64BE(); + return *(int64_t *)&v; +} +int64_t ByteReader::ReadI64LE() { + auto v = ReadU64LE(); + return *(int64_t *)&v; +} +float ByteReader::ReadF32BE() { + auto v = ReadU32BE(); + return *(float *)&v; +} +float ByteReader::ReadF32LE() { + auto v = ReadU32LE(); + return *(float *)&v; +} +double ByteReader::ReadF64BE() { + auto v = ReadU64BE(); + return *(double *)&v; +} +double ByteReader::ReadF64LE() { + auto v = ReadU64LE(); + return *(double *)&v; +} + +Uuid ByteReader::ReadUuid() { + uint8_t data[16]; + if (this->strm->ReadBlock(data, 16) != 16) + throw std::runtime_error("End of file"); + return Serialization::BitConverter::ToUuid(data[0]); +} +void ByteReader::ReadUuid(Uuid &uuid) { + uint8_t data[16]; + if (this->strm->ReadBlock(data, 16) != 16) + throw std::runtime_error("End of file"); + Serialization::BitConverter::ToUuid(data[0], uuid); +} + +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/src/Streams/ByteWriter.cpp b/src/Streams/ByteWriter.cpp index 5137646..5b4d5d5 100644 --- a/src/Streams/ByteWriter.cpp +++ b/src/Streams/ByteWriter.cpp @@ -1,140 +1,130 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Streams/ByteWriter.hpp" #include "TessesFramework/Serialization/BitConverter.hpp" -namespace Tesses::Framework::Streams -{ - std::shared_ptr ByteWriter::GetStream() - { - return this->strm; - } - ByteWriter::ByteWriter(std::shared_ptr strm) - { - this->strm = strm; - } - - void ByteWriter::WriteU8(uint8_t v) - { - strm->WriteByte(v); - } - void ByteWriter::WriteU16BE(uint16_t v) - { - uint8_t b[2]; - b[0] = (uint8_t)(v >> 8); - b[1] = (uint8_t)v; - strm->WriteBlock(b,2); - } - void ByteWriter::WriteU16LE(uint16_t v) - { - uint8_t b[2]; - b[0] = (uint8_t)v; - b[1] = (uint8_t)(v >> 8); - strm->WriteBlock(b,2); - } - void ByteWriter::WriteU32BE(uint32_t v) - { - uint8_t b[4]; - b[0] = (uint8_t)(v >> 24); - b[1] = (uint8_t)(v >> 16); - b[2] = (uint8_t)(v >> 8); - b[3] = (uint8_t)v; - strm->WriteBlock(b,4); - } - void ByteWriter::WriteU32LE(uint32_t v) - { - uint8_t b[4]; - b[0] = (uint8_t)v; - b[1] = (uint8_t)(v >> 8); - b[2] = (uint8_t)(v >> 16); - b[3] = (uint8_t)(v >> 24); - - strm->WriteBlock(b,4); - } - void ByteWriter::WriteU64BE(uint64_t v) - { - uint8_t b[8]; - b[0] = (uint8_t)(v >> 56); - b[1] = (uint8_t)(v >> 48); - b[2] = (uint8_t)(v >> 40); - b[3] = (uint8_t)(v >> 32); - b[4] = (uint8_t)(v >> 24); - b[5] = (uint8_t)(v >> 16); - b[6] = (uint8_t)(v >> 8); - b[7] = (uint8_t)v; - strm->WriteBlock(b,8); - } - void ByteWriter::WriteU64LE(uint64_t v) - { - uint8_t b[8]; - - b[0] = (uint8_t)v; - b[1] = (uint8_t)(v >> 8); - b[2] = (uint8_t)(v >> 16); - b[3] = (uint8_t)(v >> 24); - b[4] = (uint8_t)(v >> 32); - b[5] = (uint8_t)(v >> 40); - b[6] = (uint8_t)(v >> 48); - b[7] = (uint8_t)(v >> 56); - strm->WriteBlock(b,8); - } - void ByteWriter::WriteI8(int8_t v) - { - uint8_t data = *(uint8_t*)&v; - WriteU8(data); - } - void ByteWriter::WriteI16BE(int16_t v) - { - uint16_t data = *(uint16_t*)&v; - WriteU16BE(data); - } - void ByteWriter::WriteI16LE(int16_t v) - { - uint16_t data = *(uint16_t*)&v; - WriteU16LE(data); - } - void ByteWriter::WriteI32BE(int32_t v) - { - uint32_t data = *(uint32_t*)&v; - WriteU32BE(data); - } - void ByteWriter::WriteI32LE(int32_t v) - { - uint32_t data = *(uint32_t*)&v; - WriteU32LE(data); - } - void ByteWriter::WriteI64BE(int64_t v) - { - uint64_t data = *(uint64_t*)&v; - WriteU64BE(data); - } - void ByteWriter::WriteI64LE(int64_t v) - { - uint64_t data = *(uint64_t*)&v; - WriteU64LE(data); - } - void ByteWriter::WriteF32BE(float v) - { - uint32_t data = *(uint32_t*)&v; - WriteU32BE(data); - } - void ByteWriter::WriteF32LE(float v) - { - uint32_t data = *(uint32_t*)&v; - WriteU32LE(data); - } - void ByteWriter::WriteF64BE(double v) - { - uint64_t data = *(uint64_t*)&v; - WriteU64BE(data); - } - void ByteWriter::WriteF64LE(double v) - { - uint64_t data = *(uint64_t*)&v; - WriteU64LE(data); - } - void ByteWriter::WriteUuid(const Uuid& uuid) - { - uint8_t data[16]; - Serialization::BitConverter::FromUuid(data[0], uuid); - this->strm->WriteBlock(data, 16); - } - -} \ No newline at end of file +namespace Tesses::Framework::Streams { +std::shared_ptr ByteWriter::GetStream() { return this->strm; } +ByteWriter::ByteWriter(std::shared_ptr strm) { this->strm = strm; } + +void ByteWriter::WriteU8(uint8_t v) { strm->WriteByte(v); } +void ByteWriter::WriteU16BE(uint16_t v) { + uint8_t b[2]; + b[0] = (uint8_t)(v >> 8); + b[1] = (uint8_t)v; + strm->WriteBlock(b, 2); +} +void ByteWriter::WriteU16LE(uint16_t v) { + uint8_t b[2]; + b[0] = (uint8_t)v; + b[1] = (uint8_t)(v >> 8); + strm->WriteBlock(b, 2); +} +void ByteWriter::WriteU32BE(uint32_t v) { + uint8_t b[4]; + b[0] = (uint8_t)(v >> 24); + b[1] = (uint8_t)(v >> 16); + b[2] = (uint8_t)(v >> 8); + b[3] = (uint8_t)v; + strm->WriteBlock(b, 4); +} +void ByteWriter::WriteU32LE(uint32_t v) { + uint8_t b[4]; + b[0] = (uint8_t)v; + b[1] = (uint8_t)(v >> 8); + b[2] = (uint8_t)(v >> 16); + b[3] = (uint8_t)(v >> 24); + + strm->WriteBlock(b, 4); +} +void ByteWriter::WriteU64BE(uint64_t v) { + uint8_t b[8]; + b[0] = (uint8_t)(v >> 56); + b[1] = (uint8_t)(v >> 48); + b[2] = (uint8_t)(v >> 40); + b[3] = (uint8_t)(v >> 32); + b[4] = (uint8_t)(v >> 24); + b[5] = (uint8_t)(v >> 16); + b[6] = (uint8_t)(v >> 8); + b[7] = (uint8_t)v; + strm->WriteBlock(b, 8); +} +void ByteWriter::WriteU64LE(uint64_t v) { + uint8_t b[8]; + + b[0] = (uint8_t)v; + b[1] = (uint8_t)(v >> 8); + b[2] = (uint8_t)(v >> 16); + b[3] = (uint8_t)(v >> 24); + b[4] = (uint8_t)(v >> 32); + b[5] = (uint8_t)(v >> 40); + b[6] = (uint8_t)(v >> 48); + b[7] = (uint8_t)(v >> 56); + strm->WriteBlock(b, 8); +} +void ByteWriter::WriteI8(int8_t v) { + uint8_t data = *(uint8_t *)&v; + WriteU8(data); +} +void ByteWriter::WriteI16BE(int16_t v) { + uint16_t data = *(uint16_t *)&v; + WriteU16BE(data); +} +void ByteWriter::WriteI16LE(int16_t v) { + uint16_t data = *(uint16_t *)&v; + WriteU16LE(data); +} +void ByteWriter::WriteI32BE(int32_t v) { + uint32_t data = *(uint32_t *)&v; + WriteU32BE(data); +} +void ByteWriter::WriteI32LE(int32_t v) { + uint32_t data = *(uint32_t *)&v; + WriteU32LE(data); +} +void ByteWriter::WriteI64BE(int64_t v) { + uint64_t data = *(uint64_t *)&v; + WriteU64BE(data); +} +void ByteWriter::WriteI64LE(int64_t v) { + uint64_t data = *(uint64_t *)&v; + WriteU64LE(data); +} +void ByteWriter::WriteF32BE(float v) { + uint32_t data = *(uint32_t *)&v; + WriteU32BE(data); +} +void ByteWriter::WriteF32LE(float v) { + uint32_t data = *(uint32_t *)&v; + WriteU32LE(data); +} +void ByteWriter::WriteF64BE(double v) { + uint64_t data = *(uint64_t *)&v; + WriteU64BE(data); +} +void ByteWriter::WriteF64LE(double v) { + uint64_t data = *(uint64_t *)&v; + WriteU64LE(data); +} +void ByteWriter::WriteUuid(const Uuid &uuid) { + uint8_t data[16]; + Serialization::BitConverter::FromUuid(data[0], uuid); + this->strm->WriteBlock(data, 16); +} + +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/src/Streams/FileStream.cpp b/src/Streams/FileStream.cpp index 6e4dd9c..34f5e08 100644 --- a/src/Streams/FileStream.cpp +++ b/src/Streams/FileStream.cpp @@ -1,123 +1,126 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Streams/FileStream.hpp" #if defined(_WIN32) #include #undef min #endif -namespace Tesses::Framework::Streams -{ - void FileStream::SetMode(std::string mode) - { - this->canRead = false; - this->canWrite = false; - this->canSeek = true; - if(mode.size() >= 1) - { - if(mode[0] == 'r') - { - this->canRead = true; - } - else if(mode[0] == 'w') - { - this->canWrite = true; - } - else if(mode[0] == 'a') - { - this->canSeek = false; - this->canWrite = true; - } - } - - if(((mode.size() >= 2 && mode[1] == '+') || (mode.size() >= 2 && mode[1] == 'b' && mode[2] == '+'))) - { +namespace Tesses::Framework::Streams { +void FileStream::SetMode(std::string mode) { + this->canRead = false; + this->canWrite = false; + this->canSeek = true; + if (mode.size() >= 1) { + if (mode[0] == 'r') { this->canRead = true; + } else if (mode[0] == 'w') { + this->canWrite = true; + } else if (mode[0] == 'a') { + this->canSeek = false; this->canWrite = true; } } - FileStream::FileStream(std::filesystem::path p, std::string mode) - { - std::string str = p.string(); - this->f = fopen(str.c_str(),mode.c_str()); - this->canSeek = true; - this->owns=true; - this->SetMode(mode); - } - FileStream::FileStream(FILE* f, bool owns, std::string mode , bool canSeek) - { - this->f = f; - this->owns = owns; - this->SetMode(mode); - this->canSeek = canSeek; - } - size_t FileStream::Read(uint8_t* buff, size_t sz) - { - if(!CanRead()) throw std::runtime_error("Cannot read from stream"); - return fread(buff,1, sz, this->f); - } - size_t FileStream::Write(const uint8_t* buff, size_t sz) - { - if(!CanWrite()) throw std::runtime_error("Cannot write to stream"); - return fwrite(buff,1, sz, f); - } - bool FileStream::CanRead() - { - return this->canRead && this->f; - } - bool FileStream::CanWrite() - { - return this->canWrite && this->f; - } - bool FileStream::CanSeek() - { - return this->canSeek && this->f; - } - bool FileStream::EndOfStream() - { - if(!f) return true; - return feof(this->f); - } - int64_t FileStream::GetPosition() - { - - if(!f) return 0; - #if defined(_WIN32) - return (int64_t)_ftelli64(this->f); - #else - return (int64_t)ftello(this->f); - #endif - } - void FileStream::Flush() - { - - if(!f) return; - fflush(this->f); - } - void FileStream::Seek(int64_t pos, SeekOrigin whence) - { - - if(!f) return; - #if defined(_WIN32) - _fseeki64(this->f,pos,whence == SeekOrigin::Begin ? SEEK_SET : whence == SeekOrigin::Current ? SEEK_CUR : SEEK_END); - #else - fseeko(this->f,(off_t)pos,whence == SeekOrigin::Begin ? SEEK_SET : whence == SeekOrigin::Current ? SEEK_CUR : SEEK_END); - #endif - } - FileStream::~FileStream() - { - if(!f) return; - if(this->owns) - { - fclose(this->f); - f=NULL; - } - } - void FileStream::Close() - { - if(!f) return; - if(this->owns) - { - fclose(this->f); - f=NULL; - } + if (((mode.size() >= 2 && mode[1] == '+') || + (mode.size() >= 2 && mode[1] == 'b' && mode[2] == '+'))) { + this->canRead = true; + this->canWrite = true; } } +FileStream::FileStream(std::filesystem::path p, std::string mode) { + std::string str = p.string(); + this->f = fopen(str.c_str(), mode.c_str()); + this->canSeek = true; + this->owns = true; + this->SetMode(mode); +} +FileStream::FileStream(FILE *f, bool owns, std::string mode, bool canSeek) { + this->f = f; + this->owns = owns; + this->SetMode(mode); + this->canSeek = canSeek; +} +size_t FileStream::Read(uint8_t *buff, size_t sz) { + if (!CanRead()) + throw std::runtime_error("Cannot read from stream"); + return fread(buff, 1, sz, this->f); +} +size_t FileStream::Write(const uint8_t *buff, size_t sz) { + if (!CanWrite()) + throw std::runtime_error("Cannot write to stream"); + return fwrite(buff, 1, sz, f); +} +bool FileStream::CanRead() { return this->canRead && this->f; } +bool FileStream::CanWrite() { return this->canWrite && this->f; } +bool FileStream::CanSeek() { return this->canSeek && this->f; } +bool FileStream::EndOfStream() { + if (!f) + return true; + return feof(this->f); +} + +int64_t FileStream::GetPosition() { + + if (!f) + return 0; +#if defined(_WIN32) + return (int64_t)_ftelli64(this->f); +#else + return (int64_t)ftello(this->f); +#endif +} +void FileStream::Flush() { + + if (!f) + return; + fflush(this->f); +} +void FileStream::Seek(int64_t pos, SeekOrigin whence) { + + if (!f) + return; +#if defined(_WIN32) + _fseeki64(this->f, pos, + whence == SeekOrigin::Begin ? SEEK_SET + : whence == SeekOrigin::Current ? SEEK_CUR + : SEEK_END); +#else + fseeko(this->f, (off_t)pos, + whence == SeekOrigin::Begin ? SEEK_SET + : whence == SeekOrigin::Current ? SEEK_CUR + : SEEK_END); +#endif +} +FileStream::~FileStream() { + if (!f) + return; + if (this->owns) { + fclose(this->f); + f = NULL; + } +} +void FileStream::Close() { + if (!f) + return; + if (this->owns) { + fclose(this->f); + f = NULL; + } +} +} // namespace Tesses::Framework::Streams diff --git a/src/Streams/MemoryStream.cpp b/src/Streams/MemoryStream.cpp index a1e8960..9c56165 100644 --- a/src/Streams/MemoryStream.cpp +++ b/src/Streams/MemoryStream.cpp @@ -1,74 +1,66 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Streams/MemoryStream.hpp" -namespace Tesses::Framework::Streams -{ - MemoryStream::MemoryStream(bool isWritable) - { - this->offset=0; - this->writable = isWritable; +namespace Tesses::Framework::Streams { +MemoryStream::MemoryStream(bool isWritable) { + this->offset = 0; + this->writable = isWritable; +} +std::vector &MemoryStream::GetBuffer() { return this->buffer; } +size_t MemoryStream::Read(uint8_t *buff, size_t sz) { + if (this->offset >= this->buffer.size()) + return 0; + + size_t toRead = std::min(sz, this->buffer.size() - this->offset); + + memcpy(buff, this->buffer.data() + this->offset, toRead); + this->offset += toRead; + return toRead; +} +size_t MemoryStream::Write(const uint8_t *buff, size_t sz) { + if (!this->writable) + return 0; + if (this->offset > this->buffer.size()) { + this->buffer.resize(this->offset + sz); } - std::vector& MemoryStream::GetBuffer() - { - return this->buffer; + this->buffer.insert(this->buffer.begin() + this->offset, buff, buff + sz); + this->offset += sz; + return sz; +} +bool MemoryStream::CanRead() { return true; } +bool MemoryStream::CanWrite() { return this->writable; } +bool MemoryStream::CanSeek() { return true; } +int64_t MemoryStream::GetLength() { return this->buffer.size(); } +int64_t MemoryStream::GetPosition() { return (int64_t)this->offset; } +void MemoryStream::Seek(int64_t pos, SeekOrigin whence) { + switch (whence) { + case SeekOrigin::Begin: + this->offset = (size_t)pos; + break; + case SeekOrigin::Current: + this->offset += (size_t)pos; + break; + case SeekOrigin::End: + this->offset = (size_t)(this->buffer.size() + pos); + break; } - size_t MemoryStream::Read(uint8_t* buff, size_t sz) - { - if(this->offset >= this->buffer.size()) return 0; - - size_t toRead = std::min(sz, this->buffer.size()-this->offset); - - memcpy(buff, this->buffer.data() + this->offset, toRead); - this->offset += toRead; - return toRead; - } - size_t MemoryStream::Write(const uint8_t* buff, size_t sz) - { - if(!this->writable) return 0; - if(this->offset > this->buffer.size()) - { - this->buffer.resize(this->offset+sz); - } - this->buffer.insert(this->buffer.begin()+this->offset, buff, buff+sz); - this->offset+=sz; - return sz; - } - bool MemoryStream::CanRead() - { - return true; - } - bool MemoryStream::CanWrite() - { - return this->writable; - } - bool MemoryStream::CanSeek() - { - return true; - } - int64_t MemoryStream::GetLength() - { - return this->buffer.size(); - } - int64_t MemoryStream::GetPosition() - { - return (int64_t)this->offset; - } - void MemoryStream::Seek(int64_t pos, SeekOrigin whence) - { - switch(whence) - { - case SeekOrigin::Begin: - this->offset = (size_t)pos; - break; - case SeekOrigin::Current: - this->offset += (size_t)pos; - break; - case SeekOrigin::End: - this->offset = (size_t)(this->buffer.size() + pos); - break; - } - } - void MemoryStream::Close() - { - this->buffer.clear(); - } -} \ No newline at end of file +} +void MemoryStream::Close() { this->buffer.clear(); } +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/src/Streams/NetworkStream.cpp b/src/Streams/NetworkStream.cpp index ae9622c..8b9465b 100644 --- a/src/Streams/NetworkStream.cpp +++ b/src/Streams/NetworkStream.cpp @@ -1,19 +1,36 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Streams/NetworkStream.hpp" #include "TessesFramework/Http/HttpUtils.hpp" #include -#include #include +#include using HttpUtils = Tesses::Framework::Http::HttpUtils; #if defined(TESSESFRAMEWORK_ENABLE_NETWORKING) - - #if defined(GEKKO) #define ss_family sin_family #endif -#if defined(GEKKO) && !(defined(TESSESFRAMEWORK_USE_WII_SOCKET) && defined(HW_RVL)) +#if defined(GEKKO) && \ + !(defined(TESSESFRAMEWORK_USE_WII_SOCKET) && defined(HW_RVL)) #include #define NETWORK_GETSOCKNAME net_getsockname #define NETWORK_RECV net_recv @@ -22,11 +39,12 @@ using HttpUtils = Tesses::Framework::Http::HttpUtils; #error "Not supported yet" #else - #if defined(_WIN32) -#include #include #include + +#include + #include #if defined __has_include #if __has_include() @@ -37,18 +55,18 @@ using HttpUtils = Tesses::Framework::Http::HttpUtils; #undef min #pragma comment(lib, "ws2_32.lib") - #else extern "C" { +#include +#include +#include #include #include #include -#include -#include -#include #include -#if defined(AF_UNIX) && !defined(GEKKO) && !defined(__SWITCH__) && !defined(__PS2__) +#if defined(AF_UNIX) && !defined(GEKKO) && !defined(__SWITCH__) && \ + !defined(__PS2__) #include #endif @@ -57,8 +75,10 @@ extern "C" { #endif #if defined(GEKKO) -extern "C" uint32_t if_config( char *local_ip, char *netmask, char *gateway,bool use_dhcp, int max_retries); -#elif !defined(_WIN32) && !defined(__ANDROID__) && !defined(__SWITCH__) && !defined(__PS2__) +extern "C" uint32_t if_config(char *local_ip, char *netmask, char *gateway, + bool use_dhcp, int max_retries); +#elif !defined(_WIN32) && !defined(__ANDROID__) && !defined(__SWITCH__) && \ + !defined(__PS2__) #include #endif @@ -87,164 +107,151 @@ extern "C" uint32_t if_config( char *local_ip, char *netmask, char *gateway,bool #undef AF_INET6 - namespace Tesses::Framework::Streams { - std::string StringifyIP(struct sockaddr* addr); - std::vector> NetworkStream::GetIPs(bool ipV6) - { - std::vector> ipConfig; +std::string StringifyIP(struct sockaddr *addr); +std::vector> +NetworkStream::GetIPs(bool ipV6) { + std::vector> ipConfig; - #if defined(GEKKO) - //if_config( char *local_ip, char *netmask, char *gateway,bool use_dhcp, int max_retries); - char localIp[16]; - char netmask[16]; - char gateway[16]; - if_config(localIp,netmask, gateway, true, 1); - ipConfig.push_back(std::pair("net",localIp)); - #elif defined(_WIN32) - //Thanks https://www.youtube.com/watch?v=K446bcFeE3s - ULONG family = ipV6 ? 0 : AF_INET; - ULONG flags = 0; - ULONG size = 15000; - PIP_ADAPTER_ADDRESSES addresses = NULL; - addresses = (PIP_ADAPTER_ADDRESSES)malloc(size); +#if defined(GEKKO) + // if_config( char *local_ip, char *netmask, char *gateway,bool use_dhcp, + // int max_retries); + char localIp[16]; + char netmask[16]; + char gateway[16]; + if_config(localIp, netmask, gateway, true, 1); + ipConfig.push_back(std::pair("net", localIp)); +#elif defined(_WIN32) + // Thanks https://www.youtube.com/watch?v=K446bcFeE3s + ULONG family = ipV6 ? 0 : AF_INET; + ULONG flags = 0; + ULONG size = 15000; + PIP_ADAPTER_ADDRESSES addresses = NULL; + addresses = (PIP_ADAPTER_ADDRESSES)malloc(size); - int retval = GetAdaptersAddresses(family, flags, 0, addresses, &size); - if(retval != 0) { - free(addresses); - return {}; - } - PIP_ADAPTER_ADDRESSES addrPtr = addresses; - while(addrPtr != NULL) - { - auto fname = addrPtr->FriendlyName; - - size_t len = WideCharToMultiByte(CP_UTF8, 0,fname,-1,NULL, 0, NULL, NULL); - std::string name(len, 0); - WideCharToMultiByte(CP_UTF8, 0,fname,-1,name.data(), len, NULL, NULL); - - auto ip = addrPtr->FirstUnicastAddress; - while(ip != NULL) - { - ipConfig.push_back(std::pair(name, StringifyIP(ip->Address.lpSockaddr))); - ip = ip->Next; - } - - addrPtr = addrPtr->Next; - } + int retval = GetAdaptersAddresses(family, flags, 0, addresses, &size); + if (retval != 0) { free(addresses); + return {}; + } + PIP_ADAPTER_ADDRESSES addrPtr = addresses; + while (addrPtr != NULL) { + auto fname = addrPtr->FriendlyName; + size_t len = + WideCharToMultiByte(CP_UTF8, 0, fname, -1, NULL, 0, NULL, NULL); + std::string name(len, 0); + WideCharToMultiByte(CP_UTF8, 0, fname, -1, name.data(), len, NULL, + NULL); - #elif defined(__ANDROID__) || defined(__SWITCH__) || defined(__PS2__) - - #else - struct ifaddrs *ifAddrStruct = NULL; - errno = 0; - if(getifaddrs(&ifAddrStruct) == -1) - { - - freeifaddrs(ifAddrStruct); - return {}; + auto ip = addrPtr->FirstUnicastAddress; + while (ip != NULL) { + ipConfig.push_back(std::pair( + name, StringifyIP(ip->Address.lpSockaddr))); + ip = ip->Next; } - for (struct ifaddrs *ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL) - continue; - if (ifa->ifa_addr->sa_family == AF_INET) { // IPv4 - ipConfig.push_back(std::pair(ifa->ifa_name, StringifyIP(ifa->ifa_addr))); + addrPtr = addrPtr->Next; + } + free(addresses); - } - #if defined(AF_INET6) - if (ifa->ifa_addr->sa_family == AF_INET6 && ipV6) { // IPv6 +#elif defined(__ANDROID__) || defined(__SWITCH__) || defined(__PS2__) - ipConfig.push_back(std::pair(ifa->ifa_name, StringifyIP(ifa->ifa_addr))); - - } - #endif - } +#else + struct ifaddrs *ifAddrStruct = NULL; + errno = 0; + if (getifaddrs(&ifAddrStruct) == -1) { freeifaddrs(ifAddrStruct); - #endif - return ipConfig; - + return {}; } - void SetPort(struct sockaddr* addr, uint16_t port) - { - if(addr->sa_family == AF_INET) - { - struct sockaddr_in* a = (struct sockaddr_in*)addr;\ - a->sin_port = htons(port); - } - #if defined(AF_INET6) - if(addr->sa_family == AF_INET6) - { - struct sockaddr_in6* a = (struct sockaddr_in6*)addr;\ - a->sin6_port = htons(port); + for (struct ifaddrs *ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + if (ifa->ifa_addr->sa_family == AF_INET) { // IPv4 + ipConfig.push_back(std::pair( + ifa->ifa_name, StringifyIP(ifa->ifa_addr))); } - #endif +#if defined(AF_INET6) + if (ifa->ifa_addr->sa_family == AF_INET6 && ipV6) { // IPv6 - } - static uint16_t getPort(struct sockaddr* addr) - { - if(addr->sa_family == AF_INET) - { - struct sockaddr_in* a = (struct sockaddr_in*)addr;\ - return ntohs(a->sin_port); + ipConfig.push_back(std::pair( + ifa->ifa_name, StringifyIP(ifa->ifa_addr))); } - #if defined(AF_INET6) - if(addr->sa_family == AF_INET6) - { - struct sockaddr_in6* a = (struct sockaddr_in6*)addr;\ - return ntohs(a->sin6_port); - - } - #endif - return 0; - } - bool IPParse(std::string str,struct sockaddr_storage* addr) - { - memset(addr,0,sizeof(struct sockaddr_storage)); - uint8_t ip[16]; - - #if defined(__PS2__) - if(inet_aton(str.c_str(),ip) == 1) - #else - if(inet_pton(AF_INET,str.c_str(),ip)==1) - #endif - { - struct sockaddr_in* inAddr = (struct sockaddr_in*)addr; - inAddr->sin_family = AF_INET; - memcpy(&inAddr->sin_addr,ip,4); - return true; - } - #if defined(AF_INET6) - if(inet_pton(AF_INET6,str.c_str(),ip)==1) - { - - struct sockaddr_in6* inAddr = (struct sockaddr_in6*)addr; - - inAddr->sin6_family = AF_INET6; - memcpy(&inAddr->sin6_addr,ip,16); - return 6; - } - #endif - return false; +#endif } - std::string StringifyIP(struct sockaddr* addr) - { - if(addr->sa_family == AF_INET) - { - uint8_t* ip = (uint8_t*)&(((struct sockaddr_in*)addr)->sin_addr); - return std::to_string((uint32_t)ip[0]) + "." + std::to_string((uint32_t)ip[1]) + "." + std::to_string((uint32_t)ip[2]) + "." + std::to_string((uint32_t)ip[3]); - } - #if defined(AF_INET6) - if(addr->sa_family == AF_INET6) - { - uint8_t* ip = (uint8_t*)&(((struct sockaddr_in6*)addr)->sin6_addr); + freeifaddrs(ifAddrStruct); +#endif + return ipConfig; +} +void SetPort(struct sockaddr *addr, uint16_t port) { + if (addr->sa_family == AF_INET) { + struct sockaddr_in *a = (struct sockaddr_in *)addr; + a->sin_port = htons(port); + } +#if defined(AF_INET6) + if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *a = (struct sockaddr_in6 *)addr; + a->sin6_port = htons(port); + } +#endif +} +static uint16_t getPort(struct sockaddr *addr) { + if (addr->sa_family == AF_INET) { + struct sockaddr_in *a = (struct sockaddr_in *)addr; + return ntohs(a->sin_port); + } +#if defined(AF_INET6) + if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *a = (struct sockaddr_in6 *)addr; + return ntohs(a->sin6_port); + } +#endif + return 0; +} +bool IPParse(std::string str, struct sockaddr_storage *addr) { + memset(addr, 0, sizeof(struct sockaddr_storage)); + uint8_t ip[16]; - return std::string({ +#if defined(__PS2__) + if (inet_aton(str.c_str(), ip) == 1) +#else + if (inet_pton(AF_INET, str.c_str(), ip) == 1) +#endif + { + struct sockaddr_in *inAddr = (struct sockaddr_in *)addr; + inAddr->sin_family = AF_INET; + memcpy(&inAddr->sin_addr, ip, 4); + return true; + } +#if defined(AF_INET6) + if (inet_pton(AF_INET6, str.c_str(), ip) == 1) { + + struct sockaddr_in6 *inAddr = (struct sockaddr_in6 *)addr; + + inAddr->sin6_family = AF_INET6; + memcpy(&inAddr->sin6_addr, ip, 16); + return 6; + } +#endif + return false; +} + +std::string StringifyIP(struct sockaddr *addr) { + if (addr->sa_family == AF_INET) { + uint8_t *ip = (uint8_t *)&(((struct sockaddr_in *)addr)->sin_addr); + return std::to_string((uint32_t)ip[0]) + "." + + std::to_string((uint32_t)ip[1]) + "." + + std::to_string((uint32_t)ip[2]) + "." + + std::to_string((uint32_t)ip[3]); + } +#if defined(AF_INET6) + if (addr->sa_family == AF_INET6) { + uint8_t *ip = (uint8_t *)&(((struct sockaddr_in6 *)addr)->sin6_addr); + + return std::string({ HttpUtils::NibbleToHex((ip[0] >> 4) & 0x0F), HttpUtils::NibbleToHex(ip[0] & 0x0F), HttpUtils::NibbleToHex((ip[1] >> 4) & 0x0F), @@ -284,763 +291,626 @@ namespace Tesses::Framework::Streams { HttpUtils::NibbleToHex(ip[14] & 0x0F), HttpUtils::NibbleToHex((ip[15] >> 4) & 0x0F), HttpUtils::NibbleToHex(ip[15] & 0x0F), - }); - - - - - } - #endif - return ""; + }); } +#endif + return ""; +} - typedef union { - uint32_t addr; - uint8_t addr_parts[4]; - } my_addr_t; +typedef union { + uint32_t addr; + uint8_t addr_parts[4]; +} my_addr_t; - bool NetworkStream::DataAvailable(int timeout) - { - pollfd fd; - fd.events = POLLIN; - fd.fd = this->sock; - int res= NETWORK_POLL(&fd,1,timeout); - if (res == -1) { - if(errno == EINTR) return false; - throw std::runtime_error("poll() failed"); - } - else if(res == 0) return false; - else if(fd.revents & POLLIN) - return true; - else if(fd.revents & (POLLERR | POLLNVAL)) - { - this->endOfStream=true; +bool NetworkStream::DataAvailable(int timeout) { + pollfd fd; + fd.events = POLLIN; + fd.fd = this->sock; + int res = NETWORK_POLL(&fd, 1, timeout); + if (res == -1) { + if (errno == EINTR) return false; - } + throw std::runtime_error("poll() failed"); + } else if (res == 0) + return false; + else if (fd.revents & POLLIN) + return true; + else if (fd.revents & (POLLERR | POLLNVAL)) { + this->endOfStream = true; return false; } + return false; +} - NetworkStream::NetworkStream(std::string unixPath,bool isServer) - { - this->endOfStream=false; - this->owns = true; - this->success=false; - #if defined(AF_UNIX) && !defined(GEKKO) && !defined(__PS2__) && !defined(__SWITCH__) && ((defined(_WIN32) && defined(HAS_AFUNIX) ) || !defined(_WIN32)) - this->sock = NETWORK_SOCKET(AF_UNIX,SOCK_STREAM,0); - if(this->sock < 0) - { - this->success=false; - return; - } - struct sockaddr_un unx; - - memset(&unx, 0, sizeof(unx)); - unx.sun_family = AF_UNIX; - - strncpy(unx.sun_path, unixPath.c_str(),sizeof(unx.sun_path)-1); - - if(isServer) - { - unlink(unixPath.c_str()); - if(NETWORK_BIND(this->sock, (const sockaddr*)&unx, (socklen_t)sizeof(unx)) != 0) - { - std::cerr << "FAILED TO BIND: " << strerror(errno) << std::endl; - this->success = false; - return; - } - } - else - { - if(NETWORK_CONNECT(this->sock,(const sockaddr*)&unx, (socklen_t)sizeof(unx)) != 0) - { - std::cerr << "FAILED TO CONNECT: " << strerror(errno) << std::endl; - this->success=false; - return; - } - } - this->success = true; - #endif +NetworkStream::NetworkStream(std::string unixPath, bool isServer) { + this->endOfStream = false; + this->owns = true; + this->success = false; +#if defined(AF_UNIX) && !defined(GEKKO) && !defined(__PS2__) && \ + !defined(__SWITCH__) && \ + ((defined(_WIN32) && defined(HAS_AFUNIX)) || !defined(_WIN32)) + this->sock = NETWORK_SOCKET(AF_UNIX, SOCK_STREAM, 0); + if (this->sock < 0) { + this->success = false; + return; } + struct sockaddr_un unx; - TcpServer::TcpServer(std::string unixPath,int32_t backlog) - { + memset(&unx, 0, sizeof(unx)); + unx.sun_family = AF_UNIX; + strncpy(unx.sun_path, unixPath.c_str(), sizeof(unx.sun_path) - 1); - this->owns=true; - this->valid=false; - #if defined(AF_UNIX) && !defined(GEKKO) && !defined(__PS2__) && !defined(__SWITCH__) && ((defined(_WIN32) && defined(HAS_AFUNIX) ) || !defined(_WIN32)) - - this->sock = NETWORK_SOCKET(AF_UNIX,SOCK_STREAM,0); - if(this->sock < 0) - { - this->valid=false; - return; - } - struct sockaddr_un unx; - - memset(&unx, 0, sizeof(unx)); - unx.sun_family = AF_UNIX; + if (isServer) { unlink(unixPath.c_str()); - strncpy(unx.sun_path, unixPath.c_str(),sizeof(unx.sun_path)-1); - if(NETWORK_BIND(this->sock, (const sockaddr*)&unx, (socklen_t)sizeof(unx)) != 0) - { - std::cout << "FAILED TO BIND: " << strerror(errno) << std::endl; - this->valid = false; + if (NETWORK_BIND(this->sock, (const sockaddr *)&unx, + (socklen_t)sizeof(unx)) != 0) { + std::cerr << "FAILED TO BIND: " << strerror(errno) << std::endl; + this->success = false; return; } + } else { + if (NETWORK_CONNECT(this->sock, (const sockaddr *)&unx, + (socklen_t)sizeof(unx)) != 0) { + std::cerr << "FAILED TO CONNECT: " << strerror(errno) << std::endl; + this->success = false; + return; + } + } + this->success = true; +#endif +} - if(NETWORK_LISTEN(this->sock, backlog) != 0) - { - std::cout << "FAILED TO LISTEN FOR SOME REASON" << std::endl; - this->valid = false; - return; - } - this->valid = true; - #endif +TcpServer::TcpServer(std::string unixPath, int32_t backlog) { + + this->owns = true; + this->valid = false; +#if defined(AF_UNIX) && !defined(GEKKO) && !defined(__PS2__) && \ + !defined(__SWITCH__) && \ + ((defined(_WIN32) && defined(HAS_AFUNIX)) || !defined(_WIN32)) + + this->sock = NETWORK_SOCKET(AF_UNIX, SOCK_STREAM, 0); + if (this->sock < 0) { + this->valid = false; + return; + } + struct sockaddr_un unx; + + memset(&unx, 0, sizeof(unx)); + unx.sun_family = AF_UNIX; + unlink(unixPath.c_str()); + strncpy(unx.sun_path, unixPath.c_str(), sizeof(unx.sun_path) - 1); + if (NETWORK_BIND(this->sock, (const sockaddr *)&unx, + (socklen_t)sizeof(unx)) != 0) { + std::cout << "FAILED TO BIND: " << strerror(errno) << std::endl; + this->valid = false; + return; } - TcpServer::TcpServer(uint16_t port, int32_t backlog) - { - this->owns=true; + if (NETWORK_LISTEN(this->sock, backlog) != 0) { + std::cout << "FAILED TO LISTEN FOR SOME REASON" << std::endl; + this->valid = false; + return; + } + this->valid = true; +#endif +} + +TcpServer::TcpServer(uint16_t port, int32_t backlog) { + this->owns = true; + this->sock = NETWORK_SOCKET(AF_INET, SOCK_STREAM, 0); + + if (this->sock < 0) { + std::cout << "FAILED TO CREATE SOCKET FOR SOME REASON" << std::endl; + this->valid = false; + return; + } + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (port > 0) { + int on = 1; +#if defined(SO_REUSEPORT) + NETWORK_SETSOCKOPT(this->sock, SOL_SOCKET, SO_REUSEPORT, + (const char *)&on, (socklen_t)sizeof(on)); +#endif +#if defined(SO_REUSEADDR) + NETWORK_SETSOCKOPT(this->sock, SOL_SOCKET, SO_REUSEADDR, + (const char *)&on, (socklen_t)sizeof(on)); +#endif + } else { + my_addr_t addr2; + addr2.addr_parts[0] = 127; + addr2.addr_parts[1] = 0; + addr2.addr_parts[2] = 0; + addr2.addr_parts[3] = 1; + addr.sin_addr.s_addr = addr2.addr; + } + if (NETWORK_BIND(this->sock, (const sockaddr *)&addr, + (socklen_t)sizeof(addr)) != 0) { + std::cout << "FAILED TO BIND: " << strerror(errno) << std::endl; + this->valid = false; + return; + } + + if (NETWORK_LISTEN(this->sock, backlog) != 0) { + std::cout << "FAILED TO LISTEN FOR SOME REASON" << std::endl; + this->valid = false; + return; + } + this->valid = true; +} +TcpServer::TcpServer(int32_t backlog) : TcpServer(0U, backlog) {} +uint16_t TcpServer::GetPort() { + struct sockaddr_storage addr; + memset(&addr, 0, sizeof(addr)); + socklen_t len = sizeof(addr); + NETWORK_GETSOCKNAME(this->sock, (struct sockaddr *)&addr, &len); + if (addr.ss_family == AF_INET) { + return (uint16_t)ntohs(((struct sockaddr_in *)&addr)->sin_port); + } +#if defined(AF_INET6) + if (addr.ss_family == AF_INET6) { + return (uint16_t)ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); + } +#endif + return 0; +} +uint16_t NetworkStream::GetPort() { + struct sockaddr_storage addr; + memset(&addr, 0, sizeof(addr)); + socklen_t len = sizeof(addr); + NETWORK_GETSOCKNAME(this->sock, (struct sockaddr *)&addr, &len); + if (addr.ss_family == AF_INET) { + return (uint16_t)(((struct sockaddr_in *)&addr)->sin_port); + } +#if defined(AF_INET6) + if (addr.ss_family == AF_INET6) { + return (uint16_t)(((struct sockaddr_in6 *)&addr)->sin6_port); + } +#endif + return 0; +} +bool TcpServer::IsValid() { return this->valid; } +TcpServer::TcpServer(std::string ip, uint16_t port, int32_t backlog) { + this->owns = true; + struct sockaddr_storage addr; + memset(&addr, 0, sizeof(addr)); + + uint8_t ipBytes[16]; + bool success = IPParse(ip, &addr); + if (!success) { + this->valid = false; + return; + } + + SetPort((struct sockaddr *)&addr, port); + + this->sock = NETWORK_SOCKET((int)addr.ss_family, SOCK_STREAM, 0); + if (this->sock < 0) { + this->valid = false; + return; + } + + if (NETWORK_BIND(this->sock, (const sockaddr *)&addr, + (socklen_t)sizeof(addr)) != 0) { + + std::cout << "FAILED TO BIND: " << strerror(errno) << std::endl; + this->valid = false; + return; + } + + if (NETWORK_LISTEN(this->sock, backlog) != 0) { + this->valid = false; + return; + } + this->valid = true; +} +TcpServer::TcpServer(int32_t sock, bool owns) { + this->valid = sock >= 0; + this->sock = sock; + this->owns = owns; +} +TcpServer::~TcpServer() { + if (this->valid && this->owns) { + NETWORK_CLOSE(this->sock); + } + this->valid = false; +} +void TcpServer::Close() { + if (this->valid) + NETWORK_CLOSE(this->sock); + this->valid = false; +} +std::shared_ptr TcpServer::GetStream(std::string &ip, + uint16_t &port) { + struct sockaddr_storage storage; + memset(&storage, 0, sizeof(storage)); + socklen_t addrlen = (socklen_t)sizeof(storage); + + int s = NETWORK_ACCEPT(this->sock, (struct sockaddr *)&storage, &addrlen); + if (s < 0) { + return nullptr; + } + + ip = StringifyIP((struct sockaddr *)&storage); + port = getPort((struct sockaddr *)&storage); + + return std::make_shared(s, true); +} +bool NetworkStream::CanRead() { return this->success; } +bool NetworkStream::CanWrite() { return this->success; } +NetworkStream::NetworkStream(SocketType type) { + this->endOfStream = false; + this->owns = true; + this->success = false; + switch (type) { + case SocketType::ST_IPv4_TCP: +#if defined(AF_INET) this->sock = NETWORK_SOCKET(AF_INET, SOCK_STREAM, 0); - if(this->sock < 0) - { - std::cout << "FAILED TO CREATE SOCKET FOR SOME REASON" << std::endl; - this->valid=false; - return; - } +#endif + break; + case SocketType::ST_IPv4_UDP: +#if defined(AF_INET) + this->sock = NETWORK_SOCKET(AF_INET, SOCK_DGRAM, 0); - struct sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - if(port > 0) - { - int on=1; - #if defined(SO_REUSEPORT) - NETWORK_SETSOCKOPT(this->sock,SOL_SOCKET,SO_REUSEPORT,(const char*)&on, (socklen_t)sizeof(on)); - #endif - #if defined(SO_REUSEADDR) - NETWORK_SETSOCKOPT(this->sock,SOL_SOCKET,SO_REUSEADDR,(const char*)&on, (socklen_t)sizeof(on)); - #endif - } - else - { - my_addr_t addr2; - addr2.addr_parts[0] = 127; - addr2.addr_parts[1] = 0; - addr2.addr_parts[2] = 0; - addr2.addr_parts[3] = 1; - addr.sin_addr.s_addr=addr2.addr; - } - if(NETWORK_BIND(this->sock, (const sockaddr*)&addr, (socklen_t)sizeof(addr)) != 0) - { - std::cout << "FAILED TO BIND: " << strerror(errno) << std::endl; - this->valid = false; - return; - } +#endif + break; - if(NETWORK_LISTEN(this->sock, backlog) != 0) - { - std::cout << "FAILED TO LISTEN FOR SOME REASON" << std::endl; - this->valid = false; - return; - } - this->valid = true; + case SocketType::ST_IPv6_TCP: +#if defined(AF_INET6) + this->sock = NETWORK_SOCKET(AF_INET6, SOCK_STREAM, 0); + +#endif + break; + case SocketType::ST_IPv6_UDP: +#if defined(AF_INET6) + this->sock = NETWORK_SOCKET(AF_INET6, SOCK_DGRAM, 0); + +#endif + break; + case SocketType::ST_UNIX: +#if defined(AF_UNIX) && \ + ((defined(_WIN32) && defined(HAS_AFUNIX)) || !defined(_WIN32)) + this->sock = NETWORK_SOCKET(AF_UNIX, SOCK_DGRAM, 0); + +#endif + break; } - TcpServer::TcpServer(int32_t backlog) : TcpServer(0U, backlog) - { - - } - uint16_t TcpServer::GetPort() - { - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - socklen_t len = sizeof(addr); - NETWORK_GETSOCKNAME(this->sock, (struct sockaddr*)&addr, &len); - if(addr.ss_family == AF_INET) - { - return (uint16_t)ntohs(((struct sockaddr_in*)&addr)->sin_port); - } - #if defined(AF_INET6) - if(addr.ss_family == AF_INET6) - { - return (uint16_t)ntohs(((struct sockaddr_in6*)&addr)->sin6_port); - } - #endif - return 0; - } - uint16_t NetworkStream::GetPort() - { - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - socklen_t len = sizeof(addr); - NETWORK_GETSOCKNAME(this->sock, (struct sockaddr*)&addr, &len); - if(addr.ss_family == AF_INET) - { - return (uint16_t)(((struct sockaddr_in*)&addr)->sin_port); - } - #if defined(AF_INET6) - if(addr.ss_family == AF_INET6) - { - return (uint16_t)(((struct sockaddr_in6*)&addr)->sin6_port); - } - #endif - return 0; - } - bool TcpServer::IsValid() - { - return this->valid; - } - TcpServer::TcpServer(std::string ip, uint16_t port, int32_t backlog) - { - this->owns=true; - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - - uint8_t ipBytes[16]; - bool success = IPParse(ip, &addr); - if(!success) - { - this->valid=false; - return; - } - - SetPort((struct sockaddr*)&addr, port); - - this->sock = NETWORK_SOCKET((int)addr.ss_family, SOCK_STREAM, 0); - if(this->sock < 0) - { - this->valid=false; - return; - } - - - - - if(NETWORK_BIND(this->sock, (const sockaddr*)&addr, (socklen_t)sizeof(addr)) != 0) - { - - std::cout << "FAILED TO BIND: " << strerror(errno) << std::endl; - this->valid = false; - return; - } - - if(NETWORK_LISTEN(this->sock, backlog) != 0) - { - this->valid = false; - return; - } - this->valid = true; - } - TcpServer::TcpServer(int32_t sock, bool owns) - { - this->valid = sock >= 0; - this->sock = sock; - this->owns = owns; - } - TcpServer::~TcpServer() - { - if(this->valid && this->owns) - { - NETWORK_CLOSE(this->sock); - } - this->valid=false; - } - void TcpServer::Close() - { - if(this->valid) - NETWORK_CLOSE(this->sock); - this->valid=false; - } - std::shared_ptr TcpServer::GetStream(std::string& ip, uint16_t& port) - { - struct sockaddr_storage storage; - memset(&storage,0, sizeof(storage)); - socklen_t addrlen=(socklen_t)sizeof(storage); - - int s = NETWORK_ACCEPT(this->sock, (struct sockaddr*)&storage, &addrlen); - if(s < 0) - { - return nullptr; - } - - ip = StringifyIP((struct sockaddr*)&storage); - port = getPort((struct sockaddr*)&storage); - - return std::make_shared(s,true); - } - bool NetworkStream::CanRead() - { - return this->success; - } - bool NetworkStream::CanWrite() - { - return this->success; - } - NetworkStream::NetworkStream(SocketType type) - { - this->endOfStream=false; - this->owns = true; - this->success=false; - switch(type) - { - case SocketType::ST_IPv4_TCP: - #if defined(AF_INET) - this->sock = NETWORK_SOCKET(AF_INET,SOCK_STREAM, 0); - - #endif - break; - case SocketType::ST_IPv4_UDP: - #if defined(AF_INET) - this->sock = NETWORK_SOCKET(AF_INET,SOCK_DGRAM, 0); - - #endif - break; - - case SocketType::ST_IPv6_TCP: - #if defined(AF_INET6) - this->sock = NETWORK_SOCKET(AF_INET6,SOCK_STREAM, 0); - - #endif - break; - case SocketType::ST_IPv6_UDP: - #if defined(AF_INET6) - this->sock = NETWORK_SOCKET(AF_INET6,SOCK_DGRAM, 0); - - #endif - break; - case SocketType::ST_UNIX: - #if defined(AF_UNIX) && ((defined(_WIN32) && defined(HAS_AFUNIX) ) || !defined(_WIN32)) - this->sock = NETWORK_SOCKET(AF_UNIX,SOCK_DGRAM, 0); - - #endif - break; - } - if(this->sock >= 0) this->success=true; - } - NetworkStream::NetworkStream(std::string ipOrFqdn, uint16_t port, bool datagram, bool broadcast, bool supportIPv6) - { - this->endOfStream = false; - this->owns=true; - this->success=false; - std::string portStr = std::to_string((uint32_t)port); - - struct addrinfo hint; - memset(&hint, 0, sizeof(hint)); - #if defined(AF_INET6) - hint.ai_family = supportIPv6 ? AF_UNSPEC : AF_INET; - #else - hint.ai_family = AF_INET; - #endif - hint.ai_socktype = datagram ? SOCK_DGRAM : SOCK_STREAM; - - struct addrinfo* result; - - - int status = NETWORK_GETADDRINFO(ipOrFqdn.c_str(),portStr.c_str(), &hint, &result); - if(status != 0) - { - return; - } - - struct addrinfo* tmp=result; - - - while(tmp != nullptr) - { - sock = NETWORK_SOCKET(tmp->ai_family,tmp->ai_socktype,tmp->ai_protocol); - if(broadcast) - { - int broadcast = 1; - if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)) != 0) { - this->success=false; - NETWORK_CLOSE(this->sock); - continue; - } - } - - if(sock != -1) - { - this->success=true; - if(NETWORK_CONNECT(this->sock,(const sockaddr*)tmp->ai_addr,tmp->ai_addrlen) == 0) break; - this->success=false; - NETWORK_CLOSE(this->sock); - } - tmp = tmp->ai_next; - } - NETWORK_FREEADDRINFO(result); - - } - NetworkStream::NetworkStream(int32_t sock, bool owns) - { - this->endOfStream = false; - this->success = sock >= 0; - this->sock = sock; - this->owns = owns; - } - bool NetworkStream::EndOfStream() - { - return this->endOfStream; - } - void NetworkStream::Listen(int32_t backlog) - { - if(this->success) - NETWORK_LISTEN(this->sock, backlog); - } - - void NetworkStream::Bind(std::string ip, uint16_t port) - { - if(!this->success) return; - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - - uint8_t ipBytes[16]; - bool success = IPParse(ip, &addr); - if(!success) - { - this->success=false; - if(this->owns) - NETWORK_CLOSE(this->sock); - return; - } - - SetPort((struct sockaddr*)&addr, port); - int on=1; - #if defined(SO_REUSEPORT) - NETWORK_SETSOCKOPT(this->sock,SOL_SOCKET,SO_REUSEPORT,(const char*)&on, (socklen_t)sizeof(on)); - #endif - #if defined(SO_REUSEADDR) - NETWORK_SETSOCKOPT(this->sock,SOL_SOCKET,SO_REUSEADDR,(const char*)&on, (socklen_t)sizeof(on)); - #endif - int r = NETWORK_BIND(this->sock, (struct sockaddr*)&addr, sizeof(addr)); - if(r != 0) - { - - std::cout << "FAILED TO BIND: " << strerror(errno) << std::endl; - this->success=false; - if(this->owns) - NETWORK_CLOSE(this->sock); - return; - } - } - void NetworkStream::SetReuseAddress(bool reuse) - { - if(!this->success) return; - int no = reuse ? 1 : 0; - if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&no, sizeof(no)) != 0) - { - this->success=false; - if(this->owns) - NETWORK_CLOSE(this->sock); - - } - } - void NetworkStream::SetReusePort(bool reuse) - { - #if !defined(_WIN32) - if(!this->success) return; - int no = reuse ? 1 : 0; - if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&no, sizeof(no)) != 0) - { - this->success=false; - if(this->owns) - NETWORK_CLOSE(this->sock); - - } - #endif - } - void NetworkStream::SetMulticastTTL(uint8_t ttl) - { - if(!this->success) return; - #if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL) - if (NETWORK_SETSOCKOPT(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl)) != 0) - { - this->success=false; - if(this->owns) - NETWORK_CLOSE(this->sock); - - } - #endif - } - void NetworkStream::SetMulticastMembership(std::string multicastAddress, std::string ifaceIP) - { - if(!this->success) return; - #if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL) - struct sockaddr_storage maddr; - struct sockaddr_storage iaddr; - - bool success = IPParse(multicastAddress, &maddr) && IPParse(ifaceIP, &iaddr); - if(success && maddr.ss_family == AF_INET && iaddr.ss_family == AF_INET) - { - - struct ip_mreq req; - req.imr_multiaddr = ((struct sockaddr_in*)&maddr)->sin_addr; - req.imr_interface = ((struct sockaddr_in*)&iaddr)->sin_addr; - - if(NETWORK_SETSOCKOPT(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&req, sizeof(req)) != 0) - { - - this->success=false; - if(this->owns) - NETWORK_CLOSE(this->sock); - - } - - } - #endif - } - - void NetworkStream::SetBroadcast(bool bC) - { - if(!this->success) return; - int broadcast = bC ? 1 : 0; - if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)) != 0) - { - this->success=false; - if(this->owns) - NETWORK_CLOSE(this->sock); - - } - } - std::shared_ptr NetworkStream::Accept(std::string& ip, uint16_t& port) - { - if(!this->success) return nullptr; - struct sockaddr_storage storage; - socklen_t addrlen=(socklen_t)sizeof(storage); - int s = NETWORK_ACCEPT(this->sock, (struct sockaddr*)&storage, (socklen_t*)&addrlen); - if(s < 0) - { - return nullptr; - } - - ip = StringifyIP((struct sockaddr*)&storage); - port = getPort((struct sockaddr*)&storage); - - return std::make_shared((int32_t)s,(bool)true); - } - size_t NetworkStream::Read(uint8_t* buff, size_t sz) - { - - if(!this->success) return 0; - auto r = NETWORK_RECV(this->sock,(char*)buff,sz,0); - - if(r <= 0) - { - this->endOfStream=true; - return 0; - } - - return (size_t)r; - } - size_t NetworkStream::Write(const uint8_t* buff, size_t sz) - { - - if(!this->success) return 0; - - auto sz2 = NETWORK_SEND(this->sock,(const char*)buff,sz, 0); - if(sz2 <= 0) { - this->endOfStream=true; - return 0; - } - - return (size_t)sz; - } - size_t NetworkStream::ReadFrom(uint8_t* buff, size_t sz, std::string& ip, uint16_t& port) - { - if(!this->success) return 0; - struct sockaddr_storage storage; - socklen_t addrlen=(socklen_t)sizeof(storage); - auto r = NETWORK_RECVFROM(this->sock,(char*)buff,sz,0, (struct sockaddr*)&storage, (socklen_t*)&addrlen); - ip = StringifyIP((struct sockaddr*)&storage); - port = getPort((struct sockaddr*)&storage); - if(r < 0) return 0; - return (size_t)r; - - - - - - } - size_t NetworkStream::WriteTo(const uint8_t* buff, size_t sz, std::string ip, uint16_t port) - { - - if(!this->success) return 0; - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - - uint8_t ipBytes[16]; - bool success = IPParse(ip, &addr); - if(!success) - { - this->success=false; - if(this->owns) - NETWORK_CLOSE(this->sock); - return 0; - } - - SetPort((struct sockaddr*)&addr, port); - auto sz2 = NETWORK_SENDTO(this->sock,(const char*)buff,sz, 0, (const sockaddr*)&addr, (socklen_t)sizeof(addr)); - if(sz2 < 0) return 0; - return (size_t)sz2; - } - void NetworkStream::Close() - { - if(this->owns && this->success) - NETWORK_CLOSE(this->sock); - this->success=0; - } - NetworkStream::~NetworkStream() - { - if(this->owns && this->success) - NETWORK_CLOSE(this->sock); - this->success=0; - } - void NetworkStream::SetNoDelay(bool noDelay) - { - - int noDelay2 = noDelay; - NETWORK_SETSOCKOPT(this->sock, SOL_SOCKET, TCP_NODELAY, (const char*)&noDelay2,(socklen_t)sizeof(noDelay2)); - } - + if (this->sock >= 0) + this->success = true; } +NetworkStream::NetworkStream(std::string ipOrFqdn, uint16_t port, bool datagram, + bool broadcast, bool supportIPv6) { + this->endOfStream = false; + this->owns = true; + this->success = false; + std::string portStr = std::to_string((uint32_t)port); + + struct addrinfo hint; + memset(&hint, 0, sizeof(hint)); +#if defined(AF_INET6) + hint.ai_family = supportIPv6 ? AF_UNSPEC : AF_INET; +#else + hint.ai_family = AF_INET; +#endif + hint.ai_socktype = datagram ? SOCK_DGRAM : SOCK_STREAM; + + struct addrinfo *result; + + int status = + NETWORK_GETADDRINFO(ipOrFqdn.c_str(), portStr.c_str(), &hint, &result); + if (status != 0) { + return; + } + + struct addrinfo *tmp = result; + + while (tmp != nullptr) { + sock = + NETWORK_SOCKET(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol); + if (broadcast) { + int broadcast = 1; + if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_BROADCAST, + (const char *)&broadcast, + sizeof(broadcast)) != 0) { + this->success = false; + NETWORK_CLOSE(this->sock); + continue; + } + } + + if (sock != -1) { + this->success = true; + if (NETWORK_CONNECT(this->sock, (const sockaddr *)tmp->ai_addr, + tmp->ai_addrlen) == 0) + break; + this->success = false; + NETWORK_CLOSE(this->sock); + } + tmp = tmp->ai_next; + } + NETWORK_FREEADDRINFO(result); +} +NetworkStream::NetworkStream(int32_t sock, bool owns) { + this->endOfStream = false; + this->success = sock >= 0; + this->sock = sock; + this->owns = owns; +} +bool NetworkStream::EndOfStream() { return this->endOfStream; } +void NetworkStream::Listen(int32_t backlog) { + if (this->success) + NETWORK_LISTEN(this->sock, backlog); +} + +void NetworkStream::Bind(std::string ip, uint16_t port) { + if (!this->success) + return; + struct sockaddr_storage addr; + memset(&addr, 0, sizeof(addr)); + + uint8_t ipBytes[16]; + bool success = IPParse(ip, &addr); + if (!success) { + this->success = false; + if (this->owns) + NETWORK_CLOSE(this->sock); + return; + } + + SetPort((struct sockaddr *)&addr, port); + int on = 1; +#if defined(SO_REUSEPORT) + NETWORK_SETSOCKOPT(this->sock, SOL_SOCKET, SO_REUSEPORT, (const char *)&on, + (socklen_t)sizeof(on)); +#endif +#if defined(SO_REUSEADDR) + NETWORK_SETSOCKOPT(this->sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, + (socklen_t)sizeof(on)); +#endif + int r = NETWORK_BIND(this->sock, (struct sockaddr *)&addr, sizeof(addr)); + if (r != 0) { + + std::cout << "FAILED TO BIND: " << strerror(errno) << std::endl; + this->success = false; + if (this->owns) + NETWORK_CLOSE(this->sock); + return; + } +} +void NetworkStream::SetReuseAddress(bool reuse) { + if (!this->success) + return; + int no = reuse ? 1 : 0; + if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&no, + sizeof(no)) != 0) { + this->success = false; + if (this->owns) + NETWORK_CLOSE(this->sock); + } +} +void NetworkStream::SetReusePort(bool reuse) { +#if !defined(_WIN32) + if (!this->success) + return; + int no = reuse ? 1 : 0; + if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_REUSEPORT, (const char *)&no, + sizeof(no)) != 0) { + this->success = false; + if (this->owns) + NETWORK_CLOSE(this->sock); + } +#endif +} +void NetworkStream::SetMulticastTTL(uint8_t ttl) { + if (!this->success) + return; +#if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL) + if (NETWORK_SETSOCKOPT(sock, IPPROTO_IP, IP_MULTICAST_TTL, + (const char *)&ttl, sizeof(ttl)) != 0) { + this->success = false; + if (this->owns) + NETWORK_CLOSE(this->sock); + } +#endif +} +void NetworkStream::SetMulticastMembership(std::string multicastAddress, + std::string ifaceIP) { + if (!this->success) + return; +#if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL) + struct sockaddr_storage maddr; + struct sockaddr_storage iaddr; + + bool success = + IPParse(multicastAddress, &maddr) && IPParse(ifaceIP, &iaddr); + if (success && maddr.ss_family == AF_INET && iaddr.ss_family == AF_INET) { + + struct ip_mreq req; + req.imr_multiaddr = ((struct sockaddr_in *)&maddr)->sin_addr; + req.imr_interface = ((struct sockaddr_in *)&iaddr)->sin_addr; + + if (NETWORK_SETSOCKOPT(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (const char *)&req, sizeof(req)) != 0) { + + this->success = false; + if (this->owns) + NETWORK_CLOSE(this->sock); + } + } +#endif +} + +void NetworkStream::SetBroadcast(bool bC) { + if (!this->success) + return; + int broadcast = bC ? 1 : 0; + if (NETWORK_SETSOCKOPT(sock, SOL_SOCKET, SO_BROADCAST, + (const char *)&broadcast, sizeof(broadcast)) != 0) { + this->success = false; + if (this->owns) + NETWORK_CLOSE(this->sock); + } +} +std::shared_ptr NetworkStream::Accept(std::string &ip, + uint16_t &port) { + if (!this->success) + return nullptr; + struct sockaddr_storage storage; + socklen_t addrlen = (socklen_t)sizeof(storage); + int s = NETWORK_ACCEPT(this->sock, (struct sockaddr *)&storage, + (socklen_t *)&addrlen); + if (s < 0) { + return nullptr; + } + + ip = StringifyIP((struct sockaddr *)&storage); + port = getPort((struct sockaddr *)&storage); + + return std::make_shared((int32_t)s, (bool)true); +} +size_t NetworkStream::Read(uint8_t *buff, size_t sz) { + + if (!this->success) + return 0; + auto r = NETWORK_RECV(this->sock, (char *)buff, sz, 0); + + if (r <= 0) { + this->endOfStream = true; + return 0; + } + + return (size_t)r; +} +size_t NetworkStream::Write(const uint8_t *buff, size_t sz) { + + if (!this->success) + return 0; + + auto sz2 = NETWORK_SEND(this->sock, (const char *)buff, sz, 0); + if (sz2 <= 0) { + this->endOfStream = true; + return 0; + } + + return (size_t)sz; +} +size_t NetworkStream::ReadFrom(uint8_t *buff, size_t sz, std::string &ip, + uint16_t &port) { + if (!this->success) + return 0; + struct sockaddr_storage storage; + socklen_t addrlen = (socklen_t)sizeof(storage); + auto r = + NETWORK_RECVFROM(this->sock, (char *)buff, sz, 0, + (struct sockaddr *)&storage, (socklen_t *)&addrlen); + ip = StringifyIP((struct sockaddr *)&storage); + port = getPort((struct sockaddr *)&storage); + if (r < 0) + return 0; + return (size_t)r; +} +size_t NetworkStream::WriteTo(const uint8_t *buff, size_t sz, std::string ip, + uint16_t port) { + + if (!this->success) + return 0; + struct sockaddr_storage addr; + memset(&addr, 0, sizeof(addr)); + + uint8_t ipBytes[16]; + bool success = IPParse(ip, &addr); + if (!success) { + this->success = false; + if (this->owns) + NETWORK_CLOSE(this->sock); + return 0; + } + + SetPort((struct sockaddr *)&addr, port); + auto sz2 = NETWORK_SENDTO(this->sock, (const char *)buff, sz, 0, + (const sockaddr *)&addr, (socklen_t)sizeof(addr)); + if (sz2 < 0) + return 0; + return (size_t)sz2; +} +void NetworkStream::Close() { + if (this->owns && this->success) + NETWORK_CLOSE(this->sock); + this->success = 0; +} +NetworkStream::~NetworkStream() { + if (this->owns && this->success) + NETWORK_CLOSE(this->sock); + this->success = 0; +} +void NetworkStream::SetNoDelay(bool noDelay) { + + int noDelay2 = noDelay; + NETWORK_SETSOCKOPT(this->sock, SOL_SOCKET, TCP_NODELAY, + (const char *)&noDelay2, (socklen_t)sizeof(noDelay2)); +} + +} // namespace Tesses::Framework::Streams #else namespace Tesses::Framework::Streams { -TcpServer::TcpServer(int32_t sock,bool owns) -{ - -} -TcpServer::TcpServer(uint16_t port, int32_t backlog) -{ - -} -TcpServer::TcpServer(std::string ip, uint16_t port, int32_t backlog) -{ - -} -TcpServer::TcpServer(std::string unixPath,int32_t backlog) -{ - -} -std::shared_ptr TcpServer::GetStream(std::string& ip, uint16_t& port) -{ +TcpServer::TcpServer(int32_t sock, bool owns) {} +TcpServer::TcpServer(uint16_t port, int32_t backlog) {} +TcpServer::TcpServer(std::string ip, uint16_t port, int32_t backlog) {} +TcpServer::TcpServer(std::string unixPath, int32_t backlog) {} +std::shared_ptr TcpServer::GetStream(std::string &ip, + uint16_t &port) { return nullptr; } -TcpServer::~TcpServer() -{ +TcpServer::~TcpServer() {} +bool TcpServer::IsValid() { return this->valid; } +void TcpServer::Close() {} +bool NetworkStream::EndOfStream() { return true; } +bool NetworkStream::CanRead() { return false; } +bool NetworkStream::CanWrite() { return false; } -} -bool TcpServer::IsValid() - { - return this->valid; - } -void TcpServer::Close() -{ +NetworkStream::NetworkStream(SocketType type) {} +NetworkStream::NetworkStream(std::string ipOrFqdn, uint16_t port, bool datagram, + bool broadcast, bool supportIPv6) {} +NetworkStream::NetworkStream(std::string unixPath, bool isServer) {} +NetworkStream::NetworkStream(int32_t sock, bool owns) {} +void NetworkStream::Listen(int32_t backlog) {} +void NetworkStream::Bind(std::string ip, uint16_t port) {} +void NetworkStream::SetBroadcast(bool bC) {} +void NetworkStream::SetReuseAddress(bool reuse) {} +void NetworkStream::SetReusePort(bool reuse) {} +void NetworkStream::SetMulticastTTL(uint8_t ttl) {} +void NetworkStream::SetMulticastMembership(std::string multicastAddress, + std::string ifaceIP = "0.0.0.0") {} -} -bool NetworkStream::EndOfStream() { - return true; -} -bool NetworkStream::CanRead() { - return false; -} -bool NetworkStream::CanWrite() { - return false; -} - -NetworkStream::NetworkStream(SocketType type) -{ - -} -NetworkStream::NetworkStream(std::string ipOrFqdn, uint16_t port, bool datagram,bool broadcast,bool supportIPv6) -{ - -} -NetworkStream::NetworkStream(std::string unixPath, bool isServer) -{ - -} -NetworkStream::NetworkStream(int32_t sock, bool owns) -{ - -} -void NetworkStream::Listen(int32_t backlog) -{ - -} -void NetworkStream::Bind(std::string ip, uint16_t port) -{ - -} -void NetworkStream::SetBroadcast(bool bC) -{ - -} -void NetworkStream::SetReuseAddress(bool reuse) -{ - -} -void NetworkStream::SetReusePort(bool reuse) -{ - -} -void NetworkStream::SetMulticastTTL(uint8_t ttl) -{ - -} -void NetworkStream::SetMulticastMembership(std::string multicastAddress, std::string ifaceIP="0.0.0.0") -{ - -} - -std::shared_ptr NetworkStream::Accept(std::string& ip, uint16_t& port) -{ +std::shared_ptr NetworkStream::Accept(std::string &ip, + uint16_t &port) { return nullptr; } -size_t NetworkStream::Read(uint8_t* buff, size_t sz) -{ +size_t NetworkStream::Read(uint8_t *buff, size_t sz) { return 0; } +size_t NetworkStream::Write(const uint8_t *buff, size_t sz) { return 0; } +size_t NetworkStream::ReadFrom(uint8_t *buff, size_t sz, std::string &ip, + uint16_t &port) { return 0; } -size_t NetworkStream::Write(const uint8_t* buff, size_t sz) -{ +size_t NetworkStream::WriteTo(const uint8_t *buff, size_t sz, std::string ip, + uint16_t port) { return 0; } -size_t NetworkStream::ReadFrom(uint8_t* buff, size_t sz, std::string& ip, uint16_t& port) -{ - return 0; -} -size_t NetworkStream::WriteTo(const uint8_t* buff, size_t sz, std::string ip, uint16_t port) -{ - return 0; -} -std::vector> NetworkStream::GetIPs(bool ipV6) -{ +std::vector> +NetworkStream::GetIPs(bool ipV6) { return {}; } -NetworkStream::~NetworkStream() -{ - -} -void NetworkStream::SetNoDelay(bool noDelay) -{ - -} -void NetworkStream::Close() -{ - -} -uint16_t NetworkStream::GetPort() -{ - return 0; -} -uint16_t TcpServer::GetPort() -{ - return 0; -} -bool NetworkStream::DataAvailable(int to){ - return false; -} -} +NetworkStream::~NetworkStream() {} +void NetworkStream::SetNoDelay(bool noDelay) {} +void NetworkStream::Close() {} +uint16_t NetworkStream::GetPort() { return 0; } +uint16_t TcpServer::GetPort() { return 0; } +bool NetworkStream::DataAvailable(int to) { return false; } +} // namespace Tesses::Framework::Streams #endif diff --git a/src/Streams/PtyStream.cpp b/src/Streams/PtyStream.cpp index 5aaa57f..2f6320e 100644 --- a/src/Streams/PtyStream.cpp +++ b/src/Streams/PtyStream.cpp @@ -1,147 +1,163 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Streams/PtyStream.hpp" -#if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS) -#if defined(__APPLE__) +#if !defined(GEKKO) && !defined(__PS2__) && \ + !defined(_WIN32) && !defined(__SWITCH__) && \ + defined(TESSESFRAMEWORK_ENABLE_PROCESS) +#if __has_include() #include -#else +#elif __has_include() #include +#elif __has_include() +#include #endif #include -#include #include - +#include +#include +#include #endif namespace Tesses::Framework::Streams { - PtyStream::PtyStream(WindowSize windowSize,std::string filename, std::vector args, std::vector env) - { - #if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS) - this->wS = windowSize; - this->eos=false; - winsize sz; - sz.ws_col =(unsigned short)windowSize.Columns; - sz.ws_row = (unsigned short)windowSize.Rows; - sz.ws_xpixel = (unsigned short)windowSize.Width; - sz.ws_ypixel = (unsigned short)windowSize.Height; - termios ios; - cfmakeraw(&ios); +PtyStream::PtyStream(WindowSize windowSize, std::string filename, + std::vector args, + std::vector env) { +#if !defined(GEKKO) && !defined(__PS2__) && \ + !defined(_WIN32) && !defined(__SWITCH__) && \ + defined(TESSESFRAMEWORK_ENABLE_PROCESS) + this->wS = windowSize; + this->eos = false; + winsize sz; + sz.ws_col = (unsigned short)windowSize.Columns; + sz.ws_row = (unsigned short)windowSize.Rows; + sz.ws_xpixel = (unsigned short)windowSize.Width; + sz.ws_ypixel = (unsigned short)windowSize.Height; + termios ios; + cfmakeraw(&ios); - pid= forkpty(&this->socket,NULL,&ios,&sz); - if(pid == -1) - { - this->eos=true; - } - if(pid == 0) - { - char** argv = new char*[args.size()+1]; - argv[args.size()]=NULL; - char** envp = new char*[env.size()+1]; - envp[env.size()]=NULL; - - for(size_t i = 0; i < args.size();i++) - { - argv[i] = (char*)args[i].c_str(); - } - for(size_t i = 0; i < env.size();i++) - { - envp[i] = (char*)env[i].c_str(); - } - - if(execve(filename.c_str(),argv,envp) == -1) - { - perror("execve returned -1"); - exit(1); - } - } - int flags = fcntl(this->socket, F_GETFL, 0); - if(flags == -1) { - perror("fcntl F_GETFL"); - this->eos=true; - return; - } - flags |= O_NONBLOCK; - - flags=fcntl(this->socket,F_SETFL,flags); - if(flags == -1) { - perror("fcntl F_SETFL"); - - this->eos=true; - return; - } - #endif + pid = forkpty(&this->socket, NULL, &ios, &sz); + if (pid == -1) { + this->eos = true; } - bool PtyStream::EndOfStream() - { - return this->eos; - } - bool PtyStream::CanRead() - { - return true; - } - bool PtyStream::CanWrite() - { - return true; - } - size_t PtyStream::Read(uint8_t* buff, size_t sz) - { - if(this->eos) return 0; - #if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS) - auto res = read(this->socket, buff,sz); - - if(res == -1) - { - if(errno != EAGAIN && errno != EWOULDBLOCK) - this->eos=true; - return 0; + if (pid == 0) { + char **argv = new char *[args.size() + 1]; + argv[args.size()] = NULL; + char **envp = new char *[env.size() + 1]; + envp[env.size()] = NULL; + + for (size_t i = 0; i < args.size(); i++) { + argv[i] = (char *)args[i].c_str(); } - return (size_t)res; - #else + for (size_t i = 0; i < env.size(); i++) { + envp[i] = (char *)env[i].c_str(); + } + + if (execve(filename.c_str(), argv, envp) == -1) { + perror("execve returned -1"); + exit(1); + } + } + int flags = fcntl(this->socket, F_GETFL, 0); + if (flags == -1) { + perror("fcntl F_GETFL"); + this->eos = true; + return; + } + flags |= O_NONBLOCK; + + flags = fcntl(this->socket, F_SETFL, flags); + if (flags == -1) { + perror("fcntl F_SETFL"); + + this->eos = true; + return; + } +#endif +} +bool PtyStream::EndOfStream() { return this->eos; } +bool PtyStream::CanRead() { return true; } +bool PtyStream::CanWrite() { return true; } +size_t PtyStream::Read(uint8_t *buff, size_t sz) { + if (this->eos) return 0; - #endif - } - size_t PtyStream::Write(const uint8_t* buff, size_t sz) - { - #if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS) - auto res = write(this->socket, buff,sz); - return res; - #else +#if !defined(GEKKO) && !defined(__PS2__) && \ + !defined(_WIN32) && !defined(__SWITCH__) && \ + defined(TESSESFRAMEWORK_ENABLE_PROCESS) + auto res = read(this->socket, buff, sz); + + if (res == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) + this->eos = true; return 0; - #endif } - WindowSize PtyStream::GetWindowSize() - { - return this->wS; - } - void PtyStream::Resize(WindowSize windowSize) - { - #if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS) - this->wS = windowSize; - winsize sz; - sz.ws_col =(unsigned short)windowSize.Columns; - sz.ws_row = (unsigned short)windowSize.Rows; - sz.ws_xpixel = (unsigned short)windowSize.Width; - sz.ws_ypixel = (unsigned short)windowSize.Height; - - ioctl(this->socket,TIOCSWINSZ,&sz); - #endif - } - PtyStream::~PtyStream() - { - if(this->eos) return; - this->eos=true; - #if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS) - close(this->socket); - - kill((pid_t)this->pid,SIGHUP); - #endif - } - void PtyStream::Close() - { - if(this->eos) return; - this->eos=true; - #if !defined(GEKKO) && !defined(__APPLE__) && !defined(__PS2__) && !defined(_WIN32) && !defined(__SWITCH__) && !defined(__FreeBSD__) && defined(TESSESFRAMEWORK_ENABLE_PROCESS) - close(this->socket); - - kill((pid_t)this->pid,SIGHUP); - #endif - } -} \ No newline at end of file + return (size_t)res; +#else + return 0; +#endif +} +size_t PtyStream::Write(const uint8_t *buff, size_t sz) { +#if !defined(GEKKO) && !defined(__PS2__) && \ + !defined(_WIN32) && !defined(__SWITCH__) && \ + defined(TESSESFRAMEWORK_ENABLE_PROCESS) + auto res = write(this->socket, buff, sz); + return res; +#else + return 0; +#endif +} +WindowSize PtyStream::GetWindowSize() { return this->wS; } +void PtyStream::Resize(WindowSize windowSize) { +#if !defined(GEKKO) && !defined(__PS2__) && \ + !defined(_WIN32) && !defined(__SWITCH__) && \ + defined(TESSESFRAMEWORK_ENABLE_PROCESS) + this->wS = windowSize; + winsize sz; + sz.ws_col = (unsigned short)windowSize.Columns; + sz.ws_row = (unsigned short)windowSize.Rows; + sz.ws_xpixel = (unsigned short)windowSize.Width; + sz.ws_ypixel = (unsigned short)windowSize.Height; + + ioctl(this->socket, TIOCSWINSZ, &sz); +#endif +} +PtyStream::~PtyStream() { + if (this->eos) + return; + this->eos = true; +#if !defined(GEKKO) && !defined(__PS2__) && \ + !defined(_WIN32) && !defined(__SWITCH__) && \ + defined(TESSESFRAMEWORK_ENABLE_PROCESS) + close(this->socket); + + kill((pid_t)this->pid, SIGHUP); +#endif +} +void PtyStream::Close() { + if (this->eos) + return; + this->eos = true; +#if !defined(GEKKO) && !defined(__PS2__) && \ + !defined(_WIN32) && !defined(__SWITCH__) && \ + defined(TESSESFRAMEWORK_ENABLE_PROCESS) + close(this->socket); + + kill((pid_t)this->pid, SIGHUP); +#endif +} +} // namespace Tesses::Framework::Streams \ No newline at end of file diff --git a/src/Streams/Stream.cpp b/src/Streams/Stream.cpp index f801e05..d04db79 100644 --- a/src/Streams/Stream.cpp +++ b/src/Streams/Stream.cpp @@ -1,145 +1,117 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Streams/Stream.hpp" #include namespace Tesses::Framework::Streams { - int32_t Stream::ReadByte() - { - uint8_t b; - if(Read(&b, 1) == 0) return -1; - return b; - } - void Stream::WriteByte(uint8_t b) - { - Write(&b, 1); - } - size_t Stream::Read(uint8_t* buffer, size_t count) - { - return 0; - } - size_t Stream::Write(const uint8_t* buffer, size_t count) - { - return 0; - } - size_t Stream::ReadBlock(uint8_t* buffer,size_t len) - { - size_t read; - size_t readTotal = 0; - do{ - read = 1024; - if(len < 1024) - read = len; - if(read > 0) - read=this->Read(buffer,read); - - - - buffer += read; - len -= read; - readTotal += read; - } while(read > 0); - return readTotal; - } - - void Stream::WriteBlock(const uint8_t* buffer,size_t len) - { - size_t read; - do{ - read = 1024; - if(len < 1024) - read = len; - if(read > 0) - { - size_t r0=read; - read=this->Write(buffer,read); - - if(read == 0) - { - throw std::out_of_range("Failed to write!"); - } - } - - - buffer += read; - len -= read; - } while(read > 0 && !this->EndOfStream()); - } - bool Stream::CanRead() - { - return false; - } - bool Stream::CanWrite() - { - return false; - } - bool Stream::CanSeek() - { - return false; - } - bool Stream::EndOfStream() - { - return false; - } - int64_t Stream::GetPosition() - { - return 0; - } - int64_t Stream::GetLength() - { - if(!CanSeek()) return 0; - int64_t curPos = GetPosition(); - Seek(0, SeekOrigin::End); - int64_t len = GetPosition(); - Seek(curPos, SeekOrigin::Begin); - return len; - } - void Stream::Flush() - { - - } - void Stream::Seek(int64_t pos, SeekOrigin whence) - { - - } - void Stream::Close() - { - - } - - void Stream::CopyToLimit(std::shared_ptr strm,uint64_t len, size_t buffSize) - { - size_t read; - std::vector buffer(buffSize); - uint64_t offset = 0; - - do { - if(offset >= len) break; - read = (size_t)std::min(len-offset,(uint64_t)buffer.size()); - - read = this->Read(buffer.data(),read); - strm->WriteBlock(buffer.data(), read); - - offset += read; - - } while(read > 0 && !strm->EndOfStream()); - strm->Flush(); - - } - - void Stream::CopyTo(std::shared_ptr strm, size_t buffSize) - { - size_t read; - std::vector buffer(buffSize); - do { - read = this->Read(buffer.data(),buffer.size()); - strm->WriteBlock(buffer.data(), read); - - } while(read > 0 && !strm->EndOfStream()); - strm->Flush(); - - - } - Stream::~Stream() - { - - } +int32_t Stream::ReadByte() { + uint8_t b; + if (Read(&b, 1) == 0) + return -1; + return b; } +void Stream::WriteByte(uint8_t b) { Write(&b, 1); } +size_t Stream::Read(uint8_t *buffer, size_t count) { return 0; } +size_t Stream::Write(const uint8_t *buffer, size_t count) { return 0; } +size_t Stream::ReadBlock(uint8_t *buffer, size_t len) { + size_t read; + size_t readTotal = 0; + do { + read = 1024; + if (len < 1024) + read = len; + if (read > 0) + read = this->Read(buffer, read); + + buffer += read; + len -= read; + readTotal += read; + } while (read > 0); + return readTotal; +} + +void Stream::WriteBlock(const uint8_t *buffer, size_t len) { + size_t read; + do { + read = 1024; + if (len < 1024) + read = len; + if (read > 0) { + size_t r0 = read; + read = this->Write(buffer, read); + + if (read == 0) { + throw std::out_of_range("Failed to write!"); + } + } + + buffer += read; + len -= read; + } while (read > 0 && !this->EndOfStream()); +} +bool Stream::CanRead() { return false; } +bool Stream::CanWrite() { return false; } +bool Stream::CanSeek() { return false; } +bool Stream::EndOfStream() { return false; } +int64_t Stream::GetPosition() { return 0; } +int64_t Stream::GetLength() { + if (!CanSeek()) + return 0; + int64_t curPos = GetPosition(); + Seek(0, SeekOrigin::End); + int64_t len = GetPosition(); + Seek(curPos, SeekOrigin::Begin); + return len; +} +void Stream::Flush() {} +void Stream::Seek(int64_t pos, SeekOrigin whence) {} +void Stream::Close() {} + +void Stream::CopyToLimit(std::shared_ptr strm, uint64_t len, + size_t buffSize) { + size_t read; + std::vector buffer(buffSize); + uint64_t offset = 0; + + do { + if (offset >= len) + break; + read = (size_t)std::min(len - offset, (uint64_t)buffer.size()); + + read = this->Read(buffer.data(), read); + strm->WriteBlock(buffer.data(), read); + + offset += read; + + } while (read > 0 && !strm->EndOfStream()); + strm->Flush(); +} + +void Stream::CopyTo(std::shared_ptr strm, size_t buffSize) { + size_t read; + std::vector buffer(buffSize); + do { + read = this->Read(buffer.data(), buffer.size()); + strm->WriteBlock(buffer.data(), read); + + } while (read > 0 && !strm->EndOfStream()); + strm->Flush(); +} +Stream::~Stream() {} +} // namespace Tesses::Framework::Streams diff --git a/src/TF_Init.cpp b/src/TF_Init.cpp index 428afda..2d8af36 100644 --- a/src/TF_Init.cpp +++ b/src/TF_Init.cpp @@ -1,10 +1,29 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Common.hpp" -#include "TessesFramework/Streams/NetworkStream.hpp" -#include "TessesFramework/Lazy.hpp" -#include "TessesFramework/Filesystem/LocalFS.hpp" -#include "TessesFramework/Platform/Environment.hpp" #include "TessesFramework/Filesystem/FSHelpers.hpp" +#include "TessesFramework/Filesystem/LocalFS.hpp" +#include "TessesFramework/Lazy.hpp" +#include "TessesFramework/Platform/Environment.hpp" #include "TessesFramework/Serialization/Json.hpp" +#include "TessesFramework/Streams/NetworkStream.hpp" +#include "TessesFramework/Text/StringConverter.hpp" #include #include #include @@ -14,13 +33,28 @@ #include #include +#if defined(__APPLE__) +// macOS, iOS, or other Darwin-based OS +#include + +#if TARGET_OS_MAC && !TARGET_OS_IPHONE +#include + +// Specifically macOS (not iOS, tvOS, watchOS) +#endif +#endif + +#if defined(__FreeBSD__) +#include +#endif + #if defined(TESSESFRAMEWORK_ENABLE_SQLITE) extern "C" { #include "Serialization/sqlite/sqlite3.h" } #if defined(GEKKO) || defined(__SWITCH__) || defined(__PS2__) extern "C" { - sqlite3_vfs *sqlite3_demovfs(); +sqlite3_vfs *sqlite3_demovfs(); } #endif #endif @@ -29,17 +63,17 @@ extern "C" { #include #undef min #include +#include #elif defined(__SWITCH__) extern "C" { #include } #elif defined(GEKKO) +#include +#include +#include #include #include -#include -#include -#include - #if defined(HW_RVL) #if defined(TESSESFRAMEWORK_USE_WII_SOCKET) @@ -57,1054 +91,929 @@ static GXRModeObj *rmode = NULL; #include #endif +namespace Tesses::Framework { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) && \ + (defined(GEKKO) || defined(__SWITCH__)) +namespace Threading { +extern void JoinAllThreads(); +extern void LookForFinishedThreads(); +} // namespace Threading +#endif +volatile static bool isRunningSig = true; +volatile static std::atomic isRunning; +volatile static std::atomic gaming_console_events = true; -namespace Tesses::Framework -{ - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) && (defined(GEKKO) || defined(__SWITCH__)) - namespace Threading - { - extern void JoinAllThreads(); - extern void LookForFinishedThreads(); +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) +Threading::Mutex timers_mtx; +Threading::Mutex invokings_mtx; + +std::queue> invokings; +#endif + +void TF_Invoke(std::function cb) { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + invokings_mtx.Lock(); + invokings.push(cb); + invokings_mtx.Unlock(); +#else + cb(); +#endif +} +void TF_Sleep(uint32_t sleepMS) { +#if defined(_WIN32) + Sleep((DWORD)sleepMS); +#else + struct timespec ts; + + ts.tv_sec = (time_t)(sleepMS / 1000); + ts.tv_nsec = (sleepMS % 1000) * 1000000; + + nanosleep(&ts, NULL); +#endif +} +void TF_ConnectToSelf(uint16_t port) { + Tesses::Framework::Streams::NetworkStream ns("127.0.0.1", port, false, + false, false); +} +bool TF_IsRunning() { return isRunning; } +static void _sigInt(int c) { isRunningSig = false; } +void TF_RunEventLoop() { + TF_InitEventLoop(); + while (isRunning) { + TF_RunEventLoopItteration(); } - #endif - volatile static bool isRunningSig=true; - volatile static std::atomic isRunning; - volatile static std::atomic gaming_console_events=true; +} +#if defined(__SWITCH__) +bool initedConsole = false; +PadState default_pad; +#endif +uint64_t ittr = 0; - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - Threading::Mutex timers_mtx; - Threading::Mutex invokings_mtx; +static std::shared_ptr timer_handler = + std::make_shared(); - std::queue> invokings; - #endif +std::shared_ptr +TF_Timer_Handler::Make(std::shared_ptr handler) { + auto timer = new TF_Timer_Handle(handler); + std::shared_ptr handle(timer); +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + timers_mtx.Lock(); +#endif + handler->handles.push_back(handle); +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + timers_mtx.Unlock(); +#endif + return handle; +} +std::shared_ptr TF_Timer() { + return TF_Timer_Handler::Make(timer_handler); +} +std::shared_ptr TF_Timer(std::function cb, + int64_t interval, bool enabled) { + auto handle = TF_Timer(); + handle->SetCallback(cb); + handle->SetIntervalFromMilliseconds(interval); - void TF_Invoke(std::function cb) - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - invokings_mtx.Lock(); - invokings.push(cb); - invokings_mtx.Unlock(); - #else - cb(); - #endif - } - void TF_Sleep(uint32_t sleepMS) - { - #if defined(_WIN32) - Sleep((DWORD)sleepMS); - #else - struct timespec ts; - - ts.tv_sec = (time_t)(sleepMS / 1000); - ts.tv_nsec = (sleepMS % 1000) * 1000000; + handle->SetEnabled(enabled); + return handle; +} +std::shared_ptr +TF_Timer(std::function cb, + std::chrono::duration interval, bool enabled) { + auto handle = TF_Timer(); + handle->SetCallback(cb); + handle->SetIntervalFromDuration(interval); - nanosleep(&ts,NULL); - #endif - } - void TF_ConnectToSelf(uint16_t port) - { - Tesses::Framework::Streams::NetworkStream ns("127.0.0.1",port,false,false,false); + handle->SetEnabled(enabled); + return handle; +} - } - bool TF_IsRunning() - { - return isRunning; - } - static void _sigInt(int c) - { - isRunningSig=false; - } - void TF_RunEventLoop() - { - TF_InitEventLoop(); - while(isRunning) - { - TF_RunEventLoopItteration(); - } - } - #if defined(__SWITCH__) - bool initedConsole=false; - PadState default_pad; - #endif - uint64_t ittr=0; +void TF_Timer_Handler::Update() { + std::chrono::time_point + cur = std::chrono::time_point_cast( + std::chrono::steady_clock::now()); + std::vector> cbs; +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + timers_mtx.Lock(); +#endif - - static std::shared_ptr timer_handler = std::make_shared(); - - std::shared_ptr TF_Timer_Handler::Make(std::shared_ptr handler) - { - auto timer = new TF_Timer_Handle(handler); - std::shared_ptr handle(timer); - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - timers_mtx.Lock(); - #endif - handler->handles.push_back(handle); - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - timers_mtx.Unlock(); - #endif - return handle; - } - - - std::shared_ptr TF_Timer() - { - return TF_Timer_Handler::Make(timer_handler); - } - std::shared_ptr TF_Timer(std::function cb, int64_t interval, bool enabled) - { - auto handle = TF_Timer(); - handle->SetCallback(cb); - handle->SetIntervalFromMilliseconds(interval); - - handle->SetEnabled(enabled); - return handle; - } - std::shared_ptr TF_Timer(std::function cb, std::chrono::duration interval, bool enabled) - { - auto handle = TF_Timer(); - handle->SetCallback(cb); - handle->SetIntervalFromDuration(interval); - - handle->SetEnabled(enabled); - return handle; - } - - void TF_Timer_Handler::Update() - { - std::chrono::time_point cur = std::chrono::time_point_cast(std::chrono::steady_clock::now()); - std::vector> cbs; - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - timers_mtx.Lock(); - #endif - - for(auto index = this->handles.begin(); index != this->handles.end(); index++) - { - if(index->expired()) - { - this->handles.erase(index); - index--; - } - else { - auto handle = index->lock(); - if(handle && handle->enabled && (handle->last + handle->interval) <= cur && handle->cb) - { - handle->last = cur; - cbs.push_back(handle->cb); - } + for (auto index = this->handles.begin(); index != this->handles.end(); + index++) { + if (index->expired()) { + this->handles.erase(index); + index--; + } else { + auto handle = index->lock(); + if (handle && handle->enabled && + (handle->last + handle->interval) <= cur && handle->cb) { + handle->last = cur; + cbs.push_back(handle->cb); } } - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - timers_mtx.Unlock(); - #endif - for(auto item : cbs) - { - item(); - } } - void TF_Timer_Handle::SetCallback(std::function cb) - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - timers_mtx.Lock(); - #endif - this->cb = cb; - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - timers_mtx.Unlock(); - #endif +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + timers_mtx.Unlock(); +#endif + for (auto item : cbs) { + item(); } - TF_Timer_Handle::TF_Timer_Handle(std::shared_ptr handler) : timerHandler(handler) - { +} +void TF_Timer_Handle::SetCallback(std::function cb) { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + timers_mtx.Lock(); +#endif + this->cb = cb; +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + timers_mtx.Unlock(); +#endif +} +TF_Timer_Handle::TF_Timer_Handle(std::shared_ptr handler) + : timerHandler(handler) {} +void TF_Timer_Handle::SetEnabled(bool enabled) { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + timers_mtx.Lock(); +#endif + this->enabled = enabled; +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + timers_mtx.Unlock(); +#endif +} +bool TF_Timer_Handle::GetEnabled() { return this->enabled; } +void TF_Timer_Handle::SetIntervalFromDuration(std::chrono::milliseconds ms) { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + timers_mtx.Lock(); +#endif + this->interval = ms; +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + timers_mtx.Unlock(); +#endif +} +void TF_Timer_Handle::SetIntervalFromMilliseconds(int64_t ms) { + SetIntervalFromDuration(std::chrono::milliseconds(ms)); +} +std::chrono::duration +TF_Timer_Handle::GetIntervalDuration() { + return this->interval; +} +int64_t TF_Timer_Handle::GetIntervalMilliseconds() { + return this->interval.count(); +} +void TF_RunEventLoopItteration() { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) && \ + (defined(GEKKO) || defined(__SWITCH__)) + Tesses::Framework::Threading::LookForFinishedThreads(); + +#endif +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + invokings_mtx.Lock(); + auto invokes = invokings; + invokings_mtx.Unlock(); + while (!invokes.empty()) { + invokes.front()(); + invokes.pop(); } - void TF_Timer_Handle::SetEnabled(bool enabled) - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - timers_mtx.Lock(); - #endif - this->enabled = enabled; - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - timers_mtx.Unlock(); - #endif - } - bool TF_Timer_Handle::GetEnabled() - { - return this->enabled; - } - void TF_Timer_Handle::SetIntervalFromDuration(std::chrono::milliseconds ms) - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - timers_mtx.Lock(); - #endif - this->interval = ms; - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - timers_mtx.Unlock(); - #endif - } - void TF_Timer_Handle::SetIntervalFromMilliseconds(int64_t ms) - { - SetIntervalFromDuration(std::chrono::milliseconds(ms)); - } - std::chrono::duration TF_Timer_Handle::GetIntervalDuration() - { - return this->interval; - } - int64_t TF_Timer_Handle::GetIntervalMilliseconds() - { - return this->interval.count(); - } +#endif - - - - void TF_RunEventLoopItteration() - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) && (defined(GEKKO) || defined(__SWITCH__)) - Tesses::Framework::Threading::LookForFinishedThreads(); - - #endif - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - invokings_mtx.Lock(); - auto invokes = invokings; - invokings_mtx.Unlock(); - while(!invokes.empty()) - { - invokes.front()(); - invokes.pop(); - - } - #endif - - if(!isRunningSig) isRunning=false; - #if defined(GEKKO) - if(gaming_console_events) - { - PAD_ScanPads(); - if(PAD_ButtonsDown(0) & PAD_BUTTON_START) isRunning=false; - } - #elif defined(__SWITCH__) - if(gaming_console_events) - { - if(!appletMainLoop()) isRunning=false; - - padUpdate(&default_pad); - - u64 kDown = padGetButtonsDown(&default_pad); - - if (kDown & HidNpadButton_Plus) - isRunning=false; - - - if(initedConsole) - consoleUpdate(NULL); - - - } - #endif - - #if defined(_WIN32) - MSG winMSG; - if (PeekMessage(&winMSG, NULL, WM_QUIT, WM_QUIT, 1)) - { + if (!isRunningSig) + isRunning = false; +#if defined(GEKKO) + if (gaming_console_events) { + PAD_ScanPads(); + if (PAD_ButtonsDown(0) & PAD_BUTTON_START) isRunning = false; - } - #endif - - - timer_handler->Update(); - } - void TF_SetIsRunning(bool _isRunning) - { - isRunning = _isRunning; +#elif defined(__SWITCH__) + if (gaming_console_events) { + if (!appletMainLoop()) + isRunning = false; + + padUpdate(&default_pad); + + u64 kDown = padGetButtonsDown(&default_pad); + + if (kDown & HidNpadButton_Plus) + isRunning = false; + + if (initedConsole) + consoleUpdate(NULL); } - void TF_Quit() - { - isRunning=false; - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) && (defined(GEKKO) || defined(__SWITCH__)) - Tesses::Framework::Threading::JoinAllThreads(); - #endif +#endif +#if defined(_WIN32) + MSG winMSG; + if (PeekMessage(&winMSG, NULL, WM_QUIT, WM_QUIT, 1)) { + isRunning = false; } - void TF_InitEventLoop() - { - signal(SIGINT,_sigInt); - signal(SIGTERM, _sigInt); - } - void TF_Init() - { - #if defined(TESSESFRAMEWORK_ENABLE_SQLITE) - sqlite3_initialize(); - #if defined(GEKKO) || defined(__SWITCH__) || defined(__PS2__) - sqlite3_vfs_register(sqlite3_demovfs(),1); - #endif - #endif +#endif - - - tzset(); - #if defined(_WIN32) - system(" "); - signal(SIGINT, _sigInt); - signal(SIGTERM, _sigInt); - #endif - - isRunning=true; - #if defined(_WIN32) - WSADATA wsaData; - int iResult; - -// Initialize Winsock -iResult = WSAStartup(MAKEWORD(2,2), &wsaData); -if (iResult != 0) { - printf("WSAStartup failed: %d\n", iResult); - return; + timer_handler->Update(); } - #elif defined(GEKKO) - fatInitDefault(); - VIDEO_Init(); - PAD_Init(); - #if defined(HW_RVL) - #if defined(TESSESFRAMEWORK_USE_WII_SOCKET) - wiisocket_init(); - #endif - WPAD_Init(); - #endif - #elif defined(__SWITCH__) - padConfigureInput(1, HidNpadStyleSet_NpadStandard); - padInitializeDefault(&default_pad); - socketInitializeDefault(); +void TF_SetIsRunning(bool _isRunning) { isRunning = _isRunning; } +void TF_Quit() { + isRunning = false; +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) && \ + (defined(GEKKO) || defined(__SWITCH__)) + Tesses::Framework::Threading::JoinAllThreads(); +#endif +} +void TF_InitEventLoop() { + signal(SIGINT, _sigInt); + signal(SIGTERM, _sigInt); +} +void TF_Init() { +#if defined(TESSESFRAMEWORK_ENABLE_SQLITE) + sqlite3_initialize(); +#if defined(GEKKO) || defined(__SWITCH__) || defined(__PS2__) + sqlite3_vfs_register(sqlite3_demovfs(), 1); +#endif +#endif - // Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller) - #else - signal(SIGPIPE,SIG_IGN); + tzset(); +#if defined(_WIN32) + signal(SIGINT, _sigInt); + signal(SIGTERM, _sigInt); +#endif - #endif + isRunning = true; +#if defined(_WIN32) + WSADATA wsaData; + int iResult; + // Initialize Winsock + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + printf("WSAStartup failed: %d\n", iResult); + return; } - bool TF_GetConsoleEventsEnabled() - { - return gaming_console_events; - } - void TF_SetConsoleEventsEnabled(bool flag) - { - gaming_console_events=flag; - } - void TF_InitConsole() - { - #if defined(GEKKO) - rmode = VIDEO_GetPreferredMode(NULL); +#elif defined(GEKKO) + fatInitDefault(); + VIDEO_Init(); + PAD_Init(); +#if defined(HW_RVL) +#if defined(TESSESFRAMEWORK_USE_WII_SOCKET) + wiisocket_init(); +#endif + WPAD_Init(); +#endif +#elif defined(__SWITCH__) + padConfigureInput(1, HidNpadStyleSet_NpadStandard); + padInitializeDefault(&default_pad); + socketInitializeDefault(); - // Allocate memory for the display in the uncached region - xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); +// Initialize the default gamepad (which reads handheld mode inputs as well as +// the first connected controller) +#else + signal(SIGPIPE, SIG_IGN); - // Initialise the console, required for printf - console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ); - //SYS_STDIO_Report(true); +#endif +} +bool TF_GetConsoleEventsEnabled() { return gaming_console_events; } +void TF_SetConsoleEventsEnabled(bool flag) { gaming_console_events = flag; } +void TF_InitConsole() { +#if defined(GEKKO) + rmode = VIDEO_GetPreferredMode(NULL); - // Set up the video registers with the chosen mode - VIDEO_Configure(rmode); + // Allocate memory for the display in the uncached region + xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); - // Tell the video hardware where our display memory is - VIDEO_SetNextFramebuffer(xfb); + // Initialise the console, required for printf + console_init(xfb, 20, 20, rmode->fbWidth, rmode->xfbHeight, + rmode->fbWidth * VI_DISPLAY_PIX_SZ); + // SYS_STDIO_Report(true); - // Make the display visible - VIDEO_SetBlack(false); - // Flush the video register changes to the hardware - VIDEO_Flush(); + // Set up the video registers with the chosen mode + VIDEO_Configure(rmode); - // Wait for Video setup to complete + // Tell the video hardware where our display memory is + VIDEO_SetNextFramebuffer(xfb); + + // Make the display visible + VIDEO_SetBlack(false); + // Flush the video register changes to the hardware + VIDEO_Flush(); + + // Wait for Video setup to complete + VIDEO_WaitVSync(); + if (rmode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync(); - if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); + // The console understands VT terminal escape codes + // This positions the cursor on row 2, column 0 + // we can use variables for this with format codes too + // e.g. printf ("\x1b[%d;%dH", row, column ); + printf("\x1b[2;0H"); +#elif defined(__SWITCH__) + consoleInit(NULL); + initedConsole = true; +#endif +} +void TF_InitWithConsole() { + TF_Init(); + TF_InitConsole(); +#if defined(TESSESFRAMEWORK_LOGTOFILE) + printf("Enabled Logging\n"); +#endif +} +#if defined(TESSESFRAMEWORK_LOGTOFILE) +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) +Tesses::Framework::Threading::Mutex log_mtx; +#endif +void TF_Log(std::string txtToLog) { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + log_mtx.Lock(); +#endif + FILE *f = fopen("TF_Log.log", "a"); + fprintf(f, "%s\n", txtToLog.c_str()); + fclose(f); +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + log_mtx.Unlock(); +#endif +} +#endif - // The console understands VT terminal escape codes - // This positions the cursor on row 2, column 0 - // we can use variables for this with format codes too - // e.g. printf ("\x1b[%d;%dH", row, column ); - printf("\x1b[2;0H"); - #elif defined(__SWITCH__) - consoleInit(NULL); - initedConsole=true; - #endif - } - void TF_InitWithConsole() - { - TF_Init(); - TF_InitConsole(); - #if defined(TESSESFRAMEWORK_LOGTOFILE) - printf("Enabled Logging\n"); - #endif - } - #if defined(TESSESFRAMEWORK_LOGTOFILE) - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - Tesses::Framework::Threading::Mutex log_mtx; - #endif - void TF_Log(std::string txtToLog) - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - log_mtx.Lock(); - #endif - FILE* f = fopen("TF_Log.log","a"); - fprintf(f,"%s\n",txtToLog.c_str()); - fclose(f); - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - log_mtx.Unlock(); - #endif - } - #endif +static std::string TF_FileSizeBin(uint64_t bytes) { + if (bytes == 1) + return "1 Byte"; + if (bytes < 1024ULL) + return std::to_string(bytes) + " Bytes"; + if (bytes < 1024ULL * 1024ULL) + return std::to_string(bytes / 1024ULL) + " kiB"; + if (bytes < 1024ULL * 1024ULL * 1024ULL) + return std::to_string(bytes / (1024ULL * 1024ULL)) + " MiB"; + if (bytes < 1024ULL * 1024ULL * 1024ULL * 1024ULL) + return std::to_string(bytes / (1024ULL * 1024ULL * 1024ULL)) + " GiB"; + if (bytes < 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL) + return std::to_string(bytes / (1024ULL * 1024ULL * 1024ULL * 1024ULL)) + + " TiB"; + return std::to_string(bytes / + (1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL)) + + " PiB"; +} +static std::string TF_FileSizeDec(uint64_t bytes) { + if (bytes == 1) + return "1 Byte"; + if (bytes < 1000ULL) + return std::to_string(bytes) + " Bytes"; + if (bytes < 1000ULL * 1000ULL) + return std::to_string(bytes / 1000ULL) + " kB"; + if (bytes < 1000ULL * 1000ULL * 1000ULL) + return std::to_string(bytes / (1000ULL * 1000ULL)) + " MB"; + if (bytes < 1000ULL * 1000ULL * 1000ULL * 1000ULL) + return std::to_string(bytes / (1000ULL * 1000ULL * 1000ULL)) + " GB"; + if (bytes < 1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL) + return std::to_string(bytes / (1000ULL * 1000ULL * 1000ULL * 1000ULL)) + + " TB"; + return std::to_string(bytes / + (1000ULL * 1000ULL * 1000ULL * 1000ULL * 1000ULL)) + + " PB"; +} +std::string TF_FileSize(uint64_t bytes, bool usesBin) { + return usesBin ? TF_FileSizeBin(bytes) : TF_FileSizeDec(bytes); +} - static std::string TF_FileSizeBin(uint64_t bytes) - { - if(bytes == 1) return "1 Byte"; - if(bytes < 1024ULL) return std::to_string(bytes) + " Bytes"; - if(bytes < 1024ULL*1024ULL) return std::to_string(bytes / 1024ULL) + " kiB"; - if(bytes < 1024ULL*1024ULL*1024ULL) return std::to_string(bytes / (1024ULL*1024ULL)) + " MiB"; - if(bytes < 1024ULL*1024ULL*1024ULL*1024ULL) return std::to_string(bytes / (1024ULL*1024ULL*1024ULL)) + " GiB"; - if(bytes < 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL) return std::to_string(bytes / (1024ULL*1024ULL*1024ULL*1024ULL)) + " TiB"; - return std::to_string(bytes / (1024ULL*1024ULL*1024ULL*1024ULL*1024ULL)) + " PiB"; +std::optional _argv0 = std::nullopt; + +std::string TF_GetExecutableName() { +#if defined(_WIN32) + std::u16string str; + str.resize(MAX_PATH); + // GetModuleFileName with NULL gets the current executable's path + DWORD result = GetModuleFileNameW(NULL, (LPWSTR)str.data(), MAX_PATH); + if (result > 0 && result < MAX_PATH) { + str.resize(result); + std::string path; + Text::StringConverter::UTF8::FromUTF16(path, str); + return path; } - static std::string TF_FileSizeDec(uint64_t bytes) - { - if(bytes == 1) return "1 Byte"; - if(bytes < 1000ULL) return std::to_string(bytes) + " Bytes"; - if(bytes < 1000ULL*1000ULL) return std::to_string(bytes / 1000ULL) + " kB"; - if(bytes < 1000ULL*1000ULL*1000ULL) return std::to_string(bytes / (1000ULL*1000ULL)) + " MB"; - if(bytes < 1000ULL*1000ULL*1000ULL*1000ULL) return std::to_string(bytes / (1000ULL*1000ULL*1000ULL)) + " GB"; - if(bytes < 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL) return std::to_string(bytes / (1000ULL*1000ULL*1000ULL*1000ULL)) + " TB"; - return std::to_string(bytes / (1000ULL*1000ULL*1000ULL*1000ULL*1000ULL)) + " PB"; - } - std::string TF_FileSize(uint64_t bytes, bool usesBin) - { - return usesBin ? TF_FileSizeBin(bytes) : TF_FileSizeDec(bytes); +#elif defined(__linux__) + auto path = + Filesystem::LocalFS->ReadLink((Filesystem::VFSPath) "/proc/self/exe"); + return path.ToString(); +#elif defined(__FreeBSD__) + int mib[4]; + size_t len = 0; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = -1; + + if (sysctl(mib, 4, NULL, &len, NULL, 0) < 0) { + return ""; } - std::optional _argv0=std::nullopt; + std::string path; + path.resize(len); + if (sysctl(mib, 4, path.data(), &len, NULL, 0) < 0) { + return ""; + } - void TF_AllowPortable(std::string argv0) - { - using namespace Tesses::Framework::Serialization::Json; - using namespace Tesses::Framework::Platform::Environment; - using namespace Tesses::Framework::Filesystem; - _argv0 = argv0; - VFSPath path(argv0); - auto realPath=path.MakeAbsolute(); - auto dir = realPath.GetParent(); - auto portable=dir / "portable.json"; - if(LocalFS->FileExists(portable)) - { + return path; +#elif defined(__NetBSD__) + auto path = Filesystem::LocalFS->ReadLink( + (Filesystem::VFSPath) "/proc/curproc/exe"); + return path.ToString(); +#elif defined( \ + __DragonFly__) // platform doesn't work due to std::filesystem issues + auto path = Filesystem::LocalFS->ReadLink( + (Filesystem::VFSPath) "/proc/curproc/file"); + return path.ToString(); +#elif TARGET_OS_MAC && !TARGET_OS_IPHONE + std::string path; + path.resize(1024); + uint32_t bufsize = (uint32_t)path.size(); + if (_NSGetExecutablePath(path.data(), &bufsize) == 0) { + path.resize(bufsize); + return path; + } else { + path.resize(bufsize); + if (_NSGetExecutablePath(path.data(), &bufsize) == 0) + return path; + } +#endif - std::string portable_str; - Helpers::ReadAllText(LocalFS,portable, portable_str); - auto jsonObj=Json::Decode(portable_str); - JObject dict; - JObject dict2; - if(TryGetJToken(jsonObj,dict) && dict.TryGetValueAsType("portable_data",dict2)) - { + return ""; +} +void TF_AllowPortable() { + std::string path = TF_GetExecutableName(); + if (!path.empty()) + TF_AllowPortable(path); +} - if(dict.TryGetValueAsType("portable_type", portable_str)) - { - if(portable_str == "PortableApps.com") - { - //do the special stuffs for PortableApps.com based apps - auto paf_documents = GetVariable("PortableApps.comDocuments"); - auto paf_music = GetVariable("PortableApps.comMusic"); - auto paf_pictures = GetVariable("PortableApps.comPictures"); - auto paf_videos = GetVariable("PortableApps.comVideos"); - auto paf_data = GetVariable("PAL:DataDir"); +void TF_AllowPortable(std::string argv0) { + using namespace Tesses::Framework::Serialization::Json; + using namespace Tesses::Framework::Platform::Environment; + using namespace Tesses::Framework::Filesystem; + _argv0 = argv0; + VFSPath path(argv0); + auto realPath = path.MakeAbsolute(); + auto dir = realPath.GetParent(); + auto portable = dir / "portable.json"; + if (LocalFS->FileExists(portable)) { + std::string portable_str; + Helpers::ReadAllText(LocalFS, portable, portable_str); + auto jsonObj = Json::Decode(portable_str); + JObject dict; + JObject dict2; + if (TryGetJToken(jsonObj, dict) && + dict.TryGetValueAsType("portable_data", dict2)) { + if (dict.TryGetValueAsType("portable_type", portable_str)) { + if (portable_str == "PortableApps.com") { + // do the special stuffs for PortableApps.com based apps + auto paf_documents = + GetVariable("PortableApps.comDocuments"); + auto paf_music = GetVariable("PortableApps.comMusic"); + auto paf_pictures = GetVariable("PortableApps.comPictures"); + auto paf_videos = GetVariable("PortableApps.comVideos"); + auto paf_data = GetVariable("PAL:DataDir"); - bool bV=false; - if(!dict2.TryGetValueAsType("system_documents",bV) || !bV) - { - if(paf_documents) - { - portable_config.documents = LocalFS->SystemToVFSPath(*paf_documents); - } - } - if(!dict2.TryGetValueAsType("system_pictures",bV) || !bV) - { - if(paf_pictures) - { - portable_config.pictures = LocalFS->SystemToVFSPath(*paf_pictures); - } - } - if(!dict2.TryGetValueAsType("system_videos",bV) || !bV) - { - if(paf_videos) - { - portable_config.videos = LocalFS->SystemToVFSPath(*paf_videos); - } - } - if(!dict2.TryGetValueAsType("system_music",bV) || !bV) - { - if(paf_music) - { - portable_config.music = LocalFS->SystemToVFSPath(*paf_music); - } - } - if(dict2.TryGetValueAsType("user",portable_str)) - { - if(portable_str == "app") - { - if(paf_data) - portable_config.user = LocalFS->SystemToVFSPath(*paf_data) / "TF_User"; - } - else if(portable_str == "documents") - { - if(paf_documents) - portable_config.user = LocalFS->SystemToVFSPath(*paf_documents) / "TF_User"; - } - } - if(dict2.TryGetValueAsType("desktop",portable_str)) - { - if(portable_str == "tf_user") - { - if(portable_config.user) - portable_config.desktop = *(portable_config.user) / "Desktop"; - } - else if(portable_str == "documents") - { - if(paf_documents) - portable_config.desktop = LocalFS->SystemToVFSPath(*paf_documents) / "Desktop"; - } - } - if(dict2.TryGetValueAsType("downloads",portable_str)) - { - if(portable_str == "tf_user") - { - if(portable_config.user) - portable_config.downloads = *(portable_config.user) / "Downloads"; - } - else if(portable_str == "documents") - { - if(paf_documents) - portable_config.downloads = LocalFS->SystemToVFSPath(*paf_documents) / "Downloads"; - } - } - if(!dict2.TryGetValueAsType("system_config",bV) || !bV) - { - if(portable_config.user) - portable_config.config = *(portable_config.user) / "Config"; - } - if(!dict2.TryGetValueAsType("system_cache",bV) || !bV) - { - if(portable_config.user) - portable_config.cache = *(portable_config.user) / "Cache"; - } - if(!dict2.TryGetValueAsType("system_data",bV) || !bV) - { - if(portable_config.user) - portable_config.data = *(portable_config.user) / "Data"; - } - if(!dict2.TryGetValueAsType("system_state",bV) || !bV) - { - if(portable_config.user) - portable_config.state = *(portable_config.user) / "State"; - } - if(!dict2.TryGetValueAsType("system_temp",bV) || !bV) - { - if(portable_config.user) - portable_config.temp = *(portable_config.user) / "Temp"; + bool bV = false; + if (!dict2.TryGetValueAsType("system_documents", bV) || + !bV) { + if (paf_documents) { + portable_config.documents = + LocalFS->SystemToVFSPath(*paf_documents); } } - else if(portable_str == "relative") - { - if(dict2.TryGetValueAsType("user",portable_str)) - { - if(portable_str != "system") - { + if (!dict2.TryGetValueAsType("system_pictures", bV) || + !bV) { + if (paf_pictures) { + portable_config.pictures = + LocalFS->SystemToVFSPath(*paf_pictures); + } + } + if (!dict2.TryGetValueAsType("system_videos", bV) || !bV) { + if (paf_videos) { + portable_config.videos = + LocalFS->SystemToVFSPath(*paf_videos); + } + } + if (!dict2.TryGetValueAsType("system_music", bV) || !bV) { + if (paf_music) { + portable_config.music = + LocalFS->SystemToVFSPath(*paf_music); + } + } + if (dict2.TryGetValueAsType("user", portable_str)) { + if (portable_str == "app") { + if (paf_data) + portable_config.user = + LocalFS->SystemToVFSPath(*paf_data) / + "TF_User"; + } else if (portable_str == "documents") { + if (paf_documents) + portable_config.user = + LocalFS->SystemToVFSPath(*paf_documents) / + "TF_User"; + } + } + if (dict2.TryGetValueAsType("desktop", portable_str)) { + if (portable_str == "tf_user") { + if (portable_config.user) + portable_config.desktop = + *(portable_config.user) / "Desktop"; + } else if (portable_str == "documents") { + if (paf_documents) + portable_config.desktop = + LocalFS->SystemToVFSPath(*paf_documents) / + "Desktop"; + } + } + if (dict2.TryGetValueAsType("downloads", portable_str)) { + if (portable_str == "tf_user") { + if (portable_config.user) + portable_config.downloads = + *(portable_config.user) / "Downloads"; + } else if (portable_str == "documents") { + if (paf_documents) + portable_config.downloads = + LocalFS->SystemToVFSPath(*paf_documents) / + "Downloads"; + } + } + if (!dict2.TryGetValueAsType("system_config", bV) || !bV) { + if (portable_config.user) + portable_config.config = + *(portable_config.user) / "Config"; + } + if (!dict2.TryGetValueAsType("system_cache", bV) || !bV) { + if (portable_config.user) + portable_config.cache = + *(portable_config.user) / "Cache"; + } + if (!dict2.TryGetValueAsType("system_data", bV) || !bV) { + if (portable_config.user) + portable_config.data = + *(portable_config.user) / "Data"; + } + if (!dict2.TryGetValueAsType("system_state", bV) || !bV) { + if (portable_config.user) + portable_config.state = + *(portable_config.user) / "State"; + } + if (!dict2.TryGetValueAsType("system_temp", bV) || !bV) { + if (portable_config.user) + portable_config.temp = + *(portable_config.user) / "Temp"; + } + } else if (portable_str == "relative") { + if (dict2.TryGetValueAsType("user", portable_str)) { + if (portable_str != "system") { + auto userDir = dir / portable_str; + portable_config.user = + userDir.CollapseRelativeParents(); + } + } + + if (dict2.TryGetValueAsType("documents", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.documents = + *(portable_config.user) / "Documents"; + } + } else { auto userDir = dir / portable_str; - portable_config.user = userDir.CollapseRelativeParents(); - - - } - - - - } - - if(dict2.TryGetValueAsType("documents", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.documents = *(portable_config.user) / "Documents"; - } - } - else - { - auto userDir = dir / portable_str; - portable_config.documents = userDir.CollapseRelativeParents(); - - } + portable_config.documents = + userDir.CollapseRelativeParents(); } } - if(dict2.TryGetValueAsType("downloads", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.downloads = *(portable_config.user) / "Downloads"; - } - } - else - { - auto userDir = dir / portable_str; - portable_config.downloads = userDir.CollapseRelativeParents(); - - } - } - } - if(dict2.TryGetValueAsType("desktop", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.desktop = *(portable_config.user) / "Desktop"; - } - } - else - { - auto userDir = dir / portable_str; - portable_config.desktop = userDir.CollapseRelativeParents(); - - } - } - } - if(dict2.TryGetValueAsType("pictures", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.pictures = *(portable_config.user) / "Pictures"; - } - } - else - { - auto userDir = dir / portable_str; - portable_config.pictures = userDir.CollapseRelativeParents(); - - } - } - } - if(dict2.TryGetValueAsType("videos", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.videos = *(portable_config.user) / "Videos"; - } - } - else - { - auto userDir = dir / portable_str; - portable_config.videos = userDir.CollapseRelativeParents(); - - } - } - } - if(dict2.TryGetValueAsType("music", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.music = *(portable_config.user) / "Music"; - } - } - else - { - auto userDir = dir / portable_str; - portable_config.music = userDir.CollapseRelativeParents(); - - } - } - } - if(dict2.TryGetValueAsType("config", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.config = *(portable_config.user) / "Config"; - } - } - else - { - auto userDir = dir / portable_str; - portable_config.config = userDir.CollapseRelativeParents(); - - } - } - } - if(dict2.TryGetValueAsType("cache", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.cache = *(portable_config.user) / "Cache"; - } - } - else - { - auto userDir = dir / portable_str; - portable_config.cache = userDir.CollapseRelativeParents(); - - } - } - } - if(dict2.TryGetValueAsType("data", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.data = *(portable_config.user) / "Data"; - } - } - else - { - auto userDir = dir / portable_str; - portable_config.data = userDir.CollapseRelativeParents(); - - } - } - } - if(dict2.TryGetValueAsType("state", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.state = *(portable_config.user) / "State"; - } - } - else - { - auto userDir = dir / portable_str; - portable_config.state = userDir.CollapseRelativeParents(); - - } - } - } - if(dict2.TryGetValueAsType("temp", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.temp = *(portable_config.user) / "Temp"; - } - } - else - { - auto userDir = dir / portable_str; - portable_config.temp = userDir.CollapseRelativeParents(); - - } - } - } - - - - - - - - - - - - - - } - else if(portable_str == "absolute") - { - if(dict2.TryGetValueAsType("user",portable_str)) - { - if(portable_str != "system") - { + if (dict2.TryGetValueAsType("downloads", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.downloads = + *(portable_config.user) / "Downloads"; + } + } else { + auto userDir = dir / portable_str; + portable_config.downloads = + userDir.CollapseRelativeParents(); + } + } + } + if (dict2.TryGetValueAsType("desktop", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.desktop = + *(portable_config.user) / "Desktop"; + } + } else { + auto userDir = dir / portable_str; + portable_config.desktop = + userDir.CollapseRelativeParents(); + } + } + } + if (dict2.TryGetValueAsType("pictures", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.pictures = + *(portable_config.user) / "Pictures"; + } + } else { + auto userDir = dir / portable_str; + portable_config.pictures = + userDir.CollapseRelativeParents(); + } + } + } + if (dict2.TryGetValueAsType("videos", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.videos = + *(portable_config.user) / "Videos"; + } + } else { + auto userDir = dir / portable_str; + portable_config.videos = + userDir.CollapseRelativeParents(); + } + } + } + if (dict2.TryGetValueAsType("music", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.music = + *(portable_config.user) / "Music"; + } + } else { + auto userDir = dir / portable_str; + portable_config.music = + userDir.CollapseRelativeParents(); + } + } + } + if (dict2.TryGetValueAsType("config", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.config = + *(portable_config.user) / "Config"; + } + } else { + auto userDir = dir / portable_str; + portable_config.config = + userDir.CollapseRelativeParents(); + } + } + } + if (dict2.TryGetValueAsType("cache", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.cache = + *(portable_config.user) / "Cache"; + } + } else { + auto userDir = dir / portable_str; + portable_config.cache = + userDir.CollapseRelativeParents(); + } + } + } + if (dict2.TryGetValueAsType("data", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.data = + *(portable_config.user) / "Data"; + } + } else { + auto userDir = dir / portable_str; + portable_config.data = + userDir.CollapseRelativeParents(); + } + } + } + if (dict2.TryGetValueAsType("state", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.state = + *(portable_config.user) / "State"; + } + } else { + auto userDir = dir / portable_str; + portable_config.state = + userDir.CollapseRelativeParents(); + } + } + } + if (dict2.TryGetValueAsType("temp", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.temp = + *(portable_config.user) / "Temp"; + } + } else { + auto userDir = dir / portable_str; + portable_config.temp = + userDir.CollapseRelativeParents(); + } + } + } + + } else if (portable_str == "absolute") { + if (dict2.TryGetValueAsType("user", portable_str)) { + if (portable_str != "system") { + VFSPath userDir = portable_str; + portable_config.user = + userDir.CollapseRelativeParents(); + } + } + + if (dict2.TryGetValueAsType("documents", portable_str)) { + + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.documents = + *(portable_config.user) / "Documents"; + } + } else { VFSPath userDir = portable_str; - portable_config.user = userDir.CollapseRelativeParents(); - - - } - - - - } - - if(dict2.TryGetValueAsType("documents", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.documents = *(portable_config.user) / "Documents"; - } - } - else - { - VFSPath userDir = portable_str; - portable_config.documents = userDir.CollapseRelativeParents(); - - } + portable_config.documents = + userDir.CollapseRelativeParents(); } } - if(dict2.TryGetValueAsType("downloads", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.downloads = *(portable_config.user) / "Downloads"; - } - } - else - { - VFSPath userDir = portable_str; - portable_config.downloads = userDir.CollapseRelativeParents(); + } + if (dict2.TryGetValueAsType("downloads", portable_str)) { + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.downloads = + *(portable_config.user) / "Downloads"; } + } else { + VFSPath userDir = portable_str; + portable_config.downloads = + userDir.CollapseRelativeParents(); } } - if(dict2.TryGetValueAsType("desktop", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.desktop = *(portable_config.user) / "Desktop"; - } - } - else - { - VFSPath userDir = portable_str; - portable_config.desktop = userDir.CollapseRelativeParents(); + } + if (dict2.TryGetValueAsType("desktop", portable_str)) { + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.desktop = + *(portable_config.user) / "Desktop"; } + } else { + VFSPath userDir = portable_str; + portable_config.desktop = + userDir.CollapseRelativeParents(); } } - if(dict2.TryGetValueAsType("pictures", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.pictures = *(portable_config.user) / "Pictures"; - } - } - else - { - VFSPath userDir = portable_str; - portable_config.pictures = userDir.CollapseRelativeParents(); + } + if (dict2.TryGetValueAsType("pictures", portable_str)) { + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.pictures = + *(portable_config.user) / "Pictures"; } + } else { + VFSPath userDir = portable_str; + portable_config.pictures = + userDir.CollapseRelativeParents(); } } - if(dict2.TryGetValueAsType("videos", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.videos = *(portable_config.user) / "Videos"; - } - } - else - { - VFSPath userDir = portable_str; - portable_config.videos = userDir.CollapseRelativeParents(); + } + if (dict2.TryGetValueAsType("videos", portable_str)) { + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.videos = + *(portable_config.user) / "Videos"; } + } else { + VFSPath userDir = portable_str; + portable_config.videos = + userDir.CollapseRelativeParents(); } } - if(dict2.TryGetValueAsType("music", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.music = *(portable_config.user) / "Music"; - } - } - else - { - VFSPath userDir = portable_str; - portable_config.music = userDir.CollapseRelativeParents(); + } + if (dict2.TryGetValueAsType("music", portable_str)) { + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.music = + *(portable_config.user) / "Music"; } + } else { + VFSPath userDir = portable_str; + portable_config.music = + userDir.CollapseRelativeParents(); } } - if(dict2.TryGetValueAsType("config", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.config = *(portable_config.user) / "Config"; - } - } - else - { - VFSPath userDir = portable_str; - portable_config.config = userDir.CollapseRelativeParents(); + } + if (dict2.TryGetValueAsType("config", portable_str)) { + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.config = + *(portable_config.user) / "Config"; } + } else { + VFSPath userDir = portable_str; + portable_config.config = + userDir.CollapseRelativeParents(); } } - if(dict2.TryGetValueAsType("cache", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.cache = *(portable_config.user) / "Cache"; - } - } - else - { - VFSPath userDir = portable_str; - portable_config.cache = userDir.CollapseRelativeParents(); + } + if (dict2.TryGetValueAsType("cache", portable_str)) { + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.cache = + *(portable_config.user) / "Cache"; } + } else { + VFSPath userDir = portable_str; + portable_config.cache = + userDir.CollapseRelativeParents(); } } - if(dict2.TryGetValueAsType("data", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.data = *(portable_config.user) / "Data"; - } - } - else - { - VFSPath userDir = portable_str; - portable_config.data = userDir.CollapseRelativeParents(); + } + if (dict2.TryGetValueAsType("data", portable_str)) { + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.data = + *(portable_config.user) / "Data"; } + } else { + VFSPath userDir = portable_str; + portable_config.data = + userDir.CollapseRelativeParents(); } } - if(dict2.TryGetValueAsType("state", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.state = *(portable_config.user) / "State"; - } - } - else - { - VFSPath userDir = portable_str; - portable_config.state = userDir.CollapseRelativeParents(); + } + if (dict2.TryGetValueAsType("state", portable_str)) { + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.state = + *(portable_config.user) / "State"; } + } else { + VFSPath userDir = portable_str; + portable_config.state = + userDir.CollapseRelativeParents(); } } - if(dict2.TryGetValueAsType("temp", portable_str)) - { - - if(portable_str != "system") - { - if(portable_str == "default") - { - if(portable_config.user) - { - portable_config.temp = *(portable_config.user) / "Temp"; - } - } - else - { - VFSPath userDir = portable_str; - portable_config.temp = userDir.CollapseRelativeParents(); + } + if (dict2.TryGetValueAsType("temp", portable_str)) { + if (portable_str != "system") { + if (portable_str == "default") { + if (portable_config.user) { + portable_config.temp = + *(portable_config.user) / "Temp"; } + } else { + VFSPath userDir = portable_str; + portable_config.temp = + userDir.CollapseRelativeParents(); } } - } } } } } - - std::optional TF_GetCommandName() - { - return _argv0; - } } + +} // namespace Tesses::Framework diff --git a/src/Text/HeaderGenerator.cpp b/src/Text/HeaderGenerator.cpp index 800c905..319b964 100644 --- a/src/Text/HeaderGenerator.cpp +++ b/src/Text/HeaderGenerator.cpp @@ -1,68 +1,83 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Text/HeaderGenerator.hpp" namespace Tesses::Framework::Text { - - void GenerateCHeaderFile(std::shared_ptr strm,std::string name, std::shared_ptr writer) - { - const size_t BLK_SZ=1024; - writer->WriteLine("#pragma once"); - writer->WriteLine("#if defined(__cplusplus)"); - writer->WriteLine("extern \"C\" {"); - writer->WriteLine("#endif"); - writer->WriteLine("#include "); - writer->WriteLine("#include "); - writer->Write("const uint8_t "); - writer->Write(name); - writer->WriteLine("_data[] = {"); - uint64_t total = 0; - size_t read; - - std::vector data(BLK_SZ); - bool first=true; +void GenerateCHeaderFile(std::shared_ptr strm, + std::string name, + std::shared_ptr writer) { + const size_t BLK_SZ = 1024; + writer->WriteLine("#pragma once"); + writer->WriteLine("#if defined(__cplusplus)"); + writer->WriteLine("extern \"C\" {"); + writer->WriteLine("#endif"); + writer->WriteLine("#include "); + writer->WriteLine("#include "); + writer->Write("const uint8_t "); + writer->Write(name); + writer->WriteLine("_data[] = {"); + uint64_t total = 0; + size_t read; - do { - read = strm->ReadBlock(data.data(), data.size()); + std::vector data(BLK_SZ); + bool first = true; - for(size_t i = 0; i < read; i++) - { - if(!first) writer->Write(", "); - writer->Write((uint64_t)data[i]); - first=false; - } - total += read; - } while(read != 0); - - + do { + read = strm->ReadBlock(data.data(), data.size()); - writer->WriteLine("};"); - writer->Write("const size_t "); - writer->Write(name); - writer->Write("_length = "); - writer->Write(total); - writer->WriteLine(";"); + for (size_t i = 0; i < read; i++) { + if (!first) + writer->Write(", "); + writer->Write((uint64_t)data[i]); + first = false; + } + total += read; + } while (read != 0); - writer->WriteLine("#if defined(__cplusplus)"); - writer->WriteLine("}"); - writer->WriteLine("#endif"); + writer->WriteLine("};"); + writer->Write("const size_t "); + writer->Write(name); + writer->Write("_length = "); + writer->Write(total); + writer->WriteLine(";"); - } - - std::string GenerateCHeaderFile(std::shared_ptr strm,std::string name) - { - auto writer=std::make_shared(); - GenerateCHeaderFile(strm,name,writer); - return writer->GetString(); - } - void GenerateCHeaderFile(const std::vector& data,std::string name, std::shared_ptr writer) - { - auto ms = std::make_shared(false); - ms->GetBuffer() = data; - GenerateCHeaderFile(ms,name,writer); - } - std::string GenerateCHeaderFile(const std::vector& data,std::string name) - { - auto writer = std::make_shared(); - GenerateCHeaderFile(data,name,writer); - return writer->GetString(); - } -}; \ No newline at end of file + writer->WriteLine("#if defined(__cplusplus)"); + writer->WriteLine("}"); + writer->WriteLine("#endif"); +} + +std::string GenerateCHeaderFile(std::shared_ptr strm, + std::string name) { + auto writer = std::make_shared(); + GenerateCHeaderFile(strm, name, writer); + return writer->GetString(); +} +void GenerateCHeaderFile(const std::vector &data, std::string name, + std::shared_ptr writer) { + auto ms = std::make_shared(false); + ms->GetBuffer() = data; + GenerateCHeaderFile(ms, name, writer); +} +std::string GenerateCHeaderFile(const std::vector &data, + std::string name) { + auto writer = std::make_shared(); + GenerateCHeaderFile(data, name, writer); + return writer->GetString(); +} +}; // namespace Tesses::Framework::Text \ No newline at end of file diff --git a/src/Text/StringConverter.cpp b/src/Text/StringConverter.cpp index a0ff9f3..cd82a4b 100644 --- a/src/Text/StringConverter.cpp +++ b/src/Text/StringConverter.cpp @@ -1,270 +1,244 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Text/StringConverter.hpp" namespace Tesses::Framework::Text::StringConverter { - void UTF8::FromUTF16(std::basic_string& utf8, const std::basic_string& utf16) - { - for (size_t i=0; i < utf16.size();i++) - { - char32_t c = utf16[i]; - if ((c & 0xFC00) == 0xD800) - { - c = (c & 0x03FF) << 10; - i++; - if (i >= utf16.size()) return; +void UTF8::FromUTF16(std::basic_string &utf8, + const std::basic_string &utf16) { + for (size_t i = 0; i < utf16.size(); i++) { + char32_t c = utf16[i]; + if ((c & 0xFC00) == 0xD800) { + c = (c & 0x03FF) << 10; + i++; + if (i >= utf16.size()) + return; + char32_t c2 = utf16[i]; + if ((c2 & 0xFC00) != 0xDC00) + continue; - char32_t c2 = utf16[i]; - if ((c2 & 0xFC00) != 0xDC00) continue; + c |= (c2 & 0x03FF); - - c |= (c2 & 0x03FF); - - c += 0x10000; - } - - if (c <= 0x7F) - { - utf8.push_back((char)c); - } - else if (c >= 0x80 && c <= 0x7FF) - { - uint8_t high = 0b11000000 | ((c >> 6) & 0b00011111); - uint8_t low = 0b10000000 | (c & 0b00111111); - utf8.push_back((char)high); - utf8.push_back((char)low); - } - else if (c >= 0x800 && c <= 0xFFFF) - { - uint8_t highest = 0b11100000 | ((c >> 12) & 0b00001111); - uint8_t high = 0b10000000 | ((c >> 6) & 0b00111111); - uint8_t low = 0b10000000 | (c & 0b00111111); - utf8.push_back((char)highest); - utf8.push_back((char)high); - utf8.push_back((char)low); - } - else if (c >= 0x010000 && c <= 0x10FFFF) - { - uint8_t highest = 0b11110000 | ((c >> 18) & 0b00000111); - uint8_t high = 0b10000000 | ((c >> 12) & 0b00111111); - uint8_t low = 0b10000000 | ((c >> 6) & 0b00111111); - uint8_t lowest = 0b10000000 | (c & 0b00111111); - utf8.push_back((char)highest); - utf8.push_back((char)high); - utf8.push_back((char)low); - utf8.push_back((char)lowest); - } - + c += 0x10000; } - } - void UTF8::FromUTF32(std::basic_string& utf8, const std::basic_string& utf32) - { - for (auto c : utf32) - { - if (c <= 0x7F) - { - utf8.push_back((char)c); - } - else if (c >= 0x80 && c <= 0x7FF) - { - uint8_t high = 0b11000000 | ((c >> 6) & 0b00011111); - uint8_t low = 0b10000000 | (c & 0b00111111); - utf8.push_back((char)high); - utf8.push_back((char)low); - } - else if (c >= 0x800 && c <= 0xFFFF) - { - uint8_t highest = 0b11100000 | ((c >> 12) & 0b00001111); - uint8_t high = 0b10000000 | ((c >> 6) & 0b00111111); - uint8_t low = 0b10000000 | (c & 0b00111111); - utf8.push_back((char)highest); - utf8.push_back((char)high); - utf8.push_back((char)low); - } - else if (c >= 0x010000 && c <= 0x10FFFF) - { - uint8_t highest = 0b11110000 | ((c >> 18) & 0b00000111); - uint8_t high = 0b10000000 | ((c >> 12) & 0b00111111); - uint8_t low = 0b10000000 | ((c >> 6) & 0b00111111); - uint8_t lowest = 0b10000000 | (c & 0b00111111); - utf8.push_back((char)highest); - utf8.push_back((char)high); - utf8.push_back((char)low); - utf8.push_back((char)lowest); - } + if (c <= 0x7F) { + utf8.push_back((char)c); + } else if (c >= 0x80 && c <= 0x7FF) { + uint8_t high = 0b11000000 | ((c >> 6) & 0b00011111); + uint8_t low = 0b10000000 | (c & 0b00111111); + utf8.push_back((char)high); + utf8.push_back((char)low); + } else if (c >= 0x800 && c <= 0xFFFF) { + uint8_t highest = 0b11100000 | ((c >> 12) & 0b00001111); + uint8_t high = 0b10000000 | ((c >> 6) & 0b00111111); + uint8_t low = 0b10000000 | (c & 0b00111111); + utf8.push_back((char)highest); + utf8.push_back((char)high); + utf8.push_back((char)low); + } else if (c >= 0x010000 && c <= 0x10FFFF) { + uint8_t highest = 0b11110000 | ((c >> 18) & 0b00000111); + uint8_t high = 0b10000000 | ((c >> 12) & 0b00111111); + uint8_t low = 0b10000000 | ((c >> 6) & 0b00111111); + uint8_t lowest = 0b10000000 | (c & 0b00111111); + utf8.push_back((char)highest); + utf8.push_back((char)high); + utf8.push_back((char)low); + utf8.push_back((char)lowest); } } - - void UTF16::FromUTF8(std::basic_string& utf16, const std::basic_string& utf8) - { - for (size_t i = 0; i < utf8.size();i++) - { - uint8_t c = (uint8_t)utf8[i]; - char32_t cres = 0; - if (c <= 127) - { - cres = (char32_t)c; - } - else if ((c & 0b11100000) == 0b11000000) - { - if (i + 1 < utf8.size()) - { - - uint8_t c2 = (uint8_t)utf8[++i]; - cres |= c2 & 0b00111111; - cres |= (c & 0b00011111) << 6; - - } - else { - i++; - continue; - }; - } - else if ((c & 0b11110000) == 0b11100000) - { - if (i + 2 < utf8.size()) - { - uint8_t c2 = (uint8_t)utf8[++i]; - uint8_t c3 = (uint8_t)utf8[++i]; - cres |= c3 & 0b00111111; - cres |= (c2 & 0b00111111) << 6; - cres |= (c & 0b00001111) << 12; - - } - else { i += 2; continue; } - } - else if ((c & 0b11111000) == 0b11110000) - { - if (i + 3 < utf8.size()) - { - uint8_t c2 = (uint8_t)utf8[++i]; - uint8_t c3 = (uint8_t)utf8[++i]; - uint8_t c4 = (uint8_t)utf8[++i]; - - cres |= c4 & 0b00111111; - cres |= (c3 & 0b00111111) << 6; - cres |= (c2 & 0b00111111) << 12; - cres |= (c & 0b00000111) << 18; - - } - else { i += 3; continue; } - } - if (cres >= 0x10000 && cres <= 0x10FFFF) - { - auto subtracted = cres - 0x10000; - - auto high = (0x3FF & (subtracted >> 10)) | 0xD800; - auto low = (0x3FF & subtracted) | 0xDC00; - - utf16.push_back(high); - utf16.push_back(low); - } - else { - utf16.push_back((char16_t)cres); - } - +} +void UTF8::FromUTF32(std::basic_string &utf8, + const std::basic_string &utf32) { + for (auto c : utf32) { + if (c <= 0x7F) { + utf8.push_back((char)c); + } else if (c >= 0x80 && c <= 0x7FF) { + uint8_t high = 0b11000000 | ((c >> 6) & 0b00011111); + uint8_t low = 0b10000000 | (c & 0b00111111); + utf8.push_back((char)high); + utf8.push_back((char)low); + } else if (c >= 0x800 && c <= 0xFFFF) { + uint8_t highest = 0b11100000 | ((c >> 12) & 0b00001111); + uint8_t high = 0b10000000 | ((c >> 6) & 0b00111111); + uint8_t low = 0b10000000 | (c & 0b00111111); + utf8.push_back((char)highest); + utf8.push_back((char)high); + utf8.push_back((char)low); + } else if (c >= 0x010000 && c <= 0x10FFFF) { + uint8_t highest = 0b11110000 | ((c >> 18) & 0b00000111); + uint8_t high = 0b10000000 | ((c >> 12) & 0b00111111); + uint8_t low = 0b10000000 | ((c >> 6) & 0b00111111); + uint8_t lowest = 0b10000000 | (c & 0b00111111); + utf8.push_back((char)highest); + utf8.push_back((char)high); + utf8.push_back((char)low); + utf8.push_back((char)lowest); } } - void UTF16::FromUTF32(std::basic_string& utf16, const std::basic_string& utf32) - { - for (auto cres : utf32) - { - if (cres >= 0x10000 && cres <= 0x10FFFF) - { - auto subtracted = cres - 0x10000; +} - auto high = (0x3FF & (subtracted >> 10)) | 0xD800; - auto low = (0x3FF & subtracted) | 0xDC00; +void UTF16::FromUTF8(std::basic_string &utf16, + const std::basic_string &utf8) { + for (size_t i = 0; i < utf8.size(); i++) { + uint8_t c = (uint8_t)utf8[i]; + char32_t cres = 0; + if (c <= 127) { + cres = (char32_t)c; + } else if ((c & 0b11100000) == 0b11000000) { + if (i + 1 < utf8.size()) { - utf16.push_back(high); - utf16.push_back(low); - } - else { - utf16.push_back((char16_t)cres); - } - } - } + uint8_t c2 = (uint8_t)utf8[++i]; + cres |= c2 & 0b00111111; + cres |= (c & 0b00011111) << 6; - void UTF32::FromUTF8(std::basic_string& utf32, const std::basic_string& utf8) - { - for (size_t i = 0; i < utf8.size();i++) - { - uint8_t c = (uint8_t)utf8[i]; - char32_t cres = 0; - if (c <= 127) - { - cres=(char32_t)c; - } - else if ((c & 0b11100000) == 0b11000000) - { - if (i + 1 < utf8.size()) - { - - uint8_t c2 = (uint8_t)utf8[++i]; - cres |= c2 & 0b00111111; - cres |= (c & 0b00011111) << 6; - - } - else { - i++; - continue; - }; - } - else if ((c & 0b11110000) == 0b11100000) - { - if (i + 2 < utf8.size()) - { - uint8_t c2 = (uint8_t)utf8[++i]; - uint8_t c3 = (uint8_t)utf8[++i]; - cres |= c3 & 0b00111111; - cres |= (c2 & 0b00111111) << 6; - cres |= (c & 0b00001111) << 12; - - } - else { i += 2; continue; } - } - else if ((c & 0b11111000) == 0b11110000) - { - if (i + 3 < utf8.size()) - { - uint8_t c2 = (uint8_t)utf8[++i]; - uint8_t c3 = (uint8_t)utf8[++i]; - uint8_t c4 = (uint8_t)utf8[++i]; - - cres |= c4 & 0b00111111; - cres |= (c3 & 0b00111111) << 6; - cres |= (c2 & 0b00111111) << 12; - cres |= (c & 0b00000111) << 18; - - } - else { i += 3; continue; } - } - utf32.push_back(cres); - } - - } - - void UTF32::FromUTF16(std::basic_string& utf32, const std::basic_string& utf16) - { - for (size_t i = 0; i < utf16.size();i++) - { - char32_t c = utf16[i]; - if ((c & 0xFC00) == 0xD800) - { - c = (c & 0x03FF) << 10; + } else { i++; - if (i >= utf16.size()) return; + continue; + }; + } else if ((c & 0b11110000) == 0b11100000) { + if (i + 2 < utf8.size()) { + uint8_t c2 = (uint8_t)utf8[++i]; + uint8_t c3 = (uint8_t)utf8[++i]; + cres |= c3 & 0b00111111; + cres |= (c2 & 0b00111111) << 6; + cres |= (c & 0b00001111) << 12; - - char32_t c2 = utf16[i]; - if ((c2 & 0xFC00) != 0xDC00) continue; - - - c |= (c2 & 0x03FF); - - c += 0x10000; + } else { + i += 2; + continue; } - utf32.push_back(c); + } else if ((c & 0b11111000) == 0b11110000) { + if (i + 3 < utf8.size()) { + uint8_t c2 = (uint8_t)utf8[++i]; + uint8_t c3 = (uint8_t)utf8[++i]; + uint8_t c4 = (uint8_t)utf8[++i]; + + cres |= c4 & 0b00111111; + cres |= (c3 & 0b00111111) << 6; + cres |= (c2 & 0b00111111) << 12; + cres |= (c & 0b00000111) << 18; + + } else { + i += 3; + continue; + } + } + if (cres >= 0x10000 && cres <= 0x10FFFF) { + auto subtracted = cres - 0x10000; + + auto high = (0x3FF & (subtracted >> 10)) | 0xD800; + auto low = (0x3FF & subtracted) | 0xDC00; + + utf16.push_back(high); + utf16.push_back(low); + } else { + utf16.push_back((char16_t)cres); } } -} \ No newline at end of file +} +void UTF16::FromUTF32(std::basic_string &utf16, + const std::basic_string &utf32) { + for (auto cres : utf32) { + if (cres >= 0x10000 && cres <= 0x10FFFF) { + auto subtracted = cres - 0x10000; + + auto high = (0x3FF & (subtracted >> 10)) | 0xD800; + auto low = (0x3FF & subtracted) | 0xDC00; + + utf16.push_back(high); + utf16.push_back(low); + } else { + utf16.push_back((char16_t)cres); + } + } +} + +void UTF32::FromUTF8(std::basic_string &utf32, + const std::basic_string &utf8) { + for (size_t i = 0; i < utf8.size(); i++) { + uint8_t c = (uint8_t)utf8[i]; + char32_t cres = 0; + if (c <= 127) { + cres = (char32_t)c; + } else if ((c & 0b11100000) == 0b11000000) { + if (i + 1 < utf8.size()) { + + uint8_t c2 = (uint8_t)utf8[++i]; + cres |= c2 & 0b00111111; + cres |= (c & 0b00011111) << 6; + + } else { + i++; + continue; + }; + } else if ((c & 0b11110000) == 0b11100000) { + if (i + 2 < utf8.size()) { + uint8_t c2 = (uint8_t)utf8[++i]; + uint8_t c3 = (uint8_t)utf8[++i]; + cres |= c3 & 0b00111111; + cres |= (c2 & 0b00111111) << 6; + cres |= (c & 0b00001111) << 12; + + } else { + i += 2; + continue; + } + } else if ((c & 0b11111000) == 0b11110000) { + if (i + 3 < utf8.size()) { + uint8_t c2 = (uint8_t)utf8[++i]; + uint8_t c3 = (uint8_t)utf8[++i]; + uint8_t c4 = (uint8_t)utf8[++i]; + + cres |= c4 & 0b00111111; + cres |= (c3 & 0b00111111) << 6; + cres |= (c2 & 0b00111111) << 12; + cres |= (c & 0b00000111) << 18; + + } else { + i += 3; + continue; + } + } + utf32.push_back(cres); + } +} + +void UTF32::FromUTF16(std::basic_string &utf32, + const std::basic_string &utf16) { + for (size_t i = 0; i < utf16.size(); i++) { + char32_t c = utf16[i]; + if ((c & 0xFC00) == 0xD800) { + c = (c & 0x03FF) << 10; + i++; + if (i >= utf16.size()) + return; + + char32_t c2 = utf16[i]; + if ((c2 & 0xFC00) != 0xDC00) + continue; + + c |= (c2 & 0x03FF); + + c += 0x10000; + } + utf32.push_back(c); + } +} +} // namespace Tesses::Framework::Text::StringConverter \ No newline at end of file diff --git a/src/TextStreams/StdIOReader.cpp b/src/TextStreams/StdIOReader.cpp index 9b5f6f3..a10a617 100644 --- a/src/TextStreams/StdIOReader.cpp +++ b/src/TextStreams/StdIOReader.cpp @@ -1,31 +1,43 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ #include "TessesFramework/TextStreams/StdIOReader.hpp" +#include "TessesFramework/Console.hpp" +namespace Tesses::Framework::TextStreams { +ConsoleReader::ConsoleReader() {} +bool ConsoleReader::ReadBlock(std::string &str, size_t len) { + size_t i = 0; -namespace Tesses::Framework::TextStreams -{ - ConsoleReader::ConsoleReader() - { + for (; i < len;) { + int rd = Console::Read(); + if (rd == -1) + break; - } - bool ConsoleReader::ReadBlock(std::string& str,size_t len) - { - std::vector buff(len); - - size_t read=0; - size_t readTotal=0; - uint8_t* buffOff=buff.data(); - do { - read=fread(buffOff,1,len,stdin); - if(read != 0) {readTotal+= read;len-=read; buffOff+=read;} - } while(read != 0); - if(readTotal == 0) return false; - str.append((const char*)buff.data(), readTotal); - - return true; + str.push_back((char)rd); + + i++; } - ConsoleReader StdIn() - { - return ConsoleReader(); - } -} \ No newline at end of file + if (i == 0) + return false; + + return true; +} + +ConsoleReader StdIn() { return ConsoleReader(); } +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/src/TextStreams/StdIOWriter.cpp b/src/TextStreams/StdIOWriter.cpp index 12bfcf4..8d28501 100644 --- a/src/TextStreams/StdIOWriter.cpp +++ b/src/TextStreams/StdIOWriter.cpp @@ -1,42 +1,35 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/TextStreams/StdIOWriter.hpp" -#if defined(__PS2__) -#include -#else -#include -#endif -namespace Tesses::Framework::TextStreams -{ - ConsoleWriter::ConsoleWriter(bool isError) : TextWriter() - { - this->isError=isError; - } +#include "TessesFramework/Console.hpp" +namespace Tesses::Framework::TextStreams { +ConsoleWriter::ConsoleWriter(bool isError) : TextWriter() { + this->isError = isError; +} - void ConsoleWriter::WriteData(const char* text, size_t len) - { - #if defined(__PS2__) - char lenThing[10];//%.2048s - while(len > 0) { - int b = std::min((int)2047,(int)len); - snprintf(lenThing,18,"%%.%is",b); - - scr_printf(lenThing,text); - - len -= b; - text += b; - } - #else - if(isError) - fwrite(text,1,len,stderr); - else - fwrite(text,1,len,stdout); - #endif +void ConsoleWriter::WriteData(const char *text, size_t len) { + if (isError) { + Console::ErrorView(std::string_view(text, len)); + } else { + Console::WriteView(std::string_view(text, len)); } - ConsoleWriter StdOut() - { - return ConsoleWriter(false); - } - ConsoleWriter StdErr() - { - return ConsoleWriter(true); - } -} \ No newline at end of file +} +ConsoleWriter StdOut() { return ConsoleWriter(false); } +ConsoleWriter StdErr() { return ConsoleWriter(true); } +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/src/TextStreams/StreamReader.cpp b/src/TextStreams/StreamReader.cpp index 3f9ad53..b4c2da1 100644 --- a/src/TextStreams/StreamReader.cpp +++ b/src/TextStreams/StreamReader.cpp @@ -1,46 +1,54 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/TextStreams/StreamReader.hpp" #include "TessesFramework/Streams/FileStream.hpp" using Stream = Tesses::Framework::Streams::Stream; using FileStream = Tesses::Framework::Streams::FileStream; namespace Tesses::Framework::TextStreams { - - StreamReader::StreamReader(std::filesystem::path path) : StreamReader(std::make_shared(path,"rb")) - { - } - bool StreamReader::Rewind() - { - if(this->strm->CanSeek()) - { - this->strm->Seek((int64_t)0,Tesses::Framework::Streams::SeekOrigin::Begin); - return true; - } - return false; - } - StreamReader::StreamReader(std::shared_ptr strm) : TextReader() - { - this->strm = strm; - } - - std::shared_ptr StreamReader::GetStream() - { - return (this->strm); - } - - bool StreamReader::ReadBlock(std::string& str, size_t len) - { - std::vector buff(len); - - len = strm->ReadBlock(buff.data(),len); - if(len == 0) { return false;} - str.append((const char*)buff.data(), len); - - +StreamReader::StreamReader(std::filesystem::path path) + : StreamReader(std::make_shared(path, "rb")) {} +bool StreamReader::Rewind() { + if (this->strm->CanSeek()) { + this->strm->Seek((int64_t)0, + Tesses::Framework::Streams::SeekOrigin::Begin); return true; } - StreamReader::~StreamReader() - { - + return false; +} +StreamReader::StreamReader(std::shared_ptr strm) : TextReader() { + this->strm = strm; +} + +std::shared_ptr StreamReader::GetStream() { return (this->strm); } + +bool StreamReader::ReadBlock(std::string &str, size_t len) { + std::vector buff(len); + + len = strm->ReadBlock(buff.data(), len); + if (len == 0) { + return false; } -}; \ No newline at end of file + str.append((const char *)buff.data(), len); + + return true; +} +StreamReader::~StreamReader() {} +}; // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/src/TextStreams/StreamWriter.cpp b/src/TextStreams/StreamWriter.cpp index c8ceadd..29a387d 100644 --- a/src/TextStreams/StreamWriter.cpp +++ b/src/TextStreams/StreamWriter.cpp @@ -1,28 +1,37 @@ -#include "TessesFramework/Streams/FileStream.hpp" +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/TextStreams/StreamWriter.hpp" +#include "TessesFramework/Streams/FileStream.hpp" using Stream = Tesses::Framework::Streams::Stream; using FileStream = Tesses::Framework::Streams::FileStream; -namespace Tesses::Framework::TextStreams -{ - std::shared_ptr StreamWriter::GetStream() - { - return this->strm; - } - StreamWriter::StreamWriter(std::shared_ptr strm) : TextWriter() - { +namespace Tesses::Framework::TextStreams { +std::shared_ptr StreamWriter::GetStream() { return this->strm; } +StreamWriter::StreamWriter(std::shared_ptr strm) : TextWriter() { - this->strm = strm; - } - StreamWriter::StreamWriter(std::filesystem::path filename, bool append) : StreamWriter(std::make_shared(filename, append ? "ab" : "wb")) - { - - } - void StreamWriter::WriteData(const char* text, size_t len) - { - this->strm->WriteBlock((const uint8_t*)text, len); - } - StreamWriter::~StreamWriter() - { - } -} \ No newline at end of file + this->strm = strm; +} +StreamWriter::StreamWriter(std::filesystem::path filename, bool append) + : StreamWriter( + std::make_shared(filename, append ? "ab" : "wb")) {} +void StreamWriter::WriteData(const char *text, size_t len) { + this->strm->WriteBlock((const uint8_t *)text, len); +} +StreamWriter::~StreamWriter() {} +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/src/TextStreams/StringReader.cpp b/src/TextStreams/StringReader.cpp index 5f466e9..c01e831 100644 --- a/src/TextStreams/StringReader.cpp +++ b/src/TextStreams/StringReader.cpp @@ -1,38 +1,45 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/TextStreams/StringReader.hpp" namespace Tesses::Framework::TextStreams { - StringReader::StringReader() - { - this->offset=0; - this->str=""; - } - StringReader::StringReader(std::string str) - { - this->offset=0; - this->str=str; - } - size_t& StringReader::GetOffset() - { - return this->offset; - } - std::string& StringReader::GetString() - { - return this->str; - } - bool StringReader::Rewind() - { - this->offset=0; +StringReader::StringReader() { + this->offset = 0; + this->str = ""; +} +StringReader::StringReader(std::string str) { + this->offset = 0; + this->str = str; +} +size_t &StringReader::GetOffset() { return this->offset; } +std::string &StringReader::GetString() { return this->str; } +bool StringReader::Rewind() { + this->offset = 0; + return true; +} +bool StringReader::ReadBlock(std::string &str, size_t sz) { + if (this->offset < this->str.size()) { + size_t len = std::min(sz, this->str.size() - this->offset); + str.insert(str.size(), this->str.data() + this->offset, len); + offset += len; return true; } - bool StringReader::ReadBlock(std::string& str,size_t sz) - { - if(this->offset < this->str.size()) - { - size_t len = std::min(sz,this->str.size()-this->offset); - str.insert(str.size(),this->str.data()+this->offset,len); - offset+=len; - return true; - } - return false; - } -} \ No newline at end of file + return false; +} +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/src/TextStreams/StringWriter.cpp b/src/TextStreams/StringWriter.cpp index ddf30fe..96626ea 100644 --- a/src/TextStreams/StringWriter.cpp +++ b/src/TextStreams/StringWriter.cpp @@ -1,21 +1,28 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/TextStreams/StringWriter.hpp" -namespace Tesses::Framework::TextStreams -{ - std::string& StringWriter::GetString() - { - return this->text; - } - StringWriter::StringWriter() : TextWriter() - { - - } - StringWriter::StringWriter(std::string str) : TextWriter() - { - this->text = str; - } - void StringWriter::WriteData(const char* text, size_t len) - { - this->text.append(text,len); - } -} \ No newline at end of file +namespace Tesses::Framework::TextStreams { +std::string &StringWriter::GetString() { return this->text; } +StringWriter::StringWriter() : TextWriter() {} +StringWriter::StringWriter(std::string str) : TextWriter() { this->text = str; } +void StringWriter::WriteData(const char *text, size_t len) { + this->text.append(text, len); +} +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/src/TextStreams/TextReader.cpp b/src/TextStreams/TextReader.cpp index d528d6b..a7d0e77 100644 --- a/src/TextStreams/TextReader.cpp +++ b/src/TextStreams/TextReader.cpp @@ -1,101 +1,128 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/TextStreams/TextReader.hpp" -namespace Tesses::Framework::TextStreams -{ - bool TextReader::Rewind() - { +namespace Tesses::Framework::TextStreams { +bool TextReader::Rewind() { return false; } +int32_t TextReader::ReadChar() { + std::string txt; + this->ReadBlock(txt, 1); + if (txt.empty()) { + eof = true; + return -1; + } + return (uint8_t)txt[0]; +} +std::string TextReader::ReadLine() { + std::string str = {}; + ReadLine(str); + return str; +} +bool TextReader::ReadLineHttp(std::string &str) { + if (eof) return false; - } - int32_t TextReader::ReadChar() - { - std::string txt; - this->ReadBlock(txt,1); - if(txt.empty()) { eof=true; return -1;} - return (uint8_t)txt[0]; - } - std::string TextReader::ReadLine() - { - std::string str = {}; - ReadLine(str); - return str; - } - bool TextReader::ReadLineHttp(std::string& str) - { - if(eof) return false; - bool ret = false; - int32_t r = -1; - do { - r = ReadChar(); - if(r == -1) {break;} - if(r == '\r') continue; - if(r == '\n') break; - str.push_back((char)(uint8_t)r); - ret = true; - } while(r != -1); - return ret; - } - bool TextReader::ReadLine(std::string& str) - { - - if(eof) return false; - bool ret = false; - int32_t r = -1; - do { - r = ReadChar(); - if(r == -1) break; - if(r == '\r') continue; - if(r == '\n') return true; - str.push_back((char)(uint8_t)r); - ret = true; - } while(r != -1); - return ret; - } - void TextReader::ReadAllLines(std::vector& lines) - { - if(eof) return; - int32_t r = -1; - std::string builder; - do { - r = ReadChar(); - if(r == -1) break; - if(r == '\r') continue; - if(r == '\n') { - lines.push_back(builder); - builder.clear(); - continue; - } - builder += (char)r; - - } while(r != -1); - } - - std::string TextReader::ReadToEnd() - { - std::string str = {}; - ReadToEnd(str); - return str; - } - - void TextReader::ReadToEnd(std::string& str) - { - - if(eof) return; - while(ReadBlock(str,1024)); - } - void TextReader::CopyTo(TextWriter& writer, size_t buffSz) - { - - if(eof) return; - std::string str = {}; - while(ReadBlock(str,buffSz)) - { - writer.Write(str); - str.clear(); + bool ret = false; + int32_t r = -1; + do { + r = ReadChar(); + if (r == -1) { + break; } - } + if (r == '\r') + continue; + if (r == '\n') + break; + str.push_back((char)(uint8_t)r); + ret = true; + } while (r != -1); + return ret; +} +bool TextReader::ReadLine(std::string &str) { - TextReader::~TextReader() - { + if (eof) + return false; + bool ret = false; + int32_t r = -1; + do { + r = ReadChar(); + if (r == -1) + break; + if (r == '\n') + { + if(!str.empty() && str.back() == '\r') str.resize(str.size()-1); + return true; + } + str.push_back((char)(uint8_t)r); + ret = true; + } while (r != -1); + + if(!str.empty() && str.back() == '\r') str.resize(str.size()-1); + return ret; +} +void TextReader::ReadAllLines(std::vector &lines) { + if (eof) + return; + int32_t r = -1; + std::string builder; + do { + r = ReadChar(); + if (r == -1) + break; + if (r == '\n') { + + if(!builder.empty() && builder.back() == '\r') + builder.resize(builder.size()-1); + + + lines.push_back(builder); + builder.clear(); + continue; + } + builder += (char)r; + + } while (r != -1); +} + +std::string TextReader::ReadToEnd() { + std::string str = {}; + ReadToEnd(str); + return str; +} + +void TextReader::ReadToEnd(std::string &str) { + + if (eof) + return; + while (ReadBlock(str, 1024)) + ; +} +void TextReader::CopyTo(TextWriter &writer, size_t buffSz) { + + if (eof) + return; + std::string str = {}; + while (ReadBlock(str, buffSz)) { + writer.Write(str); + str.clear(); } -} \ No newline at end of file +} + +TextReader::~TextReader() {} +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/src/TextStreams/TextWriter.cpp b/src/TextStreams/TextWriter.cpp index 5431280..d382dd7 100644 --- a/src/TextStreams/TextWriter.cpp +++ b/src/TextStreams/TextWriter.cpp @@ -1,115 +1,102 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/TextStreams/TextWriter.hpp" #include "TessesFramework/Http/HttpUtils.hpp" -namespace Tesses::Framework::TextStreams -{ - void TextWriter::Write(int64_t n) - { - std::string text = std::to_string(n); - WriteData(text.c_str(),text.size()); - } - void TextWriter::Write(uint64_t n) - { - std::string text = std::to_string(n); - WriteData(text.c_str(),text.size()); - } - - void TextWriter::Write(const void* ptr) - { - std::string text = "0x"; - uintptr_t ptr2 = (uintptr_t)ptr; +namespace Tesses::Framework::TextStreams { +void TextWriter::Write(int64_t n) { + std::string text = std::to_string(n); + WriteData(text.c_str(), text.size()); +} +void TextWriter::Write(uint64_t n) { + std::string text = std::to_string(n); + WriteData(text.c_str(), text.size()); +} - for(size_t i = 1; i <= sizeof(ptr); i++) - { - uint8_t v = (uint8_t)(ptr2 >> (int)((sizeof(ptr) - i) * 8)); - text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v >> 4)); - text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v)); - } - WriteData(text.c_str(),text.size()); - } - void TextWriter::Write(const char* ptr) - { - WriteData(ptr,strlen(ptr)); - } - void TextWriter::Write(char c) - { - WriteData(&c,1); - } - void TextWriter::Write(double d) - { - std::string text = std::to_string(d); - WriteData(text.c_str(),text.size()); - } - - - void TextWriter::WriteLine(int64_t n) - { - std::string text = std::to_string(n); - text.append(newline); - WriteData(text.c_str(),text.size()); - } - void TextWriter::WriteLine(uint64_t n) - { - std::string text = std::to_string(n); - text.append(newline); - WriteData(text.c_str(),text.size()); - } - void TextWriter::WriteLine(const void* ptr) - { - std::string text = "0x"; - uintptr_t ptr2 = (uintptr_t)ptr; +void TextWriter::Write(const void *ptr) { + std::string text = "0x"; + uintptr_t ptr2 = (uintptr_t)ptr; - for(size_t i = 1; i <= sizeof(ptr); i++) - { - uint8_t v = (uint8_t)(ptr2 >> (int)((sizeof(ptr) - i) * 8)); - text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v >> 4)); - text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v)); - } - text.append(newline); - WriteData(text.c_str(),text.size()); + for (size_t i = 1; i <= sizeof(ptr); i++) { + uint8_t v = (uint8_t)(ptr2 >> (int)((sizeof(ptr) - i) * 8)); + text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v >> 4)); + text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v)); } - void TextWriter::WriteLine(const char* ptr) - { - std::string text = ptr; - text.append(newline); - WriteData(text.c_str(),text.size()); - } - void TextWriter::WriteLine(char c) - { - std::string text = {c}; - text.append(newline); - WriteData(text.c_str(),text.size()); - } - void TextWriter::WriteLine(double d) - { - std::string text = std::to_string(d); - text.append(newline); - WriteData(text.c_str(),text.size()); - } - TextWriter::TextWriter() - { - #if defined(WIN32) || defined(_WIN32) - newline = "\r\n"; - #else - newline = "\n"; - #endif - } - void TextWriter::Write(std::string txt) - { - WriteData(txt.c_str(),txt.size()); - } - void TextWriter::WriteLine(std::string txt) - { - std::string str = txt; - str.append(newline); - Write(str); - } - void TextWriter::WriteLine() - { - Write(newline); - } - TextWriter::~TextWriter() - { + WriteData(text.c_str(), text.size()); +} +void TextWriter::Write(const char *ptr) { WriteData(ptr, strlen(ptr)); } +void TextWriter::Write(char c) { WriteData(&c, 1); } +void TextWriter::Write(double d) { + std::string text = std::to_string(d); + WriteData(text.c_str(), text.size()); +} +void TextWriter::WriteLine(int64_t n) { + std::string text = std::to_string(n); + text.append(newline); + WriteData(text.c_str(), text.size()); +} +void TextWriter::WriteLine(uint64_t n) { + std::string text = std::to_string(n); + text.append(newline); + WriteData(text.c_str(), text.size()); +} +void TextWriter::WriteLine(const void *ptr) { + std::string text = "0x"; + uintptr_t ptr2 = (uintptr_t)ptr; + + for (size_t i = 1; i <= sizeof(ptr); i++) { + uint8_t v = (uint8_t)(ptr2 >> (int)((sizeof(ptr) - i) * 8)); + text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v >> 4)); + text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v)); } -} \ No newline at end of file + text.append(newline); + WriteData(text.c_str(), text.size()); +} +void TextWriter::WriteLine(const char *ptr) { + std::string text = ptr; + text.append(newline); + WriteData(text.c_str(), text.size()); +} +void TextWriter::WriteLine(char c) { + std::string text = {c}; + text.append(newline); + WriteData(text.c_str(), text.size()); +} +void TextWriter::WriteLine(double d) { + std::string text = std::to_string(d); + text.append(newline); + WriteData(text.c_str(), text.size()); +} +TextWriter::TextWriter() { +#if defined(WIN32) || defined(_WIN32) + newline = "\r\n"; +#else + newline = "\n"; +#endif +} +void TextWriter::Write(std::string txt) { WriteData(txt.c_str(), txt.size()); } +void TextWriter::WriteLine(std::string txt) { + std::string str = txt; + str.append(newline); + Write(str); +} +void TextWriter::WriteLine() { Write(newline); } +TextWriter::~TextWriter() {} +} // namespace Tesses::Framework::TextStreams \ No newline at end of file diff --git a/src/Threading/Mutex.cpp b/src/Threading/Mutex.cpp index b25870d..153df4b 100644 --- a/src/Threading/Mutex.cpp +++ b/src/Threading/Mutex.cpp @@ -1,106 +1,99 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Threading/Mutex.hpp" #include #include #if defined(_WIN32) #include #undef min -#elif defined(GEKKO) -#include #else #include #endif -namespace Tesses::Framework::Threading -{ - - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - class MutexHiddenFieldData : public HiddenFieldData - { - public: - #if defined(_WIN32) - HANDLE mtx; - #elif defined(GEKKO) - mutex_t mtx; - #else - pthread_mutex_t mtx; - pthread_mutexattr_t attr; - #endif - ~MutexHiddenFieldData() - { - #if defined(_WIN32) - CloseHandle(mtx); - #elif defined(GEKKO) - LWP_MutexDestroy(mtx); - - #else - pthread_mutex_destroy(&mtx); - pthread_mutexattr_destroy(&attr); - #endif - } - }; - #endif - Mutex::Mutex() - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - auto md=this->data.AllocField(); - #if defined(_WIN32) - md->mtx = CreateMutex(NULL,false,NULL); - #elif defined(GEKKO) - md->mtx = LWP_MUTEX_NULL; - LWP_MutexInit(&md->mtx, true); - - #else - pthread_mutexattr_init(&md->attr); - pthread_mutexattr_settype(&md->attr,PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&md->mtx,&md->attr); - - #endif - #endif - } - void Mutex::Lock() - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - auto md = this->data.GetField(); - #if defined(_WIN32) - WaitForSingleObject(md->mtx, INFINITE); - #elif defined(GEKKO) - LWP_MutexLock(md->mtx); - - #else - pthread_mutex_lock(&md->mtx); - #endif - #endif - } - void Mutex::Unlock() - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - auto md = this->data.GetField(); - #if defined(_WIN32) - ReleaseMutex(md->mtx); - #elif defined(GEKKO) - LWP_MutexUnlock(md->mtx); - - #else - pthread_mutex_unlock(&md->mtx); - #endif - #endif - } - bool Mutex::TryLock() - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - auto md = this->data.GetField(); - #if defined(_WIN32) - return WaitForSingleObject(md->mtx, 100) == WAIT_OBJECT_0; - #elif defined(GEKKO) - return LWP_MutexTryLock(md->mtx) == 0; - - #else - return pthread_mutex_trylock(&md->mtx) == 0; - #endif - #endif - } - Mutex::~Mutex() - { - +namespace Tesses::Framework::Threading { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) +class MutexHiddenFieldData : public HiddenFieldData { + public: +#if defined(_WIN32) + HANDLE mtx; +#else + pthread_mutex_t mtx; + pthread_mutexattr_t attr; +#endif + ~MutexHiddenFieldData() { +#if defined(_WIN32) + CloseHandle(mtx); +#else + pthread_mutex_destroy(&mtx); + pthread_mutexattr_destroy(&attr); +#endif } }; +#endif +Mutex::Mutex() { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + auto md = this->data.AllocField(); +#if defined(_WIN32) + md->mtx = CreateMutex(NULL, false, NULL); +#elif defined(GEKKO) + md->mtx = LWP_MUTEX_NULL; + LWP_MutexInit(&md->mtx, true); + +#else + pthread_mutexattr_init(&md->attr); + pthread_mutexattr_settype(&md->attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&md->mtx, &md->attr); + +#endif +#endif +} +void Mutex::Lock() { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + auto md = this->data.GetField(); +#if defined(_WIN32) + WaitForSingleObject(md->mtx, INFINITE); + +#else + pthread_mutex_lock(&md->mtx); +#endif +#endif +} +void Mutex::Unlock() { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + auto md = this->data.GetField(); +#if defined(_WIN32) + ReleaseMutex(md->mtx); +#else + pthread_mutex_unlock(&md->mtx); +#endif +#endif +} +bool Mutex::TryLock() { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + auto md = this->data.GetField(); +#if defined(_WIN32) + return WaitForSingleObject(md->mtx, 100) == WAIT_OBJECT_0; + +#else + return pthread_mutex_trylock(&md->mtx) == 0; +#endif +#endif +} +Mutex::~Mutex() {} +}; // namespace Tesses::Framework::Threading diff --git a/src/Threading/Thread.cpp b/src/Threading/Thread.cpp index c2b412e..55cb09d 100644 --- a/src/Threading/Thread.cpp +++ b/src/Threading/Thread.cpp @@ -1,219 +1,214 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ #include "TessesFramework/Threading/Thread.hpp" +#include "TessesFramework/Common.hpp" +#include "TessesFramework/Threading/Mutex.hpp" #include #include -#include "TessesFramework/Threading/Mutex.hpp" -#include "TessesFramework/Common.hpp" #if defined(__SWITCH__) extern "C" { -#include #include +#include } #endif -namespace Tesses::Framework::Threading -{ +namespace Tesses::Framework::Threading { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) +#if defined(__SWITCH__) +Mutex needed_to_be_joined_mtx; +class NeedToBeJoinnedThread { - #if defined(__SWITCH__) || defined(GEKKO) - Mutex needed_to_be_joined_mtx; - class NeedToBeJoinnedThread { - - static void* cb(void* data) - { - - auto ntbjt = static_cast(data); - - ntbjt->hasInvoked=true; - TF_LOG("About to call thread func"); - if(ntbjt->_cb) - ntbjt->_cb(); - TF_LOG("Finished calling thread func"); - ntbjt->hasExited=true; + static void *cb(void *data) { - return NULL; - } - std::function _cb; - std::atomic hasInvoked=false; - #if defined(__SWITCH__) - pthread_t thrd; - #elif defined(GEKKO) - lwp_t thrd; - #endif - public: - NeedToBeJoinnedThread(std::function cb) - { - this->_cb = cb; - joinned=false; - joinning=false; - hasExited=false; - #if defined(GEKKO) - LWP_CreateThread(&thrd, this->cb, static_cast(this), nullptr,12000, 98); - #elif defined(__SWITCH__) - pthread_create(&thrd,NULL,this->cb,static_cast(this)); - - #endif - } - std::atomic joinned; - std::atomic joinning; - std::atomic hasExited; - void Join(); - void WaitTillInvoked() - { - while(!hasInvoked); - TF_LOG("Invoked!"); - } - }; + auto ntbjt = static_cast(data); - void NeedToBeJoinnedThread::Join() - { - if(joinning) - { - return; - } - joinning=true; - #if defined(__SWITCH__) - pthread_join(this->thrd,NULL); - #elif defined(GEKKO) - void* res; - LWP_JoinThread(this->thrd,&res); - #endif - joinned=true; - //start the joinning process + ntbjt->hasInvoked = true; + TF_LOG("About to call thread func"); + if (ntbjt->_cb) + ntbjt->_cb(); + TF_LOG("Finished calling thread func"); + ntbjt->hasExited = true; + + return NULL; } + std::function _cb; + std::atomic hasInvoked = false; + + pthread_t thrd; + + public: + NeedToBeJoinnedThread(std::function cb) { + this->_cb = cb; + joinned = false; + joinning = false; + hasExited = false; + + pthread_create(&thrd, NULL, this->cb, static_cast(this)); - std::vector> needToBeJoinnedThread; - void JoinAllThreads() - { - needed_to_be_joined_mtx.Lock(); - for(auto item : needToBeJoinnedThread) - { - item->Join(); - } - needToBeJoinnedThread.clear(); - needed_to_be_joined_mtx.Unlock(); - } - void LookForFinishedThreads() - { - - needed_to_be_joined_mtx.Lock(); - for(auto index = needToBeJoinnedThread.begin(); index < needToBeJoinnedThread.end(); index++) - { - auto& idx = *index; - if(idx->hasExited) - { - if(idx->joinning) while(!idx->joinned); - TF_LOG("ABOUT TO JOIN"); - idx->Join(); - TF_LOG("JOINNED"); - needToBeJoinnedThread.erase(index); - index--; - } - } - needed_to_be_joined_mtx.Unlock(); + std::atomic joinned; + std::atomic joinning; + std::atomic hasExited; + void Join(); + void WaitTillInvoked() { + while (!hasInvoked) + ; + TF_LOG("Invoked!"); } - - #endif - class ThreadHiddenFieldData : public HiddenFieldData { - public: - #if defined(_WIN32) +}; - HANDLE thrd; - DWORD thrdId; - - - - #elif defined(__SWITCH__) || defined(GEKKO) - std::shared_ptr thread; - #else - pthread_t thrd; - #endif - - - std::function fn; - - std::atomic hasInvoked; - }; - - - #if defined(_WIN32) - static DWORD __stdcall cb(LPVOID data) - #elif defined(__SWITCH__) - static void cb(void* data) - #else - static void* cb(void* data) - #endif - { - auto thrd = static_cast(data); - - auto fn = thrd->fn; - thrd->hasInvoked=true; - fn(); - #if !defined(_WIN32) && !defined(__SWITCH__) - return NULL; - #elif(__SWITCH__) - - #else - return 0; - #endif - } - #endif - - Thread::Thread(std::function fn) - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - auto data = this->data.AllocField(); - data->hasInvoked=false; - data->fn = fn; - #if defined(_WIN32) - data->thrd = CreateThread(NULL,0,cb,static_cast(data), 0, &data->thrdId); - while(!data->hasInvoked); - #elif defined(__SWITCH__) || defined(GEKKO) - data->thread = std::make_shared(fn); - data->thread->WaitTillInvoked(); - //threadCreate(,cb,static_cast(data),NULL,8000000,0x00,-2); - #else - pthread_create(&data->thrd,NULL,cb,static_cast(data)); - while(!data->hasInvoked); - //thrd_create(&thrd, cb, static_cast(this)); - #endif - - #endif - } - void Thread::Detach() - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - auto data = this->data.GetField(); - - #if defined(_WIN32) - CloseHandle(data->thrd); - #elif defined(__SWITCH__) || defined(GEKKO) - TF_LOG("Detaching"); - needed_to_be_joined_mtx.Lock(); - needToBeJoinnedThread.push_back(data->thread); - needed_to_be_joined_mtx.Unlock(); - - TF_LOG("Detached!"); - #else - pthread_detach(data->thrd); - #endif - - #endif +void NeedToBeJoinnedThread::Join() { + if (joinning) { + return; } + joinning = true; + pthread_join(this->thrd, NULL); - void Thread::Join() - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - auto data = this->data.GetField(); - #if defined(_WIN32) - WaitForSingleObject(data->thrd, INFINITE); - #elif defined(__SWITCH__) || defined(GEKKO) - data->thread->Join(); - #else - pthread_join(data->thrd,NULL); - #endif - #endif - } + joinned = true; + // start the joinning process } + +std::vector> needToBeJoinnedThread; +void JoinAllThreads() { + needed_to_be_joined_mtx.Lock(); + for (auto item : needToBeJoinnedThread) { + item->Join(); + } + needToBeJoinnedThread.clear(); + needed_to_be_joined_mtx.Unlock(); +} +void LookForFinishedThreads() { + + needed_to_be_joined_mtx.Lock(); + for (auto index = needToBeJoinnedThread.begin(); + index < needToBeJoinnedThread.end(); index++) { + auto &idx = *index; + if (idx->hasExited) { + if (idx->joinning) + while (!idx->joinned) + ; + TF_LOG("ABOUT TO JOIN"); + idx->Join(); + TF_LOG("JOINNED"); + needToBeJoinnedThread.erase(index); + index--; + } + } + needed_to_be_joined_mtx.Unlock(); +} + +#endif +class ThreadHiddenFieldData : public HiddenFieldData { + public: +#if defined(_WIN32) + + HANDLE thrd; + DWORD thrdId; + +#elif defined(__SWITCH__) + std::shared_ptr thread; +#else + pthread_t thrd; +#endif + + std::function fn; + + std::atomic hasInvoked; +}; + +#if defined(_WIN32) +static DWORD __stdcall cb(LPVOID data) +#elif defined(__SWITCH__) +static void cb(void *data) +#else +static void *cb(void *data) +#endif +{ + auto thrd = static_cast(data); + + auto fn = thrd->fn; + thrd->hasInvoked = true; + fn(); +#if !defined(_WIN32) && !defined(__SWITCH__) + return NULL; +#elif (__SWITCH__) + +#else + return 0; +#endif +} +#endif + +Thread::Thread(std::function fn) { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + auto data = this->data.AllocField(); + data->hasInvoked = false; + data->fn = fn; +#if defined(_WIN32) + data->thrd = + CreateThread(NULL, 0, cb, static_cast(data), 0, &data->thrdId); + while (!data->hasInvoked) + ; +#elif defined(__SWITCH__) + data->thread = std::make_shared(fn); + data->thread->WaitTillInvoked(); + // threadCreate(,cb,static_cast(data),NULL,8000000,0x00,-2); +#else + pthread_create(&data->thrd, NULL, cb, static_cast(data)); + while (!data->hasInvoked) + ; + // thrd_create(&thrd, cb, static_cast(this)); +#endif + +#endif +} +void Thread::Detach() { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + auto data = this->data.GetField(); + +#if defined(_WIN32) + CloseHandle(data->thrd); +#elif defined(__SWITCH__) + TF_LOG("Detaching"); + needed_to_be_joined_mtx.Lock(); + needToBeJoinnedThread.push_back(data->thread); + needed_to_be_joined_mtx.Unlock(); + + TF_LOG("Detached!"); +#else + pthread_detach(data->thrd); +#endif + +#endif +} + +void Thread::Join() { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + auto data = this->data.GetField(); +#if defined(_WIN32) + WaitForSingleObject(data->thrd, INFINITE); +#elif defined(__SWITCH__) + data->thread->Join(); +#else + pthread_join(data->thrd, NULL); +#endif +#endif +} +} // namespace Tesses::Framework::Threading diff --git a/src/Threading/ThreadPool.cpp b/src/Threading/ThreadPool.cpp index 48582aa..92bb5be 100644 --- a/src/Threading/ThreadPool.cpp +++ b/src/Threading/ThreadPool.cpp @@ -1,88 +1,93 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Threading/ThreadPool.hpp" #if !defined(GEKKO) #include #endif -namespace Tesses::Framework::Threading -{ - size_t ThreadPool::GetNumberOfCores() - { - #if defined(GEKKO) - return 1; - #elif defined(TESSESFRAMEWORK_ENABLE_THREADING) - return (size_t)std::thread::hardware_concurrency(); - #else - return 1; - #endif - } - size_t ThreadPool::ThreadCount() - { - return this->threads.size(); - } - bool ThreadPool::Empty() - { - bool qie; - this->mtx.Lock(); - qie = this->callbacks.empty(); - this->mtx.Unlock(); - return qie; - } - ThreadPool::ThreadPool(size_t threads) - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - this->isRunning=true; - for(size_t i = 0; i < threads; i++) - { - this->threads.push_back(new Thread([this,i]()->void{ - while(true) - { - this->mtx.Lock(); - - if(!this->isRunning) - { - this->mtx.Unlock(); - return; - } +namespace Tesses::Framework::Threading { +size_t ThreadPool::GetNumberOfCores() { +#if defined(GEKKO) + return 1; +#elif defined(TESSESFRAMEWORK_ENABLE_THREADING) + return (size_t)std::thread::hardware_concurrency(); +#else + return 1; +#endif +} +size_t ThreadPool::ThreadCount() { return this->threads.size(); } +bool ThreadPool::Empty() { + bool qie; + this->mtx.Lock(); + qie = this->callbacks.empty(); + this->mtx.Unlock(); + return qie; +} +ThreadPool::ThreadPool(size_t threads) { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + this->isRunning = true; + for (size_t i = 0; i < threads; i++) { + this->threads.push_back(new Thread([this, i]() -> void { + while (true) { + this->mtx.Lock(); - std::function fn=nullptr; - - if(!this->callbacks.empty()) - { - fn=this->callbacks.front(); - this->callbacks.pop(); - } + if (!this->isRunning) { this->mtx.Unlock(); - if(fn) - fn(i); + return; } - })); - } - #endif - } - void ThreadPool::Schedule(std::function cb) - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - this->mtx.Lock(); - this->callbacks.push(cb); - this->mtx.Unlock(); - #endif - } - ThreadPool::~ThreadPool() - { - #if defined(TESSESFRAMEWORK_ENABLE_THREADING) - while(true) - { - this->mtx.Lock(); - auto emp=this->callbacks.empty(); - if(emp) this->isRunning=false; - this->mtx.Unlock(); - if(emp) break; - } - for(auto item : this->threads) - { - item->Join(); - delete item; - } - #endif + std::function fn = nullptr; + + if (!this->callbacks.empty()) { + fn = this->callbacks.front(); + this->callbacks.pop(); + } + this->mtx.Unlock(); + if (fn) + fn(i); + } + })); } -} \ No newline at end of file +#endif +} +void ThreadPool::Schedule(std::function cb) { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + this->mtx.Lock(); + this->callbacks.push(cb); + this->mtx.Unlock(); +#endif +} +ThreadPool::~ThreadPool() { +#if defined(TESSESFRAMEWORK_ENABLE_THREADING) + while (true) { + this->mtx.Lock(); + auto emp = this->callbacks.empty(); + if (emp) + this->isRunning = false; + this->mtx.Unlock(); + if (emp) + break; + } + + for (auto item : this->threads) { + item->Join(); + delete item; + } +#endif +} +} // namespace Tesses::Framework::Threading \ No newline at end of file diff --git a/src/Uuid.cpp b/src/Uuid.cpp index 7832d9f..0e1e0d9 100644 --- a/src/Uuid.cpp +++ b/src/Uuid.cpp @@ -1,214 +1,207 @@ +/* + TessesFramework a library to make C++ easier for me, used in CrossLang: + https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #include "TessesFramework/Uuid.hpp" -#include "TessesFramework/Http/HttpUtils.hpp" #include "TessesFramework/Crypto/Crypto.hpp" +#include "TessesFramework/Http/HttpUtils.hpp" namespace Tesses::Framework { - Uuid Uuid::Generate() - { - //xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx - Uuid uuid; - Uuid::Generate(uuid); - return uuid; +Uuid Uuid::Generate() { + // xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx + Uuid uuid; + Uuid::Generate(uuid); + return uuid; +} + +void Uuid::Generate(Uuid &uuid) { + std::vector bytes(16); + Crypto::RandomBytes(bytes, "TF_UUID"); + + uuid.time_low = (uint32_t)bytes[0]; + uuid.time_low |= (uint32_t)bytes[1] << 8; + uuid.time_low |= (uint32_t)bytes[2] << 16; + uuid.time_low |= (uint32_t)bytes[3] << 24; + uuid.time_mid = (uint16_t)bytes[4]; + uuid.time_mid |= (uint16_t)bytes[5] << 8; + uuid.time_hi_and_version = (uint16_t)bytes[6]; + uuid.time_hi_and_version |= (uint16_t)bytes[7] << 8; + uuid.clock_seq_hi_and_reserved = bytes[8]; + uuid.clock_seq_low = bytes[9]; + for (size_t i = 0; i < 6; i++) { + uuid.node[i] = bytes[i + 10]; } + uuid.time_hi_and_version &= ~0x00F0; + uuid.time_hi_and_version |= 0x0040; + uuid.clock_seq_hi_and_reserved &= ~0b11000000; + uuid.clock_seq_hi_and_reserved |= 0b10000000; +} - void Uuid::Generate(Uuid& uuid) - { - std::vector bytes(16); - Crypto::RandomBytes(bytes, "TF_UUID"); - - uuid.time_low = (uint32_t)bytes[0]; - uuid.time_low |= (uint32_t)bytes[1] << 8; - uuid.time_low |= (uint32_t)bytes[2] << 16; - uuid.time_low |= (uint32_t)bytes[3] << 24; - uuid.time_mid = (uint16_t)bytes[4]; - uuid.time_mid |= (uint16_t)bytes[5] << 8; - uuid.time_hi_and_version = (uint16_t)bytes[6]; - uuid.time_hi_and_version |= (uint16_t)bytes[7] << 8; - uuid.clock_seq_hi_and_reserved = bytes[8]; - uuid.clock_seq_low = bytes[9]; - for(size_t i = 0; i < 6; i++) - { - uuid.node[i] = bytes[i+10]; - } - uuid.time_hi_and_version &= ~0x00F0; - uuid.time_hi_and_version |= 0x0040; - uuid.clock_seq_hi_and_reserved &= ~0b11000000; - uuid.clock_seq_hi_and_reserved |= 0b10000000; - - - } - - bool Uuid::TryParse(std::string text, Uuid& uuid) - { - std::array hex_digits; - size_t hex_offset = 0; - size_t text_offset = 0; - for(; text_offset < text.size(); text_offset++) - { - if(text[text_offset] == '{' && (text_offset != 0 || hex_offset != 0)) +bool Uuid::TryParse(std::string text, Uuid &uuid) { + std::array hex_digits; + size_t hex_offset = 0; + size_t text_offset = 0; + for (; text_offset < text.size(); text_offset++) { + if (text[text_offset] == '{' && (text_offset != 0 || hex_offset != 0)) + return false; + if (text[text_offset] == '}' && hex_offset < 32) + return false; + if (text[text_offset] == '-' && hex_offset != 8 && hex_offset != 12 && + hex_offset != 16 && hex_offset != 20) + return false; + if ((text[text_offset] >= 'A' && text[text_offset] <= 'F') || + (text[text_offset] >= 'a' && text[text_offset] <= 'f') || + text[text_offset] >= '0' && text[text_offset] <= '9') { + if (hex_offset >= 32) return false; - if(text[text_offset] == '}' && hex_offset < 32) - return false; - if(text[text_offset] == '-' && hex_offset != 8 && hex_offset != 12 && hex_offset != 16 && hex_offset != 20) - return false; - if((text[text_offset] >= 'A' && text[text_offset] <= 'F') || (text[text_offset] >= 'a' && text[text_offset] <= 'f') || text[text_offset] >= '0' && text[text_offset] <= '9') - { - if(hex_offset >= 32) return false; - hex_digits[hex_offset] = Http::HttpUtils::HexToNibble(text[text_offset]); - hex_offset++; - } - else return false; - - } - - uint8_t b = hex_digits[0] << 4 | hex_digits[1]; - uuid.time_low = (uint32_t)b; - b = hex_digits[2] << 4 | hex_digits[3]; - uuid.time_low |= (uint32_t)b << 8; - b = hex_digits[4] << 4 | hex_digits[5]; - uuid.time_low |= (uint32_t)b << 16; - b = hex_digits[6] << 4 | hex_digits[7]; - uuid.time_low |= (uint32_t)b << 24; - - b = hex_digits[8] << 4 | hex_digits[9]; - uuid.time_mid = (uint16_t)b; - b = hex_digits[10] << 4 | hex_digits[11]; - uuid.time_mid |= (uint16_t)b << 8; - - b = hex_digits[12] << 4 | hex_digits[13]; - uuid.time_hi_and_version = (uint16_t)b; - b = hex_digits[14] << 4 | hex_digits[15]; - uuid.time_hi_and_version |= (uint16_t)b << 8; - - uuid.clock_seq_hi_and_reserved = hex_digits[16] << 4 | hex_digits[17]; - - uuid.clock_seq_low = hex_digits[18] << 4 | hex_digits[19]; - - for(size_t i = 0; i < 6; i++) - { - uuid.node[i] = hex_digits[20+(i*2)] << 4 | hex_digits[21+(i*2)]; - } - - return true; - } - //9c4994e7-3c82-4c30-a459-8fdcd960b4ac - - std::string Uuid::ToString(UuidStringifyConfig cfg) const - { - bool hasCurly = ((int)cfg & (int)UuidStringifyConfig::HasCurly) != 0; - bool isUppercase = ((int)cfg & (int)UuidStringifyConfig::IsUppercase) != 0; - bool hasDash = ((int)cfg & (int)UuidStringifyConfig::HasDashes) != 0; - - std::string uuid_str = ""; - if(hasCurly) - uuid_str += "{"; - - uint8_t byte = (uint8_t)(this->time_low & 0xFF); - - uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase); - byte = (uint8_t)((this->time_low >> 8) & 0xFF); - uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase); - byte = (uint8_t)((this->time_low >> 16) & 0xFF); - uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase); - byte = (uint8_t)((this->time_low >> 24) & 0xFF); - uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase); - - if(hasDash) - uuid_str += "-"; - - byte = (uint8_t)(this->time_mid & 0xFF); - - uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase); - - byte = (uint8_t)((this->time_mid >> 8) & 0xFF); - - uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase); - - if(hasDash) - uuid_str += "-"; - - byte = (uint8_t)(this->time_hi_and_version & 0xFF); - - uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase); - - byte = (uint8_t)((this->time_hi_and_version >> 8) & 0xFF); - - uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase); - if(hasDash) - uuid_str += "-"; - - uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_hi_and_reserved>>4,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_hi_and_reserved,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_low>>4,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_low,isUppercase); - if(hasDash) - uuid_str += "-"; - - for(size_t i = 0; i < 6; i++) - { - byte = this->node[i]; - uuid_str += Http::HttpUtils::NibbleToHex(byte>>4,isUppercase); - uuid_str += Http::HttpUtils::NibbleToHex(byte,isUppercase); - } - - if(hasCurly) - uuid_str += "}"; - return uuid_str; - - - + hex_digits[hex_offset] = + Http::HttpUtils::HexToNibble(text[text_offset]); + hex_offset++; + } else + return false; } - bool Uuid::IsEmpty() const - { - return this->time_low == 0 && - this->time_mid == 0 && - this->time_hi_and_version == 0 && - this->clock_seq_hi_and_reserved == 0 && - this->clock_seq_low == 0 && - this->node[0] == 0 && - this->node[1] == 0 && - this->node[2] == 0 && - this->node[3] == 0 && - this->node[4] == 0 && - this->node[5] == 0; + uint8_t b = hex_digits[0] << 4 | hex_digits[1]; + uuid.time_low = (uint32_t)b; + b = hex_digits[2] << 4 | hex_digits[3]; + uuid.time_low |= (uint32_t)b << 8; + b = hex_digits[4] << 4 | hex_digits[5]; + uuid.time_low |= (uint32_t)b << 16; + b = hex_digits[6] << 4 | hex_digits[7]; + uuid.time_low |= (uint32_t)b << 24; + + b = hex_digits[8] << 4 | hex_digits[9]; + uuid.time_mid = (uint16_t)b; + b = hex_digits[10] << 4 | hex_digits[11]; + uuid.time_mid |= (uint16_t)b << 8; + + b = hex_digits[12] << 4 | hex_digits[13]; + uuid.time_hi_and_version = (uint16_t)b; + b = hex_digits[14] << 4 | hex_digits[15]; + uuid.time_hi_and_version |= (uint16_t)b << 8; + + uuid.clock_seq_hi_and_reserved = hex_digits[16] << 4 | hex_digits[17]; + + uuid.clock_seq_low = hex_digits[18] << 4 | hex_digits[19]; + + for (size_t i = 0; i < 6; i++) { + uuid.node[i] = hex_digits[20 + (i * 2)] << 4 | hex_digits[21 + (i * 2)]; } - bool operator==(const Uuid& left, const Uuid& right) - { - return left.time_low == right.time_low && - left.time_mid == right.time_mid && - left.time_hi_and_version == right.time_hi_and_version && - left.clock_seq_hi_and_reserved == right.clock_seq_hi_and_reserved && - left.clock_seq_low == right.clock_seq_low && - left.node[0] == right.node[0] && - left.node[1] == right.node[1] && - left.node[2] == right.node[2] && - left.node[3] == right.node[3] && - left.node[4] == right.node[4] && - left.node[5] == right.node[5]; - + return true; +} +// 9c4994e7-3c82-4c30-a459-8fdcd960b4ac + +std::string Uuid::ToString(UuidStringifyConfig cfg) const { + bool hasCurly = ((int)cfg & (int)UuidStringifyConfig::HasCurly) != 0; + bool isUppercase = ((int)cfg & (int)UuidStringifyConfig::IsUppercase) != 0; + bool hasDash = ((int)cfg & (int)UuidStringifyConfig::HasDashes) != 0; + + std::string uuid_str = ""; + if (hasCurly) + uuid_str += "{"; + + uint8_t byte = (uint8_t)(this->time_low & 0xFF); + + uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase); + uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase); + byte = (uint8_t)((this->time_low >> 8) & 0xFF); + uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase); + uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase); + byte = (uint8_t)((this->time_low >> 16) & 0xFF); + uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase); + uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase); + byte = (uint8_t)((this->time_low >> 24) & 0xFF); + uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase); + uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase); + + if (hasDash) + uuid_str += "-"; + + byte = (uint8_t)(this->time_mid & 0xFF); + + uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase); + uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase); + + byte = (uint8_t)((this->time_mid >> 8) & 0xFF); + + uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase); + uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase); + + if (hasDash) + uuid_str += "-"; + + byte = (uint8_t)(this->time_hi_and_version & 0xFF); + + uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase); + uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase); + + byte = (uint8_t)((this->time_hi_and_version >> 8) & 0xFF); + + uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase); + uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase); + if (hasDash) + uuid_str += "-"; + + uuid_str += Http::HttpUtils::NibbleToHex( + this->clock_seq_hi_and_reserved >> 4, isUppercase); + uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_hi_and_reserved, + isUppercase); + uuid_str += + Http::HttpUtils::NibbleToHex(this->clock_seq_low >> 4, isUppercase); + uuid_str += Http::HttpUtils::NibbleToHex(this->clock_seq_low, isUppercase); + if (hasDash) + uuid_str += "-"; + + for (size_t i = 0; i < 6; i++) { + byte = this->node[i]; + uuid_str += Http::HttpUtils::NibbleToHex(byte >> 4, isUppercase); + uuid_str += Http::HttpUtils::NibbleToHex(byte, isUppercase); } - bool operator!=(const Uuid& left, const Uuid& right) - { - return left.time_low != right.time_low && - left.time_mid != right.time_mid && - left.time_hi_and_version != right.time_hi_and_version && - left.clock_seq_hi_and_reserved != right.clock_seq_hi_and_reserved && - left.clock_seq_low != right.clock_seq_low && - left.node[0] != right.node[0] && - left.node[1] != right.node[1] && - left.node[2] != right.node[2] && - left.node[3] != right.node[3] && - left.node[4] != right.node[4] && - left.node[5] != right.node[5]; - } -} \ No newline at end of file + + if (hasCurly) + uuid_str += "}"; + return uuid_str; +} + +bool Uuid::IsEmpty() const { + return this->time_low == 0 && this->time_mid == 0 && + this->time_hi_and_version == 0 && + this->clock_seq_hi_and_reserved == 0 && this->clock_seq_low == 0 && + this->node[0] == 0 && this->node[1] == 0 && this->node[2] == 0 && + this->node[3] == 0 && this->node[4] == 0 && this->node[5] == 0; +} + +bool operator==(const Uuid &left, const Uuid &right) { + return left.time_low == right.time_low && left.time_mid == right.time_mid && + left.time_hi_and_version == right.time_hi_and_version && + left.clock_seq_hi_and_reserved == right.clock_seq_hi_and_reserved && + left.clock_seq_low == right.clock_seq_low && + left.node[0] == right.node[0] && left.node[1] == right.node[1] && + left.node[2] == right.node[2] && left.node[3] == right.node[3] && + left.node[4] == right.node[4] && left.node[5] == right.node[5]; +} +bool operator!=(const Uuid &left, const Uuid &right) { + return left.time_low != right.time_low && left.time_mid != right.time_mid && + left.time_hi_and_version != right.time_hi_and_version && + left.clock_seq_hi_and_reserved != right.clock_seq_hi_and_reserved && + left.clock_seq_low != right.clock_seq_low && + left.node[0] != right.node[0] && left.node[1] != right.node[1] && + left.node[2] != right.node[2] && left.node[3] != right.node[3] && + left.node[4] != right.node[4] && left.node[5] != right.node[5]; +} +} // namespace Tesses::Framework \ No newline at end of file