add rustfmt and fmtall.sh

This commit is contained in:
creating2morrow 2023-05-09 17:28:07 -04:00
parent 438b22838e
commit e9ef5778d1
38 changed files with 1158 additions and 585 deletions

6
.rustfmt.toml Normal file
View file

@ -0,0 +1,6 @@
# cargo +nightly fmt
comment_width = 100
format_code_in_doc_comments = true
imports_granularity = "Crate"
imports_layout = "Vertical"
wrap_comments = true

View file

@ -20,11 +20,22 @@ NEVidebla-MESago (invisible message)
* install dependencies
* ubuntu example: `sudo apt update -y && sudo apt upgrade -y`
* `sudo apt install -y libssl-dev build-essential libgpgme-dev`
* download and run i2prouter start (optional: setup to run on boot similar tor daemon)
* `git clone https://github/com/creating2morrow/nevmes`
* `cd nevmes && ./scripts/build_all_and_run.sh "-- -h"`
* gui built with rust [egui](https://docs.rs/egui/latest/egui/)
## Installation Mananger
* additional required software can be downloaded from the gui home or `Binaries` links below
* hashes are in core [lib.rs](./nevmes-core/src/lib.rs)
* download and run i2p
* `/i2p/i2prouter start`
* `/i2p/i2prouter install` (optional: setup to run on boot similar to tor daemon)
* download i2p-zero, put the path in the connection manager or cli `--i2p-zero-dir` flag
* download monero, update connection manager or cli
* `--monero-blockchain-dir`, where to put lmdb for monero (e.g. path/to/ssd)
* `--monero-location`, path to monero download
## Contributing and Releasing
```bash
@ -36,7 +47,10 @@ NEVidebla-MESago (invisible message)
```
* code on dev branch
* run `./scripts/fmtall.sh` before committing
* pull request to dev branch
* todo => `TODO(name): detailed work`
* docs on all `pub fn` and `pub struct`
* merge dev to vX.X.X
* merge vX.X.X to main
* tag release v.X.X.X every friday (if stable changes)
@ -63,7 +77,6 @@ NEVidebla-MESago (invisible message)
* nevmes-auth - `internal` auth server
* nevmes-contact - `internal` add contacts server
* nevmes-core - application core logic
* nevmes-gui - primary user interface
* nevmes-message - `internal` message tx/read etc. server
* nevmes - `external` primary server for contact share, payment, message rx etc.
@ -74,16 +87,8 @@ NEVidebla-MESago (invisible message)
* [i2p-zero](https://github.com/i2p-zero/i2p-zero/releases/tag/v1.20) - (not included) tunnel creation
* [i2p](https://geti2p.net/en/download) - http proxy (not included, *i2p-zero http proxy not working)
most of the complex logic stays in nevmes-core, exported from [lib.rs](./nevmes-core/src/lib.rs)
## Manual
[the manual](./docs/man.md)
## Known issues
* gui password and screen lock needs fixing up
* timeout out JWP payment approval screen with infinite loading
* prove payment edge where payment succeeds but jwp is empty, currently require new payment
* test framework (in progress)
* docs on all `fn` and `structs`
* i2pd installer on home screen?
* and more daemon info and wallet functionality (multisig)

View file

@ -0,0 +1,6 @@
# cargo +nightly fmt
comment_width = 100
format_code_in_doc_comments = true
imports_granularity = "Crate"
imports_layout = "Vertical"
wrap_comments = true

View file

@ -1,17 +1,20 @@
use rocket::http::Status;
use rocket::response::status::Custom;
use rocket::serde::json::Json;
use rocket::get;
use rocket::{
get,
http::Status,
response::status::Custom,
serde::json::Json,
};
use nevmes_core::{auth, models::*};
use nevmes_core::{
auth,
models::*,
};
/// Login with wallet signature
///
/// Creates user on initial login
///
#[get("/login/<signature>/<aid>/<uid>")]
pub async fn login
(aid: String, uid: String,signature: String) -> Custom<Json<Authorization>> {
pub async fn login(aid: String, uid: String, signature: String) -> Custom<Json<Authorization>> {
let m_auth: Authorization = auth::verify_login(aid, uid, signature).await;
Custom(Status::Created, Json(m_auth))
}

View file

@ -14,6 +14,5 @@ async fn rocket() -> _ {
};
env_logger::init();
log::info!("nevmes-auth is online");
rocket::custom(&config)
.mount("/", routes![controller::login])
rocket::custom(&config).mount("/", routes![controller::login])
}

View file

@ -0,0 +1,6 @@
# cargo +nightly fmt
comment_width = 100
format_code_in_doc_comments = true
imports_granularity = "Crate"
imports_layout = "Vertical"
wrap_comments = true

View file

@ -1,41 +1,53 @@
use rocket::http::Status;
use rocket::response::status::Custom;
use rocket::serde::json::Json;
use rocket::{get, post};
use rocket::{
get,
http::Status,
post,
response::status::Custom,
serde::json::Json,
};
use nevmes_core::{auth, contact, models::*, proof, utils, reqres};
use nevmes_core::{
auth,
contact,
models::*,
proof,
reqres,
utils,
};
/// Add contact
#[post("/", data="<req_contact>")]
pub async fn add_contact
(req_contact: Json<Contact>,_token: auth::BearerToken) -> Custom<Json<Contact>> {
#[post("/", data = "<req_contact>")]
pub async fn add_contact(
req_contact: Json<Contact>,
_token: auth::BearerToken,
) -> Custom<Json<Contact>> {
let res_contact = contact::create(&req_contact).await;
if res_contact.cid == utils::empty_string() {
return Custom(Status::BadRequest, Json(Default::default()))
return Custom(Status::BadRequest, Json(Default::default()));
}
Custom(Status::Ok, Json(res_contact))
}
/// Return all contacts
#[get("/")]
pub async fn get_contacts
(_token: auth::BearerToken) -> Custom<Json<Vec<Contact>>> {
pub async fn get_contacts(_token: auth::BearerToken) -> Custom<Json<Vec<Contact>>> {
let contacts = contact::find_all();
Custom(Status::Ok, Json(contacts))
}
/// trust contact
#[post("/<key>")]
pub async fn trust_contact
(key: String, _token: auth::BearerToken) -> Status {
pub async fn trust_contact(key: String, _token: auth::BearerToken) -> Status {
contact::trust_gpg(key);
Status::Ok
}
/// prove payment
#[get("/<contact>", data="<proof_req>")]
pub async fn prove_payment
(contact: String, proof_req: Json<proof::TxProof>, _token: auth::BearerToken
#[get("/<contact>", data = "<proof_req>")]
pub async fn prove_payment(
contact: String,
proof_req: Json<proof::TxProof>,
_token: auth::BearerToken,
) -> Custom<Json<reqres::Jwp>> {
let r_jwp = proof::prove_payment(contact, &proof_req).await;
Custom(Status::Ok, Json(r_jwp.unwrap()))

View file

@ -1,13 +1,31 @@
use crate::{args, models::*, db, monero, reqres, user, utils};
use crate::{
args,
db,
models::*,
monero,
reqres,
user,
utils,
};
use clap::Parser;
use log::{debug, error, info};
use log::{
debug,
error,
info,
};
use rocket::http::Status;
use rocket::outcome::Outcome;
use rocket::request::FromRequest;
use rocket::{request, Request};
use rocket::{
http::Status,
outcome::Outcome,
request,
request::FromRequest,
Request,
};
use hmac::{Hmac, Mac};
use hmac::{
Hmac,
Mac,
};
use jwt::*;
use sha2::Sha384;
use std::collections::BTreeMap;
@ -70,8 +88,7 @@ fn update_expiration(f_auth: Authorization, address: &String) -> Authorization {
}
/// Performs the signature verfication against stored auth
pub async fn verify_login
(aid: String, uid: String, signature: String) -> Authorization {
pub async fn verify_login(aid: String, uid: String, signature: String) -> Authorization {
let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await;
let address = m_address.result.address;
let f_auth: Authorization = find(&aid);
@ -94,16 +111,23 @@ pub async fn verify_login
let u_auth = Authorization::update_uid(f_auth, String::from(&u.uid));
let s = db::Interface::open();
db::Interface::delete(&s.env, &s.handle, &u_auth.aid);
db::Interface::write(&s.env, &s.handle, &u_auth.aid, &Authorization::to_db(&u_auth));
return u_auth
db::Interface::write(
&s.env,
&s.handle,
&u_auth.aid,
&Authorization::to_db(&u_auth),
);
return u_auth;
} else if f_user.xmr_address != utils::empty_string() {
info!("returning user");
let m_access = verify_access(&address, &signature).await;
if !m_access { return Default::default() }
if !m_access {
return Default::default();
}
return f_auth;
} else {
error!("error creating user");
return Default::default()
return Default::default();
}
}
@ -172,7 +196,7 @@ impl<'r> FromRequest<'r> for BearerToken {
let env = utils::get_release_env();
let dev = utils::ReleaseEnvironment::Development;
if env == dev {
return Outcome::Success(BearerToken(utils::empty_string()))
return Outcome::Success(BearerToken(utils::empty_string()));
}
let token = request.headers().get_one("token");
let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await;

View file

@ -1,7 +1,19 @@
// Contact repo/service layer
use crate::{db, gpg, models::*, utils, reqres, monero, i2p};
use crate::{
db,
gpg,
i2p,
models::*,
monero,
reqres,
utils,
};
use log::{
debug,
error,
info,
};
use rocket::serde::json::Json;
use log::{debug, error, info};
use std::error::Error;
/// Create a new contact
@ -42,7 +54,7 @@ pub fn find(cid: &String) -> Contact {
let r = db::Interface::read(&s.env, &s.handle, &String::from(cid));
if r == utils::empty_string() {
error!("contact not found");
return Default::default()
return Default::default();
}
Contact::from_db(String::from(cid), r)
}
@ -54,7 +66,7 @@ pub fn find_all() -> Vec<Contact> {
let r = db::Interface::read(&s.env, &s.handle, &String::from(list_key));
if r == utils::empty_string() {
error!("contact index not found");
return Default::default()
return Default::default();
}
let v_cid = r.split(",");
let v: Vec<String> = v_cid.map(|s| String::from(s)).collect();
@ -84,13 +96,20 @@ pub async fn share() -> Contact {
let gpg_key = gpg::export_key().unwrap_or(Vec::new());
let i2p_address = i2p::get_destination();
let xmr_address = m_address.result.address;
Contact { cid: utils::empty_string(),gpg_key,i2p_address,xmr_address }
Contact {
cid: utils::empty_string(),
gpg_key,
i2p_address,
xmr_address,
}
}
pub fn exists(from: &String) -> bool {
let all = find_all();
let mut addresses: Vec<String> = Vec::new();
for c in all { addresses.push(c.i2p_address); }
for c in all {
addresses.push(c.i2p_address);
}
return addresses.contains(from);
}
@ -103,7 +122,9 @@ pub fn exists(from: &String) -> bool {
/// using the app already have some level of knowledge about each other.
///
/// Without signing the key message encryption and sending is not possible.
pub fn trust_gpg(key: String) { gpg::sign_key(&key).unwrap(); }
pub fn trust_gpg(key: String) {
gpg::sign_key(&key).unwrap();
}
/// Get invoice for jwp creation
pub async fn request_invoice(contact: String) -> Result<reqres::Invoice, Box<dyn Error>> {
@ -111,14 +132,16 @@ pub async fn request_invoice(contact: String) -> Result<reqres::Invoice, Box<dyn
let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build();
match client?.get(format!("http://{}/invoice", contact)).send().await {
match client?
.get(format!("http://{}/invoice", contact))
.send()
.await
{
Ok(response) => {
let res = response.json::<reqres::Invoice>().await;
debug!("invoice request response: {:?}", res);
match res {
Ok(r) => {
Ok(r)
},
Ok(r) => Ok(r),
_ => Ok(Default::default()),
}
}
@ -135,14 +158,16 @@ pub async fn add_contact_request(contact: String) -> Result<Contact, Box<dyn Err
let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build();
match client?.get(format!("http://{}/share", contact)).send().await {
match client?
.get(format!("http://{}/share", contact))
.send()
.await
{
Ok(response) => {
let res = response.json::<Contact>().await;
debug!("share response: {:?}", res);
match res {
Ok(r) => {
Ok(r)
},
Ok(r) => Ok(r),
_ => Ok(Default::default()),
}
}

View file

@ -1,8 +1,16 @@
// db created and exported from here
extern crate lmdb_rs as lmdb;
use log::{debug, error};
use lmdb::{EnvBuilder, DbFlags, Environment, DbHandle};
use lmdb::{
DbFlags,
DbHandle,
EnvBuilder,
Environment,
};
use log::{
debug,
error,
};
use crate::utils;
@ -14,10 +22,17 @@ pub struct Interface {
impl Interface {
pub fn open() -> Self {
let release_env = utils::get_release_env();
let file_path = format!("/home/{}/.nevmes/", std::env::var("USER").unwrap_or(String::from("user")));
let file_path = format!(
"/home/{}/.nevmes/",
std::env::var("USER").unwrap_or(String::from("user"))
);
let mut env_str: &str = "test-lmdb";
if release_env != utils::ReleaseEnvironment::Development { env_str = "lmdb"; };
let env = EnvBuilder::new().open(format!("{}/{}", file_path, env_str), 0o777).unwrap();
if release_env != utils::ReleaseEnvironment::Development {
env_str = "lmdb";
};
let env = EnvBuilder::new()
.open(format!("{}/{}", file_path, env_str), 0o777)
.unwrap();
let handle = env.get_default_db(DbFlags::empty()).unwrap();
Interface { env, handle }
}
@ -26,12 +41,14 @@ impl Interface {
{
// get a database bound to this transaction
let db = txn.bind(&h);
let pair = vec![(k,v)];
for &(key, value) in pair.iter() { db.set(&key, &value).unwrap(); }
let pair = vec![(k, v)];
for &(key, value) in pair.iter() {
db.set(&key, &value).unwrap();
}
}
match txn.commit() {
Err(_) => error!("failed to commit!"),
Ok(_) => ()
Ok(_) => (),
}
}
pub fn read(e: &Environment, h: &DbHandle, k: &str) -> String {
@ -51,11 +68,11 @@ impl Interface {
{
// get a database bound to this transaction
let db = txn.bind(&h);
db.del::<>(&k).unwrap_or_else(|_| error!("failed to delete"));
db.del(&k).unwrap_or_else(|_| error!("failed to delete"));
}
match txn.commit() {
Err(_) => error!("failed to commit!"),
Ok(_) => ()
Ok(_) => (),
}
}
}

View file

@ -1,8 +1,19 @@
use log::{debug, error, info};
use std::process::Command;
use crate::{
i2p,
utils,
};
use gpgme::*;
use std::{error::Error, fs::File, io::Write};
use crate::{utils, i2p};
use log::{
debug,
error,
info,
};
use std::{
error::Error,
fs::File,
io::Write,
process::Command,
};
/// Searches for key, returns empty string if none exists
///
@ -73,7 +84,8 @@ pub fn import_key(cid: String, key: Vec<u8>) -> Result<(), Box<dyn Error>> {
let mut data = Data::from_seekable_stream(input)?;
let mode = None;
mode.map(|m| data.set_encoding(m));
ctx.import(&mut data).map_err(|e| format!("import failed {:?}", e))?;
ctx.import(&mut data)
.map_err(|e| format!("import failed {:?}", e))?;
utils::stage_cleanup(filename);
Ok(())
}
@ -82,19 +94,23 @@ pub fn encrypt(name: String, body: &Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>>
let proto = Protocol::OpenPgp;
let mut ctx = Context::from_protocol(proto)?;
ctx.set_armor(true);
let keys: Vec<Key> = ctx.find_keys([&name])?
let keys: Vec<Key> = ctx
.find_keys([&name])?
.filter_map(|x| x.ok())
.filter(|k| k.can_encrypt())
.collect();
let filename = format!("{}.nevmes", name);
let mut f = File::create(&filename)?;
f.write_all(body)?;
let mut input = File::open(&filename)
.map_err(|e| format!("can't open file `{}': {}", filename, e))?;
let mut input =
File::open(&filename).map_err(|e| format!("can't open file `{}': {}", filename, e))?;
let mut output = Vec::new();
ctx.encrypt(&keys, &mut input, &mut output)
.map_err(|e| format!("encrypting failed: {:?}", e))?;
debug!("encrypted message body: {}", String::from_utf8(output.iter().cloned().collect()).unwrap_or(utils::empty_string()));
debug!(
"encrypted message body: {}",
String::from_utf8(output.iter().cloned().collect()).unwrap_or(utils::empty_string())
);
utils::stage_cleanup(filename);
Ok(output)
}
@ -106,8 +122,8 @@ pub fn decrypt(mid: &String, body: &Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>>
let filename = format!("{}.nevmes", mid);
let mut f = File::create(&filename)?;
f.write_all(&body)?;
let mut input = File::open(&filename)
.map_err(|e| format!("can't open file `{}': {}", filename, e))?;
let mut input =
File::open(&filename).map_err(|e| format!("can't open file `{}': {}", filename, e))?;
let mut output = Vec::new();
ctx.decrypt(&mut input, &mut output)
.map_err(|e| format!("decrypting failed: {:?}", e))?;
@ -125,7 +141,8 @@ pub fn write_gen_batch() -> Result<(), Box<dyn Error>> {
Subkey-Curve: Curve25519
Name-Real: {}
Name-Email: {}
Expire-Date: 0", name, name
Expire-Date: 0",
name, name
);
let filename = format!("genkey-batch");
let mut f = File::create(&filename)?;
@ -157,11 +174,13 @@ pub fn sign_key(key: &str) -> Result<(), Box<dyn Error>> {
.get_secret_key(app_key)
.map_err(|e| format!("unable to find signing key: {:?}", e))?;
debug!("app key: {:?}", key.id());
k2s_ctx.add_signer(&key)
k2s_ctx
.add_signer(&key)
.map_err(|e| format!("add_signer() failed: {:?}", e))?;
}
k2s_ctx.sign_key(&key_to_sign, None::<String>, Default::default())
k2s_ctx
.sign_key(&key_to_sign, None::<String>, Default::default())
.map_err(|e| format!("signing failed: {:?}", e))?;
println!("Signed key for {}", key);

View file

@ -1,9 +1,24 @@
use std::{fs, env, process::Command};
use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use crate::{args, utils};
use crate::{
args,
utils,
};
use clap::Parser;
use std::time::Duration;
use log::{
debug,
error,
info,
warn,
};
use serde::{
Deserialize,
Serialize,
};
use std::{
env,
fs,
process::Command,
time::Duration,
};
// TODO(c2m): debug i2p-zero http proxy
@ -24,13 +39,13 @@ impl I2pStatus {
#[derive(Debug, Deserialize, Serialize)]
pub struct HttpProxyStatus {
pub open: bool
pub open: bool,
}
#[derive(Debug)]
pub enum ProxyStatus {
Opening,
Open
Open,
}
impl ProxyStatus {
@ -56,7 +71,9 @@ struct Tunnels {
impl Default for Tunnels {
fn default() -> Self {
Tunnels { tunnels: Vec::new() }
Tunnels {
tunnels: Vec::new(),
}
}
}
@ -103,7 +120,11 @@ fn create_tunnel() {
let args = args::Args::parse();
let path = args.i2p_zero_dir;
let output = Command::new(format!("{}/router/bin/tunnel-control.sh", path))
.args(["server.create", "127.0.0.1", &format!("{}",utils::get_app_port())])
.args([
"server.create",
"127.0.0.1",
&format!("{}", utils::get_app_port()),
])
.spawn()
.expect("i2p-zero failed to create a tunnel");
debug!("{:?}", output.stdout);
@ -125,17 +146,20 @@ pub fn get_destination() -> String {
env::var("USER").unwrap_or(String::from("user"))
);
// Don't panic if i2p-zero isn't installed
let contents = match fs::read_to_string(file_path)
{
let contents = match fs::read_to_string(file_path) {
Ok(file) => file,
_=> utils::empty_string(),
_ => utils::empty_string(),
};
if contents != utils::empty_string() {
let input = format!(r#"{contents}"#);
let mut j: Tunnels = serde_json::from_str(&input).unwrap_or(Default::default());
let destination: String = j.tunnels.remove(0).dest.ok_or(utils::empty_string())
let destination: String = j
.tunnels
.remove(0)
.dest
.ok_or(utils::empty_string())
.unwrap_or(utils::empty_string());
return destination
return destination;
}
utils::empty_string()
}

View file

@ -9,19 +9,22 @@ pub mod models; // db structs
pub mod monero; // monero-wallet-rpc interface
pub mod proof; // external auth/payment proof module
pub mod reqres; // http request/responses
pub mod utils; // misc.
pub mod user; // user rep/service layer
pub mod user;
pub mod utils; // misc. // user rep/service layer
pub const NEVMES_JWP_SECRET_KEY: &str = "NEVMES_JWP_SECRET_KEY";
pub const NEVMES_JWT_SECRET_KEY: &str = "NEVMES_JWT_SECRET_KEY";
/// The latest monero release download
pub const MONERO_RELEASE_VERSION: &str = "monero-linux-x64-v0.18.2.2.tar.bz2";
pub const MONERO_RELEASE_HASH: &str = "186800de18f67cca8475ce392168aabeb5709a8f8058b0f7919d7c693786d56b";
pub const MONERO_RELEASE_HASH: &str =
"186800de18f67cca8475ce392168aabeb5709a8f8058b0f7919d7c693786d56b";
/// The latest i2p-zero release version
pub const I2P_ZERO_RELEASE_VERSION: &str = "v1.20";
pub const I2P_ZERO_RELEASH_HASH: &str = "7e7216b281624ec464b55217284017576d109eaba7b35f7e4994ae2a78634de7";
pub const I2P_ZERO_RELEASH_HASH: &str =
"7e7216b281624ec464b55217284017576d109eaba7b35f7e4994ae2a78634de7";
/// The latest i2pd release version
pub const I2P_RELEASE_VERSION: &str = "2.2.1";
pub const I2P_RELEASE_HASH: &str = "c9879b8f69ea13c758672c2fa083dc2e0abb289e0fc9a55af98f9f1795f82659";
pub const I2P_RELEASE_HASH: &str =
"c9879b8f69ea13c758672c2fa083dc2e0abb289e0fc9a55af98f9f1795f82659";
// DO NOT EDIT BELOW THIS LINE

View file

@ -1,9 +1,21 @@
// Message repo/service layer
use crate::{contact, db, models::*, utils, reqres, i2p, gpg};
use std::error::Error;
use log::{debug, error, info};
use crate::{
contact,
db,
gpg,
i2p,
models::*,
reqres,
utils,
};
use log::{
debug,
error,
info,
};
use reqwest::StatusCode;
use rocket::serde::json::Json;
use std::error::Error;
/// Create a new message
pub async fn create(m: Json<Message>, jwp: String) -> Message {
@ -12,8 +24,7 @@ pub async fn create(m: Json<Message>, jwp: String) -> Message {
let created = chrono::offset::Utc::now().timestamp();
// get contact public gpg key and encrypt the message
debug!("sending message: {:?}", &m);
let e_body = gpg::encrypt(
String::from(&m.to), &m.body).unwrap_or(Vec::new());
let e_body = gpg::encrypt(String::from(&m.to), &m.body).unwrap_or(Vec::new());
let new_message = Message {
mid: String::from(&f_mid),
uid: String::from(&m.uid),
@ -45,10 +56,14 @@ pub async fn create(m: Json<Message>, jwp: String) -> Message {
pub async fn rx(m: Json<Message>) {
// make sure the message isn't something strange
let is_valid = validate_message(&m);
if !is_valid { return; }
if !is_valid {
return;
}
// don't allow messages from outside the contact list
let is_in_contact_list = contact::exists(&m.from);
if !is_in_contact_list { return; }
if !is_in_contact_list {
return;
}
let f_mid: String = format!("m{}", utils::generate_rnd());
let new_message = Message {
mid: String::from(&f_mid),
@ -79,7 +94,7 @@ pub fn find(mid: &String) -> Message {
let r = db::Interface::read(&s.env, &s.handle, &String::from(mid));
if r == utils::empty_string() {
error!("message not found");
return Default::default()
return Default::default();
}
Message::from_db(String::from(mid), r)
}
@ -120,22 +135,28 @@ pub fn find_all() -> Vec<Message> {
/// Tx message
async fn send_message(out: &Message, jwp: &str) -> Result<(), Box<dyn Error>> {
let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build();
// check if the contact is online
let is_online: bool = is_contact_online(&out.to, String::from(jwp)).await.unwrap_or(false);
let is_online: bool = is_contact_online(&out.to, String::from(jwp))
.await
.unwrap_or(false);
if is_online {
return match client?.post(format!("http://{}/message/rx", out.to))
.header("proof", jwp).json(&out).send().await {
return match client?
.post(format!("http://{}/message/rx", out.to))
.header("proof", jwp)
.json(&out)
.send()
.await
{
Ok(response) => {
let status = response.status();
debug!("send response: {:?}", status.as_str());
if status == StatusCode::OK || status == StatusCode::PAYMENT_REQUIRED {
remove_from_fts(String::from(&out.mid));
return Ok(())
return Ok(());
} else {
Ok(())
}
@ -144,7 +165,7 @@ async fn send_message(out: &Message, jwp: &str) -> Result<(), Box<dyn Error>> {
error!("failed to send message due to: {:?}", e);
Ok(())
}
}
};
} else {
send_to_retry(String::from(&out.mid)).await;
Ok(())
@ -170,15 +191,23 @@ async fn is_contact_online(contact: &String, jwp: String) -> Result<bool, Box<dy
let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build();
match client?.get(format!("http://{}/xmr/rpc/version", contact))
.header("proof", jwp).send().await {
match client?
.get(format!("http://{}/xmr/rpc/version", contact))
.header("proof", jwp)
.send()
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcVersionResponse>().await;
debug!("check is contact online by version response: {:?}", res);
match res {
Ok(r) => {
if r.result.version != 0 { Ok(true) } else { Ok(false) }
},
if r.result.version != 0 {
Ok(true)
} else {
Ok(false)
}
}
_ => Ok(false),
}
}
@ -204,7 +233,10 @@ async fn send_to_retry(mid: String) {
if String::from(&r).contains(&String::from(&mid)) {
msg_list = r;
}
debug!("writing fts message index {} for id: {}", msg_list, list_key);
debug!(
"writing fts message index {} for id: {}",
msg_list, list_key
);
db::Interface::write(&s.env, &s.handle, &String::from(list_key), &msg_list);
// restart fts if not empty
let list_key = format!("fts");
@ -230,9 +262,20 @@ fn remove_from_fts(mid: String) {
debug!("fts is empty");
}
let pre_v_fts = r.split(",");
let v: Vec<String> = pre_v_fts.map(|s| if s != &mid { String::from(s)} else { utils::empty_string()} ).collect();
let v: Vec<String> = pre_v_fts
.map(|s| {
if s != &mid {
String::from(s)
} else {
utils::empty_string()
}
})
.collect();
let msg_list = v.join(",");
debug!("writing fts message index {} for id: {}", msg_list, list_key);
debug!(
"writing fts message index {} for id: {}",
msg_list, list_key
);
db::Interface::write(&s.env, &s.handle, &String::from(list_key), &msg_list);
}
@ -286,13 +329,12 @@ fn validate_message(j: &Json<Message>) -> bool {
j.mid.len() < utils::string_limit()
&& j.body.len() < utils::message_limit()
&& j.to == i2p::get_destination()
&& j.uid .len() < utils::string_limit()
&& j.uid.len() < utils::string_limit()
}
fn is_fts_clear(r: String) -> bool {
let v_mid = r.split(",");
let v: Vec<String> = v_mid.map(|s| String::from(s)).collect();
debug!("fts contents: {:#?}", v);
v.len() >= 2 && v[v.len()-1] == utils::empty_string()
&& v[0] == utils::empty_string()
v.len() >= 2 && v[v.len() - 1] == utils::empty_string() && v[0] == utils::empty_string()
}

View file

@ -1,5 +1,8 @@
use crate::utils;
use rocket::serde::{Serialize, Deserialize};
use rocket::serde::{
Deserialize,
Serialize,
};
#[derive(Debug, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
@ -161,7 +164,14 @@ impl Message {
};
let from = v.remove(0);
let to = v.remove(0);
Message { mid: k, uid, body, created, from, to, }
Message {
mid: k,
uid,
body,
created,
from,
to,
}
}
}

View file

@ -1,7 +1,20 @@
use crate::{args, reqres, utils::{self, get_release_env, ReleaseEnvironment}, proof};
use crate::{
args,
proof,
reqres,
utils::{
self,
get_release_env,
ReleaseEnvironment,
},
};
use clap::Parser;
use diqwest::WithDigestAuth;
use log::{debug, error, info};
use log::{
debug,
error,
info,
};
use std::process::Command;
/// Current xmr ring size updated here.
@ -88,7 +101,9 @@ pub enum LockTimeLimit {
impl LockTimeLimit {
pub fn value(&self) -> u64 {
match *self { LockTimeLimit::Blocks => 20, }
match *self {
LockTimeLimit::Blocks => 20,
}
}
}
@ -125,17 +140,22 @@ pub fn start_rpc() {
let login = get_rpc_creds();
let daemon_address = get_rpc_daemon();
let rpc_login = format!("{}:{}", &login.username, &login.credential);
let mut wallet_dir = format!("/home/{}/.nevmes/stagenet/wallet/",
let mut wallet_dir = format!(
"/home/{}/.nevmes/stagenet/wallet/",
std::env::var("USER").unwrap_or(String::from("user")),
);
let release_env = get_release_env();
if release_env == ReleaseEnvironment::Development {
let args = [
"--rpc-bind-port", &port,
"--wallet-dir", &wallet_dir,
"--rpc-login", &rpc_login,
"--daemon-address", &daemon_address,
"--stagenet"
"--rpc-bind-port",
&port,
"--wallet-dir",
&wallet_dir,
"--rpc-login",
&rpc_login,
"--daemon-address",
&daemon_address,
"--stagenet",
];
let output = Command::new(format!("{}/monero-wallet-rpc", bin_dir))
.args(args)
@ -143,11 +163,20 @@ pub fn start_rpc() {
.expect("monero-wallet-rpc failed to start");
debug!("{:?}", output.stdout);
} else {
wallet_dir = format!("/home/{}/.nevmes/wallet/",
wallet_dir = format!(
"/home/{}/.nevmes/wallet/",
std::env::var("USER").unwrap_or(String::from("user")),
);
let args = ["--rpc-bind-port", &port, "--wallet-dir", &wallet_dir,
"--rpc-login", &rpc_login, "--daemon-address", &daemon_address];
let args = [
"--rpc-bind-port",
&port,
"--wallet-dir",
&wallet_dir,
"--rpc-login",
&rpc_login,
"--daemon-address",
&daemon_address,
];
let output = Command::new(format!("{}/monero-wallet-rpc", bin_dir))
.args(args)
.spawn()
@ -190,7 +219,10 @@ fn get_rpc_creds() -> RpcLogin {
let args = args::Args::parse();
let username = String::from(args.monero_rpc_username);
let credential = String::from(args.monero_rpc_cred);
RpcLogin { username, credential }
RpcLogin {
username,
credential,
}
}
fn get_rpc_daemon() -> String {
@ -209,8 +241,12 @@ pub async fn get_version() -> reqres::XmrRpcVersionResponse {
method: RpcFields::GetVersion.value(),
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcVersionResponse>().await;
debug!("get version response: {:?}", res);
@ -248,8 +284,12 @@ pub async fn verify_signature(address: String, data: String, signature: String)
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcVerifyResponse>().await;
debug!("verify response: {:?}", res);
@ -284,8 +324,12 @@ pub async fn create_wallet(filename: String) -> bool {
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
// The result from wallet creation is empty
let res = response.text().await;
@ -296,11 +340,11 @@ pub async fn create_wallet(filename: String) -> bool {
return false;
}
true
},
}
_ => false,
}
}
Err(_) => false
Err(_) => false,
}
}
@ -309,9 +353,7 @@ pub async fn open_wallet(filename: String) -> bool {
info!("opening wallet for {}", &filename);
let client = reqwest::Client::new();
let host = get_rpc_host();
let params = reqres::XmrRpcOpenWalletParams {
filename,
};
let params = reqres::XmrRpcOpenWalletParams { filename };
let req = reqres::XmrRpcOpenRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(),
@ -320,8 +362,12 @@ pub async fn open_wallet(filename: String) -> bool {
};
debug!("open request: {:?}", req);
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
// The result from wallet operation is empty
let res = response.text().await;
@ -332,11 +378,11 @@ pub async fn open_wallet(filename: String) -> bool {
return false;
}
return true;
},
}
_ => false,
}
}
Err(_) => false
Err(_) => false,
}
}
@ -345,9 +391,7 @@ pub async fn close_wallet(filename: String) -> bool {
info!("closing wallet for {}", &filename);
let client = reqwest::Client::new();
let host = get_rpc_host();
let params = reqres::XmrRpcOpenWalletParams {
filename,
};
let params = reqres::XmrRpcOpenWalletParams { filename };
let req = reqres::XmrRpcOpenRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(),
@ -355,8 +399,12 @@ pub async fn close_wallet(filename: String) -> bool {
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
// The result from wallet operation is empty
let res = response.text().await;
@ -366,7 +414,7 @@ pub async fn close_wallet(filename: String) -> bool {
_ => false,
}
}
Err(_) => false
Err(_) => false,
}
}
@ -376,7 +424,10 @@ pub async fn get_balance() -> reqres::XmrRpcBalanceResponse {
let client = reqwest::Client::new();
let host = get_rpc_host();
let params: reqres::XmrRpcBalanceParams = reqres::XmrRpcBalanceParams {
account_index: 0, address_indices: vec![0], all_accounts: false, strict: false,
account_index: 0,
address_indices: vec![0],
all_accounts: false,
strict: false,
};
let req = reqres::XmrRpcBalanceRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(),
@ -385,8 +436,12 @@ pub async fn get_balance() -> reqres::XmrRpcBalanceResponse {
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcBalanceResponse>().await;
debug!("balance response: {:?}", res);
@ -395,7 +450,7 @@ pub async fn get_balance() -> reqres::XmrRpcBalanceResponse {
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -404,9 +459,7 @@ pub async fn get_address() -> reqres::XmrRpcAddressResponse {
info!("fetching wallet address");
let client = reqwest::Client::new();
let host = get_rpc_host();
let params: reqres::XmrRpcAddressParams = reqres::XmrRpcAddressParams {
account_index: 0,
};
let params: reqres::XmrRpcAddressParams = reqres::XmrRpcAddressParams { account_index: 0 };
let req = reqres::XmrRpcAddressRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(),
@ -414,8 +467,12 @@ pub async fn get_address() -> reqres::XmrRpcAddressResponse {
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcAddressResponse>().await;
debug!("address response: {:?}", res);
@ -424,7 +481,7 @@ pub async fn get_address() -> reqres::XmrRpcAddressResponse {
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -434,7 +491,9 @@ pub async fn validate_address(address: &String) -> reqres::XmrRpcValidateAddress
let client = reqwest::Client::new();
let host = get_rpc_host();
let params: reqres::XmrRpcValidateAddressParams = reqres::XmrRpcValidateAddressParams {
address: String::from(address), any_net_type: false, allow_openalias: true,
address: String::from(address),
any_net_type: false,
allow_openalias: true,
};
let req = reqres::XmrRpcValidateAddressRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(),
@ -443,17 +502,23 @@ pub async fn validate_address(address: &String) -> reqres::XmrRpcValidateAddress
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcValidateAddressResponse>().await;
let res = response
.json::<reqres::XmrRpcValidateAddressResponse>()
.await;
debug!("validate_address response: {:?}", res);
match res {
Ok(res) => res,
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
// START Multisig
@ -469,8 +534,12 @@ pub async fn prepare_wallet() -> reqres::XmrRpcPrepareResponse {
method: RpcFields::Prepare.value(),
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcPrepareResponse>().await;
debug!("prepare response: {:?}", res);
@ -479,7 +548,7 @@ pub async fn prepare_wallet() -> reqres::XmrRpcPrepareResponse {
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -499,8 +568,12 @@ pub async fn make_wallet(info: Vec<String>) -> reqres::XmrRpcMakeResponse {
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcMakeResponse>().await;
debug!("make response: {:?}", res);
@ -509,7 +582,7 @@ pub async fn make_wallet(info: Vec<String>) -> reqres::XmrRpcMakeResponse {
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -528,8 +601,12 @@ pub async fn finalize_wallet(info: Vec<String>) -> reqres::XmrRpcFinalizeRespons
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcFinalizeResponse>().await;
debug!("finalize response: {:?}", res);
@ -538,7 +615,7 @@ pub async fn finalize_wallet(info: Vec<String>) -> reqres::XmrRpcFinalizeRespons
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -553,8 +630,12 @@ pub async fn export_multisig_info() -> reqres::XmrRpcExportResponse {
method: RpcFields::Export.value(),
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcExportResponse>().await;
debug!("export msig response: {:?}", res);
@ -563,7 +644,7 @@ pub async fn export_multisig_info() -> reqres::XmrRpcExportResponse {
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -572,9 +653,7 @@ pub async fn import_multisig_info(info: Vec<String>) -> reqres::XmrRpcImportResp
info!("import msig wallet");
let client = reqwest::Client::new();
let host = get_rpc_host();
let params = reqres::XmrRpcImportParams {
info,
};
let params = reqres::XmrRpcImportParams { info };
let req = reqres::XmrRpcImportRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(),
@ -582,8 +661,12 @@ pub async fn import_multisig_info(info: Vec<String>) -> reqres::XmrRpcImportResp
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcImportResponse>().await;
debug!("import msig info response: {:?}", res);
@ -592,7 +675,7 @@ pub async fn import_multisig_info(info: Vec<String>) -> reqres::XmrRpcImportResp
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -601,9 +684,7 @@ pub async fn sign_multisig(tx_data_hex: String) -> reqres::XmrRpcSignMultisigRes
info!("sign msig txset");
let client = reqwest::Client::new();
let host = get_rpc_host();
let params = reqres::XmrRpcSignMultisigParams {
tx_data_hex,
};
let params = reqres::XmrRpcSignMultisigParams { tx_data_hex };
let req = reqres::XmrRpcSignMultisigRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(),
@ -611,8 +692,12 @@ pub async fn sign_multisig(tx_data_hex: String) -> reqres::XmrRpcSignMultisigRes
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcSignMultisigResponse>().await;
debug!("sign msig txset response: {:?}", res);
@ -621,7 +706,7 @@ pub async fn sign_multisig(tx_data_hex: String) -> reqres::XmrRpcSignMultisigRes
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
// END Multisig
@ -644,8 +729,12 @@ pub async fn check_tx_proof(txp: &proof::TxProof) -> reqres::XmrRpcCheckTxProofR
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcCheckTxProofResponse>().await;
debug!("check_tx_proof response: {:?}", res);
@ -654,7 +743,7 @@ pub async fn check_tx_proof(txp: &proof::TxProof) -> reqres::XmrRpcCheckTxProofR
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -675,8 +764,12 @@ pub async fn get_tx_proof(ptxp: proof::TxProof) -> reqres::XmrRpcGetTxProofRespo
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcGetTxProofResponse>().await;
debug!("get_tx_proof response: {:?}", res);
@ -685,7 +778,7 @@ pub async fn get_tx_proof(ptxp: proof::TxProof) -> reqres::XmrRpcGetTxProofRespo
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -695,7 +788,7 @@ pub async fn get_transfer_by_txid(txid: &str) -> reqres::XmrRpcGetTxByIdResponse
let client = reqwest::Client::new();
let host = get_rpc_host();
let params: reqres::XmrRpcGetTxByIdParams = reqres::XmrRpcGetTxByIdParams {
txid: String::from(txid)
txid: String::from(txid),
};
let req = reqres::XmrRpcGetTxByIdRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(),
@ -704,8 +797,12 @@ pub async fn get_transfer_by_txid(txid: &str) -> reqres::XmrRpcGetTxByIdResponse
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcGetTxByIdResponse>().await;
debug!("get_transfer_by_txid response: {:?}", res);
@ -714,7 +811,7 @@ pub async fn get_transfer_by_txid(txid: &str) -> reqres::XmrRpcGetTxByIdResponse
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -740,8 +837,12 @@ pub async fn transfer(d: reqres::Destination) -> reqres::XmrRpcTransferResponse
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcTransferResponse>().await;
debug!("{} response: {:?}", RpcFields::Transfer.value(), res);
@ -750,7 +851,7 @@ pub async fn transfer(d: reqres::Destination) -> reqres::XmrRpcTransferResponse
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -767,8 +868,12 @@ pub async fn sweep_all(address: String) -> reqres::XmrRpcSweepAllResponse {
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcSweepAllResponse>().await;
debug!("{} response: {:?}", RpcFields::SweepAll.value(), res);
@ -777,7 +882,7 @@ pub async fn sweep_all(address: String) -> reqres::XmrRpcSweepAllResponse {
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -786,7 +891,8 @@ pub async fn create_address() -> reqres::XmrRpcCreateAddressResponse {
info!("creating new subaddress");
let client = reqwest::Client::new();
let host = get_rpc_host();
let params: reqres::XmrRpcCreateAddressParams = reqres::XmrRpcCreateAddressParams { account_index: 0 };
let params: reqres::XmrRpcCreateAddressParams =
reqres::XmrRpcCreateAddressParams { account_index: 0 };
let req = reqres::XmrRpcCreateAddressRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(),
@ -794,8 +900,12 @@ pub async fn create_address() -> reqres::XmrRpcCreateAddressResponse {
params,
};
let login: RpcLogin = get_rpc_creds();
match client.post(host).json(&req)
.send_with_digest_auth(&login.username, &login.credential).await {
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcCreateAddressResponse>().await;
debug!("{} response: {:?}", RpcFields::CreateAddress.value(), res);
@ -804,7 +914,7 @@ pub async fn create_address() -> reqres::XmrRpcCreateAddressResponse {
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}
@ -829,6 +939,6 @@ pub async fn get_info() -> reqres::XmrDaemonGetInfoResponse {
_ => Default::default(),
}
}
Err(_) => Default::default()
Err(_) => Default::default(),
}
}

View file

@ -1,14 +1,31 @@
use crate::{db, monero, reqres, utils};
use log::{error, info};
use crate::{
db,
monero,
reqres,
utils,
};
use log::{
error,
info,
};
use rocket::{
http::Status,
outcome::Outcome,
request,
request::FromRequest,
Request,
};
use std::error::Error;
use rocket::http::Status;
use rocket::outcome::Outcome;
use rocket::request::FromRequest;
use rocket::{request, Request};
use hmac::{Hmac, Mac};
use hmac::{
Hmac,
Mac,
};
use jwt::*;
use serde::{Deserialize, Serialize};
use serde::{
Deserialize,
Serialize,
};
use sha2::Sha512;
use std::collections::BTreeMap;
@ -43,7 +60,11 @@ pub async fn create_invoice() -> reqres::Invoice {
let address = c_address.result.address;
let pay_threshold = utils::get_payment_threshold();
let conf_threshold = utils::get_conf_threshold();
reqres::Invoice { address, conf_threshold, pay_threshold }
reqres::Invoice {
address,
conf_threshold,
pay_threshold,
}
}
/// Technically the same process as creating a JWT
@ -94,7 +115,12 @@ pub async fn prove_payment(contact: String, txp: &TxProof) -> Result<reqres::Jwp
let host = utils::get_i2p_http_proxy();
let proxy = reqwest::Proxy::http(&host)?;
let client = reqwest::Client::builder().proxy(proxy).build();
match client?.post(format!("http://{}/prove", contact)).json(txp).send().await {
match client?
.post(format!("http://{}/prove", contact))
.json(txp)
.send()
.await
{
Ok(response) => {
let res = response.json::<reqres::Jwp>().await;
log::debug!("prove payment response: {:?}", res);
@ -106,7 +132,7 @@ pub async fn prove_payment(contact: String, txp: &TxProof) -> Result<reqres::Jwp
db::Interface::delete(&s.env, &s.handle, &k);
db::Interface::write(&s.env, &s.handle, &k, &r.jwp);
Ok(r)
},
}
_ => Ok(Default::default()),
}
}
@ -137,7 +163,11 @@ pub async fn prove_payment(contact: String, txp: &TxProof) -> Result<reqres::Jwp
#[derive(Debug)]
pub struct PaymentProof(String);
impl PaymentProof { pub fn get_jwp(self) -> String { self.0 } }
impl PaymentProof {
pub fn get_jwp(self) -> String {
self.0
}
}
#[derive(Debug)]
pub enum PaymentProofError {
@ -205,7 +235,10 @@ impl<'r> FromRequest<'r> for PaymentProof {
}
Err(e) => {
error!("jwp error: {:?}", e);
return Outcome::Failure((Status::PaymentRequired, PaymentProofError::Invalid));
return Outcome::Failure((
Status::PaymentRequired,
PaymentProofError::Invalid,
));
}
};
}
@ -224,17 +257,19 @@ async fn validate_proof(txp: &TxProof) -> TxProof {
let p = monero::check_tx_proof(txp).await;
let cth = utils::get_conf_threshold();
let pth = utils::get_payment_threshold();
let lgtm = p.result.good && !p.result.in_pool
let lgtm = p.result.good
&& !p.result.in_pool
&& unlock_time < monero::LockTimeLimit::Blocks.value()
&& p.result.confirmations < cth && p.result.received >= pth;
&& p.result.confirmations < cth
&& p.result.received >= pth;
if lgtm {
return TxProof {
subaddress: String::from(&txp.subaddress),
hash: String::from(&txp.hash),
confirmations: p.result.confirmations,
message: String::from(&txp.message),
signature: String::from(&txp.signature)
}
signature: String::from(&txp.signature),
};
}
Default::default()
}
@ -248,6 +283,8 @@ async fn validate_subaddress(subaddress: &String) -> bool {
let m_address = monero::get_address().await;
let all_address = m_address.result.addresses;
let mut address_list: Vec<String> = Vec::new();
for s_address in all_address { address_list.push(s_address.address); }
for s_address in all_address {
address_list.push(s_address.address);
}
return address_list.contains(&subaddress);
}

View file

@ -1,5 +1,8 @@
use crate::utils;
use serde::{Deserialize, Serialize};
use serde::{
Deserialize,
Serialize,
};
// All http requests and responses are here
@ -363,10 +366,7 @@ pub struct SubAddressIndex {
impl Default for SubAddressIndex {
fn default() -> Self {
SubAddressIndex {
major: 0,
minor: 0,
}
SubAddressIndex { major: 0, minor: 0 }
}
}
@ -820,13 +820,12 @@ impl Default for XmrRpcCreateAddressResponse {
address_index: 0,
address_indices: Vec::new(),
addresses: Vec::new(),
}
},
}
}
}
// END XMR Structs
/// Container for the message decryption
#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]

View file

@ -1,7 +1,15 @@
// User repo/service layer
use crate::{db, models::*, utils};
use crate::{
db,
models::*,
utils,
};
use log::{
debug,
error,
info,
};
use rocket::serde::json::Json;
use log::{debug, error, info};
// This module is only used for remote access
@ -26,7 +34,7 @@ pub fn find(uid: &String) -> User {
let r = db::Interface::read(&s.env, &s.handle, &String::from(uid));
if r == utils::empty_string() {
error!("user not found");
return Default::default()
return Default::default();
}
User::from_db(String::from(uid), r)
}

View file

@ -1,13 +1,32 @@
use rand_core::RngCore;
use crate::{
args,
db,
gpg,
i2p,
message,
models,
monero,
reqres,
utils,
};
use clap::Parser;
use log::{
debug,
error,
info,
warn,
};
use rand_core::RngCore;
use rocket::serde::json::Json;
use crate::{args, db, i2p, message, models, monero, gpg, utils, reqres };
use log::{info, debug, error, warn};
use std::time::Duration;
/// Enum for selecting hash validation
#[derive(PartialEq)]
enum ExternalSoftware { I2P, I2PZero, XMR }
enum ExternalSoftware {
I2P,
I2PZero,
XMR,
}
/// Handles the state for the installation manager popup
pub struct Installations {
@ -87,14 +106,22 @@ impl ReleaseEnvironment {
pub fn start_core(conn: &Connections) {
let env = if !conn.mainnet { "dev" } else { "prod" };
let args = [
"--monero-location", &conn.monero_location,
"--monero-blockchain-dir", &conn.blockchain_dir,
"--monero-rpc-host", &conn.rpc_host,
"--monero-rpc-daemon", &conn.daemon_host,
"--monero-rpc-username", &conn.rpc_username,
"--monero-rpc-cred", &conn.rpc_credential,
"--i2p-zero-dir", &conn.i2p_zero_dir,
"-r", env,
"--monero-location",
&conn.monero_location,
"--monero-blockchain-dir",
&conn.blockchain_dir,
"--monero-rpc-host",
&conn.rpc_host,
"--monero-rpc-daemon",
&conn.daemon_host,
"--monero-rpc-username",
&conn.rpc_username,
"--monero-rpc-cred",
&conn.rpc_credential,
"--i2p-zero-dir",
&conn.i2p_zero_dir,
"-r",
env,
];
let output = std::process::Command::new("./nevmes")
.args(args)
@ -107,7 +134,9 @@ pub fn start_core(conn: &Connections) {
pub fn is_using_remote_node() -> bool {
let args = args::Args::parse();
let r = args.remote_node;
if r { warn!("using a remote node may harm privacy"); }
if r {
warn!("using a remote node may harm privacy");
}
r
}
@ -196,12 +225,20 @@ pub fn message_to_json(m: &models::Message) -> Json<models::Message> {
}
/// Instead of putting `String::from("")`
pub fn empty_string() -> String { String::from("") }
pub fn empty_string() -> String {
String::from("")
}
// DoS prevention
pub const fn string_limit() -> usize { 512 }
pub const fn gpg_key_limit() -> usize { 4096 }
pub const fn message_limit() -> usize { 9999 }
pub const fn string_limit() -> usize {
512
}
pub const fn gpg_key_limit() -> usize {
4096
}
pub const fn message_limit() -> usize {
9999
}
/// Generate application gpg keys at startup if none exist
async fn gen_app_gpg() {
@ -219,15 +256,19 @@ async fn gen_app_gpg() {
/// Handles panic! for missing wallet directory
fn create_wallet_dir() {
let file_path = format!("/home/{}/.nevmes",
std::env::var("USER").unwrap_or(String::from("user")));
let file_path = format!(
"/home/{}/.nevmes",
std::env::var("USER").unwrap_or(String::from("user"))
);
let s_output = std::process::Command::new("mkdir")
.args(["-p", &format!("{}/stagenet/wallet", file_path)])
.spawn().expect("failed to create dir");
.spawn()
.expect("failed to create dir");
debug!("{:?}", s_output);
let m_output = std::process::Command::new("mkdir")
.args(["-p", &format!("{}/wallet", file_path)])
.spawn().expect("failed to create dir");
.spawn()
.expect("failed to create dir");
debug!("{:?}", m_output);
}
@ -243,8 +284,7 @@ async fn gen_app_wallet() {
} else {
m_wallet = monero::open_wallet(String::from(filename)).await;
if m_wallet {
let m_address: reqres::XmrRpcAddressResponse =
monero::get_address().await;
let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await;
info!("app wallet address: {}", m_address.result.address)
}
}
@ -282,7 +322,7 @@ pub fn get_jwt_secret_key() -> String {
let r = db::Interface::read(&s.env, &s.handle, crate::NEVMES_JWT_SECRET_KEY);
if r == utils::empty_string() {
error!("JWT key not found");
return Default::default()
return Default::default();
}
r
}
@ -292,7 +332,7 @@ pub fn get_jwp_secret_key() -> String {
let r = db::Interface::read(&s.env, &s.handle, crate::NEVMES_JWP_SECRET_KEY);
if r == utils::empty_string() {
error!("JWP key not found");
return Default::default()
return Default::default();
}
r
}
@ -302,21 +342,30 @@ fn start_micro_servers() {
info!("starting auth server");
let mut auth_path = "nevmes-auth/target/debug/nevmes_auth";
let env = get_release_env();
if env == ReleaseEnvironment::Production { auth_path = "nevmes_auth"; }
if env == ReleaseEnvironment::Production {
auth_path = "nevmes_auth";
}
let a_output = std::process::Command::new(auth_path)
.spawn().expect("failed to start auth server");
.spawn()
.expect("failed to start auth server");
debug!("{:?}", a_output.stdout);
info!("starting contact server");
let mut contact_path = "nevmes-contact/target/debug/nevmes_contact";
if env == ReleaseEnvironment::Production { contact_path = "nevmes_contact"; }
if env == ReleaseEnvironment::Production {
contact_path = "nevmes_contact";
}
let c_output = std::process::Command::new(contact_path)
.spawn().expect("failed to start contact server");
.spawn()
.expect("failed to start contact server");
debug!("{:?}", c_output.stdout);
info!("starting message server");
let mut message_path = "nevmes-message/target/debug/nevmes_message";
if env == ReleaseEnvironment::Production { message_path = "nevmes_message"; }
if env == ReleaseEnvironment::Production {
message_path = "nevmes_message";
}
let m_output = std::process::Command::new(message_path)
.spawn().expect("failed to start message server");
.spawn()
.expect("failed to start message server");
debug!("{:?}", m_output.stdout);
}
@ -327,9 +376,12 @@ fn start_gui() {
info!("starting gui");
let mut gui_path = "nevmes-gui/target/debug/nevmes_gui";
let env = get_release_env();
if env == ReleaseEnvironment::Production { gui_path = "nevmes-gui"; }
if env == ReleaseEnvironment::Production {
gui_path = "nevmes-gui";
}
let g_output = std::process::Command::new(gui_path)
.spawn().expect("failed to start gui");
.spawn()
.expect("failed to start gui");
debug!("{:?}", g_output.stdout);
}
}
@ -338,10 +390,16 @@ fn start_gui() {
pub async fn start_up() {
info!("nevmes is starting up");
let args = args::Args::parse();
if args.remote_access { start_micro_servers(); }
if args.clear_fts { clear_fts(); }
if args.remote_access {
start_micro_servers();
}
if args.clear_fts {
clear_fts();
}
gen_signing_keys();
if !is_using_remote_node() { monero::start_daemon(); }
if !is_using_remote_node() {
monero::start_daemon();
}
create_wallet_dir();
// wait for daemon for a bit
tokio::time::sleep(std::time::Duration::new(5, 0)).await;
@ -354,7 +412,11 @@ pub async fn start_up() {
gen_app_gpg().await;
let env: String = get_release_env().value();
start_gui();
{ tokio::spawn(async { message::retry_fts().await; }); }
{
tokio::spawn(async {
message::retry_fts().await;
});
}
info!("{} - nevmes is online", env);
}
@ -393,7 +455,9 @@ pub fn kill_child_processes(cm: bool) {
/// We can restart fts from since it gets terminated when empty
pub fn restart_retry_fts() {
tokio::spawn(async move { message::retry_fts().await; });
tokio::spawn(async move {
message::retry_fts().await;
});
}
/// Called on app startup if `--clear-fts` flag is passed.
@ -428,7 +492,10 @@ pub async fn install_software(installations: Installations) -> bool {
info!("installing i2p");
let i2p_version = crate::I2P_RELEASE_VERSION;
let i2p_jar = format!("i2pinstall_{}.jar", i2p_version);
let link = format!("https://download.i2p2.no/releases/{}/{}", i2p_version, i2p_jar);
let link = format!(
"https://download.i2p2.no/releases/{}/{}",
i2p_version, i2p_jar
);
let curl = std::process::Command::new("curl")
.args(["-O#", &link])
.status();
@ -441,8 +508,8 @@ pub async fn install_software(installations: Installations) -> bool {
.spawn()
.expect("i2p gui installation failed");
debug!("{:?}", jar_output.stdout);
},
_=> error!("i2p download failed")
}
_ => error!("i2p download failed"),
}
valid_i2p_hash = validate_installation_hash(ExternalSoftware::I2P, &i2p_jar);
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
@ -451,8 +518,10 @@ pub async fn install_software(installations: Installations) -> bool {
info!("installing i2p-zero");
let i2p_version = crate::I2P_ZERO_RELEASE_VERSION;
let i2p_zero_zip = format!("i2p-zero-linux.{}.zip", i2p_version);
let link = format!("https://github.com/i2p-zero/i2p-zero/releases/download/{}/{}",
i2p_version, i2p_zero_zip);
let link = format!(
"https://github.com/i2p-zero/i2p-zero/releases/download/{}/{}",
i2p_version, i2p_zero_zip
);
let curl = std::process::Command::new("curl")
.args(["-LO#", &link])
.status();
@ -465,15 +534,18 @@ pub async fn install_software(installations: Installations) -> bool {
.spawn()
.expect("i2p unzip failed");
debug!("{:?}", unzip_output.stdout);
},
_=> error!("i2p-zero download failed")
}
_ => error!("i2p-zero download failed"),
}
valid_i2p_zero_hash = validate_installation_hash(ExternalSoftware::I2PZero, &i2p_zero_zip);
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
if installations.xmr {
info!("installing monero");
let link = format!("https://downloads.getmonero.org/cli/{}", crate::MONERO_RELEASE_VERSION);
let link = format!(
"https://downloads.getmonero.org/cli/{}",
crate::MONERO_RELEASE_VERSION
);
let curl = std::process::Command::new("curl")
.args(["-O#", &link])
.status();
@ -486,10 +558,13 @@ pub async fn install_software(installations: Installations) -> bool {
.spawn()
.expect("monero tar extraction failed");
debug!("{:?}", tar_output.stdout);
},
_=> error!("monero download failed")
}
valid_xmr_hash = validate_installation_hash(ExternalSoftware::XMR, &String::from(crate::MONERO_RELEASE_VERSION));
_ => error!("monero download failed"),
}
valid_xmr_hash = validate_installation_hash(
ExternalSoftware::XMR,
&String::from(crate::MONERO_RELEASE_VERSION),
);
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
valid_i2p_hash && valid_i2p_zero_hash && valid_xmr_hash
@ -506,7 +581,9 @@ fn validate_installation_hash(sw: ExternalSoftware, filename: &String) -> bool {
String::from(crate::MONERO_RELEASE_HASH)
};
let sha_output = std::process::Command::new("sha256sum")
.arg(filename).output().expect("hash validation failed");
.arg(filename)
.output()
.expect("hash validation failed");
let str_sha = String::from_utf8(sha_output.stdout).unwrap();
let split1 = str_sha.split(" ");
let mut v: Vec<String> = split1.map(|s| String::from(s)).collect();

6
nevmes-gui/.rustfmt.toml Normal file
View file

@ -0,0 +1,6 @@
# cargo +nightly fmt
comment_width = 100
format_code_in_doc_comments = true
imports_granularity = "Crate"
imports_layout = "Vertical"
wrap_comments = true

View file

@ -1,7 +1,13 @@
use nevmes_core::*;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::mpsc::{
Receiver,
Sender,
};
use crate::{ADD_CONTACT_TIMEOUT_SECS, BLOCK_TIME_IN_SECS_EST};
use crate::{
ADD_CONTACT_TIMEOUT_SECS,
BLOCK_TIME_IN_SECS_EST,
};
// TODO(c2m): better error handling with and error_tx/error_rx channel
// hook into the error thread and show toast messages as required
@ -454,7 +460,10 @@ impl eframe::App for AddressBookApp {
.labelled_by(find_contact_label.id);
});
ui.label("\n");
use egui_extras::{Column, TableBuilder};
use egui_extras::{
Column,
TableBuilder,
};
let table = TableBuilder::new(ui)
.striped(true)
.resizable(true)
@ -679,7 +688,10 @@ fn send_payment_req(
if check_txp.result.good && check_txp.result.confirmations > 0 {
break;
}
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_TIME_IN_SECS_EST as u64)).await;
tokio::time::sleep(std::time::Duration::from_secs(
BLOCK_TIME_IN_SECS_EST as u64,
))
.await;
retry_count += 1;
}
write_gui_db(

View file

@ -3,8 +3,13 @@
use eframe::egui;
use nevmes_core::*;
use std::sync::mpsc::{Receiver, Sender};
use std::time::Duration;
use std::{
sync::mpsc::{
Receiver,
Sender,
},
time::Duration,
};
pub struct HomeApp {
connections: utils::Connections,
@ -73,9 +78,11 @@ impl Default for HomeApp {
let s_i2p_status = false;
let s_can_refresh = false;
let c_xmr_logo = std::fs::read("./assets/xmr.png").unwrap_or(Vec::new());
let logo_xmr = egui_extras::RetainedImage::from_image_bytes("./assets/xmr.png", &c_xmr_logo).unwrap();
let logo_xmr =
egui_extras::RetainedImage::from_image_bytes("./assets/xmr.png", &c_xmr_logo).unwrap();
let c_i2p_logo = std::fs::read("./assets/i2p.png").unwrap_or(Vec::new());
let logo_i2p = egui_extras::RetainedImage::from_image_bytes("./assets/i2p.png", &c_i2p_logo).unwrap();
let logo_i2p =
egui_extras::RetainedImage::from_image_bytes("./assets/i2p.png", &c_i2p_logo).unwrap();
Self {
connections,
core_timeout_rx,
@ -140,7 +147,9 @@ impl eframe::App for HomeApp {
}
if let Ok(install) = self.installation_rx.try_recv() {
self.is_installing = !install;
if !install && self.is_loading { self.has_install_failed = true }
if !install && self.is_loading {
self.has_install_failed = true
}
self.is_loading = false;
}
if let Ok(timeout) = self.core_timeout_rx.try_recv() {
@ -251,7 +260,11 @@ impl eframe::App for HomeApp {
if !self.is_loading {
if ui.button("Install").clicked() {
self.is_loading = true;
install_software_req(self.installation_tx.clone(), ctx.clone(), &self.installations);
install_software_req(
self.installation_tx.clone(),
ctx.clone(),
&self.installations,
);
}
}
}
@ -389,18 +402,23 @@ fn send_reset_refresh(tx: Sender<bool>, ctx: egui::Context, init: bool) {
});
}
fn start_core_timeout
(tx: Sender<bool>, ctx: egui::Context) {
fn start_core_timeout(tx: Sender<bool>, ctx: egui::Context) {
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_secs(crate::START_CORE_TIMEOUT_SECS)).await;
tokio::time::sleep(std::time::Duration::from_secs(
crate::START_CORE_TIMEOUT_SECS,
))
.await;
log::error!("start nevmes-core timeout");
let _ = tx.send(true);
ctx.request_repaint();
});
}
fn install_software_req
(tx: Sender<bool>, ctx: egui::Context, installations: &utils::Installations) {
fn install_software_req(
tx: Sender<bool>,
ctx: egui::Context,
installations: &utils::Installations,
) {
let req_install: utils::Installations = utils::Installations {
i2p: installations.i2p,
i2p_zero: installations.i2p_zero,

View file

@ -1,6 +1,9 @@
use crate::CREDENTIAL_KEY;
use nevmes_core::*;
use sha2::{Digest, Sha512};
use sha2::{
Digest,
Sha512,
};
/// TODO(c2m): Create a more secure locking mechanism
///

View file

@ -1,5 +1,8 @@
use nevmes_core::*;
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc::{
Receiver,
Sender,
};
pub struct MailBoxApp {
decrypted_message: String,
@ -26,11 +29,12 @@ impl Default for MailBoxApp {
impl eframe::App for MailBoxApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// Hook into async channel threads
//-----------------------------------------------------------------------------------
if let Ok(refresh) = self.refresh_on_delete_rx.try_recv() {
if refresh { self.message_init = false; }
if refresh {
self.message_init = false;
}
}
// initial message load
@ -59,7 +63,10 @@ impl eframe::App for MailBoxApp {
self.messages = message::find_all();
}
ui.label("\n");
use egui_extras::{Column, TableBuilder};
use egui_extras::{
Column,
TableBuilder,
};
let table = TableBuilder::new(ui)
.striped(true)
@ -90,12 +97,14 @@ impl eframe::App for MailBoxApp {
ui.strong("");
});
})
.body(|mut body|
.body(|mut body| {
for m in &self.messages {
let row_height = 200.0;
body.row(row_height, |mut row| {
row.col(|ui| {
let h = chrono::NaiveDateTime::from_timestamp_opt(m.created, 0).unwrap().to_string();
let h = chrono::NaiveDateTime::from_timestamp_opt(m.created, 0)
.unwrap()
.to_string();
ui.label(format!("{}", h));
});
row.col(|ui| {
@ -105,8 +114,10 @@ impl eframe::App for MailBoxApp {
ui.label(format!("{}", m.to));
});
row.col(|ui| {
ui.label(format!("{}",
String::from_utf8(m.body.iter().cloned().collect()).unwrap()));
ui.label(format!(
"{}",
String::from_utf8(m.body.iter().cloned().collect()).unwrap()
));
});
row.col(|ui| {
ui.style_mut().wrap = Some(false);
@ -121,23 +132,30 @@ impl eframe::App for MailBoxApp {
self.is_showing_decryption = true;
d = Default::default();
bytes = Vec::new();
log::debug!("cleared decryption bytes: {:?} string: {}", bytes, d.body);
log::debug!(
"cleared decryption bytes: {:?} string: {}",
bytes,
d.body
);
}
}
if ui.button("Delete").clicked() {
message::delete(&m.mid);
refresh_on_delete_req(self.refresh_on_delete_tx.clone(), ctx.clone())
refresh_on_delete_req(
self.refresh_on_delete_tx.clone(),
ctx.clone(),
)
}
});
});
});
}
});
});
}
}
fn refresh_on_delete_req
(tx: Sender<bool>, ctx: egui::Context) {
fn refresh_on_delete_req(tx: Sender<bool>, ctx: egui::Context) {
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
log::error!("refreshing messages....");

View file

@ -1,14 +1,13 @@
mod home;
mod address_book;
mod home;
mod lock_screen;
mod mailbox;
mod settings;
mod wallet;
pub use address_book::AddressBookApp;
pub use lock_screen::LockScreenApp;
pub use home::HomeApp;
pub use lock_screen::LockScreenApp;
pub use mailbox::MailBoxApp;
pub use settings::SettingsApp;
pub use wallet::WalletApp;

View file

@ -1,5 +1,8 @@
use nevmes_core::*;
use sha2::{Digest, Sha512};
use sha2::{
Digest,
Sha512,
};
use crate::CREDENTIAL_KEY;

View file

@ -1,7 +1,10 @@
use nevmes_core::*;
use image::Luma;
use nevmes_core::*;
use qrcode::QrCode;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::mpsc::{
Receiver,
Sender,
};
pub struct WalletApp {
pub init: bool,
@ -90,8 +93,8 @@ impl eframe::App for WalletApp {
self.init = true;
self.is_qr_set = true;
let contents = std::fs::read(&file_path).unwrap_or(Vec::new());
self.qr = egui_extras::RetainedImage::from_image_bytes(
"qr.png", &contents,).unwrap();
self.qr =
egui_extras::RetainedImage::from_image_bytes("qr.png", &contents).unwrap();
ctx.request_repaint();
}
self.qr.show(ui);
@ -120,7 +123,11 @@ impl eframe::App for WalletApp {
ui.text_edit_singleline(&mut self.sweep_address)
.labelled_by(sweep_label.id);
if ui.button("Sweep").clicked() {
send_sweep_all_req(self.xmr_sweep_all_tx.clone(), ctx.clone(), self.sweep_address.clone());
send_sweep_all_req(
self.xmr_sweep_all_tx.clone(),
ctx.clone(),
self.sweep_address.clone(),
);
self.sweep_address = utils::empty_string();
self.is_showing_sweep_result = true;
self.is_loading = true;
@ -138,7 +145,11 @@ fn send_address_req(tx: Sender<reqres::XmrRpcAddressResponse>, ctx: egui::Contex
});
}
fn send_sweep_all_req(tx: Sender<reqres::XmrRpcSweepAllResponse>, ctx: egui::Context, address: String) {
fn send_sweep_all_req(
tx: Sender<reqres::XmrRpcSweepAllResponse>,
ctx: egui::Context,
address: String,
) {
tokio::spawn(async move {
let result: reqres::XmrRpcSweepAllResponse = monero::sweep_all(address).await;
let _ = tx.send(result);

View file

@ -8,7 +8,7 @@ mod wrap_app;
/// key for fetching the login credential hash
pub const CREDENTIAL_KEY: &str = "NEVMES_GUI_KEY";
/// TODO(c2m): configurable lock screen timeout
pub const LOCK_SCREEN_TIMEOUT_SECS: u64 = 60*5;
pub const LOCK_SCREEN_TIMEOUT_SECS: u64 = 60 * 5;
/// interval to search for credential on initial gui load
pub const CRED_CHECK_INTERVAL: u64 = 5;
/// monero estimated block time in seconds

View file

@ -1,6 +1,9 @@
use nevmes_core::*;
use sha2::{Sha512, Digest};
use crate::CREDENTIAL_KEY;
use nevmes_core::*;
use sha2::{
Digest,
Sha512,
};
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct LoginApp {
@ -12,7 +15,10 @@ impl Default for LoginApp {
fn default() -> Self {
let credential = utils::empty_string();
let is_cred_generated = false;
LoginApp { credential, is_cred_generated }
LoginApp {
credential,
is_cred_generated,
}
}
}
@ -44,4 +50,3 @@ impl eframe::App for LoginApp {
});
}
}

View file

@ -32,7 +32,15 @@ fn main() -> Result<(), eframe::Error> {
// Execute the runtime in its own thread.
// The future doesn't have to do anything. In this example, it just sleeps forever.
std::thread::spawn(move || {
rt.block_on(async { loop { tokio::time::sleep(Duration::from_secs(3600)).await; } })
rt.block_on(async {
loop {
tokio::time::sleep(Duration::from_secs(3600)).await;
}
})
});
eframe::run_native("nevmes-gui-v0.3.0-alpha", options, Box::new(|cc| Box::new(nevmes_gui::WrapApp::new(cc))),)
eframe::run_native(
"nevmes-gui-v0.3.0-alpha",
options,
Box::new(|cc| Box::new(nevmes_gui::WrapApp::new(cc))),
)
}

View file

@ -2,9 +2,10 @@
use eframe::glow;
use nevmes_core::*;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::mpsc::{
Receiver,
Sender,
};
// ----------------------------------------------------------------------------
@ -65,7 +66,6 @@ pub struct State {
lock_timer_tx: Sender<bool>,
lock_timer_rx: Receiver<bool>,
// end async notifications
}
impl Default for State {
@ -169,7 +169,9 @@ impl eframe::App for WrapApp {
}
}
if let Ok(lock_timer) = self.state.lock_timer_rx.try_recv() {
if lock_timer { self.state.lock_timer += 1 }
if lock_timer {
self.state.lock_timer += 1
}
}
#[cfg(not(target_arch = "wasm32"))]
@ -195,7 +197,11 @@ impl eframe::App for WrapApp {
send_inc_lock_timer_req(self.state.lock_timer_tx.clone(), ctx.clone());
}
if (!self.state.is_screen_locking && self.state.is_cred_set) || app_initializing {
self.send_lock_refresh(self.state.is_screen_locked_tx.clone(), ctx.clone(), app_initializing);
self.send_lock_refresh(
self.state.is_screen_locked_tx.clone(),
ctx.clone(),
app_initializing,
);
self.state.is_screen_locking = true;
}
self.show_selected_app(ctx, frame);
@ -210,7 +216,6 @@ impl eframe::App for WrapApp {
fn on_exit(&mut self, _gl: Option<&glow::Context>) {
utils::kill_child_processes(false);
}
}
impl WrapApp {
@ -275,9 +280,12 @@ impl WrapApp {
tokio::spawn(async move {
log::debug!("locking screen");
if !init {
tokio::time::sleep(std::time::Duration::from_secs(crate::LOCK_SCREEN_TIMEOUT_SECS)).await;
tokio::time::sleep(std::time::Duration::from_secs(
crate::LOCK_SCREEN_TIMEOUT_SECS,
))
.await;
}
let _= tx.send(true);
let _ = tx.send(true);
ctx.request_repaint();
});
}
@ -303,10 +311,10 @@ impl WrapApp {
let r = db::Interface::read(&s.env, &s.handle, crate::CREDENTIAL_KEY);
if r == utils::empty_string() {
log::debug!("credential not found");
let _= tx.send(false);
let _ = tx.send(false);
ctx.request_repaint();
} else {
let _= tx.send(true);
let _ = tx.send(true);
ctx.request_repaint();
break;
}
@ -327,7 +335,7 @@ fn send_inc_lock_timer_req(tx: Sender<bool>, ctx: egui::Context) {
log::debug!("starting the lock screen timer");
loop {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
let _= tx.send(true);
let _ = tx.send(true);
ctx.request_repaint();
}
});

View file

@ -0,0 +1,6 @@
# cargo +nightly fmt
comment_width = 100
format_code_in_doc_comments = true
imports_granularity = "Crate"
imports_layout = "Vertical"
wrap_comments = true

View file

@ -1,30 +1,41 @@
use rocket::http::Status;
use rocket::response::status::Custom;
use rocket::serde::json::Json;
use rocket::{get, post};
use rocket::{
get,
http::Status,
post,
response::status::Custom,
serde::json::Json,
};
use nevmes_core::{auth, message, models::*, proof, reqres};
use nevmes_core::{
auth,
message,
models::*,
proof,
reqres,
};
/// Send message
#[post("/", data="<m_req>")]
pub async fn send_message
(m_req: Json<Message>, token: proof::PaymentProof) -> Custom<Json<Message>> {
#[post("/", data = "<m_req>")]
pub async fn send_message(
m_req: Json<Message>,
token: proof::PaymentProof,
) -> Custom<Json<Message>> {
let res: Message = message::create(m_req, token.get_jwp()).await;
Custom(Status::Ok, Json(res))
}
/// Return all messages
#[get("/")]
pub async fn get_messages
(_token: auth::BearerToken) -> Custom<Json<Vec<Message>>> {
pub async fn get_messages(_token: auth::BearerToken) -> Custom<Json<Vec<Message>>> {
let messages = message::find_all();
Custom(Status::Ok, Json(messages))
}
/// decrypt a message body
#[get("/<mid>")]
pub async fn decrypt
(mid: String, _token: auth::BearerToken
pub async fn decrypt(
mid: String,
_token: auth::BearerToken,
) -> Custom<Json<reqres::DecryptedMessageBody>> {
let d_message = message::decrypt_body(mid);
Custom(Status::Ok, Json(d_message))

View file

@ -1,8 +1,8 @@
#[macro_use]
extern crate rocket;
use nevmes_message::*;
use nevmes_core::*;
use nevmes_message::*;
// The only changes in here should be mounting new controller methods

8
scripts/fmtall.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
# Run from the nevmes root
cd nevmes-auth && cargo +nightly fmt
cd ../nevmes-contact && cargo +nightly fmt
cd ../nevmes-core && cargo +nightly fmt
cd ../nevmes-gui && cargo +nightly fmt
cd ../nevmes-message && cargo +nightly fmt
cd ../ && cargo +nightly fmt

View file

@ -1,9 +1,20 @@
use rocket::http::Status;
use rocket::response::status::Custom;
use rocket::serde::json::Json;
use rocket::{get, post};
use rocket::{
get,
http::Status,
post,
response::status::Custom,
serde::json::Json,
};
use nevmes_core::{contact, i2p, message, models, monero, proof, reqres};
use nevmes_core::{
contact,
i2p,
message,
models,
monero,
proof,
reqres,
};
// JSON APIs exposed over i2p

View file

@ -1,28 +1,42 @@
#[macro_use]
extern crate rocket;
use rocket::http::Status;
use rocket::serde::json::Json;
use rocket::response::status::Custom;
use rocket::{
http::Status,
response::status::Custom,
serde::json::Json,
};
use nevmes::*;
use nevmes_core::*;
#[catch(402)]
fn payment_required() -> Custom<Json<reqres::ErrorResponse>> {
Custom(Status::PaymentRequired,
Json(reqres::ErrorResponse { error: String::from("Payment required") }))
Custom(
Status::PaymentRequired,
Json(reqres::ErrorResponse {
error: String::from("Payment required"),
}),
)
}
#[catch(404)]
fn not_found() -> Custom<Json<reqres::ErrorResponse>> {
Custom(Status::NotFound,
Json(reqres::ErrorResponse { error: String::from("Resource does not exist") }))
Custom(
Status::NotFound,
Json(reqres::ErrorResponse {
error: String::from("Resource does not exist"),
}),
)
}
#[catch(500)]
fn internal_error() -> Custom<Json<reqres::ErrorResponse>> {
Custom(Status::InternalServerError,
Json(reqres::ErrorResponse { error: String::from("Internal server error") }))
Custom(
Status::InternalServerError,
Json(reqres::ErrorResponse {
error: String::from("Internal server error"),
}),
)
}
// The only changes below here should be mounting new controller methods