Skip to content

Commit 2e6083e

Browse files
committed
Implemented qwk upload.
1 parent 52cc600 commit 2e6083e

File tree

4 files changed

+134
-38
lines changed

4 files changed

+134
-38
lines changed

crates/icy_board_engine/src/icy_board/state/user_commands/pcb/qwk.rs

+101-14
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
use std::{
22
collections::HashMap,
3-
fs::File,
4-
io::{BufWriter, Write},
3+
io::{Cursor, Read, Seek, Write},
54
};
65

76
use bstr::BString;
87
use chrono::{DateTime, Local, Utc};
98
use jamjam::{
10-
jam::JamMessageBase,
9+
jam::{JamMessage, JamMessageBase},
1110
qwk::{control::ControlDat, qwk_message::QWKMessage},
1211
util::basic_real::BasicReal,
1312
};
@@ -27,6 +26,8 @@ use crate::{
2726
vm::TerminalTarget,
2827
};
2928

29+
use super::u_upload_file::create_protocol;
30+
3031
const MASK_CONFNUMBERS: &str = "0123456789-SDL?";
3132

3233
impl IcyBoardState {
@@ -103,7 +104,7 @@ impl IcyBoardState {
103104
break;
104105
}
105106
"U" => {
106-
// TODO
107+
self.upload_qwk_reply().await?;
107108
break;
108109
}
109110
"S" => {
@@ -125,11 +126,10 @@ impl IcyBoardState {
125126
} else {
126127
self.session.page_len as usize - 4
127128
};
128-
let conferences = &self.board.lock().await.conferences.clone();
129129
let mut done = false;
130130

131+
let conferences = &self.board.lock().await.conferences.clone();
131132
let mut number_to_msgid = Vec::new();
132-
133133
for (i, conf) in conferences.iter().enumerate() {
134134
if let Some(areas) = &conf.areas {
135135
for (j, _area) in areas.iter().enumerate() {
@@ -304,7 +304,7 @@ impl IcyBoardState {
304304
async fn create_qwk_packet(&mut self) -> Res<()> {
305305
let output_path = temp_file::empty().path().to_path_buf();
306306
fs::create_dir_all(&output_path).await?;
307-
let qwk_package = output_path.join("mail.qwk");
307+
let mut qwk_package = output_path.join("mail.qwk");
308308

309309
{
310310
let board = self.board.lock().await;
@@ -350,13 +350,15 @@ impl IcyBoardState {
350350
return Ok(());
351351
};
352352

353-
let conferences: crate::icy_board::conferences::ConferenceBase = board.conferences.clone();
353+
if !control_dat.bbs_id.is_empty() {
354+
qwk_package = output_path.join(format!("{}.qwk", control_dat.bbs_id));
355+
}
356+
357+
let conferences = board.conferences.clone();
354358
let mut conference_number = 1;
355-
let messages_dat: File = File::create(&output_path.join("messages.dat"))?;
356-
let mut msg_writer = BufWriter::new(messages_dat);
357-
let mut header = format!("Produced by Icy Board {}", env!("CARGO_PKG_VERSION")).bytes().collect::<Vec<u8>>();
358-
header.resize(128, b' ');
359-
msg_writer.write_all(&header)?;
359+
let mut msg_writer = format!("Produced by Icy Board {}", env!("CARGO_PKG_VERSION")).bytes().collect::<Vec<u8>>();
360+
msg_writer.resize(128, b' ');
361+
360362
let mut cur_block = 2;
361363
let mut ndx_data = HashMap::new();
362364
for (i, conf) in conferences.iter().enumerate() {
@@ -428,7 +430,7 @@ impl IcyBoardState {
428430
zip.write_all(&control_dat.to_vec())?;
429431

430432
zip.start_file("messages.dat", SimpleFileOptions::default())?;
431-
zip.write_all(&fs::read(output_path.join("messages.dat")).await?)?;
433+
zip.write_all(&msg_writer)?;
432434

433435
for (cnf, ndx) in ndx_data.iter() {
434436
zip.start_file(&format!("{:03}.ndx", cnf), SimpleFileOptions::default())?;
@@ -445,4 +447,89 @@ impl IcyBoardState {
445447
fs::remove_dir_all(output_path).await?;
446448
Ok(())
447449
}
450+
451+
async fn upload_qwk_reply(&mut self) -> Res<()> {
452+
let cur_protocol = if let Some(user) = &self.session.current_user {
453+
user.protocol.clone()
454+
} else {
455+
String::new()
456+
};
457+
458+
let prot_str = self.ask_protocols(&cur_protocol).await?;
459+
if prot_str.is_empty() {
460+
return Ok(());
461+
}
462+
let Some(protocol) = self.get_protocol(prot_str).await else {
463+
return Ok(());
464+
};
465+
466+
let mut prot = create_protocol(&protocol);
467+
let bbs_id = {
468+
let board = self.board.lock().await;
469+
if board.config.qwk_settings.bbs_id.is_empty() {
470+
board.config.board.name.clone()
471+
} else {
472+
board.config.qwk_settings.bbs_id.clone()
473+
}
474+
};
475+
match prot.initiate_recv(&mut *self.connection).await {
476+
Ok(mut state) => {
477+
while !state.is_finished {
478+
if let Err(e) = prot.update_transfer(&mut *self.connection, &mut state).await {
479+
log::error!("Error while updating file transfer with {:?} : {}", protocol, e);
480+
self.display_text(IceText::TransferAborted, display_flags::NEWLINE).await?;
481+
break;
482+
}
483+
}
484+
self.display_text(IceText::TransferSuccessful, display_flags::NEWLINE | display_flags::LFBEFORE)
485+
.await?;
486+
487+
for (x, path) in state.recieve_state.finished_files {
488+
self.display_text(IceText::ExtractingMessages, display_flags::NEWLINE | display_flags::LFBEFORE)
489+
.await?;
490+
491+
let mut archive = zip::ZipArchive::new(std::fs::File::open(&path)?)?;
492+
if let Ok(mut arch) = archive.by_name(&format!("{}.MSG", bbs_id)) {
493+
let mut buf = Vec::new();
494+
arch.read_to_end(&mut buf)?;
495+
let mut cursor = Cursor::new(buf);
496+
cursor.seek(std::io::SeekFrom::Start(128))?;
497+
498+
let conferences = &self.board.lock().await.conferences.clone();
499+
let mut number_to_msgid = Vec::new();
500+
for (i, conf) in conferences.iter().enumerate() {
501+
if let Some(areas) = &conf.areas {
502+
for (j, _area) in areas.iter().enumerate() {
503+
number_to_msgid.push((i, j));
504+
}
505+
}
506+
}
507+
508+
while let Ok(msg) = QWKMessage::read(&mut cursor, true) {
509+
if let Some((conf, area)) = number_to_msgid.get(msg.msg_number as usize) {
510+
let jam_msg = JamMessage::default()
511+
.with_from(msg.from)
512+
.with_to(msg.to)
513+
.with_subject(msg.subj)
514+
.with_date_time(Utc::now())
515+
.with_text(msg.text);
516+
self.send_message(*conf as i32, *area as i32, jam_msg, IceText::ReplySuccessful).await?;
517+
} else {
518+
self.display_text(IceText::ReplyFailed, display_flags::NEWLINE).await?;
519+
}
520+
}
521+
} else {
522+
self.display_text(IceText::ErrorExtracting, display_flags::NEWLINE).await?;
523+
}
524+
525+
std::fs::remove_file(&path)?;
526+
}
527+
}
528+
Err(e) => {
529+
log::error!("Error while initiating file transfer with {:?} : {}", protocol, e);
530+
self.println(TerminalTarget::Both, &format!("Error: {}", e)).await?;
531+
}
532+
}
533+
Ok(())
534+
}
448535
}

crates/icy_board_engine/src/icy_board/state/user_commands/pcb/u_upload_file.rs

+29-20
Original file line numberDiff line numberDiff line change
@@ -76,28 +76,10 @@ impl IcyBoardState {
7676
}
7777

7878
let protocol_str: String = self.session.current_user.as_ref().unwrap().protocol.clone();
79-
let mut protocol = None;
80-
for p in self.get_board().await.protocols.iter() {
81-
if p.is_enabled && p.char_code == protocol_str {
82-
protocol = Some(p.send_command.clone());
83-
break;
84-
}
85-
}
79+
let protocol = self.get_protocol(protocol_str).await;
8680

8781
if let Some(protocol) = protocol {
88-
let mut prot: Box<dyn Protocol> = match protocol {
89-
TransferProtocolType::None => todo!(),
90-
TransferProtocolType::ASCII => todo!(),
91-
TransferProtocolType::XModem => Box::new(XYmodem::new(XYModemVariant::XModem)),
92-
TransferProtocolType::XModemCRC => Box::new(XYmodem::new(XYModemVariant::XModemCRC)),
93-
TransferProtocolType::XModem1k => Box::new(XYmodem::new(XYModemVariant::XModem1k)),
94-
TransferProtocolType::XModem1kG => Box::new(XYmodem::new(XYModemVariant::XModem1kG)),
95-
TransferProtocolType::YModem => Box::new(XYmodem::new(XYModemVariant::YModem)),
96-
TransferProtocolType::YModemG => Box::new(XYmodem::new(XYModemVariant::YModemG)),
97-
TransferProtocolType::ZModem => Box::new(Zmodem::new(1024)),
98-
TransferProtocolType::ZModem8k => Box::new(Zmodem::new(8 * 1024)),
99-
TransferProtocolType::External(_) => todo!(),
100-
};
82+
let mut prot = create_protocol(&protocol);
10183

10284
match prot.initiate_recv(&mut *self.connection).await {
10385
Ok(mut state) => {
@@ -139,4 +121,31 @@ impl IcyBoardState {
139121
}
140122
Ok(())
141123
}
124+
125+
pub async fn get_protocol(&mut self, protocol_str: String) -> Option<TransferProtocolType> {
126+
let mut protocol = None;
127+
for p in self.get_board().await.protocols.iter() {
128+
if p.is_enabled && p.char_code == protocol_str {
129+
protocol = Some(p.send_command.clone());
130+
break;
131+
}
132+
}
133+
protocol
134+
}
135+
}
136+
137+
pub fn create_protocol(protocol: &TransferProtocolType) -> Box<dyn Protocol> {
138+
match *protocol {
139+
TransferProtocolType::None => todo!(),
140+
TransferProtocolType::ASCII => todo!(),
141+
TransferProtocolType::XModem => Box::new(XYmodem::new(XYModemVariant::XModem)),
142+
TransferProtocolType::XModemCRC => Box::new(XYmodem::new(XYModemVariant::XModemCRC)),
143+
TransferProtocolType::XModem1k => Box::new(XYmodem::new(XYModemVariant::XModem1k)),
144+
TransferProtocolType::XModem1kG => Box::new(XYmodem::new(XYModemVariant::XModem1kG)),
145+
TransferProtocolType::YModem => Box::new(XYmodem::new(XYModemVariant::YModem)),
146+
TransferProtocolType::YModemG => Box::new(XYmodem::new(XYModemVariant::YModemG)),
147+
TransferProtocolType::ZModem => Box::new(Zmodem::new(1024)),
148+
TransferProtocolType::ZModem8k => Box::new(Zmodem::new(8 * 1024)),
149+
TransferProtocolType::External(_) => todo!(),
150+
}
142151
}

crates/jamjam/src/qwk/qwk_message.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use bstr::{BString, ByteSlice};
33
use crate::{pcboard::PCB_TXT_EOL_PTR, qwk::QwkError};
44
use std::{
55
fs::File,
6-
io::{BufReader, BufWriter, Read, Write},
6+
io::{BufReader, Read, Write},
77
};
88

99
pub enum MessageType {
@@ -97,7 +97,7 @@ impl QWKMessage {
9797
self.active_flag != MSG_ACTIVE
9898
}
9999

100-
pub fn read(file: &mut BufReader<File>, is_extended: bool) -> crate::Result<Self> {
100+
pub fn read(mut file: impl Read, is_extended: bool) -> crate::Result<Self> {
101101
let data = &mut [0; Self::HEADER_SIZE];
102102
file.read_exact(data)?;
103103
let mut data = &data[..];
@@ -164,7 +164,7 @@ impl QWKMessage {
164164
})
165165
}
166166

167-
pub fn write(&self, file: &mut BufWriter<File>, is_extended: bool) -> crate::Result<usize> {
167+
pub fn write(&self, mut file: impl Write, is_extended: bool) -> crate::Result<usize> {
168168
let mut header: Vec<u8> = Vec::with_capacity(128);
169169
header.push(self.status);
170170
header.extend(format!("{:<7}", self.msg_number).as_bytes());

docs/feature_parity.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ If something is missing just let me know.
8383
| REPLY | Reply Message | 💯 |
8484
| USER | User List | 90% |
8585
| WHO |WHO is Online | 90% |
86-
| QWK | QWK command | 50% | Needed some changes. Upload missing atm [^2]
86+
| QWK | QWK command | 70% | Upload needs testing [^2]
8787
| CHAT | Group Chat| 0% | Note: There are working PPEs for that
8888
| BD/DB | Batch Download | 0% |
8989
| BU/UB | Batch Upload | 0% |

0 commit comments

Comments
 (0)