diff --git a/Cargo.lock b/Cargo.lock index 39100ac..9781151 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1625,7 +1625,7 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "matrix-commander" -version = "0.4.0" +version = "0.4.1" dependencies = [ "clap", "colored", @@ -1981,9 +1981,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.3" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] @@ -2649,18 +2649,18 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" dependencies = [ "bitflags 2.6.0", "errno", @@ -3119,9 +3119,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.3" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 40a868c..6fd3b6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "matrix-commander" -version = "0.4.0" +version = "0.4.1" edition = "2021" description = "simple but convenient CLI-based Matrix client app for sending and receiving" documentation = "https://docs.rs/matrix-commander" @@ -33,7 +33,7 @@ rpassword = "7.3" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } thiserror = "1.0" -tokio = { version = "1.39", default-features = false, features = ["rt-multi-thread", "macros",] } +tokio = { version = "1.40", default-features = false, features = ["rt-multi-thread", "macros",] } tracing = "0.1" tracing-subscriber = "0.3" update-informer = "1.1" diff --git a/README.md b/README.md index cc7671b..124e5a1 100644 --- a/README.md +++ b/README.md @@ -254,42 +254,37 @@ Options: --verify Perform account verification. Details:: By default, no verification is performed. Verification is currently offered via Manual and Emoji. - Manual verification is simpler but does less. Try: --bootstrap - --password mypassword --verify manual. Manual only verfies devices + Manual verification is simpler but does less. Try: '--bootstrap + --password mypassword --verify manual'. Manual only verfies devices one-directionally. See https://docs.rs/matrix-sdk/0.7/matrix_sdk/encryption/identities/struct.Device.html#method.verify - for more info on Manual verification. If verification is desired, run - this program in the foreground (not as a service) and without a pipe. - While verification is optional it is highly recommended, and it is - recommended to be done right after (or together with) the --login - action. Verification is always interactive, i.e. it required keyboard - input. Verification questions will be printed on stdout and the user - has to respond via the keyboard to accept or reject verification. - Once verification is complete, the program may be run as a service. - Verification is best done as follows: Perform a cross-device - verification, that means, perform a verification between two devices - of the *same* user. For that, open (e.g.) Element in a browser, make - sure Element is using the same user account as the - "matrix-commander-rs" user (specified with --user-login at --login). - On older versions of the Element webpage go to the room that is the - "matrix-commander-rs" default room (specified with --room-default at - --login). OK, in the web-browser you are now the same user and in the - same room as "matrix-commander-rs". Now click the round 'i' 'Room - Info' icon, then click 'People', click the appropriate user (the - "matrix-commander-rs" user), click red 'Not Trusted' text which - indicated an untrusted device, then click the square 'Interactively - verify by Emoji' button (one of 3 button choices). On newer versions - of Element, now go to the main menu (click your personal icon top - left), go into All Settings, go into Sessions. Select the currently - unverified session, click Verify With Other Device, and that will - bring up the emojis. At this point both web-page and - "matrix-commander-rs" in terminal show a set of emoji icons and + for more info on Manual verification. One can first do 'manual' + verification and then 'emoji' verification. If verification is + desired, run this program in the foreground (not as a service) and + without a pipe. While verification is optional it is highly + recommended, and it is recommended to be done right after (or + together with) the --login action. Verification is always + interactive, i.e. it required keyboard input. Verification questions + will be printed on stdout and the user has to respond via the + keyboard to accept or reject verification. Once verification is + complete, the program may be run as a service. Different Matrix + clients (like Element app on cell phone, Element website in browser, + other clients) have the "Verification" button hidden in different + menus or GUI elements. Sometimes it is labelled "Not trusted", + sometimes "Verify" or "Verify by emoji", sometimes "Verify With Other + Device". Verification is best done as follows: Run + 'matrix-commander-rs --verify emoji ...' and have the program waiting + for inputs and for invitations. Find the appropriate "verify" button + on your other client, click it, and thereby publish a "verification + invitation". Once received by "matrix-commander-rs" it will print the + emojis in the terminal. At this point both your client as well as + "matrix-commander-rs" in the terminal show a set of emoji icons and names. Compare them visually. Confirm on both sides (Yes, They Match, Got it), finally click OK. You should see a green shield and also see - that the "matrix-commander-rs" device is now green and verified in - the webpage. In the terminal you should see a text message indicating - success. You can do something similar to verify with other users on - other rooms + that the matrix-commander-rs device is now green and verified. In the + terminal you should see a text message indicating success. It has + been tested with Element app on cell phone and Element webpage in + browser. Verification is done one device at a time [default: none] @@ -301,7 +296,7 @@ Options: --bootstrap Details:: By default, no bootstrapping is performed. Bootstrapping is - useful for verification. --bootstrap creates cross sogning keys. If + useful for verification. --bootstrap creates cross signing keys. If you have trouble verifying with --verify manual, use --bootstrap before. Use --password to provide password. If --password is not given it will read password from command line (stdin). See also diff --git a/VERSION b/VERSION index 1d0ba9e..267577d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.0 +0.4.1 diff --git a/help.manual.txt b/help.manual.txt index 11e92e2..1b9652a 100644 --- a/help.manual.txt +++ b/help.manual.txt @@ -144,42 +144,37 @@ Options: --verify Perform account verification. Details:: By default, no verification is performed. Verification is currently offered via Manual and Emoji. - Manual verification is simpler but does less. Try: --bootstrap - --password mypassword --verify manual. Manual only verfies devices + Manual verification is simpler but does less. Try: '--bootstrap + --password mypassword --verify manual'. Manual only verfies devices one-directionally. See https://docs.rs/matrix-sdk/0.7/matrix_sdk/encryption/identities/struct.Device.html#method.verify - for more info on Manual verification. If verification is desired, run - this program in the foreground (not as a service) and without a pipe. - While verification is optional it is highly recommended, and it is - recommended to be done right after (or together with) the --login - action. Verification is always interactive, i.e. it required keyboard - input. Verification questions will be printed on stdout and the user - has to respond via the keyboard to accept or reject verification. - Once verification is complete, the program may be run as a service. - Verification is best done as follows: Perform a cross-device - verification, that means, perform a verification between two devices - of the *same* user. For that, open (e.g.) Element in a browser, make - sure Element is using the same user account as the - "matrix-commander-rs" user (specified with --user-login at --login). - On older versions of the Element webpage go to the room that is the - "matrix-commander-rs" default room (specified with --room-default at - --login). OK, in the web-browser you are now the same user and in the - same room as "matrix-commander-rs". Now click the round 'i' 'Room - Info' icon, then click 'People', click the appropriate user (the - "matrix-commander-rs" user), click red 'Not Trusted' text which - indicated an untrusted device, then click the square 'Interactively - verify by Emoji' button (one of 3 button choices). On newer versions - of Element, now go to the main menu (click your personal icon top - left), go into All Settings, go into Sessions. Select the currently - unverified session, click Verify With Other Device, and that will - bring up the emojis. At this point both web-page and - "matrix-commander-rs" in terminal show a set of emoji icons and + for more info on Manual verification. One can first do 'manual' + verification and then 'emoji' verification. If verification is + desired, run this program in the foreground (not as a service) and + without a pipe. While verification is optional it is highly + recommended, and it is recommended to be done right after (or + together with) the --login action. Verification is always + interactive, i.e. it required keyboard input. Verification questions + will be printed on stdout and the user has to respond via the + keyboard to accept or reject verification. Once verification is + complete, the program may be run as a service. Different Matrix + clients (like Element app on cell phone, Element website in browser, + other clients) have the "Verification" button hidden in different + menus or GUI elements. Sometimes it is labelled "Not trusted", + sometimes "Verify" or "Verify by emoji", sometimes "Verify With Other + Device". Verification is best done as follows: Run + 'matrix-commander-rs --verify emoji ...' and have the program waiting + for inputs and for invitations. Find the appropriate "verify" button + on your other client, click it, and thereby publish a "verification + invitation". Once received by "matrix-commander-rs" it will print the + emojis in the terminal. At this point both your client as well as + "matrix-commander-rs" in the terminal show a set of emoji icons and names. Compare them visually. Confirm on both sides (Yes, They Match, Got it), finally click OK. You should see a green shield and also see - that the "matrix-commander-rs" device is now green and verified in - the webpage. In the terminal you should see a text message indicating - success. You can do something similar to verify with other users on - other rooms + that the matrix-commander-rs device is now green and verified. In the + terminal you should see a text message indicating success. It has + been tested with Element app on cell phone and Element webpage in + browser. Verification is done one device at a time [default: none] @@ -191,7 +186,7 @@ Options: --bootstrap Details:: By default, no bootstrapping is performed. Bootstrapping is - useful for verification. --bootstrap creates cross sogning keys. If + useful for verification. --bootstrap creates cross signing keys. If you have trouble verifying with --verify manual, use --bootstrap before. Use --password to provide password. If --password is not given it will read password from command line (stdin). See also diff --git a/src/main.rs b/src/main.rs index 5f3a5ed..a659467 100644 --- a/src/main.rs +++ b/src/main.rs @@ -870,10 +870,11 @@ pub struct Args { /// verification is performed. /// Verification is currently offered via Manual and Emoji. /// Manual verification is simpler but does less. - /// Try: --bootstrap --password mypassword --verify manual. + /// Try: '--bootstrap --password mypassword --verify manual'. /// Manual only verfies devices one-directionally. See /// https://docs.rs/matrix-sdk/0.7/matrix_sdk/encryption/identities/struct.Device.html#method.verify /// for more info on Manual verification. + /// One can first do 'manual' verification and then 'emoji' verification. /// If verification is desired, run this program in the /// foreground (not as a service) and without a pipe. /// While verification is optional it is highly recommended, and it @@ -884,32 +885,27 @@ pub struct Args { /// will be printed on stdout and the user has to respond /// via the keyboard to accept or reject verification. /// Once verification is complete, the program may be - /// run as a service. Verification is best done as follows: - /// Perform a cross-device verification, that means, perform a - /// verification between two devices of the *same* user. For that, - /// open (e.g.) Element in a browser, make sure Element is using the - /// same user account as the "matrix-commander-rs" user (specified with - /// --user-login at --login). - /// On older versions of the Element webpage go to the room - /// that is the "matrix-commander-rs" default room (specified with - /// --room-default at --login). OK, in the web-browser you are now the - /// same user and in the same room as "matrix-commander-rs". - /// Now click the round 'i' 'Room Info' icon, then click 'People', - /// click the appropriate user (the "matrix-commander-rs" user), - /// click red 'Not Trusted' text - /// which indicated an untrusted device, then click the square - /// 'Interactively verify by Emoji' button (one of 3 button choices). - /// On newer versions of Element, now go to the main menu (click - /// your personal icon top left), go into All Settings, go into Sessions. - /// Select the currently unverified session, click Verify With Other Device, - /// and that will bring up the emojis. - /// At this point both web-page and "matrix-commander-rs" in terminal + /// run as a service. + /// Different Matrix clients (like Element app on cell phone, + /// Element website in browser, other clients) have the + /// "Verification" button hidden in different menus or GUI + /// elements. Sometimes it is labelled "Not trusted", sometimes "Verify" + /// or "Verify by emoji", sometimes "Verify With Other Device". + /// Verification is best done as follows: + /// Run 'matrix-commander-rs --verify emoji ...' and have the + /// program waiting for inputs and for invitations. + /// Find the appropriate "verify" button on your other client, click it, + /// and thereby publish a "verification invitation". Once received by + /// "matrix-commander-rs" + /// it will print the emojis in the terminal. + /// At this point both your client as well as "matrix-commander-rs" in the terminal /// show a set of emoji icons and names. Compare them visually. /// Confirm on both sides (Yes, They Match, Got it), finally click OK. /// You should see a green shield and also see that the - /// "matrix-commander-rs" device is now green and verified in the webpage. + /// matrix-commander-rs device is now green and verified. /// In the terminal you should see a text message indicating success. - /// You can do something similar to verify with other users on other rooms. + /// It has been tested with Element app on cell phone and Element webpage in + /// browser. Verification is done one device at a time. #[arg(long, value_enum, value_name = "VERIFICATION_METHOD", default_value_t = Verify::default(), ignore_case = true, )] @@ -919,7 +915,7 @@ pub struct Args { /// Details:: /// By default, no /// bootstrapping is performed. Bootstrapping is useful for verification. - /// --bootstrap creates cross sogning keys. + /// --bootstrap creates cross signing keys. /// If you have trouble verifying with --verify manual, use --bootstrap before. /// Use --password to provide password. If --password is not given it will read /// password from command line (stdin). See also @@ -2820,7 +2816,11 @@ pub(crate) async fn cli_message(client: &Client, ap: &Args) -> Result<(), Error> } else { io::stdin().read_to_string(&mut line)?; } - line + // line.trim_end().to_string() // remove /n at end of string + line.strip_suffix("\r\n") + .or(line.strip_suffix("\n")) + .unwrap_or(&line) + .to_string() // remove /n at end of string } else if msg == r"_" { let mut eof = false; while !eof { @@ -3383,6 +3383,21 @@ async fn main() -> Result<(), Error> { return Ok(()); }; + // -m not used but data being piped into stdin? + if ap.message.is_empty() && !stdin().is_terminal() { + // make it more compatible with the Python version of this tool + debug!( + "-m is empty, but there is something piped into stdin. Let's assume '-m -' \ + and read and send the information piped in on stdin." + ); + ap.message.push("-".to_string()); + }; + debug!( + "message {:?}, is_terminal() = {:?} (if it not the terminal than it is a pipe on stdin)", + ap.message, + stdin().is_terminal() + ); + if !(!ap.login.is_none() // get actions || ap.whoami diff --git a/src/mclient.rs b/src/mclient.rs index 5a141d7..e722ae9 100644 --- a/src/mclient.rs +++ b/src/mclient.rs @@ -152,8 +152,18 @@ pub(crate) async fn convert_to_full_room_id( if room.starts_with('!') { return; } + if room.starts_with("\\!") { + room.remove(0); // remove escape + return; + } + if room.starts_with("\\#") { + room.remove(0); // remove escape + return; + } + if room.starts_with('#') { - match RoomAliasId::parse(room.clone()) { + match RoomAliasId::parse(room.clone().replace("\\#", "#")) { + //remove possible escape Ok(id) => match client.resolve_room_alias(&id).await { Ok(res) => { room.clear(); @@ -867,7 +877,8 @@ pub(crate) async fn get_room_info( debug!("Getting room info"); for (i, roomstr) in rooms.iter().enumerate() { debug!("Room number {} with room id {}", i, roomstr); - let room_id = match RoomId::parse(roomstr) { + let room_id = match RoomId::parse(roomstr.replace("\\!", "!")) { + // remove possible escape Ok(ref inner) => inner.clone(), Err(ref e) => { error!("Invalid room id: {:?} {:?}", roomstr, e); @@ -1127,7 +1138,10 @@ pub(crate) async fn room_create( let vis = Visibility::Private; let mut invites = vec![]; if is_dm { - usr = match UserId::parse(>::as_ref(&users2[i])) { + usr = match UserId::parse(>::as_ref( + &users2[i].replace("\\@", "@"), + )) { + // remove possible escape Ok(u) => u, Err(ref e) => { err_count += 1; @@ -1190,7 +1204,8 @@ pub(crate) async fn room_leave( // convert Vec of strings into a slice of array of OwnedRoomIds let mut roomids: Vec = Vec::new(); for room_id in room_ids { - roomids.push(match RoomId::parse(room_id.clone()) { + roomids.push(match RoomId::parse(room_id.clone().replace("\\!", "!")) { + //remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -1251,7 +1266,8 @@ pub(crate) async fn room_forget( // convert Vec of strings into a slice of array of OwnedRoomIds let mut roomids: Vec = Vec::new(); for room_id in room_ids { - roomids.push(match RoomId::parse(room_id.clone()) { + roomids.push(match RoomId::parse(room_id.clone().replace("\\!", "!")) { + // remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -1311,7 +1327,10 @@ pub(crate) async fn room_invite( let mut roomids: Vec = Vec::new(); for room_id in room_ids { roomids.push( - match RoomId::parse(>::as_ref(room_id)) { + match RoomId::parse(>::as_ref( + &room_id.replace("\\!", "!"), + )) { + // remove possible escapes Ok(id) => id, Err(ref e) => { error!( @@ -1328,7 +1347,10 @@ pub(crate) async fn room_invite( let mut userids: Vec = Vec::new(); for user_id in user_ids { userids.push( - match UserId::parse(>::as_ref(user_id)) { + match UserId::parse(>::as_ref( + &user_id.replace("\\@", "@"), + )) { + // remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -1402,7 +1424,10 @@ pub(crate) async fn room_join( let mut roomids: Vec = Vec::new(); for room_id in room_ids { roomids.push( - match RoomId::parse(>::as_ref(room_id)) { + match RoomId::parse(>::as_ref( + &room_id.replace("\\!", "!"), + )) { + // remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -1459,7 +1484,10 @@ pub(crate) async fn room_ban( let mut roomids: Vec = Vec::new(); for room_id in room_ids { roomids.push( - match RoomId::parse(>::as_ref(room_id)) { + match RoomId::parse(>::as_ref( + &room_id.replace("\\!", "!"), + )) { + // remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -1476,7 +1504,10 @@ pub(crate) async fn room_ban( let mut userids: Vec = Vec::new(); for user_id in user_ids { userids.push( - match UserId::parse(>::as_ref(user_id)) { + match UserId::parse(>::as_ref( + &user_id.replace("\\!", "!"), + )) { + // remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -1576,7 +1607,10 @@ pub(crate) async fn room_kick( let mut roomids: Vec = Vec::new(); for room_id in room_ids { roomids.push( - match RoomId::parse(>::as_ref(room_id)) { + match RoomId::parse(>::as_ref( + &room_id.replace("\\!", "!"), + )) { + // remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -1593,7 +1627,10 @@ pub(crate) async fn room_kick( let mut userids: Vec = Vec::new(); for user_id in user_ids { userids.push( - match UserId::parse(>::as_ref(user_id)) { + match UserId::parse(>::as_ref( + &user_id.replace("\\@", "@"), + )) { + // remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -1690,7 +1727,10 @@ pub(crate) async fn room_get_visibility( let mut roomids: Vec = Vec::new(); for room_id in room_ids { roomids.push( - match RoomId::parse(>::as_ref(room_id)) { + match RoomId::parse(>::as_ref( + &room_id.replace("\\!", "!"), + )) { + // remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -1831,7 +1871,10 @@ pub(crate) async fn room_get_state( let mut roomids: Vec = Vec::new(); for room_id in room_ids { roomids.push( - match RoomId::parse(>::as_ref(room_id)) { + match RoomId::parse(>::as_ref( + &room_id.replace("\\!", "!"), + )) { + // remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -1930,7 +1973,10 @@ pub(crate) async fn joined_members( let mut roomids: Vec = Vec::new(); for room_id in room_ids { roomids.push( - match RoomId::parse(>::as_ref(room_id)) { + match RoomId::parse(>::as_ref( + &room_id.replace("\\!", "!"), + )) { + //remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -1992,16 +2038,19 @@ pub(crate) async fn room_resolve_alias( // convert Vec of strings into a slice of array of OwnedRoomAliasIds let mut aliasids: Vec = Vec::new(); for alias_id in alias_ids { - aliasids.push(match RoomAliasId::parse(alias_id.clone()) { - Ok(id) => id, - Err(ref e) => { - error!( - "Error: invalid alias id {:?}. Error reported is {:?}.", - alias_id, e - ); - continue; - } - }); + aliasids.push( + match RoomAliasId::parse(alias_id.clone().replace("\\#", "#")) { + // remove possible escape + Ok(id) => id, + Err(ref e) => { + error!( + "Error: invalid alias id {:?}. Error reported is {:?}.", + alias_id, e + ); + continue; + } + }, + ); } for (i, id) in aliasids.iter().enumerate() { debug!("In position {} we have room alias id {:?}.", i, id,); @@ -2042,7 +2091,10 @@ pub(crate) async fn room_enable_encryption( let mut roomids: Vec = Vec::new(); for room_id in room_ids { roomids.push( - match RoomId::parse(>::as_ref(room_id)) { + match RoomId::parse(>::as_ref( + &room_id.replace("\\!", "!"), + )) { + // remove possible escape Ok(id) => id, Err(ref e) => { error!( @@ -2249,7 +2301,7 @@ pub(crate) async fn message( } let mut err_count = 0u32; for roomname in roomnames.iter() { - let proom = RoomId::parse(roomname).unwrap(); + let proom = RoomId::parse(roomname.replace("\\!", "!")).unwrap(); // remove possible escape debug!("In message(): parsed room name is {:?}", proom); let room = client.get_room(&proom).ok_or(Error::InvalidRoom)?; for fmsg in fmsgs.iter() { @@ -2294,7 +2346,7 @@ pub(crate) async fn file( let mut err_count = 0u32; let mut pb: PathBuf; for roomname in roomnames.iter() { - let proom = RoomId::parse(roomname).unwrap(); + let proom = RoomId::parse(roomname.replace("\\!", "!")).unwrap(); // remove possible escape debug!("In file(): parsed room name is {:?}", proom); let room = client.get_room(&proom).ok_or(Error::InvalidRoom)?; for mut filename in filenames.iter() {