Skip to content

Commit

Permalink
Support both file formats in UI.
Browse files Browse the repository at this point in the history
  • Loading branch information
martinling committed Sep 10, 2024
1 parent 8e6d5b8 commit 965d041
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 38 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ built = { version = "0.7.4", features = ["cargo-lock", "git2"] }
[dependencies]
bytemuck = "1.14.1"
bytemuck_derive = "1.5.0"
gtk = { version = "0.8.0", package = "gtk4" }
gtk = { version = "0.8.0", package = "gtk4", features = ["v4_4"] }
num_enum = "0.7.2"
once_cell = "1.19.0"
pcap-file = "2.0.0"
Expand Down
132 changes: 95 additions & 37 deletions src/ui.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::cell::RefCell;
use std::ffi::OsStr;
use std::io::{Read, Write};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::time::{Duration, Instant};

#[cfg(feature="step-decoder")]
use std::{io::Read, net::TcpListener};
use std::net::TcpListener;

#[cfg(feature="record-ui-test")]
use std::sync::Mutex;
Expand Down Expand Up @@ -81,6 +83,8 @@ use crate::file::{
LoaderItem,
PcapLoader,
PcapSaver,
PcapNgLoader,
PcapNgSaver,
};
use crate::model::{GenericModel, TrafficModel, DeviceModel};
use crate::row_data::{
Expand Down Expand Up @@ -118,9 +122,15 @@ enum FileAction {
Save,
}

#[derive(Copy, Clone, PartialEq)]
enum FileFormat {
Pcap,
PcapNg,
}

enum StopState {
Disabled,
Pcap(Cancellable),
File(Cancellable),
Cynthion(CynthionStop),
}

Expand Down Expand Up @@ -592,7 +602,7 @@ pub fn activate(application: &Application) -> Result<(), Error> {
}

pub fn open(file: &gio::File) -> Result<(), Error> {
start_pcap(FileAction::Load, file.clone())
start_file(FileAction::Load, file.clone())
}

fn create_view<Item, Model, RowData>(
Expand Down Expand Up @@ -902,23 +912,31 @@ fn choose_file(action: FileAction) -> Result<(), Error> {
let window = borrow.as_ref();
match action {
Load => gtk::FileChooserDialog::new(
Some("Open pcap file"),
Some("Open file"),
window,
gtk::FileChooserAction::Open,
&[("Open", gtk::ResponseType::Accept)]
),
Save => gtk::FileChooserDialog::new(
Some("Save pcap file"),
Some("Save file"),
window,
gtk::FileChooserAction::Save,
&[("Save", gtk::ResponseType::Accept)]
),
}
});
let pcap = gtk::FileFilter::new();
let pcapng = gtk::FileFilter::new();
pcap.add_suffix("pcap");
pcapng.add_suffix("pcapng");
pcap.set_name(Some("pcap (*.pcap)"));
pcapng.set_name(Some("pcap-NG (*.pcapng)"));
chooser.add_filter(&pcap);
chooser.add_filter(&pcapng);
chooser.connect_response(move |dialog, response| {
if response == gtk::ResponseType::Accept {
if let Some(file) = dialog.file() {
display_error(start_pcap(action, file));
display_error(start_file(action, file));
}
dialog.destroy();
}
Expand All @@ -927,8 +945,19 @@ fn choose_file(action: FileAction) -> Result<(), Error> {
Ok(())
}

fn start_pcap(action: FileAction, file: gio::File) -> Result<(), Error> {
fn start_file(action: FileAction, file: gio::File) -> Result<(), Error> {
use FileAction::*;
use FileFormat::*;
let basename = match file.basename() {
None => bail!("Could not determine format without file name"),
Some(name) => name,
};
let format = match basename.extension().and_then(OsStr::to_str) {
Some("pcap") => Pcap,
Some("pcapng") => PcapNg,
_ => bail!("Could not determine format from file name '{}'",
basename.display())
};
let writer = if action == Load {
Some(reset_capture()?)
} else {
Expand All @@ -946,13 +975,11 @@ fn start_pcap(action: FileAction, file: gio::File) -> Result<(), Error> {
ui.selector.set_sensitive(false);
ui.capture_button.set_sensitive(false);
ui.stop_button.set_sensitive(true);
ui.stop_state = StopState::Pcap(cancel_handle.clone());
ui.stop_state = StopState::File(cancel_handle.clone());
ui.vbox.insert_child_after(&ui.separator, Some(&ui.vertical_panes));
ui.vbox.insert_child_after(&ui.progress_bar, Some(&ui.separator));
ui.show_progress = Some(action);
ui.file_name = file
.basename()
.map(|path| path.to_string_lossy().to_string());
ui.file_name = Some(basename.to_string_lossy().to_string());
let capture = ui.capture.clone();
let packet_count = capture.packet_index.len();
CURRENT.store(0, Ordering::Relaxed);
Expand All @@ -963,8 +990,8 @@ fn start_pcap(action: FileAction, file: gio::File) -> Result<(), Error> {
std::thread::spawn(move || {
let start_time = Instant::now();
let result = match action {
Load => load_pcap(file, writer.unwrap(), cancel_handle),
Save => save_pcap(file, capture, cancel_handle),
Load => load_file(file, format, writer.unwrap(), cancel_handle),
Save => save_file(file, format, capture, cancel_handle),
};
let duration = Instant::now().duration_since(start_time);
if result.is_ok() {
Expand Down Expand Up @@ -1003,26 +1030,21 @@ fn start_pcap(action: FileAction, file: gio::File) -> Result<(), Error> {
})
}

fn load_pcap(file: gio::File,
writer: CaptureWriter,
cancel_handle: Cancellable)
-> Result<(), Error>
fn load<Source, Loader>(
writer: CaptureWriter,
source: Source
) -> Result<(), Error>
where
Source: Read,
Loader: GenericLoader<Source> + 'static
{
use LoaderItem::*;
let info = file.query_info("standard::*",
FileQueryInfoFlags::NONE,
Some(&cancel_handle))?;
if info.has_attribute(gio::FILE_ATTRIBUTE_STANDARD_SIZE) {
let file_size = info.size() as u64;
TOTAL.store(file_size, Ordering::Relaxed);
}
let source = file.read(Some(&cancel_handle))?.into_read();
let mut loader = PcapLoader::new(source)?;
let mut decoder = Decoder::new(writer)?;
#[cfg(feature="step-decoder")]
let (mut client, _addr) =
TcpListener::bind("127.0.0.1:46563")?.accept()?;
let mut decoder = Decoder::new(writer)?;
let mut loader = Loader::new(source)?;
loop {
use LoaderItem::*;
match loader.next() {
Packet(packet) => {
#[cfg(feature="step-decoder")] {
Expand Down Expand Up @@ -1050,16 +1072,37 @@ fn load_pcap(file: gio::File,
Ok(())
}

fn save_pcap(file: gio::File,
mut capture: CaptureReader,
fn load_file(file: gio::File,
format: FileFormat,
writer: CaptureWriter,
cancel_handle: Cancellable)
-> Result<(), Error>
{
use FileFormat::*;
let info = file.query_info("standard::*",
FileQueryInfoFlags::NONE,
Some(&cancel_handle))?;
if info.has_attribute(gio::FILE_ATTRIBUTE_STANDARD_SIZE) {
let file_size = info.size() as u64;
TOTAL.store(file_size, Ordering::Relaxed);
}
let source = file.read(Some(&cancel_handle))?.into_read();
match format {
Pcap => load::<_, PcapLoader<_>>(writer, source),
PcapNg => load::<_, PcapNgLoader<_>>(writer, source),
}
}

fn save<Dest, Saver>(
mut capture: CaptureReader,
dest: Dest)
-> Result<(), Error>
where
Saver: GenericSaver<Dest>,
Dest: Write
{
let packet_count = capture.packet_index.len();
let dest = file
.replace(None, false, FileCreateFlags::NONE, Some(&cancel_handle))?
.into_write();
let mut saver = PcapSaver::new(dest)?;
let mut saver = Saver::new(dest)?;
for (result, i) in capture.timestamped_packets()?.zip(0..packet_count) {
let (timestamp_ns, packet) = result?;
saver.add_packet(&packet, timestamp_ns)?;
Expand All @@ -1068,15 +1111,30 @@ fn save_pcap(file: gio::File,
break;
}
}
saver.close()?;
Ok(())
saver.close()
}

fn save_file(file: gio::File,
format: FileFormat,
capture: CaptureReader,
cancel_handle: Cancellable)
-> Result<(), Error>
{
use FileFormat::*;
let dest = file
.replace(None, false, FileCreateFlags::NONE, Some(&cancel_handle))?
.into_write();
match format {
Pcap => save::<_, PcapSaver<_>>(capture, dest),
PcapNg => save::<_, PcapNgSaver<_>>(capture, dest),
}
}

pub fn stop_operation() -> Result<(), Error> {
with_ui(|ui| {
match std::mem::replace(&mut ui.stop_state, StopState::Disabled) {
StopState::Disabled => {},
StopState::Pcap(cancel_handle) => {
StopState::File(cancel_handle) => {
STOP.store(true, Ordering::Relaxed);
cancel_handle.cancel();
},
Expand Down

0 comments on commit 965d041

Please sign in to comment.