初始提交:

1. 添加登陆和验证相关code。已ok
2. 获取数据并放置到剪切板相关code 已ok
3. 定义交互的数据头,以便于长msg的切割:
    ${index}@${revicesize}@
    index: 拆分后的数据包的编号两个字符使用ascii编码
    revicesize:已接收的数据的长度,三个字符
4. 新建一个线程去持续监听:当鼠标焦点移开某应用程序窗口时触发获取
This commit is contained in:
Ekko.bao 2024-08-07 21:11:57 +08:00
parent f01527291a
commit a77a15f6b6
4 changed files with 257 additions and 0 deletions

17
Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "clipboard_sync"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1.39.2", features = ["macros", "rt-multi-thread"] }
tokio-macros = "2.4.0"
reqwest="0.12.5"
log="0.4.22"
env_logger="0.8"
url = "2.2"
percent-encoding = "2.1"
trust-dns-resolver = "0.20"
select = "0.5"
scraper = "0.12"
arboard = "3.4.0"

174
src/lib.rs Normal file
View File

@ -0,0 +1,174 @@
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
use reqwest::header::{HeaderMap, CONTENT_TYPE, COOKIE};
use reqwest::{Client, Response};
use scraper::{Html, Selector};
use std::error::Error;
use std::io;
use std::thread;
extern crate log;
pub mod wclip;
pub struct ClipboardSync {
user_name: String,
password: String,
web: Client,
ip: String,
cookies: String,
clip: wclip::Wclip,
}
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 {
percent_encode(input.as_bytes(), NON_ALPHANUMERIC).to_string()
}
pub struct ClipboardMsgs {
pub msgs: Vec<String>,
}
impl ClipboardSync {
pub fn new(ip: &str, user_name: &str, password: &str) -> ClipboardSync {
ClipboardSync {
user_name: user_name.to_string(),
password: password.to_string(),
web: Client::new(),
ip: ip.to_string(),
cookies: "".to_string(),
clip: wclip::Wclip::new(),
}
}
pub async fn run(&mut self) Result<(), Box<dyn Error>>{
self.login()?;
let arctx = Arc::new(Mutex::new(self));
let ctx = arctx.clone();
let handle = thread::spawn(move || {
let mut ctx = ctx.lock().unwrap()
loop{
ctx.update_msg().await;
}
})
thread::join(handle).unwrap();
Ok(())
}
async fn verify(&self) -> Result<Response, Box<dyn Error>> {
// 开始验证
let security_check_url = format!("http://{}/SSWeb/rd/j_security_check", 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".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", self.cookies.parse().unwrap());
let contents = format!(
"j_username={}&j_password={}",
quote(&self.user_name),
quote(&self.password)
);
let resp = self
.web
.post(security_check_url)
.headers(headers)
.body(contents)
.send()
.await?;
Ok(resp)
}
fn get_cookies(response: &Response) -> String {
let mut session = String::new();
let headers: &HeaderMap = response.headers();
if let Some(header_value) = headers.get("Set-Cookie") {
if let Ok(value) = header_value.to_str() {
log::info!("Set-Cookie: {}", value);
session = value.split(';').next().unwrap().to_string();
}
} else {
log::error!("cookies not found");
}
session
}
pub async fn login(&mut self) -> Result<(), Box<dyn Error>> {
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);
let mut resp = response.text().await?;
log::trace!("Response: {}", resp);
if resp.contains("loginForm") {
let response = self.verify().await?;
resp = response.text().await?;
log::trace!("verify Response: {}", resp);
}
if resp.contains("<title>Copy & Paste</title>") {
self.parse_msg(&resp).await;
Ok(())
} else {
Err(Box::new(io::Error::new(io::ErrorKind::Other, "登录失败")))
}
}
pub asyn fn update_msg(&mut self) -> Result<(), 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排序不要设置
//d-16544-p=1 页码
let url = format!("http://{}/SSWeb/rd/copy_paste.jsp?d-16544-p=1&d-16544-s=2", 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("DNT", "1".parse().unwrap());
headers.insert("Upgrade-Insecure-Requests", "1".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("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());
let response = self.web.get(url).headers(headers).send().await?;
let resp = response.text().await?;
self.parse_msg(&resp).await;
Ok(())
}
pub async fn parse_msg(&mut self, html: &str) -> Result<ClipboardMsgs, Box<dyn Error>> {
// log::info!("html: {}", html);
let document = Html::parse_document(html);
let selector = Selector::parse("code").unwrap();
for element in document.select(&selector) {
//let text = element.text().collect::<Vec<_>>().concat();
let text = element.text().collect::<String>();
log::info!("Found: [{}]", text);
if text[0] == '@' && text[6] == '@' {
items = text.split('@').map(|x| x.trim()).collect::<Vec<_>>();
index = items.next();
index = items.next();
}
self.clip.set(&text);
break;
}
Ok(ClipboardMsgs { msgs: vec![] })
}
}

45
src/main.rs Normal file
View File

@ -0,0 +1,45 @@
extern crate log;
use clipboard_sync::ClipboardSync;
use std::net::IpAddr;
use trust_dns_resolver::{
config::{ResolverConfig, ResolverOpts},
Resolver,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
// // 创建自定义 DNS 解析器
// let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap();
// // 要解析的域名
// let response = resolver.lookup_ip("www.baidu.com");
// match response {
// Ok(response) => {
// for ip in response {
// match ip {
// IpAddr::V4(v4) => println!("IPv4: {}", v4),
// IpAddr::V6(v6) => println!("IPv6: {}", v6),
// }
// }
// }
// Err(e) => {
// 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);
}
}
// 获取 Cookies
//if let Some(cookies) = response.headers().get(COOKIE) {
// println!("Cookies: {:?}", cookies);
//} else {
// println!("No cookies found.");
//}
Ok(())
}

21
src/wclip/mod.rs Normal file
View File

@ -0,0 +1,21 @@
use arboard::Clipboard;
pub struct Wclip {
clipboard: Clipboard,
}
impl Wclip {
pub fn new() -> Wclip {
Wclip {
clipboard: Clipboard::new().unwrap(),
}
}
pub fn set(&mut self, text: &str) {
self.clipboard.set_text(text).unwrap();
}
pub fn get(&mut self) -> String {
self.clipboard.get_text().unwrap()
}
}