commit b94147fa8cba7f5988adac0b02c0d4e848474820 Author: Paul Date: Fri Jul 19 21:24:21 2024 +0200 initial commit diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..ab5c622 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,151 @@ +--- +kind: pipeline +type: docker +name: default-amd64 + +platform: + os: linux + arch: amd64 + +steps: + - name: build and test + image: rust:1 + pull: always + commands: + - apt-get update -y + - apt-get install -y figlet + - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_amd64 + - chmod +x /usr/bin/sccache + - cargo build --verbose --all + - cargo test --verbose --all + environment: + RUSTC_WRAPPER: /usr/bin/sccache + SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com + SCCACHE_WEBDAV_KEY_PREFIX: sccache + volumes: + - name: cargo + path: /usr/local/cargo/registry + - name: apt + path: /var/cache/apt + when: + event: + exclude: + - tag + - name: release + image: rust:1 + pull: always + commands: + - apt-get update -y + - apt-get install -y figlet + - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_amd64 + - chmod +x /usr/bin/sccache + - cargo build --release --verbose --all + - cd target/release + - tar -czvf superchat-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz superchat + environment: + RUSTC_WRAPPER: /usr/bin/sccache + SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com + SCCACHE_WEBDAV_KEY_PREFIX: sccache + volumes: + - name: cargo + path: /usr/local/cargo/registry + - name: apt + path: /var/cache/apt + when: + event: + - tag + - name: publish + image: plugins/gitea-release + settings: + base_url: https://git.paulbsd.com + api_key: + from_secret: gitea_token + files: "target/release/*.tar.gz" + environment: + PLUGIN_TITLE: "" + when: + event: + - tag +volumes: + - name: cargo + host: + path: /home/drone/cache/cargo + - name: apt + host: + path: /home/drone/cache/apt + +--- +kind: pipeline +type: docker +name: default-arm64 + +platform: + os: linux + arch: arm64 + +steps: + - name: build and test + image: rust:1 + pull: always + commands: + - apt-get update -y + - apt-get install -y figlet + - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_arm64 + - chmod +x /usr/bin/sccache + - cargo build --verbose --all + - cargo test --verbose --all + environment: + RUSTC_WRAPPER: /usr/bin/sccache + SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com + SCCACHE_WEBDAV_KEY_PREFIX: sccache + volumes: + - name: cargo + path: /usr/local/cargo/registry + - name: apt + path: /var/cache/apt + when: + event: + exclude: + - tag + - name: release + image: rust:1 + pull: always + commands: + - apt-get update -y + - apt-get install -y figlet + - curl -o /usr/bin/sccache https://assets.paulbsd.com/sccache_linux_arm64 + - chmod +x /usr/bin/sccache + - cargo build --release --verbose --all + - cd target/release + - tar -czvf superchat-${DRONE_TAG}-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.tar.gz superchat + environment: + RUSTC_WRAPPER: /usr/bin/sccache + SCCACHE_WEBDAV_ENDPOINT: https://sccache.paulbsd.com + SCCACHE_WEBDAV_KEY_PREFIX: sccache + volumes: + - name: cargo + path: /usr/local/cargo/registry + - name: apt + path: /var/cache/apt + when: + event: + - tag + - name: publish + image: plugins/gitea-release + settings: + base_url: https://git.paulbsd.com + api_key: + from_secret: gitea_token + files: "target/release/*.tar.gz" + environment: + PLUGIN_TITLE: "" + when: + event: + - tag +volumes: + - name: cargo + host: + path: /home/drone/cache/cargo + - name: apt + host: + path: /home/drone/cache/apt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/ABOUT.md b/ABOUT.md new file mode 100644 index 0000000..6531fb7 --- /dev/null +++ b/ABOUT.md @@ -0,0 +1,7 @@ +SuperChat {{ VERSION }} +Chat application for hackers + +SuperChat is a chat application using AX25 protocol +Use at your own risk, please conform to regulatories in your country + +Copyright © 2024 PaulBSD (https://www.paulbsd.com) diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ab527f3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,996 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "ax25" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fec6189a57a9668a17aa3368d110b1451450df47b85200f43d93357fb21f3b" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crossterm" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "cursive" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5438eb16bdd8af51b31e74764fef5d0a9260227a5ec82ba75c9d11ce46595839" +dependencies = [ + "ahash", + "cfg-if", + "crossbeam-channel", + "crossterm", + "cursive_core", + "lazy_static", + "libc", + "log", + "signal-hook", + "termion", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "cursive_core" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4db3b58161228d0dcb45c7968c5e74c3f03ad39e8983e58ad7d57061aa2cd94d" +dependencies = [ + "ahash", + "crossbeam-channel", + "enum-map", + "enumset", + "lazy_static", + "log", + "num", + "owning_ref", + "time", + "unicode-segmentation", + "unicode-width", + "xi-unicode", +] + +[[package]] +name = "darling" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enumset" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "kiss-tnc" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e84204b9275531c6722b5eafd267503a71e2d6e8e08ab4a054682e3888566c06" +dependencies = [ + "thiserror", + "tokio", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "miniz_oxide" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" + +[[package]] +name = "object" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "owning_ref" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.1", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "redox_termios" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "superchat" +version = "0.1.0" +dependencies = [ + "ax25", + "base64", + "cursive", + "kiss-tnc", + "quote", + "serde", + "serde_json", + "syn", + "tokio", +] + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termion" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" +dependencies = [ + "libc", + "numtoa", + "redox_syscall 0.2.16", + "redox_termios", +] + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "xi-unicode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8171aa6 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "superchat" +version = "0.1.0" +edition = "2021" + +[dependencies] +ax25 = { version = "0.3" } +base64 = { version = "0.22" } +kiss-tnc = { version = "0.2.2" } +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1" } +syn = { version = "2.0" } +quote = { version = "1" } +tokio = { version = "1.36", features = ["full"] } + +[target.'cfg(windows)'.dependencies] +cursive = { version = "0.20", features = ["crossterm-backend"], default-features = false } + +[target.'cfg(unix)'.dependencies] +cursive = { version = "0.20", features = ["termion-backend"], default-features = false } + +[lib] +proc-macro = true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4346cbe --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +Copyright 2024 © PaulBSD + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of this project. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8d6b702 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# SuperChat + +## Summary + +superchat is a chat tool to talk with friends over AX.25 with direwolf on kiss/tcp. + +## License + +```text +Copyright (c) 2024 PaulBSD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of this project. +``` \ No newline at end of file diff --git a/SuperChat.txt b/SuperChat.txt new file mode 100644 index 0000000..be7ad79 --- /dev/null +++ b/SuperChat.txt @@ -0,0 +1,6 @@ + ____ ____ _ _ +/ ___| _ _ _ __ ___ _ __ / ___| |__ __ _| |_ +\___ \| | | | '_ \ / _ \ '__| | | '_ \ / _` | __| + ___) | |_| | |_) | __/ | | |___| | | | (_| | |_ +|____/ \__,_| .__/ \___|_| \____|_| |_|\__,_|\__| + |_| \ No newline at end of file diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..11dea2c --- /dev/null +++ b/src/config.rs @@ -0,0 +1,99 @@ +use serde::{Deserialize, Serialize}; +use std::fs::*; +use std::io::prelude::*; +use tokio::sync::mpsc::*; + +pub const APPNAME: &'static str = "SuperChat"; +const CONFIGFILE: &'static str = ".superchat.json"; + +pub struct Context { + pub config: Config, + pub sender: Sender>, +} + +impl Context { + pub fn new(config: Config, sender: Sender>) -> Self { + Self { config, sender } + } +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct Config { + pub pseudo: Option, + pub channel: Option, + pub set_theme: Option, + pub enc: Option, + pub enc_key: Option, +} + +impl Config { + pub fn new(pseudo: &str, channel: &str, set_theme: bool, enc: bool, enc_key: &str) -> Self { + Config { + pseudo: Some(pseudo.to_string()), + channel: Some(channel.to_string()), + set_theme: Some(set_theme), + enc: Some(enc), + enc_key: Some(enc_key.to_string()), + } + } + + #[cfg(unix)] + fn getconfigfile(&self) -> String { + format!("{}/{}", std::env::var("HOME").unwrap(), CONFIGFILE) + } + + #[cfg(windows)] + fn getconfigfile(&self) -> String { + format!( + "{}/{}", + std::env::home_dir().unwrap().to_str().unwrap(), + CONFIGFILE + ) + } + + pub fn load(&mut self) { + let content: String = + std::fs::read_to_string(self.getconfigfile()).unwrap_or("{}".to_string()); + let res: Config = serde_json::from_str::(content.as_str()).unwrap(); + match std::fs::metadata(self.getconfigfile()) { + Ok(_o) => { + *self = res; + } + Err(_e) => { + *self = Config::new("", "", false, false, ""); + return; + } + } + } + + pub fn save(&self) { + let mut file = File::create(self.getconfigfile()).unwrap(); + let content = serde_json::to_string_pretty(&self).unwrap(); + file.write_all(content.as_bytes()).unwrap(); + } + + fn _is_set(&self) -> bool { + return (!self.pseudo.is_none() + && self.pseudo.clone().unwrap_or("".to_string()) != "".to_string()) + && (!self.channel.is_none() + && self.channel.clone().unwrap_or("".to_string()) != "".to_string()); + } +} + +impl std::fmt::Debug for Config { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "pseudo: '{}', channel: '{}', set_theme: {}", + self.pseudo.clone().unwrap_or(String::from("")), + self.channel.clone().unwrap_or(String::from("")), + self.set_theme.clone().unwrap_or(false) + ) + } +} + +impl std::fmt::Display for Config { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/kiss.rs b/src/kiss.rs new file mode 100644 index 0000000..ff7459a --- /dev/null +++ b/src/kiss.rs @@ -0,0 +1,170 @@ +use crate::utils::*; + +use ax25::frame::*; +use base64::{engine::general_purpose::STANDARD, Engine as _}; +use kiss_tnc::Tnc; +use serde::{Deserialize, Serialize}; +use std::fmt::Display; +use std::sync::Arc; + +use tokio::sync::mpsc::*; +use tokio::sync::*; + +const AX25SENDER: &'static str = "14SC1"; +const AX25RECEIVER: &'static str = "14SC2"; +const KISS_ADDR: &'static str = "localhost:8001"; + +#[derive(Serialize, Deserialize, Clone, Default)] +pub struct Frame { + pub message: Message, +} + +impl Frame { + pub fn new(sender: String, receiver: String, msg_type: String, data: String) -> Self { + Self { + message: Message { + sender, + receiver, + msg_type, + data, + }, + } + } + + pub fn as_msg_line(&self) -> String { + self.message.as_msg_line() + } + + fn format(&self) -> String { + format!( + "{sender}>{receiver}:{message}", + sender = AX25SENDER, + receiver = AX25RECEIVER, + message = self.message.as_json_string() + ) + } + + pub fn from_net(frame: &Vec) -> Option { + let frame = Ax25Frame::from_bytes(frame.as_slice()).unwrap(); + match frame.content { + FrameContent::UnknownContent(info) => { + let encoded_data = String::from_utf8(info.raw).unwrap(); + let decoded_data = + String::from_utf8(STANDARD.decode(encoded_data).unwrap()).unwrap(); + let message: Message = serde_json::from_str(decoded_data.as_str()).unwrap(); + Some(Self { message }); + } + _ => return None, + }; + None + } + + pub fn to_ax25(&self) -> Vec { + let source = { + let mut address = Address::default(); + address.callsign = AX25SENDER.to_string(); + address + }; + let destination = { + let mut address = Address::default(); + address.callsign = AX25RECEIVER.to_string(); + address + }; + let mut msg: Vec = vec![0, 0]; + let encoded_data: String = STANDARD.encode(self.message.as_json_string().as_bytes()); + msg.extend(encoded_data.bytes()); + + let frame = Ax25Frame { + source: source, + destination: destination, + route: vec![], + command_or_response: Some(CommandResponse::Command), + content: FrameContent::UnknownContent(UnknownContent { raw: msg }), + }; + frame.to_bytes() + } +} + +#[derive(Serialize, Deserialize, Clone, Default)] +pub struct Message { + pub sender: String, + pub receiver: String, + pub msg_type: String, + pub data: String, +} + +impl Message { + fn as_msg_line(&self) -> String { + format!("{sender}: {data}", sender = self.sender, data = self.data) + } + + fn as_json_string(&self) -> String { + serde_json::to_string(self).unwrap() + } +} + +impl Display for Message { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.data) + } +} + +pub async fn init_kiss( + receiver: &Arc>>>, + sender: &Arc>>>, +) -> bool { + let res: bool; + let mut tnc = match Tnc::connect_tcp(KISS_ADDR).await { + Ok(o) => { + res = true; + o + } + Err(_e) => { + return false; + } + }; + + let receiver = Arc::clone(&receiver); + let sender = Arc::clone(&sender); + tokio::spawn(async move { + loop { + let mut receiver = receiver.write().await; + let sender = sender.read().await; + tokio::select! { + in_msg = receiver.recv() => { + match in_msg { + Some(in_msg) => { + match tnc.send_frame(in_msg.as_slice()).await { + Ok(_) => {}, + Err(e) => { + println!{"recv not ok {e}"}; + }, + } + match tnc.flush().await { + Ok(_) => {}, + Err(e) => { + println!{"flush not ok {e}"}; + }, + }; + } + None => {} + } + } + out_msg = tnc.read_frame() => { + match out_msg { + Ok(out_msg) => { + let (_port, data) = out_msg; + let msg = String::from_utf8(data).unwrap_or("".to_string()); + sender.send(msg.as_bytes().to_vec()).await.unwrap(); + }, + Err(_e) => { + sender.send("err".as_bytes().to_vec()).await.unwrap(); + }, + } + } + } + sleep_ms(30).await; + } + }); + res +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7840778 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,27 @@ +mod config; + +use proc_macro::TokenStream; +use quote::quote; +use std::fs::*; +use syn::*; + +#[proc_macro] +pub fn banner(_input: TokenStream) -> TokenStream { + let output = std::process::Command::new("figlet") + .args([config::APPNAME]) + .stdout(std::process::Stdio::piped()) + .output() + .unwrap(); + let figlet_out = String::from_utf8(output.stdout).unwrap(); + let result = quote!({ concat!(#figlet_out) }); + TokenStream::from(result) +} + +#[proc_macro] +pub fn readfile(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as syn::LitStr); + let filename = input.value(); + let file_content = read_to_string(filename).unwrap(); + let result = quote!({ concat!(#file_content) }); + TokenStream::from(result) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b2290d0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,43 @@ +pub mod config; +pub mod kiss; +pub mod utils; +pub mod window; + +pub use config::*; +pub use superchat::*; +pub use utils::*; + +use std::sync::Arc; + +use tokio::runtime::Runtime; +use tokio::sync::mpsc::*; +use tokio::sync::*; + +fn main() { + let mut cfg: Config = Config::new("", "", false, false, ""); + cfg.load(); + + let mut c = window::new_window(); + + let (app_to_net_s, app_to_net_r): (Sender>, Receiver>) = channel(10); + let (net_to_app_s, net_to_app_r): (Sender>, Receiver>) = channel(10); + + let net_to_app_r = Arc::new(RwLock::new(net_to_app_r)); + let net_to_app_s = Arc::new(RwLock::new(net_to_app_s)); + let app_to_net_r = Arc::new(RwLock::new(app_to_net_r)); + + let ctx: Context = Context::new(cfg, app_to_net_s); + let window_ctx = Arc::new(RwLock::new(ctx)); + let wctx_window = Arc::clone(&window_ctx); + + let rt = Runtime::new().unwrap(); + let kiss_init_res: bool = rt.block_on(crate::kiss::init_kiss(&app_to_net_r, &net_to_app_s)); + + c.set_user_data(wctx_window); + let mut r = window::build_window(&mut c, &kiss_init_res); + + while r.is_running() { + window::handle_kiss(&mut r, &net_to_app_r); + r.step(); + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..9ec167c --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,9 @@ +use tokio::time::{sleep, Duration}; + +pub async fn sleep_ms(ms: u64) { + sleep(Duration::from_millis(ms)).await; +} + +pub async fn sleep_s(sec: u64) { + sleep(Duration::from_secs(sec)).await; +} diff --git a/src/window.rs b/src/window.rs new file mode 100644 index 0000000..66ae7dd --- /dev/null +++ b/src/window.rs @@ -0,0 +1,366 @@ +use std::rc::Rc; +use std::sync::Arc; + +use crate::config::*; +use crate::kiss::*; +use crate::*; + +use cursive::align::*; +use cursive::event::*; +use cursive::menu; +use cursive::theme::{BorderStyle, Palette}; +use cursive::traits::*; +use cursive::views::*; +use cursive::With; +use cursive::XY; +use cursive::{Cursive, CursiveRunnable, CursiveRunner}; + +use tokio::sync::*; + +const PROMPT_BOX_SIZE: usize = 40; +const LOG_HEIGHT_PADDING: usize = 10; + +#[cfg(unix)] +const BANNER: &str = banner!(); +#[cfg(windows)] +const BANNER: &str = readfile!("SuperChat.txt"); + +const LICENSE: &str = readfile!("LICENSE"); +const ABOUT: &str = readfile!("ABOUT.md"); + +pub fn new_window() -> CursiveRunnable { + #[cfg(windows)] + let mut c = cursive::crossterm(); + #[cfg(unix)] + let mut c = cursive::termion(); + c.set_window_title(APPNAME); + c +} + +pub fn handle_kiss( + cr: &mut CursiveRunner<&mut Cursive>, + receiver: &Arc>>>, +) { + let mut receiver = receiver.blocking_write(); + let msg = receiver.try_recv(); + match msg { + Ok(msg) => { + //if msg == "err" { + // cr.pop_layer(); + // return; + //} + recv_msg(cr, &msg); + } + Err(_e) => {} + }; +} + +pub fn build_window<'a>( + c: &'a mut CursiveRunnable, + kiss_init_res: &bool, +) -> CursiveRunner<&'a mut Cursive> { + c.add_global_callback('q', Cursive::quit); + c.add_global_callback(Key::Esc, |c| c.select_menubar()); + //c.on_event(Event::Key(Key::Esc)); + //c.on_event(Event::Refresh); + + apply_options(c); + + c.menubar() + .add_subtree( + "File", + menu::Tree::new() + .leaf("New chat", |c| { + let (_size, clear_range) = get_clear_range(c); + c.call_on_name("messages", |msgs: &mut LinearLayout| { + msgs.clear(); + for _ in clear_range { + msgs.add_child(DummyView.fixed_height(1)); + } + }) + .unwrap(); + }) + .delimiter() + .leaf("Quit", |c| c.quit()), + ) + .add_subtree( + "Edit", + menu::Tree::new().leaf("Options", move |c| { + options(c); + }), + ) + .add_subtree( + "Help", + menu::Tree::new().leaf(format!("About {APPNAME}",), |c| { + let mut about = ABOUT.to_string(); + about = about.replace("{{ VERSION }}", env!("CARGO_PKG_VERSION")); + c.add_layer(Dialog::info(about)); + }), + ); + + c.set_autohide_menu(false); + + match kiss_init_res { + false => { + c.add_layer( + Dialog::around( + TextView::new("Can't connect to direwolf on port 8001").h_align(HAlign::Center), + ) + .button("OK", move |c| { + c.quit(); + }), + ); + } + true => { + c.add_layer( + Dialog::around( + LinearLayout::vertical() + .child(TextView::new(BANNER).h_align(HAlign::Center)) + .child(TextView::new(LICENSE).h_align(HAlign::Left)), + ) + .title(format!("{}", APPNAME)) + .button("OK", move |c| { + c.pop_layer(); + chan(c); + }), + ); + } + }; + let mut r = c.runner(); + r.refresh(); + return r; +} + +fn set_light(c: &mut Cursive) { + c.set_theme(cursive::theme::Theme { + shadow: true, + borders: BorderStyle::Simple, + palette: Palette::retro().with(|palette| { + use cursive::theme::BaseColor::*; + { + use cursive::theme::PaletteColor::*; + + palette[Background] = Black.light(); + palette[View] = Blue.dark(); + palette[Primary] = White.dark(); + palette[TitlePrimary] = White.light(); + palette[Secondary] = White.dark(); + } + + { + use cursive::theme::Effect::*; + use cursive::theme::PaletteStyle::*; + use cursive::theme::Style; + palette[Highlight] = Style::from(White.light()).combine(Bold); + } + }), + }); +} + +fn set_dark(c: &mut Cursive) { + c.set_theme(cursive::theme::Theme { + shadow: true, + borders: BorderStyle::Simple, + palette: Palette::retro().with(|palette| { + use cursive::theme::BaseColor::*; + { + use cursive::theme::Color::TerminalDefault; + use cursive::theme::PaletteColor::*; + + palette[Background] = TerminalDefault; + palette[View] = TerminalDefault; + palette[Primary] = White.dark(); + palette[TitlePrimary] = Blue.light(); + palette[Secondary] = Blue.light(); + } + + { + use cursive::theme::Effect::*; + use cursive::theme::PaletteStyle::*; + use cursive::theme::Style; + palette[Highlight] = Style::from(Blue.light()).combine(Bold); + } + }), + }); +} + +fn options(c: &mut Cursive) { + let ctx: Arc> = Arc::clone(c.user_data().unwrap()); + let alignment = HAlign::Left; + c.add_layer( + Dialog::around( + LinearLayout::vertical() + .child(DummyView.fixed_height(1)) + .child(TextView::new("Enter Pseudo").h_align(alignment)) + .child( + EditView::new() + .content({ + let ctx = ctx.blocking_read(); + ctx.config.pseudo.clone().unwrap() + }) + .with_name("pseudo") + .fixed_width(PROMPT_BOX_SIZE), + ) + .child(DummyView.fixed_height(1)) + .child( + TextView::new(r#"Enter Channel (set "all" if broadcast)"#).h_align(alignment), + ) + .child( + EditView::new() + .content({ + let ctx = ctx.blocking_read(); + ctx.config.channel.clone().unwrap() + }) + .with_name("channel") + .fixed_width(PROMPT_BOX_SIZE), + ) + .child(DummyView.fixed_height(1)) + .child(TextView::new("Set dark theme").h_align(alignment)) + .child( + Checkbox::new() + .with_checked({ + let ctx = ctx.blocking_read(); + ctx.config.set_theme.unwrap() + }) + .with_name("set_theme"), + ) + .child(DummyView.fixed_height(1)) + .child( + TextView::new("Enable encryption (use as your own risks)").h_align(alignment), + ) + .child( + Checkbox::new() + .with_checked({ + let ctx = ctx.blocking_read(); + ctx.config.enc.unwrap_or(false) + }) + .disabled() + .with_name("enc"), + ) + .child(DummyView.fixed_height(1)) + .child(TextView::new("Encryption key").h_align(alignment)) + .child( + EditView::new() + .content({ + let ctx = ctx.blocking_read(); + ctx.config.enc_key.clone().unwrap_or("".to_string()) + }) + .disabled() + .with_name("enc_key") + .fixed_width(PROMPT_BOX_SIZE), + ), + ) + .title(format!("{APPNAME} options")) + .button("OK", move |c| { + apply_options(c); + c.pop_layer(); + }), + ); +} + +fn apply_options(c: &mut Cursive) { + let ctx: Arc> = Arc::clone(c.user_data().unwrap()); + let mut ctx = ctx.blocking_write(); + c.call_on_name("pseudo", |v: &mut EditView| { + ctx.config.pseudo = Some(Rc::unwrap_or_clone(v.get_content())); + }); + c.call_on_name("channel", |v: &mut EditView| { + ctx.config.channel = Some(Rc::unwrap_or_clone(v.get_content())); + }); + c.call_on_name("set_theme", |v: &mut Checkbox| { + ctx.config.set_theme = Some(v.is_checked()); + }); + match ctx.config.set_theme.unwrap_or(false) { + true => set_dark(c), + false => set_light(c), + } + c.call_on_name("enc", |v: &mut Checkbox| { + ctx.config.enc = Some(v.is_checked()); + }); + c.call_on_name("enc_key", |v: &mut EditView| { + ctx.config.enc_key = Some(Rc::unwrap_or_clone(v.get_content())); + }); + ctx.config.save(); +} + +fn get_clear_range(c: &mut Cursive) -> (XY, std::ops::Range) { + let mut size = c.screen_size(); + size.x = size.x - LOG_HEIGHT_PADDING; + size.y = size.y - LOG_HEIGHT_PADDING; + let clear_range = 0..size.y - 3; + (size, clear_range) +} + +fn chan(c: &mut Cursive) { + let (size, clear_range) = get_clear_range(c); + c.add_layer(ResizedView::with_fixed_size( + (size.x, size.y), + Dialog::new().title(APPNAME).content( + LinearLayout::vertical() + .child( + LinearLayout::vertical() + .with(|messages| { + for _ in clear_range { + messages.add_child(DummyView.fixed_height(1)); + } + }) + .with_name("messages"), + ) + .child(EditView::new().on_submit(submit_msg).with_name("message")), + ), + )) +} + +fn submit_msg(c: &mut Cursive, text: &str) { + if text.is_empty() { + c.add_layer(Dialog::info("Your message is empty !")); + } else { + let mut pseudo: String = String::new(); + c.with_user_data(|ctx: &mut Arc>| { + let ctx = ctx.blocking_write(); + pseudo = ctx.config.pseudo.clone().unwrap(); + }); + c.with_user_data(|ctx: &mut Arc>| { + let ctx = ctx.blocking_write(); + let frame = Frame::new( + pseudo.clone(), + ctx.config.channel.clone().unwrap(), + "text".into(), + text.into(), + ); + ctx.sender.blocking_send(frame.to_ax25()).unwrap(); + }); + c.call_on_name("messages", |msgs: &mut LinearLayout| { + let textview: String = format!("{}: {}", pseudo, text); + msgs.add_child(TextView::new(textview).fixed_height(1)); + msgs.remove_child(0); + }) + .unwrap(); + c.call_on_name("message", |msg: &mut EditView| { + msg.set_content(""); + }) + .unwrap(); + } +} + +fn recv_msg(cr: &mut CursiveRunner<&mut Cursive>, txt: &Vec) { + let frame = Frame::from_net(txt).unwrap(); + cr.call_on_name("messages", |msgs: &mut LinearLayout| { + let textview: String = frame.as_msg_line(); + msgs.add_child(TextView::new(textview).fixed_height(1)); + }); +} + +fn show_error(cr: &mut CursiveRunner<&mut Cursive>, err_msg: String) { + cr.add_layer( + Dialog::around( + TextView::new(format!( + "Error receiving message from direwolf, please restart ({err_msg})" + )) + .h_align(HAlign::Center), + ) + .button("OK", move |c| { + c.quit(); + }), + ) +} diff --git a/templates/direwolf.conf b/templates/direwolf.conf new file mode 100644 index 0000000..738a366 --- /dev/null +++ b/templates/direwolf.conf @@ -0,0 +1,7 @@ +ADEVICE default +ACHANNELS 2 + +CHANNEL 0 +MODEM 1200 +AGWPORT 8000 +KISSPORT 8001