Skip to main content

sequoia_octopus_librnp/
error.rs

1//! Error handling.
2
3use std::{
4    fmt,
5    sync::OnceLock,
6};
7
8// Like eprintln!
9macro_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
18/// Native RNP result type.
19///
20/// This is what the RNP functions return.
21pub type RnpResult = u32;
22
23/// A wrapped RnpResult.
24///
25/// This is the type of our status code constants.  By using a type
26/// distinct from [`RnpResult`], we force a conversion through
27/// [`RnpStatus::epilogue`] (or [`RnpStatus::quiet_epilogue`]).
28#[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) // Forward.
34    }
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    /// Does this status denote success?
76    pub fn success(&self) -> bool {
77        self == &RNP_SUCCESS
78    }
79
80    /// On failure, prints a trace message in debug builds.
81    ///
82    /// Returns the [`RnpResult`] suitable as return value for RNP
83    /// functions.
84    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    /// Does not print a trace message.
95    ///
96    /// Returns the [`RnpResult`] suitable as return value for RNP
97    /// functions.
98    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
110// Common error codes
111pub 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
120// Storage
121pub const RNP_ERROR_ACCESS: RnpStatus = RnpStatus(0x11000000);
122pub const RNP_ERROR_READ: RnpStatus = RnpStatus(0x11000001);
123pub const RNP_ERROR_WRITE: RnpStatus = RnpStatus(0x11000002);
124
125// Crypto
126pub 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
140// Parsing
141pub 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/// Rustic-errors resembling the native RNP errors.
148///
149/// These errors can be used in functions returning standard errors to
150/// return a specific native RNP error.
151///
152/// # Examples
153///
154/// ```rust,no-compile
155/// #[no_mangle] pub unsafe extern "C"
156/// fn rnp_something(rnp_key: *mut RnpKey) -> RnpResult {
157///     rnp_function!(rnp_key_protect, crate::TRACE);
158///
159///     let f = || -> openpgp::Result<()> {
160///         Err(Error::NotImplemented)
161///     };
162///
163///     rnp_return!(f())
164/// }
165/// ```
166#[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
263// Used by helper functions.
264pub type Result<T> = std::result::Result<T, RnpStatus>;
265
266//#[cfg(windows)]
267pub 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        // Save messages to a log file in the current profile's
274        // directory (.../.thunderbird/$PROFILE/octopus.log).
275        //
276        // This is a bit hairy, because the code needs to be
277        // reentrant: to initialize the logger's file description, we
278        // need the location of the current profile, but finding that
279        // location also uses the logging functionality.
280        //
281        // To break this cycle, if the logger is locked, rather than
282        // wait for the lock, we simply enqueue the message in a
283        // channel.  Then when we actually have the lock, we first
284        // print any messages queued in the channel and then print out
285        // our own message.
286
287        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            // If None, the file has not yet been opened.
300            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            // We got the lock.
317
318            // If initialization fails, we set output to None.  But
319            // since it is borrowed, we need to delay it.
320            let mut kill = false;
321            if let Some((receiver, ref mut ofd)) = guard.deref_mut() {
322                if ofd.is_none() {
323                    // We need to initialize the file descriptor.
324
325                    if let Some(tbpath) = TBProfile::path() {
326                        // We found a TB profile.  Let's try to open the
327                        // log file.
328                        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                            // We failed to open the file :/
334                            kill = true;
335                        }
336                    } else {
337                        // We failed to find the TBProfile :/
338                        kill = true;
339                    }
340                }
341
342                if let Some(fd) = ofd {
343                    // First, drain the message queue.
344                    while let Ok(text) = receiver.try_recv() {
345                        let _ = writeln!(fd, "{}", text);
346                    }
347                    // Then print our own message.
348                    let _ = writeln!(fd, "{}", text);
349                    let _ = fd.flush();
350                    logged = true;
351                }
352            }
353
354            if kill {
355                *guard = None;
356            }
357        } else {
358            // Locked.  Enqueue the message for later.  If we can't
359            // send, it means initialization failed so just ignore.
360            if let Ok(_) = logger.sender.lock().unwrap().send(text.clone()) {
361                logged = true;
362            }
363        }
364
365        if ! logged {
366            // Something went wrong.  Just send it to stderr, which
367            // probably won't do anything on Windows, but if we are
368            // debugging on another platform, that will be useful.
369            eprintln!("{}", text);
370        }
371    } else {
372        // Just write to stderr.
373        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        // Currently, Rust doesn't support $( ... ) in a nested
430        // macro's definition.  See:
431        // https://users.rust-lang.org/t/nested-macros-issue/8348/2
432        #[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            // Currently, Rust doesn't support $( ... ) in a nested
463            // macro's definition.  See:
464            // https://users.rust-lang.org/t/nested-macros-issue/8348/2
465            //
466            //( $fmt: expr $(, $a: expr )* ) => {
467            //    eprintln!(concat!("sequoia-octopus: ",
468            //                      stringify!($fn_name),
469            //                      ": ", $fmt)
470            //              $(, $a )*);
471            //};
472            ( $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}