Initial commit
Diff
.gitignore | 2 ++
Cargo.lock | 835 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Cargo.toml | 22 ++++++++++++++++++++++
rustfmt.toml | 7 +++++++
src/channel.rs | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/client.rs | 381 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/connection.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/main.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/messages.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/server.rs | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/channel/response.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 1917 insertions(+)
@@ -1,0 +1,2 @@
/target
.idea/
@@ -1,0 +1,835 @@
version = 3
[[package]]
name = "actix"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f728064aca1c318585bf4bb04ffcfac9e75e508ab4e8b1bd9ba5dfe04e2cbed5"
dependencies = [
"actix-rt",
"actix_derive",
"bitflags",
"bytes",
"crossbeam-channel",
"futures-core",
"futures-sink",
"futures-task",
"futures-util",
"log",
"once_cell",
"parking_lot",
"pin-project-lite",
"smallvec",
"tokio",
"tokio-util",
]
[[package]]
name = "actix-macros"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "actix-rt"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ea16c295198e958ef31930a6ef37d0fb64e9ca3b6116e6b93a8bdae96ee1000"
dependencies = [
"actix-macros",
"futures-core",
"tokio",
]
[[package]]
name = "actix_derive"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d44b8fee1ced9671ba043476deddef739dd0959bf77030b26b738cc591737a7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "anyhow"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bytes"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
[[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.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
"cfg-if",
]
[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "encoding"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
dependencies = [
"encoding-index-japanese",
"encoding-index-korean",
"encoding-index-simpchinese",
"encoding-index-singlebyte",
"encoding-index-tradchinese",
]
[[package]]
name = "encoding-index-japanese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-korean"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-simpchinese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-singlebyte"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-tradchinese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding_index_tests"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
[[package]]
name = "futures"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]]
name = "futures-executor"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
[[package]]
name = "futures-macro"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
[[package]]
name = "futures-task"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
[[package]]
name = "futures-util"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "irc-proto"
version = "0.15.0"
dependencies = [
"bytes",
"encoding",
"thiserror",
"tokio",
"tokio-util",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[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.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "mio"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "num_cpus"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
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",
]
[[package]]
name = "regex"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "ryu"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
[[package]]
name = "serde_json"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
"lazy_static",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "socket2"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
dependencies = [
"once_cell",
]
[[package]]
name = "titanircd"
version = "0.1.0"
dependencies = [
"actix",
"actix-rt",
"anyhow",
"futures",
"irc-proto",
"itertools",
"tokio",
"tokio-stream",
"tokio-util",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "tokio"
version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys",
]
[[package]]
name = "tokio-macros"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-stream"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
"tracing",
]
[[package]]
name = "tracing"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
dependencies = [
"lazy_static",
"log",
"tracing-core",
]
[[package]]
name = "tracing-serde"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
dependencies = [
"serde",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex",
"serde",
"serde_json",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
"tracing-serde",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[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.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
@@ -1,0 +1,22 @@
[package]
name = "titanircd"
version = "0.1.0"
edition = "2021"
[dependencies]
actix = "0.13"
actix-rt = "2.7"
anyhow = "1.0"
futures = "0.3"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
tokio = { version = "1.23", features = ["full"] }
tokio-stream = { version = "0.1", features = ["net"] }
tokio-util = { version = "0.7", features = ["codec"] }
irc-proto = "0.15"
itertools = "0.10"
[patch."crates-io"]
irc-proto = { path = "../irc/irc-proto" }
@@ -1,0 +1,7 @@
edition = "2021"
newline_style = "Unix"
use_field_init_shorthand = true
@@ -1,0 +1,196 @@
mod response;
use std::collections::HashMap;
use actix::{Actor, Addr, AsyncContext, Context, Handler, MessageResult};
use irc_proto::Command;
use tracing::{error, info, instrument, Span};
use crate::{
channel::response::{ChannelNamesList, ChannelTopic},
client::Client,
connection::InitiatedConnection,
messages::{
Broadcast, ChannelJoin, ChannelList, ChannelMessage, ChannelPart, ServerDisconnect,
},
};
use crate::messages::UserNickChange;
pub struct Channel {
pub name: String,
pub clients: HashMap<Addr<Client>, InitiatedConnection>,
}
impl Actor for Channel {
type Context = Context<Self>;
}
impl Handler<Broadcast> for Channel {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: Broadcast, _ctx: &mut Self::Context) -> Self::Result {
for client in self.clients.keys() {
client.try_send(msg.clone()).unwrap();
}
}
}
impl Handler<ChannelList> for Channel {
type Result = MessageResult<ChannelList>;
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: ChannelList, _ctx: &mut Self::Context) -> Self::Result {
MessageResult(self.clients.values().cloned().collect())
}
}
impl Handler<ChannelMessage> for Channel {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: ChannelMessage, _ctx: &mut Self::Context) -> Self::Result {
let Some(sender) = self.clients.get(&msg.client) else {
error!("Received message from user not in channel");
return;
};
let nick = sender.to_nick();
for client in self.clients.keys() {
if client == &msg.client {
continue;
}
client.do_send(Broadcast {
span: Span::current(),
message: irc_proto::Message {
tags: None,
prefix: Some(nick.clone()),
command: Command::PRIVMSG(self.name.to_string(), msg.message.clone()),
},
});
}
}
}
impl Handler<UserNickChange> for Channel {
type Result = ();
fn handle(&mut self, msg: UserNickChange, _ctx: &mut Self::Context) -> Self::Result {
let Some(sender) = self.clients.get_mut(&msg.client) else {
return;
};
*sender = msg.connection;
sender.nick = msg.new_nick;
}
}
impl Handler<ChannelJoin> for Channel {
type Result = MessageResult<ChannelJoin>;
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: ChannelJoin, ctx: &mut Self::Context) -> Self::Result {
info!(self.name, msg.connection.nick, "User is joining channel");
self.clients
.insert(msg.client.clone(), msg.connection.clone());
for client in self.clients.keys() {
client.do_send(Broadcast {
span: Span::current(),
message: irc_proto::Message {
tags: None,
prefix: Some(msg.connection.to_nick()),
command: Command::JOIN(self.name.to_string(), None, None),
},
});
}
msg.client.do_send(Broadcast {
message: ChannelTopic::new(self).into_message(self.name.to_string()),
span: Span::current(),
});
for message in ChannelNamesList::new(self).into_messages(msg.connection.nick.to_string()) {
msg.client.do_send(Broadcast {
message,
span: Span::current(),
});
}
MessageResult(Ok(ctx.address()))
}
}
impl Handler<ChannelPart> for Channel {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: ChannelPart, ctx: &mut Self::Context) -> Self::Result {
let Some(client_info) = self.clients.remove(&msg.client) else {
return;
};
let message = Broadcast {
message: irc_proto::Message {
tags: None,
prefix: Some(client_info.to_nick()),
command: Command::PART(self.name.to_string(), msg.message),
},
span: Span::current(),
};
msg.client.do_send(message.clone());
ctx.notify(message);
}
}
impl Handler<ServerDisconnect> for Channel {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: ServerDisconnect, ctx: &mut Self::Context) -> Self::Result {
let Some(client_info) = self.clients.remove(&msg.client) else {
return;
};
let message = Broadcast {
span: Span::current(),
message: irc_proto::Message {
tags: None,
prefix: Some(client_info.to_nick()),
command: Command::QUIT(msg.message),
},
};
ctx.notify(message);
}
}
@@ -1,0 +1,381 @@
use std::{collections::HashMap, time::Duration};
use actix::{
fut::wrap_future, io::WriteHandler, Actor, ActorContext, ActorFutureExt, Addr, AsyncContext,
Context, Handler, MessageResult, ResponseActFuture, Running, StreamHandler,
};
use futures::FutureExt;
use irc_proto::{error::ProtocolError, ChannelExt, Command, Message};
use tokio::time::Instant;
use tracing::{error, info_span, instrument, warn, Instrument, Span, debug};
use crate::{
channel::Channel,
connection::{InitiatedConnection, MessageSink},
messages::{
Broadcast, ChannelJoin, ChannelMessage, ChannelPart, FetchClientDetails, ServerDisconnect,
UserNickChange,
},
server::Server,
SERVER_NAME,
};
pub struct Client {
pub writer: MessageSink,
pub connection: InitiatedConnection,
pub server: Addr<Server>,
pub channels: HashMap<String, Addr<Channel>>,
pub last_active: Instant,
pub graceful_shutdown: bool,
pub server_leave_reason: Option<String>,
pub span: Span,
}
impl Actor for Client {
type Context = Context<Self>;
#[instrument(parent = &self.span, skip_all)]
fn started(&mut self, ctx: &mut Self::Context) {
ctx.run_interval(Duration::from_secs(30), |this, ctx| {
let _span = info_span!(parent: &this.span, "ping").entered();
if Instant::now().duration_since(this.last_active) > Duration::from_secs(120) {
this.server_leave_reason = Some("Ping timeout: 120 seconds".to_string());
ctx.stop();
}
this.writer.write(Message {
tags: None,
prefix: None,
command: Command::PING(SERVER_NAME.to_string(), None),
});
});
}
#[instrument(parent = &self.span, skip_all)]
fn stopped(&mut self, ctx: &mut Self::Context) {
let message = self.server_leave_reason.take();
self.server.do_send(ServerDisconnect {
client: ctx.address(),
message: message.clone(),
span: Span::current(),
});
for channel in self.channels.values() {
channel.do_send(ServerDisconnect {
client: ctx.address(),
message: message.clone(),
span: Span::current(),
});
}
if self.graceful_shutdown {
self.writer.write(Message {
tags: None,
prefix: Some(self.connection.to_nick()),
command: Command::QUIT(message),
});
} else {
let message = message.unwrap_or_else(|| "Ungraceful shutdown".to_string());
self.writer.write(Message {
tags: None,
prefix: None,
command: Command::ERROR(message),
});
}
}
}
impl Handler<Broadcast> for Client {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: Broadcast, _ctx: &mut Self::Context) -> Self::Result {
self.writer.write(msg.message);
}
}
impl Handler<FetchClientDetails> for Client {
type Result = MessageResult<FetchClientDetails>;
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: FetchClientDetails, _ctx: &mut Self::Context) -> Self::Result {
MessageResult(self.connection.clone())
}
}
impl Handler<JoinChannelRequest> for Client {
type Result = ResponseActFuture<Self, ()>;
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: JoinChannelRequest, ctx: &mut Self::Context) -> Self::Result {
let mut futures = Vec::with_capacity(msg.channels.len());
for channel_name in msg.channels {
if !channel_name.is_channel_name() {
continue;
}
futures.push(
self.server
.clone()
.send(ChannelJoin {
channel_name: channel_name.to_string(),
client: ctx.address(),
connection: self.connection.clone(),
span: Span::current(),
})
.map(move |v| (channel_name, v.unwrap().unwrap())),
);
}
let fut = wrap_future::<_, Self>(
futures::future::join_all(futures.into_iter()).instrument(Span::current()),
)
.map(|result, this, _ctx| {
for (channel_name, handle) in result {
this.channels.insert(channel_name.clone(), handle);
}
});
Box::pin(fut)
}
}
impl Handler<UserNickChange> for Client {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: UserNickChange, _ctx: &mut Self::Context) -> Self::Result {
self.writer.write(Message {
tags: None,
prefix: Some(msg.connection.to_nick()),
command: Command::NICK(msg.new_nick),
});
}
}
impl StreamHandler<Result<irc_proto::Message, ProtocolError>> for Client {
#[instrument(parent = &self.span, skip_all)]
fn handle(&mut self, item: Result<irc_proto::Message, ProtocolError>, ctx: &mut Self::Context) {
let item = match item {
Ok(item) => {
debug!(?item, "Received message from client");
item
}
Err(error) => {
error!(%error, "Client sent a bad message");
return;
}
};
if item
.source_nickname()
.map_or(false, |v| v != self.connection.nick)
{
warn!("Rejecting message from client due to incorrect nick");
return;
}
#[allow(clippy::match_same_arms)]
match item.command {
Command::USER(_, _, _) | Command::PASS(_) | Command::CAP(_, _, _, _) => {
}
Command::NICK(new_nick) => {
self.server.do_send(UserNickChange {
client: ctx.address(),
connection: self.connection.clone(),
new_nick: new_nick.clone(),
span: Span::current(),
});
for channel in self.channels.values() {
channel.do_send(UserNickChange {
client: ctx.address(),
connection: self.connection.clone(),
new_nick: new_nick.clone(),
span: Span::current(),
});
}
self.connection.nick = new_nick;
}
Command::OPER(_, _) => {}
Command::UserMODE(_, _) => {}
Command::SERVICE(_, _, _, _, _, _) => {}
Command::QUIT(message) => {
self.graceful_shutdown = true;
self.server_leave_reason = message;
ctx.stop();
}
Command::SQUIT(_, _) => {}
Command::JOIN(channel_names, _passwords, _real_name) => {
let channels = channel_names
.split(',')
.filter(|v| !v.is_empty())
.map(ToString::to_string)
.collect();
ctx.notify(JoinChannelRequest {
channels,
span: Span::current(),
});
}
Command::PART(channel, message) => {
let Some(channel) = self.channels.remove(&channel) else {
return;
};
channel.do_send(ChannelPart {
client: ctx.address(),
message,
span: Span::current(),
});
}
Command::ChannelMODE(_, _) => {}
Command::TOPIC(_, _) => {}
Command::NAMES(_, _) => {}
Command::LIST(_, _) => {}
Command::INVITE(_, _) => {}
Command::KICK(_, _, _) => {}
Command::PRIVMSG(target, message) => {
if !target.is_channel_name() {
error!("Private messages not implemented");
} else if let Some(channel) = self.channels.get(&target) {
channel.do_send(ChannelMessage {
client: ctx.address(),
message,
span: Span::current(),
});
} else {
error!("User not connected to channel");
}
}
Command::NOTICE(_, _) => {}
Command::MOTD(_) => {}
Command::LUSERS(_, _) => {}
Command::VERSION(_) => {}
Command::STATS(_, _) => {}
Command::LINKS(_, _) => {}
Command::TIME(_) => {}
Command::CONNECT(_, _, _) => {}
Command::TRACE(_) => {}
Command::ADMIN(_) => {}
Command::INFO(_) => {}
Command::SERVLIST(_, _) => {}
Command::SQUERY(_, _) => {}
Command::WHO(_, _) => {}
Command::WHOIS(_, _) => {}
Command::WHOWAS(_, _, _) => {}
Command::KILL(_, _) => {}
Command::PING(_, _) => {}
Command::PONG(_, _) => {
self.last_active = Instant::now();
}
Command::ERROR(_) => {}
Command::AWAY(_) => {}
Command::REHASH => {}
Command::DIE => {}
Command::RESTART => {}
Command::SUMMON(_, _, _) => {}
Command::USERS(_) => {}
Command::WALLOPS(_) => {}
Command::USERHOST(_) => {}
Command::ISON(_) => {}
Command::SAJOIN(_, _) => {}
Command::SAMODE(_, _, _) => {}
Command::SANICK(_, _) => {}
Command::SAPART(_, _) => {}
Command::SAQUIT(_, _) => {}
Command::NICKSERV(_) => {}
Command::CHANSERV(_) => {}
Command::OPERSERV(_) => {}
Command::BOTSERV(_) => {}
Command::HOSTSERV(_) => {}
Command::MEMOSERV(_) => {}
Command::AUTHENTICATE(_) => {}
Command::ACCOUNT(_) => {}
Command::METADATA(_, _, _) => {}
Command::MONITOR(_, _) => {}
Command::BATCH(_, _, _) => {}
Command::CHGHOST(_, _) => {}
Command::Response(_, _) => {}
Command::Raw(_, _) => {}
}
}
}
impl WriteHandler<ProtocolError> for Client {
#[instrument(parent = &self.span, skip_all)]
fn error(&mut self, error: ProtocolError, _ctx: &mut Self::Context) -> Running {
error!(%error, "Failed to write message to client");
Running::Continue
}
}
#[derive(actix::Message, Debug)]
#[rtype(result = "()")]
pub struct JoinChannelRequest {
channels: Vec<String>,
span: Span,
}
@@ -1,0 +1,97 @@
use actix::io::FramedWrite;
use futures::TryStreamExt;
use irc_proto::{error::ProtocolError, Command, Message, Prefix};
use tokio::{
io::{ReadHalf, WriteHalf},
net::TcpStream,
};
use tokio_util::codec::FramedRead;
use tracing::{instrument, warn};
pub type MessageStream = FramedRead<ReadHalf<TcpStream>, irc_proto::IrcCodec>;
pub type MessageSink = FramedWrite<Message, WriteHalf<TcpStream>, irc_proto::IrcCodec>;
#[derive(Default)]
pub struct ConnectionRequest {
nick: Option<String>,
user: Option<String>,
mode: Option<String>,
real_name: Option<String>,
}
#[derive(Clone)]
pub struct InitiatedConnection {
pub nick: String,
pub user: String,
pub mode: String,
pub real_name: String,
}
impl InitiatedConnection {
#[must_use]
pub fn to_nick(&self) -> Prefix {
Prefix::Nickname(
self.nick.to_string(),
self.user.to_string(),
"my-host".to_string(),
)
}
}
impl TryFrom<ConnectionRequest> for InitiatedConnection {
type Error = ConnectionRequest;
fn try_from(value: ConnectionRequest) -> Result<Self, Self::Error> {
let ConnectionRequest {
nick: Some(nick),
user: Some(user),
mode: Some(mode),
real_name: Some(real_name),
} = value else {
return Err(value);
};
Ok(Self {
nick,
user,
mode,
real_name,
})
}
}
#[instrument(skip_all)]
pub async fn negotiate_client_connection(
s: &mut MessageStream,
) -> Result<Option<InitiatedConnection>, ProtocolError> {
let mut request = ConnectionRequest::default();
while let Some(msg) = s.try_next().await? {
#[allow(clippy::match_same_arms)]
match msg.command {
Command::PASS(_) => {}
Command::NICK(nick) => request.nick = Some(nick),
Command::USER(user, mode, real_name) => {
request.user = Some(user);
request.mode = Some(mode);
request.real_name = Some(real_name);
}
Command::CAP(_, _, _, _) => {}
_ => {
warn!(?msg, "Client sent unknown command during negotiation");
}
}
match InitiatedConnection::try_from(std::mem::take(&mut request)) {
Ok(v) => return Ok(Some(v)),
Err(v) => {
request = v;
}
}
}
Ok(None)
}
@@ -1,0 +1,92 @@
#![deny(clippy::nursery, clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
use std::collections::HashMap;
use actix::{io::FramedWrite, Actor, Addr, AsyncContext};
use actix_rt::System;
use irc_proto::IrcCodec;
use tokio::{net::TcpListener, time::Instant};
use tokio_util::codec::FramedRead;
use tracing::{error, info, info_span, Instrument};
use tracing_subscriber::EnvFilter;
use crate::{client::Client, messages::UserConnected, server::Server};
pub mod channel;
pub mod client;
pub mod connection;
pub mod messages;
pub mod server;
pub const SERVER_NAME: &str = "my.cool.server";
#[actix_rt::main]
async fn main() -> anyhow::Result<()> {
let subscriber = tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.pretty();
subscriber.init();
let server = Server::default().start();
let listener = TcpListener::bind("127.0.0.1:6697").await?;
actix_rt::spawn(start_tcp_acceptor_loop(listener, server));
info!("Server listening on 127.0.0.1:6697");
tokio::signal::ctrl_c().await?;
System::current().stop();
Ok(())
}
async fn start_tcp_acceptor_loop(listener: TcpListener, server: Addr<Server>) {
while let Ok((stream, addr)) = listener.accept().await {
let span = info_span!("connection", %addr);
let _entered = span.clone().entered();
info!("Accepted connection");
let server = server.clone();
actix_rt::spawn(async move {
let (read, writer) = tokio::io::split(stream);
let mut read = FramedRead::new(read, IrcCodec::new("utf8").unwrap());
let Some(connection) = connection::negotiate_client_connection(&mut read).await.unwrap() else {
error!("Failed to fully handshake with client, dropping connection");
return;
};
let handle = Client::create(|ctx| {
let writer = FramedWrite::new(writer, IrcCodec::new("utf8").unwrap(), ctx);
ctx.add_stream(read);
Client {
writer,
connection: connection.clone(),
server: server.clone(),
channels: HashMap::new(),
last_active: Instant::now(),
graceful_shutdown: false,
server_leave_reason: None,
span: span.clone(),
}
});
server.do_send(UserConnected { handle, connection, span });
}.instrument(info_span!("negotiation")));
}
}
@@ -1,0 +1,84 @@
use actix::{Addr, Message};
use anyhow::Result;
use tracing::Span;
use crate::{channel::Channel, client::Client, connection::InitiatedConnection};
#[derive(Message, Clone)]
#[rtype(message = "()")]
pub struct UserConnected {
pub handle: Addr<Client>,
pub connection: InitiatedConnection,
pub span: Span,
}
#[derive(Message, Clone)]
#[rtype(message = "()")]
pub struct ServerDisconnect {
pub client: Addr<Client>,
pub message: Option<String>,
pub span: Span,
}
#[derive(Message, Clone)]
#[rtype(result = "()")]
pub struct UserNickChange {
pub client: Addr<Client>,
pub connection: InitiatedConnection,
pub new_nick: String,
pub span: Span,
}
#[derive(Message)]
#[rtype(result = "Result<Addr<Channel>>")]
pub struct ChannelJoin {
pub channel_name: String,
pub client: Addr<Client>,
pub connection: InitiatedConnection,
pub span: Span,
}
#[derive(Message)]
#[rtype(result = "()")]
pub struct ChannelPart {
pub client: Addr<Client>,
pub message: Option<String>,
pub span: Span,
}
#[derive(Message)]
#[rtype(result = "Vec<crate::connection::InitiatedConnection>")]
pub struct ChannelList {
pub span: Span,
}
#[derive(Message, Clone)]
#[rtype(result = "()")]
pub struct Broadcast {
pub message: irc_proto::Message,
pub span: Span,
}
#[derive(Message)]
#[rtype(result = "crate::connection::InitiatedConnection")]
pub struct FetchClientDetails {
pub span: Span,
}
#[derive(Message)]
#[rtype(result = "()")]
pub struct ChannelMessage {
pub client: Addr<Client>,
pub message: String,
pub span: Span,
}
@@ -1,0 +1,129 @@
use std::collections::{HashMap, HashSet};
use actix::{Actor, Addr, Context, Handler, ResponseFuture};
use futures::TryFutureExt;
use irc_proto::{Command, Message, Prefix, Response};
use tracing::{instrument, Span};
use crate::{
channel::Channel,
client::Client,
messages::{Broadcast, ChannelJoin, ServerDisconnect, UserConnected, UserNickChange},
SERVER_NAME,
};
#[derive(Default)]
pub struct Server {
channels: HashMap<String, Addr<Channel>>,
clients: HashSet<Addr<Client>>,
}
impl Handler<UserConnected> for Server {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: UserConnected, _ctx: &mut Self::Context) -> Self::Result {
let responses = [
(
Response::RPL_WELCOME,
vec!["Welcome to the network jordan!jordan@proper.sick.kid"],
),
(Response::RPL_YOURHOST, vec!["Your host is a sick kid"]),
(
Response::RPL_CREATED,
vec!["This server was created at some point"],
),
(
Response::RPL_MYINFO,
vec![
SERVER_NAME,
"0.0.1",
"DOQRSZaghilopsuwz",
"CFILMPQSbcefgijklmnopqrstuvz",
"bkloveqjfI",
],
),
(
Response::RPL_ISUPPORT,
vec!["D", "are supported by this server"],
),
];
for (response, arguments) in responses {
let arguments = std::iter::once(msg.connection.nick.clone())
.chain(arguments.into_iter().map(ToString::to_string))
.collect();
msg.handle.do_send(Broadcast {
span: Span::current(),
message: Message {
tags: None,
prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
command: Command::Response(response, arguments),
},
});
}
self.clients.insert(msg.handle);
}
}
impl Handler<ServerDisconnect> for Server {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: ServerDisconnect, _ctx: &mut Self::Context) -> Self::Result {
self.clients.remove(&msg.client);
}
}
impl Handler<ChannelJoin> for Server {
type Result = ResponseFuture<<ChannelJoin as actix::Message>::Result>;
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: ChannelJoin, _ctx: &mut Self::Context) -> Self::Result {
let channel = self
.channels
.entry(msg.channel_name.clone())
.or_insert_with(|| {
Channel {
name: msg.channel_name.clone(),
clients: HashMap::new(),
}
.start()
})
.clone();
Box::pin(
channel
.send(msg)
.map_err(anyhow::Error::new)
.and_then(futures::future::ready),
)
}
}
impl Handler<UserNickChange> for Server {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: UserNickChange, _ctx: &mut Self::Context) -> Self::Result {
for client in &self.clients {
client.do_send(msg.clone());
}
}
}
impl Actor for Server {
type Context = Context<Self>;
}
@@ -1,0 +1,72 @@
use irc_proto::{Command, Message, Prefix, Response};
use crate::{channel::Channel, SERVER_NAME};
pub struct ChannelTopic {
channel_name: String,
topic: String,
}
impl ChannelTopic {
pub fn new(channel: &Channel) -> Self {
Self {
channel_name: channel.name.to_string(),
topic: "hello world!".to_string(),
}
}
pub fn into_message(self, for_user: String) -> Message {
irc_proto::Message {
tags: None,
prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
command: Command::Response(
Response::RPL_TOPIC,
vec![for_user, self.channel_name, self.topic],
),
}
}
}
pub struct ChannelNamesList {
channel_name: String,
nick_list: Vec<String>,
}
impl ChannelNamesList {
pub fn new(channel: &Channel) -> Self {
Self {
channel_name: channel.name.to_string(),
nick_list: channel
.clients
.values()
.map(|v| v.nick.to_string())
.collect(),
}
}
pub fn into_messages(self, for_user: String) -> Vec<Message> {
vec![
irc_proto::Message {
tags: None,
prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
command: Command::Response(
Response::RPL_NAMREPLY,
vec![
for_user.to_string(),
"=".to_string(),
self.channel_name,
self.nick_list.join(" "),
],
),
},
Message {
tags: None,
prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
command: Command::Response(
Response::RPL_ENDOFNAMES,
vec![for_user, "End of /NAMES list".to_string()],
),
},
]
}
}