1use std::{
4 fmt,
5 sync::OnceLock,
6};
7
8macro_rules! log {
10 ($dst:expr $(,)?) => (
11 $crate::error::log_internal($dst)
12 );
13 ($dst:expr, $($arg:tt)*) => (
14 $crate::error::log_internal(std::format!($dst, $($arg)*))
15 );
16}
17
18pub type RnpResult = u32;
22
23#[derive(Debug, Copy, Clone, PartialEq, Eq)]
29pub struct RnpStatus(RnpResult);
30
31impl fmt::UpperHex for RnpStatus {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 fmt::UpperHex::fmt(&self.0, f) }
35}
36
37impl fmt::Display for RnpStatus {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match *self {
40 RNP_SUCCESS => f.write_str("RNP_SUCCESS"),
41 RNP_ERROR_GENERIC => f.write_str("RNP_ERROR_GENERIC"),
42 RNP_ERROR_BAD_FORMAT => f.write_str("RNP_ERROR_BAD_FORMAT"),
43 RNP_ERROR_BAD_PARAMETERS => f.write_str("RNP_ERROR_BAD_PARAMETERS"),
44 RNP_ERROR_NOT_IMPLEMENTED => f.write_str("RNP_ERROR_NOT_IMPLEMENTED"),
45 RNP_ERROR_NOT_SUPPORTED => f.write_str("RNP_ERROR_NOT_SUPPORTED"),
46 RNP_ERROR_OUT_OF_MEMORY => f.write_str("RNP_ERROR_OUT_OF_MEMORY"),
47 RNP_ERROR_SHORT_BUFFER => f.write_str("RNP_ERROR_SHORT_BUFFER"),
48 RNP_ERROR_NULL_POINTER => f.write_str("RNP_ERROR_NULL_POINTER"),
49 RNP_ERROR_ACCESS => f.write_str("RNP_ERROR_ACCESS"),
50 RNP_ERROR_READ => f.write_str("RNP_ERROR_READ"),
51 RNP_ERROR_WRITE => f.write_str("RNP_ERROR_WRITE"),
52 RNP_ERROR_BAD_STATE => f.write_str("RNP_ERROR_BAD_STATE"),
53 RNP_ERROR_MAC_INVALID => f.write_str("RNP_ERROR_MAC_INVALID"),
54 RNP_ERROR_SIGNATURE_INVALID => f.write_str("RNP_ERROR_SIGNATURE_INVALID"),
55 RNP_ERROR_KEY_GENERATION => f.write_str("RNP_ERROR_KEY_GENERATION"),
56 RNP_ERROR_BAD_PASSWORD => f.write_str("RNP_ERROR_BAD_PASSWORD"),
57 RNP_ERROR_KEY_NOT_FOUND => f.write_str("RNP_ERROR_KEY_NOT_FOUND"),
58 RNP_ERROR_NO_SUITABLE_KEY => f.write_str("RNP_ERROR_NO_SUITABLE_KEY"),
59 RNP_ERROR_DECRYPT_FAILED => f.write_str("RNP_ERROR_DECRYPT_FAILED"),
60 RNP_ERROR_RNG => f.write_str("RNP_ERROR_RNG"),
61 RNP_ERROR_SIGNING_FAILED => f.write_str("RNP_ERROR_SIGNING_FAILED"),
62 RNP_ERROR_NO_SIGNATURES_FOUND => f.write_str("RNP_ERROR_NO_SIGNATURES_FOUND"),
63 RNP_ERROR_SIGNATURE_EXPIRED => f.write_str("RNP_ERROR_SIGNATURE_EXPIRED"),
64 RNP_ERROR_NOT_ENOUGH_DATA => f.write_str("RNP_ERROR_NOT_ENOUGH_DATA"),
65 RNP_ERROR_UNKNOWN_TAG => f.write_str("RNP_ERROR_UNKNOWN_TAG"),
66 RNP_ERROR_PACKET_NOT_CONSUMED => f.write_str("RNP_ERROR_PACKET_NOT_CONSUMED"),
67 RNP_ERROR_NO_USERID => f.write_str("RNP_ERROR_NO_USERID"),
68 RNP_ERROR_EOF => f.write_str("RNP_ERROR_EOF"),
69 n => write!(f, "RNP_ERROR_{:08X}", n),
70 }
71 }
72}
73
74impl RnpStatus {
75 pub fn success(&self) -> bool {
77 self == &RNP_SUCCESS
78 }
79
80 pub fn epilogue(&self, name: &str, args: Vec<String>) -> RnpResult {
85 if ! self.success() || call_tracing() || full_tracing() {
86 if cfg!(debug_assertions) {
87 log!("sequoia-octopus: TRACE: {}({}) => {}",
88 name, args.join(", "), self)
89 }
90 }
91 self.quiet_epilogue()
92 }
93
94 pub fn quiet_epilogue(&self) -> RnpResult {
99 self.0
100 }
101
102 #[cfg(test)]
103 pub const fn as_u32(&self) -> u32 {
104 self.0
105 }
106}
107
108pub const RNP_SUCCESS: RnpStatus = RnpStatus(0x00000000);
109
110pub const RNP_ERROR_GENERIC: RnpStatus = RnpStatus(0x10000000);
112pub const RNP_ERROR_BAD_FORMAT: RnpStatus = RnpStatus(0x10000001);
113pub const RNP_ERROR_BAD_PARAMETERS: RnpStatus = RnpStatus(0x10000002);
114pub const RNP_ERROR_NOT_IMPLEMENTED: RnpStatus = RnpStatus(0x10000003);
115pub const RNP_ERROR_NOT_SUPPORTED: RnpStatus = RnpStatus(0x10000004);
116pub const RNP_ERROR_OUT_OF_MEMORY: RnpStatus = RnpStatus(0x10000005);
117pub const RNP_ERROR_SHORT_BUFFER: RnpStatus = RnpStatus(0x10000006);
118pub const RNP_ERROR_NULL_POINTER: RnpStatus = RnpStatus(0x10000007);
119
120pub const RNP_ERROR_ACCESS: RnpStatus = RnpStatus(0x11000000);
122pub const RNP_ERROR_READ: RnpStatus = RnpStatus(0x11000001);
123pub const RNP_ERROR_WRITE: RnpStatus = RnpStatus(0x11000002);
124
125pub const RNP_ERROR_BAD_STATE: RnpStatus = RnpStatus(0x12000000);
127pub const RNP_ERROR_MAC_INVALID: RnpStatus = RnpStatus(0x12000001);
128pub const RNP_ERROR_SIGNATURE_INVALID: RnpStatus = RnpStatus(0x12000002);
129pub const RNP_ERROR_KEY_GENERATION: RnpStatus = RnpStatus(0x12000003);
130pub const RNP_ERROR_BAD_PASSWORD: RnpStatus = RnpStatus(0x12000004);
131pub const RNP_ERROR_KEY_NOT_FOUND: RnpStatus = RnpStatus(0x12000005);
132pub const RNP_ERROR_NO_SUITABLE_KEY: RnpStatus = RnpStatus(0x12000006);
133pub const RNP_ERROR_DECRYPT_FAILED: RnpStatus = RnpStatus(0x12000007);
134pub const RNP_ERROR_RNG: RnpStatus = RnpStatus(0x12000008);
135pub const RNP_ERROR_SIGNING_FAILED: RnpStatus = RnpStatus(0x12000009);
136pub const RNP_ERROR_NO_SIGNATURES_FOUND: RnpStatus = RnpStatus(0x1200000a);
137
138pub const RNP_ERROR_SIGNATURE_EXPIRED: RnpStatus = RnpStatus(0x1200000b);
139
140pub const RNP_ERROR_NOT_ENOUGH_DATA: RnpStatus = RnpStatus(0x13000000);
142pub const RNP_ERROR_UNKNOWN_TAG: RnpStatus = RnpStatus(0x13000001);
143pub const RNP_ERROR_PACKET_NOT_CONSUMED: RnpStatus = RnpStatus(0x13000002);
144pub const RNP_ERROR_NO_USERID: RnpStatus = RnpStatus(0x13000003);
145pub const RNP_ERROR_EOF: RnpStatus = RnpStatus(0x13000004);
146
147#[derive(thiserror::Error, Debug, Clone)]
167pub enum Error {
168 #[error("Generic")]
169 Generic,
170 #[error("BadFormat")]
171 BadFormat,
172 #[error("BadParameters")]
173 BadParameters,
174 #[error("NotImplemented")]
175 NotImplemented,
176 #[error("NotSupported")]
177 NotSupported,
178 #[error("OutOfMemory")]
179 OutOfMemory,
180 #[error("ShortBuffer")]
181 ShortBuffer,
182 #[error("NullPointer")]
183 NullPointer,
184 #[error("Access")]
185 Access,
186 #[error("Read")]
187 Read,
188 #[error("Write")]
189 Write,
190 #[error("BadState")]
191 BadState,
192 #[error("MacInvalid")]
193 MacInvalid,
194 #[error("SignatureInvalid")]
195 SignatureInvalid,
196 #[error("KeyGeneration")]
197 KeyGeneration,
198 #[error("BadPassword")]
199 BadPassword,
200 #[error("KeyNotFound")]
201 KeyNotFound,
202 #[error("NoSuitableKey")]
203 NoSuitableKey,
204 #[error("DecryptFailed")]
205 DecryptFailed,
206 #[error("RNG")]
207 RNG,
208 #[error("SigningFailed")]
209 SigningFailed,
210 #[error("NoSignaturesFound")]
211 NoSignaturesFound,
212 #[error("SignatureExpired")]
213 SignatureExpired,
214 #[error("NotEnoughData")]
215 NotEnoughData,
216 #[error("UnknownTag")]
217 UnknownTag,
218 #[error("PacketNotConsumed")]
219 PacketNotConsumed,
220 #[error("NoUserID")]
221 NoUserID,
222 #[error("EOF")]
223 EOF,
224}
225
226impl From<Error> for RnpStatus {
227 fn from(e: Error) -> RnpStatus {
228 use Error::*;
229 match e {
230 Generic => RNP_ERROR_GENERIC,
231 BadFormat => RNP_ERROR_BAD_FORMAT,
232 BadParameters => RNP_ERROR_BAD_PARAMETERS,
233 NotImplemented => RNP_ERROR_NOT_IMPLEMENTED,
234 NotSupported => RNP_ERROR_NOT_SUPPORTED,
235 OutOfMemory => RNP_ERROR_OUT_OF_MEMORY,
236 ShortBuffer => RNP_ERROR_SHORT_BUFFER,
237 NullPointer => RNP_ERROR_NULL_POINTER,
238 Access => RNP_ERROR_ACCESS,
239 Read => RNP_ERROR_READ,
240 Write => RNP_ERROR_WRITE,
241 BadState => RNP_ERROR_BAD_STATE,
242 MacInvalid => RNP_ERROR_MAC_INVALID,
243 SignatureInvalid => RNP_ERROR_SIGNATURE_INVALID,
244 KeyGeneration => RNP_ERROR_KEY_GENERATION,
245 BadPassword => RNP_ERROR_BAD_PASSWORD,
246 KeyNotFound => RNP_ERROR_KEY_NOT_FOUND,
247 NoSuitableKey => RNP_ERROR_NO_SUITABLE_KEY,
248 DecryptFailed => RNP_ERROR_DECRYPT_FAILED,
249 RNG => RNP_ERROR_RNG,
250 SigningFailed => RNP_ERROR_SIGNING_FAILED,
251 NoSignaturesFound => RNP_ERROR_NO_SIGNATURES_FOUND,
252 SignatureExpired => RNP_ERROR_SIGNATURE_EXPIRED,
253 NotEnoughData => RNP_ERROR_NOT_ENOUGH_DATA,
254 UnknownTag => RNP_ERROR_UNKNOWN_TAG,
255 PacketNotConsumed => RNP_ERROR_PACKET_NOT_CONSUMED,
256 NoUserID => RNP_ERROR_NO_USERID,
257 EOF => RNP_ERROR_EOF,
258 }
259 }
260}
261
262
263pub type Result<T> = std::result::Result<T, RnpStatus>;
265
266pub fn log_internal<T: AsRef<str>>(text: T) {
268 let text = format!("{}: {}",
269 chrono::offset::Utc::now().format("%T"),
270 text.as_ref());
271
272 if cfg!(windows) {
273 use std::fs::File;
288 use std::io::Write;
289 use std::ops::DerefMut;
290 use std::sync::Mutex;
291 use std::sync::mpsc::channel;
292 use std::sync::mpsc::Sender;
293 use std::sync::mpsc::Receiver;
294
295 use crate::tbprofile::TBProfile;
296
297 struct State {
298 sender: Mutex<Sender<String>>,
299 output: Mutex<Option<(Receiver<String>, Option<File>)>>,
301 }
302
303 static LOGGER: OnceLock<State> = OnceLock::new();
304 let logger = LOGGER.get_or_init(
305 || {
306 let (sender, receiver) = channel();
307
308 State {
309 sender: Mutex::new(sender),
310 output: Mutex::new(Some((receiver, None))),
311 }
312 });
313
314 let mut logged = false;
315 if let Ok(mut guard) = logger.output.try_lock() {
316 let mut kill = false;
321 if let Some((receiver, ref mut ofd)) = guard.deref_mut() {
322 if ofd.is_none() {
323 if let Some(tbpath) = TBProfile::path() {
326 let path = tbpath.join("octopus.log");
329 if let Ok(fd) = File::create(&path) {
330 *ofd = Some(fd);
331 eprintln!("Logging to {:?}", path);
332 } else {
333 kill = true;
335 }
336 } else {
337 kill = true;
339 }
340 }
341
342 if let Some(fd) = ofd {
343 while let Ok(text) = receiver.try_recv() {
345 let _ = writeln!(fd, "{}", text);
346 }
347 let _ = writeln!(fd, "{}", text);
349 let _ = fd.flush();
350 logged = true;
351 }
352 }
353
354 if kill {
355 *guard = None;
356 }
357 } else {
358 if let Ok(_) = logger.sender.lock().unwrap().send(text.clone()) {
361 logged = true;
362 }
363 }
364
365 if ! logged {
366 eprintln!("{}", text);
370 }
371 } else {
372 eprintln!("{}", text);
374 }
375}
376
377pub fn full_tracing() -> bool {
378 static FULL_TRACING: OnceLock<bool> = OnceLock::new();
379 *FULL_TRACING.get_or_init(
380 || std::env::var("SEQUOIA_OCTOPUS_TRACING")
381 .map(|t| t == "full")
382 .unwrap_or(false))
383}
384
385pub fn call_tracing() -> bool {
386 static FULL_TRACING: OnceLock<bool> = OnceLock::new();
387 *FULL_TRACING.get_or_init(
388 || std::env::var("SEQUOIA_OCTOPUS_TRACING")
389 .map(|t| t == "call")
390 .unwrap_or(false))
391}
392
393macro_rules! rnp_function {
394 ( $fn_name: path, $TRACE: expr ) => {
395 #[allow(dead_code, unused_mut, unused_variables)]
396 let mut args: Vec<String> = Vec::new();
397
398 #[allow(unused_macros)]
399 macro_rules! arg {
400 ($arg: expr) => {
401 args.push(format!("{:?}", $arg))
402 };
403 }
404
405 #[allow(unused_macros)]
406 macro_rules! rnp_return_status {
407 ($status: expr) => {
408 return $status.epilogue(stringify!($fn_name), args)
409 };
410 }
411
412 #[allow(unused_macros)]
413 macro_rules! rnp_success {
414 () => {
415 rnp_return_status!(RNP_SUCCESS)
416 };
417 }
418
419 #[allow(unused_macros)]
420 macro_rules! _trace {
421 ( $msg: expr ) => {
422 if $TRACE && crate::error::full_tracing() {
423 log!("sequoia-octopus: TRACE: {}: {}",
424 stringify!($fn_name), $msg);
425 }
426 };
427 }
428
429 #[allow(unused_macros)]
433 macro_rules! t {
434 ( $fmt:expr ) =>
435 { _trace!( $fmt) };
436 ( $fmt:expr, $a:expr ) =>
437 { _trace!( format!($fmt, $a)) };
438 ( $fmt:expr, $a:expr, $b:expr ) =>
439 { _trace!( format!($fmt, $a, $b)) };
440 ( $fmt:expr, $a:expr, $b:expr, $c:expr ) =>
441 { _trace!( format!($fmt, $a, $b, $c)) };
442 ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr ) =>
443 { _trace!( format!($fmt, $a, $b, $c, $d)) };
444 ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr ) =>
445 { _trace!( format!($fmt, $a, $b, $c, $d, $e)) };
446 ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr ) =>
447 { _trace!( format!($fmt, $a, $b, $c, $d, $e, $f)) };
448 ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr ) =>
449 { _trace!( format!($fmt, $a, $b, $c, $d, $e, $f, $g)) };
450 ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr ) =>
451 { _trace!( format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h)) };
452 ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr ) =>
453 { _trace!( format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h, $i)) };
454 ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr, $j:expr ) =>
455 { _trace!( format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h, $i, $j)) };
456 ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr, $j:expr, $k:expr ) =>
457 { _trace!( format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k)) };
458 }
459
460 #[allow(unused_macros)]
461 macro_rules! warn {
462 ( $fmt: expr ) => {
473 log!(concat!("sequoia-octopus: ",
474 stringify!($fn_name),
475 ": ", $fmt));
476 };
477 ( $fmt: expr, $a: expr ) => {
478 log!(concat!("sequoia-octopus: ",
479 stringify!($fn_name),
480 ": ", $fmt),
481 $a);
482 };
483 ( $fmt: expr, $a: expr, $b: expr ) => {
484 log!(concat!("sequoia-octopus: ",
485 stringify!($fn_name),
486 ": ", $fmt),
487 $a, $b);
488 };
489 ( $fmt: expr, $a: expr, $b: expr, $c: expr ) => {
490 log!(concat!("sequoia-octopus: ",
491 stringify!($fn_name),
492 ": ", $fmt),
493 $a, $b, $c);
494 };
495 }
496
497 #[allow(unused_macros)]
498 macro_rules! assert_ptr_int {
499 ( $param: expr ) => {
500 if $param.is_null() {
501 warn!("parameter {:?} is NULL", stringify!($param));
502 rnp_return_status!(crate::error::RNP_ERROR_NULL_POINTER);
503 }
504 };
505 }
506
507 #[allow(unused_macros)]
508 macro_rules! assert_ptr {
509 ( $param: expr ) => {
510 arg!($param);
511 assert_ptr_int!($param);
512 };
513 }
514
515 #[allow(unused_macros)]
516 macro_rules! assert_ptr_ref {
517 ( $param: expr ) => {{
518 assert_ptr!($param);
519 &*$param
520 }};
521 }
522
523 #[allow(unused_macros)]
524 macro_rules! assert_ptr_mut {
525 ( $param: expr ) => {{
526 assert_ptr!($param);
527 &mut *$param
528 }};
529 }
530
531 #[allow(unused_macros)]
532 macro_rules! assert_str {
533 ( $param: expr ) => {
534 assert_str!($param, false)
535 };
536 ( confidential => $param: expr ) => {
537 assert_str!($param, true)
538 };
539 ( $param: expr, $confidential: expr ) => {{
540 assert_ptr_int!($param);
541 match std::ffi::CStr::from_ptr($param).to_str() {
542 Ok(s) => {
543 if $confidential {
544 arg!("<REDACTED>")
545 } else {
546 arg!(s);
547 }
548 s
549 },
550 Err(e) => {
551 warn!("parameter {:?} is not UTF8: {}",
552 stringify!($param), e);
553 rnp_return_status!(crate::error::RNP_ERROR_BAD_PARAMETERS);
554 }
555 }
556 }};
557 }
558
559 #[allow(unused_macros)]
560 macro_rules! rnp_try {
561 ( $result: expr ) => {
562 match $result {
563 Ok(v) => v,
564 Err(e) => rnp_return_status!(e),
565 }
566 };
567 }
568
569 #[allow(unused_macros)]
570 macro_rules! rnp_try_or {
571 ( $result: expr, $fail_with: expr ) => {
572 match $result {
573 Ok(v) => v,
574 Err(_) => {
575 let s: crate::error::RnpStatus = $fail_with;
576 rnp_return_status!(s);
577 },
578 }
579 };
580 }
581
582 #[allow(unused_macros)]
583 macro_rules! rnp_return {
584 ( $expr: expr ) => {
585 rnp_return_status!(if let Err(e) = $expr {
586 warn!("{}", e);
587 if let Ok(e) = e.downcast::<crate::error::Error>() {
588 e.into()
589 } else {
590 crate::error::RNP_ERROR_GENERIC
591 }
592 } else {
593 t!("Leaving function: success");
594 crate::error::RNP_SUCCESS
595 })
596 };
597 }
598
599 #[allow(unused_macros)]
600 macro_rules! thunderbird_workaround {
601 () => {{
602 if crate::THUNDERBIRD_WORKAROUND {
603 t!("Thunderbird-specific workaround in {}:{}",
604 file!(), line!());
605 }
606 crate::THUNDERBIRD_WORKAROUND
607 }};
608 }
609
610 if crate::error::full_tracing() {
611 t!("Entering function");
612 }
613 };
614}
615
616macro_rules! global_warn {
617 ( $fmt: expr $(, $a: expr )* ) => {
618 log!(concat!("sequoia-octopus: ", $fmt)
619 $(, $a )*)
620 };
621}
622
623macro_rules! global_rnp_try_or {
624 ( $result: expr, $fail_with: expr ) => {
625 match $result {
626 Ok(v) => v,
627 Err(e) => {
628 global_warn!("{}", e);
629 return $fail_with;
630 },
631 }
632 };
633}