1. 完成消息的接收循环的编写,现在的逻辑是:

1. 如果当前的前台程序是xxx那么就进入等待移出状态,
    2. 如果当前是等待移出状态,然后切换了前台程序到其他程序那就触发一次同步
    3. 同步后进入idle状态等待聚焦到xxx
2. 完成消息的发送基础步骤的准备,自动获取剪切板数据并且根据长度进行拆包进行发送。
3. 创建一个python的flask应用方便快速测试
4. 完成命令行参数的CLI化,方便直接使用,l0就是consumer,l3就是producer
5. 完成用户名的加密解密,使用机器的硬盘序列号进行加密解密
6. 保存msg的magic和cookies,方便重启使用,
    但是目前来说cookies没有也是ok的。
    因为没有做cookies的过期处理还。每次启动都会登录处理,并且登录期间过期的话只会异常,不会恢复,
    所以这块还需要完善一下,如果登录期间发现过期了应当重新认证一下。
7. 等待完善发送的逻辑,这块本意是想要在l3的窗口鼠标移动到边缘之后保持不动就进行发送,
    但是会存在一些和l0之间时序上匹配的问题。
    如果l3另外启动一个用户界面用来交给用户触发交互呢,又做不到程序起初想要的无感的想法,这块还要再确认下
This commit is contained in:
2024-08-28 07:53:27 +08:00
parent 4d04dc2677
commit 089bf1e3ac
8 changed files with 1059 additions and 73 deletions

96
src/encrypt/mod.rs Executable file
View File

@ -0,0 +1,96 @@
use std::error::Error;
use std::process::Command;
extern crate log;
use aes::Aes128;
use block_modes::{BlockMode, Cbc};
use block_modes::block_padding::Pkcs7;
use base64::{Engine as _, engine::general_purpose};
type Aes128Cbc = Cbc<Aes128, Pkcs7>;
use std::fmt;
#[derive(Debug)]
pub enum EncryptErr {
NotBase64,
}
impl fmt::Display for EncryptErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
EncryptErr::NotBase64 => write!(f, "String is not base64"),
}
}
}
impl std::error::Error for EncryptErr {}
pub fn get_key() -> String {
let output = Command::new("wmic")
.args(&["DISKDRIVE", "get", "serialnumber"])
.output()
.expect("Failed to execute command");
let serial = String::from_utf8_lossy(&output.stdout)
.lines()
.nth(1) // 获取第二行,通常是序列号
.unwrap()
.trim()
.to_string();
log::trace!("hdd serial is {}", serial);
serial
}
pub fn is_base64(encrypted_data: &str) -> bool {
match general_purpose::STANDARD.decode(encrypted_data) {
Ok(_) => true,
Err(_) => false,
}
}
pub fn encrypt(raw_data: &str, key: &str, iv:&str) -> String {
let key = append_align(key);
let iv = append_align(iv);
let cipher = Aes128Cbc::new_from_slices(&key, &iv).unwrap();
let pos = cipher.encrypt_vec(&raw_data.as_bytes());
// println!("pos buff size is {} {}, {}", general_purpose::STANDARD.encode(&pos), pos.len(), data.len());
let ret = [iv.to_vec(), pos.to_vec()].concat();// Prepend IV for decryption
general_purpose::STANDARD.encode(ret)
}
pub fn decrypt(encrypted_data: &str, key: &str) -> Result<String, Box<dyn Error>> {
let key: Vec<u8> = append_align(key);
let encrypted_data = general_purpose::STANDARD.decode(encrypted_data)?;
let (iv, data) = encrypted_data.split_at(16); // First 16 bytes are IV
let cipher = Aes128Cbc::new_from_slices(&key, &iv)?;
let decrypted_data = cipher.decrypt_vec(data).unwrap();
let result = String::from_utf8(decrypted_data)?;
Ok(result)
}
fn append_align(uuid: &str) -> Vec<u8> {
let mut key = uuid.as_bytes().to_vec();
key.resize(16, b'_');
key
}
#[test]
fn test_get_key(){
get_key();
}
#[test]
fn test_encrypt_decrypt() {
let user_name = "ekko.bao";
let password = "PASSWORD";
let machine_id = get_key();
let encrypted_data = encrypt(password,&machine_id, user_name);
// println!("encrypted_data is {}", encrypted_data);
let ret = decrypt(&encrypted_data, &machine_id).unwrap();
println!("decrypt is {}", ret);
assert_eq!(password, ret);
}

View File

@ -1,34 +1,136 @@
use log::Log;
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
use rand::{random, Rng};
use reqwest::header::HeaderMap;
use reqwest::{Client, Response};
use scraper::{Html, Selector};
use core::ascii;
use std::error::Error;
use std::io;
use std::io::{self, Write};
use std::time::Duration;
use tokio::sync::Mutex;
use std::sync::Arc;
use tokio::{task, time};
use std::collections::BTreeMap;
extern crate log;
use serde_json::{self, to_string, Value};
use serde::Serialize;
use std::fs::File;
use std::io::BufReader;
pub mod wclip;
pub mod sys_res;
pub mod encrypt;
use sys_res::*;
pub struct ClipboardSync {
#[derive(Debug, Serialize, Default)]
pub struct Config {
user_name: String,
password: String,
cookies: String,
magic: String,
save_path: String,
}
impl Config {
pub fn new(json_file: &str) -> Result<Config, Box<dyn Error>> {
let file = File::open(json_file)?;
let reader = BufReader::new(file);
let json: Value = serde_json::from_reader(reader)?;
let user_name = json.get("user_name").expect("not found user_name in info.json").as_str().unwrap().to_string();
log::info!("user_name is {}", user_name);
let mut password = json.get("password").expect("not found password in info.json").as_str().unwrap().to_string();
let magic = match json.get("magic") {
Some(magic) => magic.as_str().unwrap().to_string(),
None => "~".to_string(),
};
let cookies = match json.get("cookies") {
Some(cookies) => cookies.as_str().unwrap().to_string(),
None => "".to_string(),
};
let gen_cfg = move |user_name:String, password:String|{
let cfg = Config {
user_name,
password,
cookies,
magic,
save_path: json_file.to_string()
};
cfg
};
drop(json);
if encrypt::is_base64(&password) {
let key = encrypt::get_key();
let _ = encrypt::decrypt(&password, &key)?;
Ok(gen_cfg(user_name, password))
} else {
let key = encrypt::get_key();
password = encrypt::encrypt(&password, &key, &user_name);
let cfg = gen_cfg(user_name, password);
cfg.update();
Ok(cfg)
}
}
pub fn update(&self) {
let json = serde_json::json!({
"user_name": self.user_name,
"password": self.password,
"cookies": self.cookies,
"magic": self.magic,
});
let json = serde_json::to_string_pretty(&json).unwrap();
let mut f = File::options().truncate(true).write(true).create(true).open(&self.save_path).unwrap();
let _ = f.write(json.as_bytes());
}
pub fn user_name(&mut self, value: Option<&str>) -> &str {
if let Some(value) = value {
self.user_name = value.to_string();
self.update();
}
&self.user_name
}
pub fn password(&mut self, value: Option<&str>) -> String {
if let Some(value) = value {
let key = encrypt::get_key();
self.password = encrypt::encrypt(&value, &key, &self.user_name);
self.update();
}
let key = encrypt::get_key();
encrypt::decrypt(&self.password, &key).expect("password cant decrypt")
}
pub fn cookies(&mut self, value: Option<&str>) -> &str {
if let Some(value) = value {
self.cookies = value.to_string();
self.update();
}
&self.cookies
}
pub fn magic(&mut self, value: Option<&str>) -> &str {
if let Some(value) = value {
self.magic = value.to_string();
self.update();
}
&self.magic
}
}
pub struct ClipboardSync {
web: Client,
ip: String,
cookies: String,
clip: wclip::Wclip,
//last message magic
cache_magic: String,
cfg: Config,
}
const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.42";
fn quote(input: &String) -> String {
fn quote(input: &str) -> String {
percent_encode(input.as_bytes(), NON_ALPHANUMERIC).to_string()
}
@ -36,14 +138,49 @@ pub struct ClipboardMsgs {
pub msgs: Vec<String>,
}
#[derive(Debug)]
enum MsgSyncState {
None,
ForceInVM,
NeedSync,
}
// 同步获取消息的线程
async fn msg_sync(ctx:Arc<Mutex<ClipboardSync>>){
async fn msg_sync(ctx:Arc<Mutex<ClipboardSync>>) {
let VM_EXE_NAME = "Code.exe".to_string();
let mut ctx = ctx.lock().await;
let mut sta = MsgSyncState::None;
let mut turn_time;
//let _ = ctx.update_msg().await;
loop{
let _ = ctx.update_msg().await;
time::sleep(Duration::new(10, 0)).await;
let prog_name = get_foredround_window_name();
if prog_name == "VMware Horizon Client"{
log::trace!("current state is {:?}", sta);
match sta{
MsgSyncState::None => {
turn_time = 200;
let prog_name = get_foredround_window_name();
if prog_name == VM_EXE_NAME {
sta = MsgSyncState::ForceInVM;
turn_time = 0;
} else {
log::trace!("foreground prog is [{}] {}, waiting: [{}] {}", prog_name, prog_name.len(), VM_EXE_NAME, VM_EXE_NAME.len());
}
},
MsgSyncState::ForceInVM => {
turn_time = 100;
let prog_name = get_foredround_window_name();
if prog_name != VM_EXE_NAME {
turn_time = 0;
sta = MsgSyncState::NeedSync;
}
},
MsgSyncState::NeedSync => {
turn_time = 0;
let _ = ctx.update_msg().await;
sta = MsgSyncState::None;
},
}
if turn_time != 0 {
time::sleep(Duration::new(0, turn_time*1000*1000)).await;
}
}
}
@ -54,26 +191,24 @@ pub async fn start_msg_sync(ctx:Arc<Mutex<ClipboardSync>>) -> Result<task::JoinH
enum Msg <'a>{
// magic str, message context
Msg((String, &'a str)),
Msg(&'a str),
//index of message pack, total message pack number, magic str, message context
Sub((u8, u8, String, &'a str)),
}
impl ClipboardSync {
pub fn new(ip: &str, user_name: &str, password: &str) -> ClipboardSync {
ClipboardSync {
user_name: user_name.to_string(),
password: password.to_string(),
pub fn new(ip: &str, info_file:&str) -> Result<ClipboardSync, Box<dyn Error>> {
let cfg = Config::new(info_file)?;
Ok(ClipboardSync {
web: Client::new(),
ip: ip.to_string(),
cookies: "".to_string(),
clip: wclip::Wclip::new(),
cache_magic: "0xff".to_string(),
}
cfg,
})
}
async fn verify(&self) -> Result<Response, Box<dyn Error>> {
async fn verify(&mut self) -> Result<Response, Box<dyn Error>> {
// 开始验证
let security_check_url = format!("http://{}/SSWeb/rd/j_security_check", self.ip);
let mut headers = HeaderMap::new();
@ -100,11 +235,11 @@ impl ClipboardSync {
.parse()
.unwrap(),
);
headers.insert("Cookie", self.cookies.parse().unwrap());
headers.insert("Cookie", self.cfg.cookies(None).parse().unwrap());
let contents = format!(
"j_username={}&j_password={}",
quote(&self.user_name),
quote(&self.password)
quote(self.cfg.user_name(None)),
quote(&self.cfg.password(None))
);
let resp = self
.web
@ -124,7 +259,7 @@ impl ClipboardSync {
session = value.split(';').next().unwrap().to_string();
}
} else {
log::error!("cookies not found");
log::warn!("cookies not found");
}
session
}
@ -132,9 +267,10 @@ impl ClipboardSync {
let mut headers = HeaderMap::new();
headers.insert("User-Agent", USER_AGENT.parse().unwrap());
let url = format!("http://{}/SSWeb/rd/copy_paste.jsp", self.ip);
let response = self.web.get(url).headers(headers).send().await?;
self.cookies = ClipboardSync::get_cookies(&response);
log::info!("cookies is {}", self.cookies);
self.cfg.cookies(Some(&ClipboardSync::get_cookies(&response)));
log::info!("cookies is [{}]", self.cfg.cookies(None));
let mut resp = response.text().await?;
log::trace!("Response: {}", resp);
if resp.contains("loginForm") {
@ -149,7 +285,7 @@ impl ClipboardSync {
}
}
pub async fn update_msg(&mut self) -> Result<(), Box<dyn Error>> {
pub async fn get_newest_msg(&mut self) -> Result<String, Box<dyn Error>> {
//http://ssweb/SSWeb/rd/copy_paste.jsp?d-16544-p=1&d-16544-s=2
//d-16544-s 2:时间从靠近现在开始排序 1:时间从最早开始排序
//d-16544-o mesage排序不要设置
@ -167,6 +303,11 @@ impl ClipboardSync {
headers.insert("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6".parse().unwrap());
let response = self.web.get(url).headers(headers).send().await?;
let resp = response.text().await?;
Ok(resp)
}
pub async fn update_msg(&mut self) -> Result<(), Box<dyn Error>> {
let resp = self.get_newest_msg().await?;
self.msg2clip(&resp).await;
Ok(())
}
@ -174,23 +315,21 @@ impl ClipboardSync {
//解析可能存在的消息数据
fn parse_msg<'a>(msg: &'a str) -> Msg {
//x|x|x| -> index|total|magic|
let head = &msg[0..7];
let body = &msg[7..];
let check = head.split('|').filter(|text| {
let text = text.as_bytes();
let bmsg = msg.as_bytes();
let head = &bmsg[0..7];
let check = head.split(|c| *c == b'|').filter(|text| {
if text.len() == 1 {
if text[0] >= b'!' && text[0] < b'|' {true} else {false}
} else {
false
}
}).map(|x|x.as_bytes()[0] - b'!').collect::<Vec<_>>();
}).map(|x|x[0] - b'!').collect::<Vec<_>>();
if check.len() != 3 {
//TODO: get magic
return Msg::Msg(("maigic".to_string(), msg))
return Msg::Msg(msg)
}
return Msg::Sub((check[0], check[1], check[2].to_string(), body));
return Msg::Sub((check[0], check[1], check[2].to_string(), &msg[7..]));
}
//将message解析并放置到剪切板上
@ -199,19 +338,27 @@ impl ClipboardSync {
let document = Html::parse_document(html);
// 所有剪切板的数据都是在code这个html lable上的
let selector = Selector::parse("code").unwrap();
//对于单个消息我们希望发送的时间作为magic所以这里通过构建一个date selector获得
let date_selector = Selector::parse("td.fixline").unwrap();
let mut msg_map = BTreeMap::new();
let mut magic_ref = "0xff".to_string();
let mut is_first = true;
let mut msg_index = 0;
for element in document.select(&selector).into_iter() {
//let text = element.text().collect::<Vec<_>>().concat();
let text = element.text().collect::<String>();
log::info!("Found: [{}]", text);
log::trace!("Found: [{}]", text);
let msg = ClipboardSync::parse_msg(text.as_str());
match msg {
//一整个消息。直接复制到剪切板即可
//一整个完整的消息。直接复制到剪切板即可
Msg::Msg(msg) => {
if magic_ref == "0xff" {
self.clip.set(msg.1);
// 因为每个date和str都是对应的所以这里直接获取指定位置的date作为magic
let magic = document.select(&date_selector).into_iter().nth(msg_index).unwrap().text().collect::<String>();
if self.cfg.magic(None) != magic {
self.clip.set(msg);
self.cfg.magic(Some(&magic));
log::info!("magic is {}, msg is {}", self.cfg.magic(None), msg);
}
break;//处理完毕。直接退出
}
},
@ -220,13 +367,13 @@ impl ClipboardSync {
magic_ref = magic;
} else {
if magic_ref != magic { // other message ignore
is_first = false;
msg_index += 1;
continue
}
}
if msg_map.len() == 0 && (
self.cache_magic == magic_ref // message is repeated ignore it
|| !is_first // we only process the newest msg
self.cfg.magic(None) == magic_ref // message is repeated ignore it
|| (msg_index != 0) // we only process the newest msg
) {
break;
}
@ -234,13 +381,140 @@ impl ClipboardSync {
msg_map.insert(index, body.to_string());
if msg_map.len() as u8 == total {
let msg = msg_map.into_iter().map(|(_key, v)| v).collect::<String>();
self.cache_magic = magic_ref; // save for next msg
self.clip.set(&msg);
if self.cfg.magic(None) != magic_ref {
log::info!("magic is {}, msg is {}", self.cfg.magic(None), msg);
self.clip.set(&msg);
self.cfg.magic(Some(&magic_ref)); // save for next msg
}
break;
}
}
}
is_first = false;
msg_index += 1;
}
}
pub async fn send_msg(&mut self, msg: &str) -> Result<(), Box<dyn Error>> {
let url = format!("http://{}/SSWeb/rd/copy_paste.jsp?send=y", self.ip);
let mut headers = HeaderMap::new();
headers.insert("Host", "ssweb".parse().unwrap());
headers.insert("Connection", "keep-alive".parse().unwrap());
headers.insert("Cache-Control", "max-age=0".parse().unwrap());
headers.insert("Origin", "http://ssweb".parse().unwrap());
headers.insert("DNT", "1".parse().unwrap());
headers.insert("Upgrade-Insecure-Requests", "1".parse().unwrap());
headers.insert("Content-Type", "application/x-www-form-urlencoded".parse().unwrap());
headers.insert("User-Agent", USER_AGENT.parse().unwrap());
headers.insert("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9".parse().unwrap());
headers.insert("Referer", "http://ssweb/SSWeb/rd/copy_paste.jsp?d-16544-p=1&d-16544-s=2".parse().unwrap());
headers.insert("Accept-Encoding", "gzip, deflate".parse().unwrap());
headers.insert("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6".parse().unwrap());
headers.insert("Cookie", format!("{}", self.cfg.cookies(None)).parse().unwrap());
let contents = format!("message={}", msg);
let _ = self.web.post(url).headers(headers).body(contents).send().await?;
// let resp = response.text().await?;
// Ok(resp)
Ok(())
}
pub async fn split_msg(&mut self, msg: &str) -> Result<Vec<String>, i32> {
let mut start = 0;
let mut msgs = Vec::new();
let mut cur = 0;
for c in msg.chars() {
let byte_count = c.len_utf8();
if cur - start + byte_count + 7 >= 300 {
//超过限额300个字符构造一个消息进行发送
msgs.push(&msg[start..cur]);
start = cur;
if msgs.len() > b'|' as usize - b'!' as usize {
log::warn!("msg is too long, ignore it");
return Err(-1);
}
}
cur += byte_count;
}
//如果拆包了的话。那么要把最后一点点数据打包一下不然会丢msg
if msgs.len() > 0 && cur != start {
msgs.push(&msg[start..cur]);
}
fn gen_magic(cache: char) -> char {
let mut rng = rand::thread_rng();
// ASCII 范围从 '!' (33) 到 '|' (124)
let start = b'!' as u8;
let end = b'|' as u8;
let mut ret = cache;
//随机生成一个和上次不一样的值即可
while ret == cache {
ret = rng.gen_range(start..end) as char;
}
ret
}
let mut ret = Vec::new();
let total = msgs.len() as u32;
if total > 0 { //大消息切片为多个小消息
let magic = gen_magic(self.cfg.magic(None).chars().next().unwrap());
for index in 0..total {
//x|x|x| -> index|total|magic|
let msg = format!("{}|{}|{}", char::from_u32(b'0' as u32 + index).unwrap(),
char::from_u32(b'0' as u32 + total).unwrap(), magic);
ret.push(msg + msgs[index as usize]);
}
self.cfg.magic(Some(&magic.to_string()));
} else { //单个消息
ret.push(msg.to_string());
}
Ok(ret)
}
pub async fn clip2msg(&mut self) {
let msg = match self.clip.get() {
Some(msg) => match self.split_msg(&msg).await {
Ok(msgs) => msgs,
Err(_) => return,
},
None => return,
};
//遍历所有分包并进行发送
for item in msg {
let _ = self.send_msg(&item).await;
}
}
}
#[tokio::test]
async fn test_message2clip()
{
use std::fs::File;
use std::io::Read;
let mut ins = ClipboardSync::new("127.0.0.1", "info.json").unwrap();
let mut html = String::new();
File::open("message.html").unwrap().read_to_string(&mut html).unwrap();
ins.msg2clip(&html).await;
}
#[tokio::test]
async fn test_start_msg_sync()
{
let ins = ClipboardSync::new("127.0.0.1:5000", "info.json").unwrap();
let ctx = Arc::new(Mutex::new(ins));
let handle = start_msg_sync(ctx.clone()).await.unwrap();
time::sleep(Duration::new(10, 0)).await;
handle.abort();
}
#[tokio::test]
async fn test_split_msg() {
let mut ins = ClipboardSync::new("127.0.0.1:5000", "info.json").unwrap();
let raw_msg = "在这个快速发展的世界中technology is evolving at an unprecedented rate! 我们需要不断学习和适应新的变化。#创新@未来$梦想%实现^挑战&机遇*合作+成长=成功~体验|分享<知识>探索{可能性}。Let's work together to create a better tomorrow! 生活充满了各种可能性我们要勇敢面对每一个挑战。1234567890!@#$%^&*()_+-=<>?[]{},.。;:‘’“”()【】《》——";
let msgs = ins.split_msg(raw_msg).await.unwrap();
println!("{:?}", msgs);
}
#[tokio::test]
async fn test_send_send_msg() {
let mut ins = ClipboardSync::new("127.0.0.1:5000", "info.json").unwrap();
let raw_msg = "在这个快速发展的世界中technology is evolving at an unprecedented rate! 我们需要不断学习和适应新的变化。#创新@未来$梦想%实现^挑战&机遇*合作+成长=成功~体验|分享<知识>探索{可能性}。Let's work together to create a better tomorrow! 生活充满了各种可能性我们要勇敢面对每一个挑战。1234567890!@#$%^&*()_+-=<>?[]{},.。;:‘’“”()【】《》——";
ins.clip.set(&raw_msg);
ins.clip2msg().await;
}

View File

@ -7,6 +7,83 @@ use trust_dns_resolver::{
};
use tokio::sync::{Mutex};
use std::sync::Arc;
use clap::{App, Arg, ArgGroup};
// const SERVER:&str = "172.19.36.79";
const SERVER:&str = "127.0.0.1:5000";
enum Character {
Producer,
Consumer,
}
fn param_parser() -> (Character, String) {
let mut character = Character::Consumer;
let matches = App::new("sstar_clipboard_sync")
.version("1.0")
.author("Ekko.bao")
.about("sstar clipboard sync")
.arg(
Arg::with_name("producer")
.short('p')
.long("producer")
.conflicts_with("consumer")
.help("Data producer")
)
.arg(
Arg::with_name("consumer")
.short('c')
.long("consumer")
.conflicts_with("producer")
.help("Data consumer")
)
.group(clap::ArgGroup::new("Character")
.args(&["consumer", "producer"])
.required(true)
.multiple(false))
.arg(
Arg::with_name("info")
.short('i')
.long("info")
.value_name("info")
.default_value("info.json")
.help("The file of running info")
)
.get_matches();
character = if matches.is_present("producer") {
Character::Producer
} else if matches.is_present("consumer") {
Character::Consumer
} else {
character
};
let info_file = matches.value_of("info").unwrap();
(character, info_file.to_string())
}
async fn producer(info: &str) {
let mut ins: ClipboardSync = match ClipboardSync::new(SERVER, &info) {
Ok(ins) => ins,
Err(e) => {
log::error!("create syns instance fail: {}", e);
return;
}
};
match ins.login().await {
Ok(()) => log::info!("login success"),
Err(e) => {
log::error!("login fail: {}", e);
return;
}
}
let ctx = Arc::new(Mutex::new(ins));
let handle = clipboard_sync::start_msg_sync(ctx.clone()).await.unwrap();
let _ = handle.await;
}
async fn consumer(_info: &str) {
log::warn!("TODO: support Producer in the feature")
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -29,22 +106,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// println!("Error: {}", e);
// }
// }
let mut ins = ClipboardSync::new("172.19.36.79", "ekko.bao", "qwer12345;");
match ins.login().await {
Ok(()) => log::info!("登录成功"),
Err(e) => {
log::error!("登录失败: {}", e);
let (character, info) = param_parser();
match character{
Character::Producer => {
producer(&info).await;
}
Character::Consumer => {
consumer(&info).await;
}
}
let ctx = Arc::new(Mutex::new(ins));
let handle = clipboard_sync::start_msg_sync(ctx.clone()).await.unwrap();
let _ = handle.await;
// 获取 Cookies
//if let Some(cookies) = response.headers().get(COOKIE) {
// println!("Cookies: {:?}", cookies);
//} else {
// println!("No cookies found.");
//}
Ok(())
}

View File

@ -1,6 +1,5 @@
extern crate winapi;
extern crate log;
use std::path::Path;
use winapi::um::winuser::{
GetCursorPos, GetSystemMetrics, SM_CXSCREEN, SM_CYSCREEN,GetWindowRect
};
@ -9,8 +8,6 @@ use winapi::shared::windef::{
RECT,
};
use winapi::um::winuser::{GetForegroundWindow, GetWindowThreadProcessId};
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use winapi::um::processthreadsapi::OpenProcess;
use winapi::um::winnt::PROCESS_QUERY_INFORMATION;
use winapi::um::psapi::GetProcessImageFileNameW;
@ -42,15 +39,14 @@ pub fn get_foredround_window_rect() -> Option<(i32, i32, i32, i32)> {
println!("GetWindowRect: {:?}", rect);
Some((rect.left, rect.top, rect.right, rect.bottom))
} else {
println!("cant get foregtound window rect");
log::error!("cant get foregtound window rect");
None
}
}
}
pub fn get_foredround_window_name() -> String {
let mut name = OsString::new();
let mut name = String::new();
unsafe {
let foreground_window = GetForegroundWindow();
@ -62,24 +58,26 @@ pub fn get_foredround_window_name() -> String {
let process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, 0, process_id);
if process_handle.is_null() {
panic!("Cant open foreground process with {}!", process_id)
log::error!("Cant open foreground process with {}!", process_id);
return "".to_string();
}
let mut image_name = [0u16; 1024];
if GetProcessImageFileNameW(process_handle, image_name.as_mut_ptr() as *mut _, image_name.len() as u32) == 0 {
panic!("Cant Get foreground prog name!")
log::error!("Cant Get foreground prog name!");
return "".to_string();
}
// let mut class_name: [u16; 256] = [0; 256];
// GetClassNameW(foreground_window, class_name.as_mut_ptr(), 256);
name = OsString::from_wide(&image_name);
name = String::from_utf16_lossy(&image_name);
// let mut class_name_str = OsString::from_wide(&class_name);
// log::info!("Class Name: {}", class_name_str.to_string_lossy().to_string());
}
let path = Path::new(&name);
let name = path.file_name().unwrap().to_string_lossy().to_string();
println!("Current Proc Name: [{}]", name);
let name = name.split('\\').last().unwrap().trim_end_matches('\0').to_string();
//let name = path.file_name().unwrap().to_string_lossy().to_string();
// println!("Current Proc Name: [{}]", name);
name
}