Compare commits
21 Commits
v0.24.2
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c97eac958b | ||
|
|
844a40e929 | ||
|
|
2238285c16 | ||
|
|
e397edb4f4 | ||
|
|
70b5039d50 | ||
|
|
326c868699 | ||
|
|
1be1967d6f | ||
|
|
5364328e2a | ||
|
|
01bcb10833 | ||
|
|
56f7b1c646 | ||
|
|
dc26c14da4 | ||
|
|
a2c70e43de | ||
|
|
b28276c42e | ||
|
|
aa3311bfab | ||
|
|
edb5b4bf25 | ||
|
|
231d3293fe | ||
|
|
febb39ab03 | ||
|
|
ebc1fa791d | ||
|
|
3ea88d9bd5 | ||
|
|
006e3618fb | ||
|
|
c4b74aa6fc |
1819
Cargo.lock
generated
1819
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
17
Cargo.toml
17
Cargo.toml
@@ -1,13 +1,13 @@
|
||||
[package]
|
||||
name = "sn0int"
|
||||
version = "0.24.2"
|
||||
version = "0.25.0"
|
||||
description = "Semi-automatic OSINT framework and package manager"
|
||||
authors = ["kpcyrd <git@rxv.cc>"]
|
||||
license = "GPL-3.0"
|
||||
repository = "https://github.com/kpcyrd/sn0int"
|
||||
categories = ["command-line-utilities"]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
members = ["sn0int-common",
|
||||
@@ -33,8 +33,8 @@ sqlite-bundled = ["libsqlite3-sys/bundled"]
|
||||
|
||||
[dependencies]
|
||||
sn0int-common = { version="0.13.0", path="sn0int-common" }
|
||||
sn0int-std = { version="=0.24.2", path="sn0int-std" }
|
||||
rustyline = "9.0"
|
||||
sn0int-std = { version="=0.25.0", path="sn0int-std" }
|
||||
rustyline = "10.0"
|
||||
log = "0.4"
|
||||
env_logger = "0.9"
|
||||
hlua-badtouch = "0.4"
|
||||
@@ -46,15 +46,14 @@ lazy_static = "1.0"
|
||||
shellwords = "1.0"
|
||||
diesel = { version = "1.0.0", features = ["sqlite", "chrono"] }
|
||||
diesel_migrations = { version = "1.3.0", features = ["sqlite"] }
|
||||
libsqlite3-sys = { version = "0.22.0", features = ["bundled-windows"] }
|
||||
libsqlite3-sys = { version = "0.25.1", features = ["bundled-windows"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
dirs-next = "2.0"
|
||||
url = "2.0"
|
||||
percent-encoding = "2.1"
|
||||
#chrootable-https = { path = "../chrootable-https" }
|
||||
chrootable-https = "0.16"
|
||||
base64 = "0.13"
|
||||
data-encoding = "2.1.2"
|
||||
data-encoding = "2.3.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_urlencoded = "0.7"
|
||||
serde_json = "1.0"
|
||||
@@ -93,7 +92,7 @@ os-version = "0.2"
|
||||
caps = "0.5"
|
||||
#syscallz = { path="../syscallz-rs" }
|
||||
syscallz = "0.16"
|
||||
nix = "0.23"
|
||||
nix = "0.24"
|
||||
|
||||
[target.'cfg(target_os="openbsd")'.dependencies]
|
||||
pledge = "0.4"
|
||||
@@ -101,5 +100,5 @@ unveil = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
#boxxy = { path = "../boxxy-rs" }
|
||||
boxxy = "0.12"
|
||||
boxxy = "0.13"
|
||||
tempfile = "3.0"
|
||||
|
||||
@@ -119,7 +119,7 @@ You might experience a sandbox failure, especially on architectures that are
|
||||
less popular. This usually looks like this::
|
||||
|
||||
[sn0int][example][kpcyrd/ctlogs] > run
|
||||
[-] Failed "example.com": EOF while parsing a value at line 1 column 0
|
||||
[-] Failed "example.com": Sandbox child has crashed
|
||||
[+] Finished kpcyrd/ctlogs (1 errors)
|
||||
|
||||
A module that never finishes could also mean an IO thread inside the worker got
|
||||
|
||||
@@ -10,8 +10,17 @@ function run()
|
||||
mqtt_subscribe(sock, '#', 0)
|
||||
|
||||
while true do
|
||||
local pkt = mqtt_recv_text(sock)
|
||||
-- read the next mqtt packet
|
||||
local pkt = mqtt_recv(sock)
|
||||
if last_err() then return end
|
||||
info(pkt)
|
||||
|
||||
local text
|
||||
if pkt then
|
||||
-- attempt to utf8 decode the body if there was a pkt
|
||||
text = utf8_decode(pkt['body'])
|
||||
if last_err() then clear_err() end
|
||||
end
|
||||
|
||||
info({pkt=pkt, text=text})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::id::ModuleID;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct WhoamiResponse {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
pub use anyhow::{Error, Context, anyhow, format_err, bail};
|
||||
pub use anyhow::{anyhow, bail, format_err, Context, Error};
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use crate::errors::*;
|
||||
use serde::{de, Serialize, Serializer, Deserialize, Deserializer};
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt;
|
||||
use std::result;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
||||
#[inline(always)]
|
||||
fn valid_char(c: char) -> bool {
|
||||
nom::character::is_alphanumeric(c as u8) || c == '-'
|
||||
@@ -71,7 +70,8 @@ impl Serialize for ModuleID {
|
||||
|
||||
impl<'de> Deserialize<'de> for ModuleID {
|
||||
fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
|
||||
where D: Deserializer<'de>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
FromStr::from_str(&s).map_err(de::Error::custom)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
pub mod api;
|
||||
pub mod errors;
|
||||
pub use crate::errors::*;
|
||||
pub mod metadata;
|
||||
pub mod id;
|
||||
pub mod metadata;
|
||||
pub use crate::id::*;
|
||||
|
||||
pub use rocket_failure_errors::StrictApiResponse as ApiResponse;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::errors::*;
|
||||
use nom::IResult;
|
||||
use nom::bytes::complete::{tag, take_until};
|
||||
use nom::combinator::map_res;
|
||||
use nom::multi::fold_many0;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
mod stealth;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::errors::*;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, PartialOrd, Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "sn0int-std"
|
||||
version = "0.24.2"
|
||||
version = "0.25.0"
|
||||
description = "sn0int - stdlib"
|
||||
authors = ["kpcyrd <git@rxv.cc>"]
|
||||
repository = "https://github.com/kpcyrd/sn0int"
|
||||
license = "GPL-3.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -31,14 +31,13 @@ pem = "1"
|
||||
url = "2.0"
|
||||
tungstenite = { version = "0.13", default-features = false }
|
||||
kuchiki = "0.8.0"
|
||||
maxminddb = "0.22"
|
||||
maxminddb = "0.23"
|
||||
x509-parser = "0.13"
|
||||
der-parser = "7"
|
||||
der-parser = "8"
|
||||
publicsuffix = { version="2", default-features=false }
|
||||
xml-rs = "0.8"
|
||||
geo = "0.19"
|
||||
geo = "0.23"
|
||||
bytes = "0.4"
|
||||
base64 = "0.13"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
mqtt-protocol = "0.11"
|
||||
sodiumoxide = { version="0.2.5", features=["use-pkg-config"] }
|
||||
@@ -50,6 +49,8 @@ img_hash_median = "4.0.0"
|
||||
bs58 = "0.4"
|
||||
digest = "0.10"
|
||||
blake2 = "0.10"
|
||||
data-encoding = "2.3.3"
|
||||
thiserror = "1.0.38"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.9"
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use bytes::Bytes;
|
||||
use blake2::Blake2bVar;
|
||||
use bytes::Bytes;
|
||||
use data_encoding::BASE64;
|
||||
use digest::{Update, VariableOutput};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde::de::{self, Deserialize, Deserializer};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use std::result;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -40,7 +41,7 @@ impl Serialize for Blob {
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let s = base64::encode(&self.bytes);
|
||||
let s = BASE64.encode(&self.bytes);
|
||||
serializer.serialize_str(&s)
|
||||
}
|
||||
}
|
||||
@@ -52,7 +53,7 @@ impl<'de> Deserialize<'de> for Blob {
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
let bytes = base64::decode(&s)
|
||||
let bytes = BASE64.decode(s.as_bytes())
|
||||
.map_err(de::Error::custom)?;
|
||||
Ok(Blob::create(Bytes::from(bytes)))
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
pub use log::{trace, debug, info, warn, error};
|
||||
pub use failure::{Error, ResultExt, format_err, bail};
|
||||
pub use failure::{bail, format_err, Error, ResultExt};
|
||||
pub use log::{debug, error, info, trace, warn};
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::errors::*;
|
||||
use crate::hlua::AnyLuaValue;
|
||||
use crate::json::LuaJsonValue;
|
||||
use geo::{LineString, Polygon, Coordinate};
|
||||
use geo::{LineString, Polygon, Coord};
|
||||
use geo::prelude::*;
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -21,7 +21,7 @@ impl Point {
|
||||
|
||||
pub fn polygon_contains(ring: &[Point], p: &Point) -> bool {
|
||||
let ring = ring.iter()
|
||||
.map(|p| Coordinate { x: p.lon, y: p.lat })
|
||||
.map(|p| Coord { x: p.lon, y: p.lat })
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let polygon = Polygon::new(LineString::from(ring), vec![]);
|
||||
|
||||
@@ -93,7 +93,7 @@ pub fn cardinal_direction_modifier(value: &exif::Value) -> Result<f64> {
|
||||
let s = s.get(0)
|
||||
.ok_or_else(|| format_err!("Cardinal direction value is empty"))?;
|
||||
|
||||
match s.get(0) {
|
||||
match s.first() {
|
||||
Some(b'N') => Ok(1.0),
|
||||
Some(b'S') => Ok(-1.0),
|
||||
Some(b'E') => Ok(1.0),
|
||||
|
||||
@@ -3,8 +3,8 @@ use hlua_badtouch as hlua;
|
||||
pub mod blobs;
|
||||
pub mod crt;
|
||||
pub mod crypto;
|
||||
mod errors;
|
||||
pub mod engine;
|
||||
mod errors;
|
||||
pub mod geo;
|
||||
pub mod geoip;
|
||||
pub mod gfx;
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
use chrootable_https::DnsResolver;
|
||||
use crate::errors::*;
|
||||
use crate::hlua::AnyLuaValue;
|
||||
use mqtt::packet::VariablePacketError;
|
||||
use crate::json::LuaJsonValue;
|
||||
use crate::sockets::{Stream, SocketOptions};
|
||||
use mqtt::{TopicFilter, QualityOfService};
|
||||
use mqtt::control::ConnectReturnCode;
|
||||
use crate::sockets::{SocketOptions, Stream};
|
||||
use chrootable_https::DnsResolver;
|
||||
use mqtt::control::fixed_header::FixedHeaderError;
|
||||
use mqtt::encodable::{Encodable, Decodable};
|
||||
use mqtt::packet::{VariablePacket, ConnectPacket, SubscribePacket, PingreqPacket};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use mqtt::control::ConnectReturnCode;
|
||||
use mqtt::encodable::{Decodable, Encodable};
|
||||
use mqtt::packet::VariablePacketError;
|
||||
use mqtt::packet::{ConnectPacket, PingreqPacket, SubscribePacket, VariablePacket};
|
||||
use mqtt::{QualityOfService, TopicFilter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::time::{Duration, Instant};
|
||||
use url::Url;
|
||||
|
||||
// a reasonable default for keep-alive
|
||||
// some servers reject 0 as invalid with a very confusing error message
|
||||
const DEFAULT_PING_INTERVAL: u64 = 90;
|
||||
const DEFAULT_KEEP_ALIVE: u16 = 120;
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct MqttOptions {
|
||||
pub username: Option<String>,
|
||||
@@ -23,10 +29,12 @@ pub struct MqttOptions {
|
||||
pub proxy: Option<SocketAddr>,
|
||||
#[serde(default)]
|
||||
pub connect_timeout: u64,
|
||||
#[serde(default)]
|
||||
pub read_timeout: u64,
|
||||
pub read_timeout: Option<u64>,
|
||||
#[serde(default)]
|
||||
pub write_timeout: u64,
|
||||
|
||||
pub ping_interval: Option<u64>,
|
||||
pub keep_alive: Option<u16>,
|
||||
}
|
||||
|
||||
impl MqttOptions {
|
||||
@@ -37,25 +45,44 @@ impl MqttOptions {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum MqttRecvError {
|
||||
#[error("Failed to read mqtt packet: {0:#}")]
|
||||
Recv(#[from] VariablePacketError),
|
||||
#[error("Failed to read mqtt packet: connection disconnected")]
|
||||
RecvDisconnect,
|
||||
#[error("Failed to interact with mqtt: {0:#}")]
|
||||
Error(Error),
|
||||
}
|
||||
|
||||
impl From<Error> for MqttRecvError {
|
||||
fn from(err: Error) -> Self {
|
||||
MqttRecvError::Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MqttClient {
|
||||
stream: Stream,
|
||||
last_ping: Instant,
|
||||
ping_interval: Option<u64>,
|
||||
}
|
||||
|
||||
impl MqttClient {
|
||||
pub fn negotiate(stream: Stream, options: &MqttOptions) -> Result<MqttClient> {
|
||||
// default to DEFAULT_PING_INTERVAL, if an explicit value of 0 was set, disable auto-ping
|
||||
let ping_interval = Some(options.ping_interval.unwrap_or(DEFAULT_PING_INTERVAL));
|
||||
ping_interval.filter(|s| *s != 0);
|
||||
|
||||
let mut client = MqttClient {
|
||||
stream,
|
||||
last_ping: Instant::now(),
|
||||
ping_interval,
|
||||
};
|
||||
|
||||
let mut pkt = ConnectPacket::new("sn0int");
|
||||
pkt.set_user_name(options.username.clone());
|
||||
pkt.set_password(options.password.clone());
|
||||
|
||||
/*
|
||||
if let Some(keep_alive) = msg.keep_alive {
|
||||
packet.set_keep_alive(keep_alive);
|
||||
}
|
||||
*/
|
||||
pkt.set_keep_alive(options.keep_alive.unwrap_or(DEFAULT_KEEP_ALIVE));
|
||||
|
||||
client.send(pkt.into())?;
|
||||
let pkt = client.recv()?;
|
||||
@@ -72,14 +99,19 @@ impl MqttClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect<R: DnsResolver>(resolver: &R, url: Url, options: &MqttOptions) -> Result<MqttClient> {
|
||||
pub fn connect<R: DnsResolver>(
|
||||
resolver: &R,
|
||||
url: Url,
|
||||
options: &MqttOptions,
|
||||
) -> Result<MqttClient> {
|
||||
let tls = match url.scheme() {
|
||||
"mqtt" => false,
|
||||
"mqtts" => true,
|
||||
_ => bail!("Invalid mqtt protocol"),
|
||||
};
|
||||
|
||||
let host = url.host_str()
|
||||
let host = url
|
||||
.host_str()
|
||||
.ok_or_else(|| format_err!("Missing host in url"))?;
|
||||
|
||||
let port = match (url.port(), tls) {
|
||||
@@ -88,29 +120,57 @@ impl MqttClient {
|
||||
(None, false) => 1883,
|
||||
};
|
||||
|
||||
// if no read timeout is configured then keep alive won't work
|
||||
let read_timeout = options.read_timeout.unwrap_or(DEFAULT_PING_INTERVAL);
|
||||
|
||||
let stream = Stream::connect_stream(resolver, host, port, &SocketOptions {
|
||||
tls,
|
||||
sni_value: None,
|
||||
disable_tls_verify: false,
|
||||
proxy: options.proxy,
|
||||
let stream = Stream::connect_stream(
|
||||
resolver,
|
||||
host,
|
||||
port,
|
||||
&SocketOptions {
|
||||
tls,
|
||||
sni_value: None,
|
||||
disable_tls_verify: false,
|
||||
proxy: options.proxy,
|
||||
|
||||
connect_timeout: options.connect_timeout,
|
||||
read_timeout: options.read_timeout,
|
||||
write_timeout: options.write_timeout,
|
||||
})?;
|
||||
connect_timeout: options.connect_timeout,
|
||||
read_timeout,
|
||||
write_timeout: options.write_timeout,
|
||||
},
|
||||
)?;
|
||||
|
||||
Self::negotiate(stream, options)
|
||||
}
|
||||
|
||||
fn maintain_ping(&mut self) -> Result<()> {
|
||||
if let Some(ping_interval) = self.ping_interval {
|
||||
if self.last_ping.elapsed() >= Duration::from_secs(ping_interval) {
|
||||
self.ping().context("Failed to ping")?;
|
||||
self.last_ping = Instant::now();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send(&mut self, pkt: VariablePacket) -> Result<()> {
|
||||
self.maintain_ping()?;
|
||||
debug!("Sending mqtt packet: {:?}", pkt);
|
||||
pkt.encode(&mut self.stream)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv(&mut self) -> std::result::Result<VariablePacket, VariablePacketError> {
|
||||
let pkt = VariablePacket::decode(&mut self.stream)?;
|
||||
fn recv(&mut self) -> std::result::Result<VariablePacket, MqttRecvError> {
|
||||
self.maintain_ping()?;
|
||||
let pkt = VariablePacket::decode(&mut self.stream).map_err(|err| match err {
|
||||
// search for any io error and check if it's ErrorKind::UnexpectedEof
|
||||
VariablePacketError::IoError(err)
|
||||
| VariablePacketError::FixedHeaderError(FixedHeaderError::IoError(err))
|
||||
if err.kind() == io::ErrorKind::UnexpectedEof =>
|
||||
{
|
||||
MqttRecvError::RecvDisconnect
|
||||
}
|
||||
_ => MqttRecvError::Recv(err),
|
||||
})?;
|
||||
debug!("Received mqtt packet: {:?}", pkt);
|
||||
Ok(pkt)
|
||||
}
|
||||
@@ -139,30 +199,35 @@ impl MqttClient {
|
||||
pub fn recv_pkt(&mut self) -> Result<Option<Pkt>> {
|
||||
match self.recv() {
|
||||
Ok(pkt) => Ok(Some(Pkt::try_from(pkt)?)),
|
||||
Err(VariablePacketError::IoError(err)) if err.kind() == io::ErrorKind::WouldBlock => Ok(None),
|
||||
Err(VariablePacketError::FixedHeaderError(FixedHeaderError::IoError(err))) if err.kind() == io::ErrorKind::WouldBlock => Ok(None),
|
||||
Err(err) => Err(Error::from(err))
|
||||
// search for any io error and check if it's ErrorKind::WouldBlock
|
||||
Err(MqttRecvError::Recv(
|
||||
VariablePacketError::IoError(err)
|
||||
| VariablePacketError::FixedHeaderError(FixedHeaderError::IoError(err)),
|
||||
)) if err.kind() == io::ErrorKind::WouldBlock => Ok(None),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ping(&mut self) -> Result<()> {
|
||||
let pkt = PingreqPacket::new();
|
||||
self.send(pkt.into())
|
||||
let pkt = VariablePacket::PingreqPacket(pkt);
|
||||
pkt.encode(&mut self.stream)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Pkt {
|
||||
#[serde(rename="publish")]
|
||||
#[serde(rename = "publish")]
|
||||
Publish(Publish),
|
||||
#[serde(rename="pong")]
|
||||
#[serde(rename = "pong")]
|
||||
Pong,
|
||||
}
|
||||
|
||||
impl Pkt {
|
||||
pub fn to_lua(&self) -> Result<AnyLuaValue> {
|
||||
let v = serde_json::to_value(&self)?;
|
||||
let v = serde_json::to_value(self)?;
|
||||
let v = LuaJsonValue::from(v).into();
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ impl Stream {
|
||||
let socket = if connect_timeout > 0 {
|
||||
TcpStream::connect_timeout(&addr, Duration::from_secs(connect_timeout))?
|
||||
} else {
|
||||
TcpStream::connect(&addr)?
|
||||
TcpStream::connect(addr)?
|
||||
};
|
||||
debug!("successfully connected to {:?}", addr);
|
||||
|
||||
@@ -138,7 +138,7 @@ impl Stream {
|
||||
tls::wrap_if_enabled(socket, host, options)
|
||||
}
|
||||
|
||||
pub fn connect_socks5_stream(proxy: &SocketAddr, host: &str, port: u16, options: &SocketOptions) -> Result<Stream> {
|
||||
pub fn connect_socks5_stream(proxy: SocketAddr, host: &str, port: u16, options: &SocketOptions) -> Result<Stream> {
|
||||
debug!("connecting to {:?}:{:?} with socks5 on {:?}", host, port, proxy);
|
||||
|
||||
let addr = match host.parse::<Ipv4Addr>() {
|
||||
@@ -146,7 +146,7 @@ impl Stream {
|
||||
_ => ProxyDest::Domain(host.to_string()),
|
||||
};
|
||||
|
||||
let fut = socks5::connect(proxy, addr, port);
|
||||
let fut = socks5::connect(&proxy, addr, port);
|
||||
|
||||
let mut rt = Runtime::new()?;
|
||||
let socket = rt.block_on(fut)?;
|
||||
@@ -211,7 +211,7 @@ impl Socket {
|
||||
Ok(Socket::new(stream))
|
||||
}
|
||||
|
||||
pub fn connect_socks5(proxy: &SocketAddr, host: &str, port: u16, options: &SocketOptions) -> Result<Socket> {
|
||||
pub fn connect_socks5(proxy: SocketAddr, host: &str, port: u16, options: &SocketOptions) -> Result<Socket> {
|
||||
let stream = Stream::connect_socks5_stream(proxy, host, port, options)?;
|
||||
Ok(Socket::new(stream))
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ pub struct TlsData {
|
||||
|
||||
impl TlsData {
|
||||
pub fn to_lua(&self) -> Result<AnyLuaValue> {
|
||||
let v = serde_json::to_value(&self)?;
|
||||
let v = serde_json::to_value(self)?;
|
||||
let v = LuaJsonValue::from(v).into();
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@ pub use chrootable_https::{Client, HttpClient, Resolver, Response};
|
||||
use chrootable_https::http::HttpTryFrom;
|
||||
use chrootable_https::http::uri::Parts;
|
||||
use chrootable_https::http::request::Builder;
|
||||
use data_encoding::BASE64;
|
||||
use rand::{Rng, thread_rng};
|
||||
use rand::distributions::Alphanumeric;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
use std::iter;
|
||||
use std::net::SocketAddr;
|
||||
use std::ops::Deref;
|
||||
@@ -160,9 +162,9 @@ impl HttpRequest {
|
||||
// add headers
|
||||
if let Some(ref auth) = self.basic_auth {
|
||||
use chrootable_https::header::AUTHORIZATION;
|
||||
let &(ref user, ref password) = auth;
|
||||
let (user, password) = auth;
|
||||
|
||||
let auth = base64::encode(&format!("{}:{}", user, password));
|
||||
let auth = BASE64.encode(format!("{}:{}", user, password).as_bytes());
|
||||
let auth = format!("Basic {}", auth);
|
||||
req.header(AUTHORIZATION, auth.as_str());
|
||||
}
|
||||
@@ -251,7 +253,8 @@ impl HttpRequest {
|
||||
if !cookies.is_empty() {
|
||||
cookies += "; ";
|
||||
}
|
||||
cookies.push_str(&format!("{}={}", key, value));
|
||||
// it's a write to a String, so panic if re-allocation fails is fine
|
||||
write!(cookies, "{}={}", key, value).expect("out of memory");
|
||||
}
|
||||
|
||||
if !cookies.is_empty() {
|
||||
@@ -317,7 +320,7 @@ impl HttpRequest {
|
||||
|
||||
impl From<HttpRequest> for AnyLuaValue {
|
||||
fn from(req: HttpRequest) -> AnyLuaValue {
|
||||
let v = serde_json::to_value(&req).unwrap();
|
||||
let v = serde_json::to_value(req).unwrap();
|
||||
LuaJsonValue::from(v).into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ pub fn decode(x: &str) -> Result<AnyLuaValue> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn append_text(stack: &mut Vec<XmlElement>, text: String) {
|
||||
fn append_text(stack: &mut [XmlElement], text: String) {
|
||||
if let Some(tail) = stack.last_mut() {
|
||||
if let Some(prev) = tail.text.as_mut() {
|
||||
prev.push_str(&text);
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::cal::{ActivityGrade, DateArg};
|
||||
use crate::models::*;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::Write;
|
||||
|
||||
const MONTH_LINES: i32 = 7;
|
||||
|
||||
@@ -54,11 +55,11 @@ fn chunk_months(ctx: &DateContext, months: &[DateSpec]) -> String {
|
||||
}
|
||||
|
||||
fn days_in_month(year: i32, month: u32) -> i64 {
|
||||
let start = Utc.ymd(year, month, 1);
|
||||
let start = Utc.with_ymd_and_hms(year, month, 1, 0, 0, 0).single().expect("Datetime is not unique");
|
||||
let end = if month == 12 {
|
||||
Utc.ymd(year + 1, 1, 1)
|
||||
Utc.with_ymd_and_hms(year + 1, 1, 1, 0, 0, 0).single().expect("Datetime is not unique")
|
||||
} else {
|
||||
Utc.ymd(year, month + 1, 1)
|
||||
Utc.with_ymd_and_hms(year, month + 1, 1, 0, 0, 0).single().expect("Datetime is not unique")
|
||||
};
|
||||
end.signed_duration_since(start).num_days()
|
||||
}
|
||||
@@ -151,7 +152,7 @@ impl DateSpec {
|
||||
bail!("Too many datespec args");
|
||||
}
|
||||
|
||||
let today = Utc::today();
|
||||
let today = Utc::now();
|
||||
let ds = match (args.get(0), args.get(1), context) {
|
||||
(None, _, None) => DateSpec::YearMonth((today.year(), today.month())),
|
||||
(None, _, Some(context)) => DateSpec::YearMonthContext((today.year(), today.month(), context)),
|
||||
@@ -171,8 +172,8 @@ impl DateSpec {
|
||||
|
||||
pub fn start(&self) -> NaiveDate {
|
||||
match self {
|
||||
DateSpec::Year(year) => NaiveDate::from_ymd(*year, 1, 1),
|
||||
DateSpec::YearMonth((year, month)) => NaiveDate::from_ymd(*year, *month, 1),
|
||||
DateSpec::Year(year) => NaiveDate::from_ymd_opt(*year, 1, 1).expect("Invalid month/day"),
|
||||
DateSpec::YearMonth((year, month)) => NaiveDate::from_ymd_opt(*year, *month, 1).expect("Invalid month/day"),
|
||||
DateSpec::YearMonthContext((year, month, context)) => {
|
||||
let mut year = *year - (*context / 12) as i32;
|
||||
let context = context % 12;
|
||||
@@ -182,21 +183,21 @@ impl DateSpec {
|
||||
} else {
|
||||
month - context
|
||||
};
|
||||
NaiveDate::from_ymd(year, month, 1)
|
||||
NaiveDate::from_ymd_opt(year, month, 1).expect("Invalid month/day")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end(&self) -> NaiveDate {
|
||||
match self {
|
||||
DateSpec::Year(year) => NaiveDate::from_ymd(year + 1, 1, 1),
|
||||
DateSpec::Year(year) => NaiveDate::from_ymd_opt(year + 1, 1, 1).expect("Invalid month/day"),
|
||||
DateSpec::YearMonth((year, month)) => {
|
||||
let (year, month) = if *month == 12 {
|
||||
(*year + 1, 1)
|
||||
} else {
|
||||
(*year, *month + 1)
|
||||
};
|
||||
NaiveDate::from_ymd(year, month, 1)
|
||||
NaiveDate::from_ymd_opt(year, month, 1).expect("Invalid month/day")
|
||||
},
|
||||
DateSpec::YearMonthContext((year, month, _context)) => {
|
||||
let (year, month) = if *month == 12 {
|
||||
@@ -204,7 +205,7 @@ impl DateSpec {
|
||||
} else {
|
||||
(*year, *month + 1)
|
||||
};
|
||||
NaiveDate::from_ymd(year, month, 1)
|
||||
NaiveDate::from_ymd_opt(year, month, 1).expect("Invalid month/day")
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -220,10 +221,10 @@ impl DateSpec {
|
||||
DateSpec::YearMonth((year, month)) => {
|
||||
let mut w = String::new();
|
||||
|
||||
let start = Utc.ymd(*year, *month, 1);
|
||||
let start = Utc.with_ymd_and_hms(*year, *month, 1, 0, 0, 0).single().expect("Datetime is not unique");
|
||||
let days = days_in_month(*year, *month) as u32;
|
||||
|
||||
w.push_str(&format!("{:^21}\n", start.format("%B %Y")));
|
||||
writeln!(w, "{:^21}", start.format("%B %Y")).expect("out of memory");
|
||||
w.push_str(" Su Mo Tu We Th Fr Sa\n");
|
||||
|
||||
let mut cur_week_day = start.weekday();
|
||||
@@ -232,7 +233,7 @@ impl DateSpec {
|
||||
|
||||
let mut week_written = week_progress * 3;
|
||||
for cur_day in 1..=days {
|
||||
let date = NaiveDate::from_ymd(*year, *month, cur_day);
|
||||
let date = NaiveDate::from_ymd_opt(*year, *month, cur_day).expect("Invalid month/day");
|
||||
|
||||
if !ctx.is_future(&date) {
|
||||
let activity = ctx.activity_for_day(&date);
|
||||
@@ -244,7 +245,7 @@ impl DateSpec {
|
||||
} else {
|
||||
w.push(' ');
|
||||
}
|
||||
w.push_str(&format!("{:2}", cur_day));
|
||||
write!(w, "{:2}", cur_day).expect("out of memory");
|
||||
week_written += 3;
|
||||
w.push_str("\x1b[0m");
|
||||
|
||||
@@ -296,7 +297,7 @@ mod tests {
|
||||
DateContext {
|
||||
events: HashMap::new(),
|
||||
max: 0,
|
||||
today: NaiveDate::from_ymd(2020, 5, 30),
|
||||
today: NaiveDate::from_ymd_opt(2020, 5, 30).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,9 +325,9 @@ mod tests {
|
||||
let ctx = DateContext {
|
||||
events,
|
||||
max: 0,
|
||||
today: NaiveDate::from_ymd(2020, 6, 6),
|
||||
today: NaiveDate::from_ymd_opt(2020, 6, 6).unwrap(),
|
||||
};
|
||||
let grade = ctx.activity_for_day(&NaiveDate::from_ymd(2020, 6, 6));
|
||||
let grade = ctx.activity_for_day(&NaiveDate::from_ymd_opt(2020, 6, 6).unwrap());
|
||||
assert_eq!(grade, ActivityGrade::None);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use chrono::prelude::*;
|
||||
use crate::cal::{ActivityGrade, DateArg};
|
||||
use crate::models::*;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
|
||||
const MIN_PER_DAY: u32 = 1440;
|
||||
|
||||
@@ -13,7 +14,7 @@ fn round_to_slice(time: &NaiveDateTime, slice_duration: u32) -> NaiveDateTime {
|
||||
let hour = time.hour();
|
||||
let mins = time.minute();
|
||||
let slice = mins - (mins % slice_duration);
|
||||
date.and_hms(hour, slice, 0)
|
||||
date.and_hms_opt(hour, slice, 0).expect("Invalid hour/min/sec")
|
||||
}
|
||||
|
||||
fn setup_graph_map(events: &[Activity], slice_duration: u32) -> (HashMap<NaiveDateTime, u64>, u64) {
|
||||
@@ -109,7 +110,7 @@ pub struct DateTimeSpec {
|
||||
|
||||
impl DateTimeSpec {
|
||||
pub fn from_args(args: &[DateArg], context: Option<u32>) -> Result<DateTimeSpec> {
|
||||
let today = Utc::today().naive_utc();
|
||||
let today = Utc::now().date_naive();
|
||||
if args.is_empty() {
|
||||
let mut start = today;
|
||||
|
||||
@@ -156,7 +157,7 @@ impl DateTimeSpec {
|
||||
// add legend
|
||||
w.push_str(&" ".repeat(11));
|
||||
for x in 0..24 {
|
||||
w.push_str(&format!("{:02}", x));
|
||||
write!(w, "{:02}", x).expect("out of memory");
|
||||
|
||||
for i in 0..ctx.hour_width() {
|
||||
if i >= 2 {
|
||||
@@ -175,7 +176,7 @@ impl DateTimeSpec {
|
||||
let mut mins = 0;
|
||||
|
||||
for _ in 0..(MIN_PER_DAY / ctx.slice_duration) {
|
||||
let time = date.and_hms(hours, mins, 0);
|
||||
let time = date.and_hms_opt(hours, mins, 0).expect("Invalid hour/min/sec");
|
||||
|
||||
if !ctx.is_future(&time) {
|
||||
let activity = ctx.activity_for_slice(&time);
|
||||
|
||||
@@ -18,7 +18,7 @@ pub struct TimeSpec {
|
||||
|
||||
impl TimeSpec {
|
||||
fn resolve(s: &str, now: NaiveDateTime) -> Result<Self> {
|
||||
let today = NaiveDateTime::new(now.date(), NaiveTime::from_hms(0, 0, 0));
|
||||
let today = NaiveDateTime::new(now.date(), NaiveTime::from_hms_opt(0, 0, 0).expect("Invalid hour/min/sec"));
|
||||
|
||||
let datetime = match s {
|
||||
"today" => today,
|
||||
@@ -122,8 +122,8 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
fn datetime() -> NaiveDateTime {
|
||||
let date = chrono::NaiveDate::from_ymd(2020, 3, 14);
|
||||
let time = chrono::NaiveTime::from_hms(16, 20, 23);
|
||||
let date = chrono::NaiveDate::from_ymd_opt(2020, 3, 14).unwrap();
|
||||
let time = chrono::NaiveTime::from_hms_opt(16, 20, 23).unwrap();
|
||||
NaiveDateTime::new(date, time)
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ impl Cmd for Args {
|
||||
.context("Failed to parse date spec")?;
|
||||
let filter = ActivityFilter {
|
||||
topic: None,
|
||||
since: Some(dts.start().and_hms(0, 0, 0)),
|
||||
until: Some(dts.end().and_hms(23, 59, 59)),
|
||||
since: Some(dts.start().and_hms_opt(0, 0, 0).expect("Invalid hour/min/sec")),
|
||||
until: Some(dts.end().and_hms_opt(23, 59, 59).expect("Invalid hour/min/sec")),
|
||||
location: false,
|
||||
};
|
||||
let events = Activity::query(rl.db(), &filter)?;
|
||||
@@ -53,12 +53,12 @@ impl Cmd for Args {
|
||||
.context("Failed to parse date spec")?;
|
||||
let filter = ActivityFilter {
|
||||
topic: None,
|
||||
since: Some(ds.start().and_hms(0, 0, 0)),
|
||||
until: Some(ds.end().and_hms(23, 59, 59)),
|
||||
since: Some(ds.start().and_hms_opt(0, 0, 0).expect("Invalid hour/min/sec")),
|
||||
until: Some(ds.end().and_hms_opt(23, 59, 59).expect("Invalid hour/min/sec")),
|
||||
location: false,
|
||||
};
|
||||
let events = Activity::query(rl.db(), &filter)?;
|
||||
let ctx = DateContext::new(&events, Utc::today().naive_utc());
|
||||
let ctx = DateContext::new(&events, Utc::now().date_naive());
|
||||
println!("{}", ds.to_term_string(&ctx));
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -2,12 +2,12 @@ use crate::errors::*;
|
||||
|
||||
use crate::cmd::Cmd;
|
||||
use crate::engine::Module;
|
||||
// use crate::models::*;
|
||||
use crate::notify::{self, Notification};
|
||||
use crate::options::{self, Opt};
|
||||
use crate::shell::Shell;
|
||||
use crate::term;
|
||||
use sn0int_std::ratelimits::Ratelimiter;
|
||||
use std::fmt::Write;
|
||||
use structopt::StructOpt;
|
||||
use structopt::clap::AppSettings;
|
||||
|
||||
@@ -52,19 +52,19 @@ pub struct ExecArgs {
|
||||
}
|
||||
|
||||
fn print_summary(module: &Module, sent: usize, errors: usize) {
|
||||
let mut out = if sent == 1 {
|
||||
String::from("Sent 1 notification")
|
||||
} else {
|
||||
format!("Sent {} notifications", sent)
|
||||
};
|
||||
let mut out = if sent == 1 {
|
||||
String::from("Sent 1 notification")
|
||||
} else {
|
||||
format!("Sent {} notifications", sent)
|
||||
};
|
||||
|
||||
out.push_str(&format!(" with {}", module.canonical()));
|
||||
write!(out, " with {}", module.canonical()).expect("out of memory");
|
||||
|
||||
if errors > 0 {
|
||||
out.push_str(&format!(" ({} errors)", errors));
|
||||
}
|
||||
if errors > 0 {
|
||||
write!(out, " ({} errors)", errors).expect("out of memory");
|
||||
}
|
||||
|
||||
term::info(&out);
|
||||
term::info(&out);
|
||||
}
|
||||
|
||||
fn send(args: SendArgs, rl: &mut Shell) -> Result<()> {
|
||||
|
||||
@@ -21,7 +21,7 @@ pub struct Config {
|
||||
impl Config {
|
||||
pub fn load() -> Result<Config> {
|
||||
let path = Config::path()?;
|
||||
Config::load_from(&path)
|
||||
Config::load_from(path)
|
||||
}
|
||||
|
||||
pub fn path() -> Result<PathBuf> {
|
||||
|
||||
@@ -5,6 +5,7 @@ use diesel::expression::SqlLiteral;
|
||||
use diesel::expression::sql_literal::sql;
|
||||
use diesel::sql_types::Bool;
|
||||
use diesel::prelude::*;
|
||||
use std::fmt::Write;
|
||||
use strum_macros::{EnumString, IntoStaticStr};
|
||||
use crate::autonoscope::{RuleSet, RuleType};
|
||||
use crate::models::*;
|
||||
@@ -502,14 +503,14 @@ impl Filter {
|
||||
for arg in args {
|
||||
if ["=", "!=", "<", ">", "<=", ">=", "like"].contains(&arg.to_lowercase().as_str()) {
|
||||
expect_value = true;
|
||||
query += &format!(" {}", arg);
|
||||
write!(query, " {}", arg)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(idx) = arg.find('=') {
|
||||
if idx != 0 {
|
||||
let (key, value) = arg.split_at(idx);
|
||||
query += &format!(" {} = {}", key, Self::escape(&value[1..]));
|
||||
write!(query, " {} = {}", key, Self::escape(&value[1..]))?;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -519,7 +520,7 @@ impl Filter {
|
||||
query.push_str(&Self::escape(arg));
|
||||
expect_value = false;
|
||||
} else {
|
||||
query += &format!(" {}", arg);
|
||||
write!(query, " {}", arg)?;
|
||||
}
|
||||
}
|
||||
debug!("Parsed query: {:?}", query);
|
||||
|
||||
@@ -300,7 +300,7 @@ impl State for LuaState {
|
||||
let id = self.random_id();
|
||||
|
||||
let sock = if let Some(proxy) = self.resolve_proxy_options(&options.proxy)? {
|
||||
Socket::connect_socks5(proxy, host, port, options)?
|
||||
Socket::connect_socks5(*proxy, host, port, options)?
|
||||
} else {
|
||||
Socket::connect(&self.dns_config, host, port, options)?
|
||||
};
|
||||
|
||||
@@ -77,7 +77,7 @@ impl<'a> Library<'a> {
|
||||
}
|
||||
|
||||
pub fn private_modules(path: &Path) -> Result<bool> {
|
||||
let metadata = fs::symlink_metadata(&path)?.file_type();
|
||||
let metadata = fs::symlink_metadata(path)?.file_type();
|
||||
if metadata.is_symlink() {
|
||||
debug!("Folder is a symlink, flagging modules as private");
|
||||
return Ok(true);
|
||||
|
||||
@@ -47,7 +47,7 @@ impl IpcParent {
|
||||
}
|
||||
|
||||
pub fn send_start(&mut self, start: &StartCommand) -> Result<()> {
|
||||
let start = serde_json::to_value(&start)?;
|
||||
let start = serde_json::to_value(start)?;
|
||||
self.send(&start)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -71,6 +71,10 @@ impl IpcParent {
|
||||
let mut line = String::new();
|
||||
let len = self.stdout.read_line(&mut line)?;
|
||||
|
||||
if len == 0 {
|
||||
bail!("Sandbox child has crashed");
|
||||
}
|
||||
|
||||
let event = serde_json::from_str(&line[..len])?;
|
||||
debug!("IpcParent received: {:?}", event);
|
||||
Ok(event)
|
||||
|
||||
@@ -94,7 +94,7 @@ impl KeyRing {
|
||||
}
|
||||
|
||||
pub fn load(path: &Path) -> Result<KeyRing> {
|
||||
let buf = fs::read(&path)
|
||||
let buf = fs::read(path)
|
||||
.context("Failed to read keyring file")?;
|
||||
serde_json::from_slice(&buf)
|
||||
.map_err(Error::from)
|
||||
@@ -103,7 +103,7 @@ impl KeyRing {
|
||||
pub fn save(&self) -> Result<()> {
|
||||
let path = Self::path()?;
|
||||
let buf = serde_json::to_string(&self)?;
|
||||
fs::write(&path, buf)
|
||||
fs::write(path, buf)
|
||||
.context("Failed to save keyring")?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -208,7 +208,7 @@ pub struct KeyRingEntry {
|
||||
|
||||
impl KeyRingEntry {
|
||||
pub fn to_lua(&self) -> Result<AnyLuaValue> {
|
||||
let v = serde_json::to_value(&self)?;
|
||||
let v = serde_json::to_value(self)?;
|
||||
let v = LuaJsonValue::from(v).into();
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
// because of diesel
|
||||
#![allow(clippy::extra_unused_lifetimes)]
|
||||
|
||||
#![warn(unused_extern_crates)]
|
||||
use hlua_badtouch as hlua;
|
||||
|
||||
23
src/paths.rs
23
src/paths.rs
@@ -9,21 +9,21 @@ pub fn sn0int_dir() -> Result<PathBuf> {
|
||||
.ok_or_else(|| format_err!("Failed to find data directory"))?;
|
||||
let path = path.join("sn0int");
|
||||
fs::create_dir_all(&path)
|
||||
.context("Failed to create data directory")?;
|
||||
.context("Failed to create sn0int data directory")?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn history_path() -> Result<PathBuf> {
|
||||
let path = sn0int_dir()?;
|
||||
let path = path.join("history");
|
||||
let path = sn0int_dir()?
|
||||
.join("history");
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn module_dir() -> Result<PathBuf> {
|
||||
let path = sn0int_dir()?;
|
||||
let path = path.join("modules");
|
||||
let path = sn0int_dir()?
|
||||
.join("modules");
|
||||
fs::create_dir_all(&path)
|
||||
.context("Failed to create module directory")?;
|
||||
.context("Failed to create modules directory")?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
@@ -31,16 +31,15 @@ pub fn data_dir() -> Result<PathBuf> {
|
||||
let path = sn0int_dir()?
|
||||
.join("data");
|
||||
fs::create_dir_all(&path)
|
||||
.context("Failed to create module directory")?;
|
||||
.context("Failed to create data directory")?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn workspace_dir(workspace: &Workspace) -> Result<PathBuf> {
|
||||
let path = sn0int_dir()?
|
||||
.join("data")
|
||||
let path = data_dir()?
|
||||
.join(workspace.as_str());
|
||||
fs::create_dir_all(&path)
|
||||
.context("Failed to create module directory")?;
|
||||
.context("Failed to create workspace directory")?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
@@ -48,7 +47,7 @@ pub fn blobs_dir(workspace: &Workspace) -> Result<PathBuf> {
|
||||
let path = workspace_dir(workspace)?
|
||||
.join("blobs");
|
||||
fs::create_dir_all(&path)
|
||||
.context("Failed to create module directory")?;
|
||||
.context("Failed to create blobs directory")?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
@@ -57,7 +56,7 @@ pub fn cache_dir() -> Result<PathBuf> {
|
||||
.ok_or_else(|| format_err!("Failed to find cache directory"))?;
|
||||
let path = path.join("sn0int");
|
||||
fs::create_dir_all(&path)
|
||||
.context("Failed to create cache directory")?;
|
||||
.context("Failed to create sn0int cache directory")?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@ pub struct Repl<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Repl<'a> {
|
||||
pub fn new(lua: Lua<'a>, state: Arc<LuaState>) -> Repl<'a> {
|
||||
let rl = Readline::with(ReplCompleter::default());
|
||||
Repl {
|
||||
pub fn new(lua: Lua<'a>, state: Arc<LuaState>) -> Result<Repl<'a>> {
|
||||
let rl = Readline::with(ReplCompleter::default())?;
|
||||
Ok(Repl {
|
||||
rl,
|
||||
lua,
|
||||
state,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn update_globals(&mut self) {
|
||||
@@ -63,7 +63,7 @@ impl<'a> Repl<'a> {
|
||||
Ok(val) => {
|
||||
if val != AnyLuaValue::LuaNil {
|
||||
let mut out = String::new();
|
||||
format_lua(&mut out, &val);
|
||||
format_lua(&mut out, &val).expect("out of memory");
|
||||
println!("{}", out);
|
||||
}
|
||||
if let Some(err) = self.state.last_error() {
|
||||
@@ -104,7 +104,7 @@ pub fn run(config: &Config) -> Result<()> {
|
||||
|
||||
let tx = DummyIpcChild::create();
|
||||
let (lua, state) = ctx::ctx(env, tx);
|
||||
let mut repl = Repl::new(lua, state);
|
||||
let mut repl = Repl::new(lua, state)?;
|
||||
|
||||
println!(r#":: sn0int v{} lua repl
|
||||
Assign variables with `a = sn0int_version()` and `return a` to print
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::errors::*;
|
||||
use crate::engine::ctx::State;
|
||||
use crate::engine::structs::{byte_array, lua_bytes};
|
||||
use crate::hlua::{self, AnyLuaValue};
|
||||
use data_encoding::{Specification, Encoding};
|
||||
use data_encoding::{BASE64, Specification, Encoding};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ fn spec(symbols: &str, padding: &str) -> Result<Encoding> {
|
||||
|
||||
pub fn base64_decode(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("base64_decode", hlua::function1(move |bytes: String| -> Result<AnyLuaValue> {
|
||||
base64::decode(&bytes)
|
||||
BASE64.decode(bytes.as_bytes())
|
||||
.map_err(|err| state.set_error(err.into()))
|
||||
.map(|bytes| lua_bytes(&bytes))
|
||||
}))
|
||||
@@ -29,7 +29,7 @@ pub fn base64_encode(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("base64_encode", hlua::function1(move |bytes: AnyLuaValue| -> Result<String> {
|
||||
byte_array(bytes)
|
||||
.map_err(|err| state.set_error(err))
|
||||
.map(|bytes| base64::encode(&bytes))
|
||||
.map(|bytes| BASE64.encode(&bytes))
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ pub fn md5(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("md5", hlua::function1(move |bytes: AnyLuaValue| -> Result<AnyLuaValue> {
|
||||
byte_array(bytes)
|
||||
.map_err(|err| state.set_error(err))
|
||||
.map(|bytes| lua_bytes(&md5::Md5::digest(&bytes)))
|
||||
.map(|bytes| lua_bytes(&md5::Md5::digest(bytes)))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub fn sha1(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("sha1", hlua::function1(move |bytes: AnyLuaValue| -> Result<AnyLuaValue> {
|
||||
byte_array(bytes)
|
||||
.map_err(|err| state.set_error(err))
|
||||
.map(|bytes| lua_bytes(&sha1::Sha1::digest(&bytes)))
|
||||
.map(|bytes| lua_bytes(&sha1::Sha1::digest(bytes)))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ pub fn sha2_256(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("sha2_256", hlua::function1(move |bytes: AnyLuaValue| -> Result<AnyLuaValue> {
|
||||
byte_array(bytes)
|
||||
.map_err(|err| state.set_error(err))
|
||||
.map(|bytes| lua_bytes(&sha2::Sha256::digest(&bytes)))
|
||||
.map(|bytes| lua_bytes(&sha2::Sha256::digest(bytes)))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ pub fn sha2_512(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("sha2_512", hlua::function1(move |bytes: AnyLuaValue| -> Result<AnyLuaValue> {
|
||||
byte_array(bytes)
|
||||
.map_err(|err| state.set_error(err))
|
||||
.map(|bytes| lua_bytes(&sha2::Sha512::digest(&bytes)))
|
||||
.map(|bytes| lua_bytes(&sha2::Sha512::digest(bytes)))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ pub fn sha3_256(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("sha3_256", hlua::function1(move |bytes: AnyLuaValue| -> Result<AnyLuaValue> {
|
||||
byte_array(bytes)
|
||||
.map_err(|err| state.set_error(err))
|
||||
.map(|bytes| lua_bytes(&sha3::Sha3_256::digest(&bytes)))
|
||||
.map(|bytes| lua_bytes(&sha3::Sha3_256::digest(bytes)))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ pub fn sha3_512(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("sha3_512", hlua::function1(move |bytes: AnyLuaValue| -> Result<AnyLuaValue> {
|
||||
byte_array(bytes)
|
||||
.map_err(|err| state.set_error(err))
|
||||
.map(|bytes| lua_bytes(&sha3::Sha3_512::digest(&bytes)))
|
||||
.map(|bytes| lua_bytes(&sha3::Sha3_512::digest(bytes)))
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ use crate::errors::*;
|
||||
use crate::engine::ctx::State;
|
||||
use crate::engine::structs::byte_array;
|
||||
use crate::hlua::{self, AnyLuaValue};
|
||||
use std::fmt::Write;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
||||
// TODO: consider deprecating this function, replace with hex_{en,de}code and hex_custom_{en,de}code
|
||||
pub fn hex(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("hex", hlua::function1(move |bytes: AnyLuaValue| -> Result<String> {
|
||||
@@ -15,7 +15,7 @@ pub fn hex(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
let mut out = String::new();
|
||||
|
||||
for b in bytes {
|
||||
out += &format!("{:02x}", b);
|
||||
write!(out, "{:02x}", b).expect("out of memory");
|
||||
}
|
||||
|
||||
out
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::json;
|
||||
|
||||
pub fn json_decode(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("json_decode", hlua::function1(move |x: String| -> Result<AnyLuaValue> {
|
||||
json::decode(&x)
|
||||
json::decode(x)
|
||||
.map_err(|err| state.set_error(err))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
use crate::errors::*;
|
||||
use crate::engine::ctx::State;
|
||||
use crate::hlua::{self, AnyLuaValue};
|
||||
use std::fmt::Write;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
||||
pub fn format_lua(out: &mut String, x: &AnyLuaValue) {
|
||||
pub fn format_lua(out: &mut String, x: &AnyLuaValue) -> Result<()> {
|
||||
match *x {
|
||||
AnyLuaValue::LuaNil => out.push_str("null"),
|
||||
AnyLuaValue::LuaString(ref x) => out.push_str(&format!("{:?}", x)),
|
||||
AnyLuaValue::LuaNumber(ref x) => out.push_str(&format!("{:?}", x)),
|
||||
AnyLuaValue::LuaAnyString(ref x) => out.push_str(&format!("{:?}", x.0)),
|
||||
AnyLuaValue::LuaBoolean(ref x) => out.push_str(&format!("{:?}", x)),
|
||||
AnyLuaValue::LuaString(ref x) => write!(out, "{:?}", x)?,
|
||||
AnyLuaValue::LuaNumber(ref x) => write!(out, "{:?}", x)?,
|
||||
AnyLuaValue::LuaAnyString(ref x) => write!(out, "{:?}", x.0)?,
|
||||
AnyLuaValue::LuaBoolean(ref x) => write!(out, "{:?}", x)?,
|
||||
AnyLuaValue::LuaArray(ref x) => {
|
||||
out.push('{');
|
||||
let mut first = true;
|
||||
|
||||
for &(ref k, ref v) in x {
|
||||
for (k, v) in x {
|
||||
if !first {
|
||||
out.push_str(", ");
|
||||
}
|
||||
|
||||
let mut key = String::new();
|
||||
format_lua(&mut key, k);
|
||||
format_lua(&mut key, k)?;
|
||||
|
||||
let mut value = String::new();
|
||||
format_lua(&mut value, v);
|
||||
format_lua(&mut value, v)?;
|
||||
|
||||
out.push_str(&format!("{}: {}", key, value));
|
||||
write!(out, "{}: {}", key, value)?;
|
||||
|
||||
first = false;
|
||||
}
|
||||
@@ -33,12 +34,14 @@ pub fn format_lua(out: &mut String, x: &AnyLuaValue) {
|
||||
},
|
||||
AnyLuaValue::LuaOther => out.push_str("LuaOther"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn info(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("info", hlua::function1(move |val: AnyLuaValue| {
|
||||
let mut out = String::new();
|
||||
format_lua(&mut out, &val);
|
||||
format_lua(&mut out, &val).expect("out of memory");
|
||||
state.info(out);
|
||||
}))
|
||||
}
|
||||
@@ -46,7 +49,7 @@ pub fn info(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
pub fn debug(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
lua.set("debug", hlua::function1(move |val: AnyLuaValue| {
|
||||
let mut out = String::new();
|
||||
format_lua(&mut out, &val);
|
||||
format_lua(&mut out, &val).expect("out of memory");
|
||||
state.debug(out);
|
||||
}))
|
||||
}
|
||||
@@ -79,7 +82,7 @@ pub fn print(lua: &mut hlua::Lua, _: Arc<dyn State>) {
|
||||
lua.set("print", hlua::function1(move |val: AnyLuaValue| {
|
||||
// println!("{:?}", val);
|
||||
let mut out = String::new();
|
||||
format_lua(&mut out, &val);
|
||||
format_lua(&mut out, &val).expect("out of memory");
|
||||
eprintln!("{}", out);
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ pub fn ws_recv_json(lua: &mut hlua::Lua, state: Arc<dyn State>) {
|
||||
.map_err(|err| state.set_error(err))?;
|
||||
|
||||
let json = if let Some(json) = json {
|
||||
let json = json::decode(&json)
|
||||
let json = json::decode(json)
|
||||
.map_err(|err| state.set_error(err))?;
|
||||
Some(json)
|
||||
} else {
|
||||
|
||||
@@ -13,11 +13,11 @@ pub fn init() -> Result<()> {
|
||||
ctx.allow_syscall(Syscall::sigaltstack)?;
|
||||
ctx.allow_syscall(Syscall::munmap)?;
|
||||
ctx.allow_syscall(Syscall::fcntl)?;
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "riscv64")))]
|
||||
ctx.allow_syscall(Syscall::fcntl64)?;
|
||||
ctx.allow_syscall(Syscall::uname)?;
|
||||
ctx.allow_syscall(Syscall::close)?;
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
|
||||
ctx.allow_syscall(Syscall::poll)?;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
ctx.allow_syscall(Syscall::ppoll)?;
|
||||
@@ -29,7 +29,7 @@ pub fn init() -> Result<()> {
|
||||
ctx.allow_syscall(Syscall::connect)?;
|
||||
#[cfg(target_arch = "x86")]
|
||||
ctx.allow_syscall(Syscall::socketcall)?;
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
|
||||
ctx.allow_syscall(Syscall::epoll_wait)?;
|
||||
ctx.allow_syscall(Syscall::epoll_pwait)?;
|
||||
ctx.allow_syscall(Syscall::getrandom)?;
|
||||
@@ -44,7 +44,7 @@ pub fn init() -> Result<()> {
|
||||
ctx.allow_syscall(Syscall::getsockopt)?;
|
||||
#[cfg(not(target_arch = "arm"))]
|
||||
ctx.allow_syscall(Syscall::mmap)?;
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "riscv64")))]
|
||||
ctx.allow_syscall(Syscall::mmap2)?;
|
||||
ctx.allow_syscall(Syscall::mremap)?;
|
||||
ctx.allow_syscall(Syscall::mprotect)?;
|
||||
@@ -77,9 +77,10 @@ pub fn init() -> Result<()> {
|
||||
ctx.allow_syscall(Syscall::lseek)?;
|
||||
#[cfg(target_arch = "arm")]
|
||||
ctx.allow_syscall(Syscall::_llseek)?;
|
||||
ctx.allow_syscall(Syscall::clone3)?;
|
||||
|
||||
ctx.set_action_for_syscall(Action::Errno(1), Syscall::openat)?;
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
|
||||
ctx.set_action_for_syscall(Action::Errno(1), Syscall::open)?;
|
||||
|
||||
ctx.load()?;
|
||||
|
||||
@@ -174,9 +174,9 @@ pub struct Shell<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Shell<'a> {
|
||||
pub fn new(config: &'a Config, db: Database, blobs: BlobStorage, psl: PslReader, library: Library<'a>, keyring: KeyRing) -> Shell<'a> {
|
||||
pub fn new(config: &'a Config, db: Database, blobs: BlobStorage, psl: PslReader, library: Library<'a>, keyring: KeyRing) -> Result<Shell<'a>> {
|
||||
let h = CmdCompleter::default();
|
||||
let rl = Readline::with(h);
|
||||
let rl = Readline::with(h)?;
|
||||
|
||||
let prompt = Prompt::new(db.name().to_string());
|
||||
|
||||
@@ -197,7 +197,7 @@ impl<'a> Shell<'a> {
|
||||
rl.reload_module_cache();
|
||||
rl.reload_keyring_cache();
|
||||
|
||||
rl
|
||||
Ok(rl)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -571,7 +571,7 @@ pub fn init<'a>(args: &Args, config: &'a Config, verbose_init: bool) -> Result<S
|
||||
}
|
||||
autoupdate.check_background(config, library.list());
|
||||
|
||||
let mut rl = Shell::new(config, db, blobs, psl, library, keyring);
|
||||
let mut rl = Shell::new(config, db, blobs, psl, library, keyring)?;
|
||||
|
||||
ttl::reap_expired(&mut rl)?;
|
||||
|
||||
|
||||
@@ -10,35 +10,29 @@ pub struct Readline<T: rustyline::Helper> {
|
||||
|
||||
impl Readline<()> {
|
||||
#[inline]
|
||||
pub fn new() -> Readline<()> {
|
||||
pub fn new() -> Result<Readline<()>> {
|
||||
Readline::init(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Readline<()> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: rustyline::Helper> Readline<T> {
|
||||
#[inline]
|
||||
pub fn with(helper: T) -> Readline<T> {
|
||||
pub fn with(helper: T) -> Result<Readline<T>> {
|
||||
Readline::init(Some(helper))
|
||||
}
|
||||
|
||||
fn init(helper: Option<T>) -> Readline<T> {
|
||||
fn init(helper: Option<T>) -> Result<Readline<T>> {
|
||||
let rl_config = rustyline::Config::builder()
|
||||
.completion_type(CompletionType::List)
|
||||
.edit_mode(EditMode::Emacs)
|
||||
.build();
|
||||
|
||||
let mut rl: Editor<T> = Editor::with_config(rl_config);
|
||||
let mut rl: Editor<T> = Editor::with_config(rl_config)?;
|
||||
rl.set_helper(helper);
|
||||
|
||||
Readline {
|
||||
Ok(Readline {
|
||||
rl,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -66,7 +66,7 @@ impl AutoUpdater {
|
||||
|
||||
pub fn save(&self) -> Result<()> {
|
||||
let config = serde_json::to_string(&self)?;
|
||||
fs::write(AutoUpdater::path()?, &config)
|
||||
fs::write(AutoUpdater::path()?, config)
|
||||
.context("Failed to write auto-update state")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub fn random_string(len: usize) -> String {
|
||||
}
|
||||
|
||||
pub fn read_line(prompt: &str) -> Result<String> {
|
||||
let mut rl = rustyline::Editor::<()>::new();
|
||||
let mut rl = rustyline::Editor::<()>::new()?;
|
||||
let mut line = rl.readline(prompt)
|
||||
.map_err(|err| match err {
|
||||
ReadlineError::Eof => format_err!("Failed to read line from input"),
|
||||
|
||||
@@ -15,6 +15,7 @@ use crate::ratelimits::{Ratelimiter, RatelimitResponse};
|
||||
use crate::shell::Shell;
|
||||
use sn0int_std::ratelimits::RatelimitSender;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
use std::result;
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
@@ -204,7 +205,7 @@ impl DatabaseEvent {
|
||||
// TODO: we don't want to copy the match arms everywhere
|
||||
let mut subject = format!("New activity: {:?}", object.topic);
|
||||
if let Some(uniq) = &object.uniq {
|
||||
subject += &format!(" ({:?})", uniq);
|
||||
write!(subject, " ({:?})", uniq).expect("out of memory");
|
||||
}
|
||||
let topic = format!("activity:{}", object.topic);
|
||||
Self::notify(rl, spinner, ratelimit, &topic, subject);
|
||||
@@ -213,20 +214,20 @@ impl DatabaseEvent {
|
||||
fn spinner_log_new_activity<T: SpinLogger>(spinner: &mut T, object: &NewActivity, verbose: u64) {
|
||||
let mut log = format!("{:?} ", object.topic);
|
||||
if let Some(uniq) = &object.uniq {
|
||||
log.push_str(&format!("({:?}) ", uniq));
|
||||
write!(log, "({:?}) ", uniq).expect("out of memory");
|
||||
}
|
||||
log.push_str(&format!("@ {}", object.time));
|
||||
write!(log, "@ {}", object.time).expect("out of memory");
|
||||
|
||||
if let (Some(ref lat), Some(ref lon)) = (object.latitude, object.longitude) {
|
||||
log.push_str(&format!(" ({}, {}", lat, lon));
|
||||
write!(log, " ({}, {}", lat, lon).expect("out of memory");
|
||||
if let Some(radius) = &object.radius {
|
||||
log.push_str(&format!(" | {}m", radius));
|
||||
write!(log, " | {}m", radius).expect("out of memory");
|
||||
}
|
||||
log.push(')');
|
||||
}
|
||||
|
||||
if verbose > 0 {
|
||||
log.push_str(&format!(": {}", object.content));
|
||||
write!(log, ": {}", object.content).expect("out of memory");
|
||||
}
|
||||
|
||||
spinner.log(&log);
|
||||
@@ -487,6 +488,7 @@ pub fn spawn(rl: &mut Shell,
|
||||
tx.send(Event2::Start);
|
||||
let event = match ipc::parent::run(module, &tx, arg, keyring, verbose, has_stdin, proxy, user_agent, options, blobs) {
|
||||
Ok(exit) => exit,
|
||||
// TODO: this should include the whole error chain
|
||||
Err(err) => ExitEvent::SetupFailed(err.to_string()),
|
||||
};
|
||||
tx.send(Event2::Exit(event));
|
||||
|
||||
Reference in New Issue
Block a user