#![allow(unused)]
use std::{
env,
fs::OpenOptions,
io::{self, Cursor},
mem,
path::PathBuf,
sync::{atomic, Arc},
time,
};
use futures::{future::Future, stream::Stream};
use hyper::{service::service_fn, Body, Chunk, Request, Response, Server};
use log::debug;
use tempfile::NamedTempFile;
use ipp_proto::{
attribute::*,
ipp::*,
request::{IppRequestResponse, PayloadKind},
AsyncIppParser, IppHeader, IppParser, IppValue,
};
use ipp_server::{handler::*, server::IppServerBuilder};
use lazy_static::lazy_static;
struct TestServer {
name: String,
start_time: time::SystemTime,
printing: atomic::AtomicBool,
spooler_dir: PathBuf,
}
impl TestServer {
fn get_printer_attribute(&self, attr: &str) -> IppAttribute {
match attr {
PRINTER_NAME => IppAttribute::new(attr, IppValue::NameWithoutLanguage(self.name.clone())),
PRINTER_INFO => IppAttribute::new(attr, IppValue::TextWithoutLanguage("Project Typemetal".to_string())),
PRINTER_STATE_MESSAGE => {
IppAttribute::new(attr, IppValue::TextWithoutLanguage("Being awesome".to_string()))
}
PRINTER_MAKE_AND_MODEL => {
IppAttribute::new(attr, IppValue::TextWithoutLanguage("Rust Printer".to_string()))
}
IPP_VERSIONS_SUPPORTED => {
let versions = vec![IppValue::Keyword("1.1".to_string())];
IppAttribute::new(attr, IppValue::ListOf(versions))
}
PRINTER_STATE => {
let state = if self.printing.load(atomic::Ordering::Relaxed) {
self.printing.store(false, atomic::Ordering::Relaxed);
IppValue::Enum(PrinterState::Processing as i32)
} else {
IppValue::Enum(PrinterState::Idle as i32)
};
IppAttribute::new(attr, state)
}
PRINTER_IS_ACCEPTING_JOBS => IppAttribute::new(attr, IppValue::Boolean(true)),
FINISHINGS_DEFAULT => IppAttribute::new(attr, IppValue::Enum(Finishings::None as i32)),
FINISHINGS_SUPPORTED => {
let finishings = vec![IppValue::Enum(Finishings::None as i32)];
IppAttribute::new(attr, IppValue::ListOf(finishings))
}
QUEUED_JOB_COUNT => IppAttribute::new(attr, IppValue::Integer(0)),
PRINTER_UP_TIME => IppAttribute::new(
attr,
IppValue::Integer(self.start_time.elapsed().unwrap().as_secs() as i32),
),
PDL_OVERRIDE_SUPPORTED => IppAttribute::new(attr, IppValue::Keyword("not-attempted".to_string())),
CHARSET_CONFIGURED => IppAttribute::new(attr, IppValue::Charset("utf-8".to_string())),
DOCUMENT_FORMAT_DEFAULT => IppAttribute::new(attr, IppValue::MimeMediaType("image/pwg-raster".to_string())),
DOCUMENT_FORMAT_SUPPORTED => {
let formats = vec![
IppValue::MimeMediaType("image/pwg-raster".to_string()),
IppValue::MimeMediaType("image/jpeg".to_string()),
];
IppAttribute::new(attr, IppValue::ListOf(formats))
}
COMPRESSION_SUPPORTED => {
let compressions = vec![IppValue::Keyword("none".to_string())];
IppAttribute::new(attr, IppValue::ListOf(compressions))
}
URI_AUTHENTICATION_SUPPORTED => {
let auths = vec![IppValue::Keyword("none".to_string())];
IppAttribute::new(attr, IppValue::ListOf(auths))
}
NATURAL_LANGUAGE_CONFIGURED => IppAttribute::new(attr, IppValue::NaturalLanguage("en".to_string())),
GENERATED_NATURAL_LANGUAGE_SUPPORTED => {
let langs = vec![IppValue::NaturalLanguage("en".to_string())];
IppAttribute::new(attr, IppValue::ListOf(langs))
}
CHARSET_SUPPORTED => {
let charsets = vec![IppValue::Charset("utf-8".to_string())];
IppAttribute::new(attr, IppValue::ListOf(charsets))
}
OPERATIONS_SUPPORTED => {
let operations = vec![
IppValue::Enum(Operation::PrintJob as i32),
IppValue::Enum(Operation::ValidateJob as i32),
IppValue::Enum(Operation::GetPrinterAttributes as i32),
];
IppAttribute::new(attr, IppValue::ListOf(operations))
}
PRINTER_STATE_REASONS => IppAttribute::new(attr, IppValue::Keyword("none".to_string())),
PRINTER_URI_SUPPORTED => {
let uris = vec![IppValue::Uri("ipp://192.168.1.217".to_string())];
IppAttribute::new(attr, IppValue::ListOf(uris))
}
URI_SECURITY_SUPPORTED => {
let securities = vec![IppValue::Keyword("none".to_string())];
IppAttribute::new(attr, IppValue::ListOf(securities))
}
_ => panic!("Got an unsupported attribute in get_printer_attribute!"),
}
}
}
impl IppRequestHandler for TestServer {
fn print_job(&self, mut req: IppRequestResponse) -> IppServerResult {
println!("Print-Job");
println!("{:?}", req.header());
println!("{:?}", req.attributes());
match req.payload_mut().take() {
Some(PayloadKind::ReceivedData(file)) => {
let new_path = self.spooler_dir.join(format!(
"{}.spl",
self.start_time
.duration_since(time::SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis()
));
match file.persist(&new_path) {
Ok(file) => println!("Saved ipp payload to {}", new_path.display()),
Err(e) => println!("Error while saving payload: {}", e),
}
}
_ => println!("No payload!"),
}
let mut resp =
IppRequestResponse::new_response(self.version(), StatusCode::SuccessfulOK, req.header().request_id);
resp.attributes_mut().add(
DelimiterTag::JobAttributes,
IppAttribute::new(JOB_URI, IppValue::Uri("ipp://192.168.1.217/jobs/foo".to_string())),
);
resp.attributes_mut().add(
DelimiterTag::JobAttributes,
IppAttribute::new(JOB_ID, IppValue::Integer(1)),
);
resp.attributes_mut().add(
DelimiterTag::JobAttributes,
IppAttribute::new(JOB_STATE, IppValue::Enum(JobState::Processing as i32)),
);
resp.attributes_mut().add(
DelimiterTag::JobAttributes,
IppAttribute::new(
JOB_STATE_REASONS,
IppValue::Keyword("completed-successfully".to_string()),
),
);
self.printing.store(true, atomic::Ordering::Relaxed);
Ok(resp)
}
fn validate_job(&self, req: IppRequestResponse) -> IppServerResult {
println!("Validate-Job");
println!("{:?}", req.header());
println!("{:?}", req.attributes());
let resp = IppRequestResponse::new_response(self.version(), StatusCode::SuccessfulOK, req.header().request_id);
Ok(resp)
}
fn get_printer_attributes(&self, req: IppRequestResponse) -> IppServerResult {
static SUPPORTED_ATTRIBUTES: &[&'static str] = &[
PRINTER_URI_SUPPORTED,
URI_SECURITY_SUPPORTED,
URI_AUTHENTICATION_SUPPORTED,
PRINTER_NAME,
PRINTER_STATE,
PRINTER_STATE_REASONS,
IPP_VERSIONS_SUPPORTED,
OPERATIONS_SUPPORTED,
CHARSET_CONFIGURED,
CHARSET_SUPPORTED,
NATURAL_LANGUAGE_CONFIGURED,
GENERATED_NATURAL_LANGUAGE_SUPPORTED,
DOCUMENT_FORMAT_DEFAULT,
DOCUMENT_FORMAT_SUPPORTED,
PRINTER_IS_ACCEPTING_JOBS,
QUEUED_JOB_COUNT,
PDL_OVERRIDE_SUPPORTED,
PRINTER_UP_TIME,
COMPRESSION_SUPPORTED,
PRINTER_STATE_MESSAGE,
PRINTER_MAKE_AND_MODEL,
FINISHINGS_DEFAULT,
FINISHINGS_SUPPORTED,
];
let mut resp =
IppRequestResponse::new_response(self.version(), StatusCode::SuccessfulOK, req.header().request_id);
let requested_attributes = req
.attributes()
.groups_of(DelimiterTag::OperationAttributes)
.get(0)
.and_then(|g| g.attributes().get(REQUESTED_ATTRIBUTES))
.map(|attr| {
attr.value()
.into_iter()
.filter_map(|e| e.as_keyword())
.map(AsRef::as_ref)
.collect::<Vec<_>>()
})
.unwrap_or(SUPPORTED_ATTRIBUTES.to_vec());
for attr in requested_attributes {
if SUPPORTED_ATTRIBUTES.contains(&attr) {
resp.attributes_mut()
.add(DelimiterTag::PrinterAttributes, self.get_printer_attribute(attr));
} else {
println!("Unsupported attribute {}", attr);
}
}
Ok(resp)
}
}
fn main() {
let args = env::args().collect::<Vec<String>>();
if args.len() < 2 {
eprintln!("Usage: {} spooler_dir", args[0]);
std::process::exit(1);
}
env_logger::init();
let _ = std::fs::create_dir_all(&args[1]);
let test_server = TestServer {
name: "IPP server example".to_string(),
start_time: time::SystemTime::now(),
printing: atomic::AtomicBool::new(false),
spooler_dir: env::args().nth(1).unwrap().into(),
};
let fut = IppServerBuilder::new(([0, 0, 0, 0], 7631))
.handler(Arc::new(test_server))
.build()
.map_err(|e| {
eprintln!("ERROR: {:?}", e);
});
hyper::rt::run(fut.map(|_| ()));
}