1
//! Error types.
2

            
3
use std::error;
4
use std::fmt;
5

            
6
use cssparser::{BasicParseError, BasicParseErrorKind, ParseErrorKind, ToCss};
7
use markup5ever::QualName;
8

            
9
#[cfg(doc)]
10
use crate::RenderingError;
11

            
12
use crate::document::NodeId;
13
use crate::io::IoError;
14
use crate::limits;
15
use crate::node::Node;
16

            
17
/// A short-lived error.
18
///
19
/// The lifetime of the error is the same as the `cssparser::ParserInput` that
20
/// was used to create a `cssparser::Parser`.  That is, it is the lifetime of
21
/// the string data that is being parsed.
22
///
23
/// The code flow will sometimes require preserving this error as a long-lived struct;
24
/// see the `impl<'i, O> AttributeResultExt<O> for Result<O, ParseError<'i>>` for that
25
/// purpose.
26
pub type ParseError<'i> = cssparser::ParseError<'i, ValueErrorKind>;
27

            
28
/// A simple error which refers to an attribute's value
29
#[derive(Debug, Clone)]
30
pub enum ValueErrorKind {
31
    /// A property with the specified name was not found
32
    UnknownProperty,
33

            
34
    /// The value could not be parsed
35
    Parse(String),
36

            
37
    // The value could be parsed, but is invalid
38
    Value(String),
39
}
40

            
41
impl ValueErrorKind {
42
343
    pub fn parse_error(s: &str) -> ValueErrorKind {
43
343
        ValueErrorKind::Parse(s.to_string())
44
343
    }
45

            
46
13
    pub fn value_error(s: &str) -> ValueErrorKind {
47
13
        ValueErrorKind::Value(s.to_string())
48
13
    }
49
}
50

            
51
impl fmt::Display for ValueErrorKind {
52
7
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53
7
        match *self {
54
            ValueErrorKind::UnknownProperty => write!(f, "unknown property name"),
55

            
56
7
            ValueErrorKind::Parse(ref s) => write!(f, "parse error: {s}"),
57

            
58
            ValueErrorKind::Value(ref s) => write!(f, "invalid value: {s}"),
59
        }
60
7
    }
61
}
62

            
63
impl<'a> From<BasicParseError<'a>> for ValueErrorKind {
64
    fn from(e: BasicParseError<'_>) -> ValueErrorKind {
65
        let BasicParseError { kind, .. } = e;
66

            
67
        let msg = match kind {
68
            BasicParseErrorKind::UnexpectedToken(_) => "unexpected token",
69
            BasicParseErrorKind::EndOfInput => "unexpected end of input",
70
            BasicParseErrorKind::AtRuleInvalid(_) => "invalid @-rule",
71
            BasicParseErrorKind::AtRuleBodyInvalid => "invalid @-rule body",
72
            BasicParseErrorKind::QualifiedRuleInvalid => "invalid qualified rule",
73
        };
74

            
75
        ValueErrorKind::parse_error(msg)
76
    }
77
}
78

            
79
/// A complete error for an attribute and its erroneous value
80
#[derive(Debug, Clone)]
81
pub struct ElementError {
82
    pub attr: QualName,
83
    pub err: ValueErrorKind,
84
}
85

            
86
impl fmt::Display for ElementError {
87
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88
        write!(f, "{:?}: {}", self.attr.expanded(), self.err)
89
    }
90
}
91

            
92
/// Errors returned when looking up a resource by URL reference.
93
#[derive(Debug, Clone)]
94
pub enum DefsLookupErrorKind {
95
    /// Error when parsing the id to lookup.
96
    InvalidId,
97

            
98
    /// For internal use only.
99
    ///
100
    // FIXME: this is returned internally from Handle.lookup_node(), and gets translated
101
    // to Ok(false).  Don't expose this internal code in the public API.
102
    NotFound,
103
}
104

            
105
impl fmt::Display for DefsLookupErrorKind {
106
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107
        match *self {
108
            DefsLookupErrorKind::InvalidId => write!(f, "invalid id"),
109
            DefsLookupErrorKind::NotFound => write!(f, "not found"),
110
        }
111
    }
112
}
113

            
114
/// Errors that can happen while rendering or measuring an SVG document.
115
///
116
/// This is the internal version of [`crate::api::RenderingError`]; they are the same
117
/// except that this one has an `InvalidTransform` variant which is only propagated
118
/// internally.  It is caught during the drawing process, and the element in question
119
/// is simply not drawn, more or less per <https://www.w3.org/TR/css-transforms-1/#transform-function-lists>
120
///
121
///   "If a transform function causes the current transformation matrix of an
122
///   object to be non-invertible, the object and its content do not get
123
///   displayed."
124
#[derive(Clone)]
125
pub enum InternalRenderingError {
126
    /// An error from the rendering backend.
127
    Rendering(String),
128

            
129
    /// A particular implementation-defined limit was exceeded.
130
    LimitExceeded(ImplementationLimit),
131

            
132
    /// A non-invertible transform was generated.
133
    ///
134
    /// This should not be a fatal error; we should catch it and just not render
135
    /// the problematic element.
136
    InvalidTransform,
137

            
138
    CircularReference(Node),
139

            
140
    /// Tried to reference an SVG element that does not exist.
141
    IdNotFound,
142

            
143
    /// Tried to reference an SVG element from a fragment identifier that is incorrect.
144
    InvalidId(String),
145

            
146
    /// Not enough memory was available for rendering.
147
    OutOfMemory(String),
148

            
149
    /// The rendering was interrupted via a [`gio::Cancellable`].
150
    Cancelled,
151
}
152

            
153
impl From<DefsLookupErrorKind> for InternalRenderingError {
154
    fn from(e: DefsLookupErrorKind) -> InternalRenderingError {
155
        match e {
156
            DefsLookupErrorKind::NotFound => InternalRenderingError::IdNotFound,
157
            _ => InternalRenderingError::InvalidId(format!("{e}")),
158
        }
159
    }
160
}
161

            
162
impl From<InvalidTransform> for InternalRenderingError {
163
2
    fn from(_: InvalidTransform) -> InternalRenderingError {
164
2
        InternalRenderingError::InvalidTransform
165
2
    }
166
}
167

            
168
impl fmt::Display for InternalRenderingError {
169
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170
        match *self {
171
            InternalRenderingError::Rendering(ref s) => write!(f, "rendering error: {s}"),
172
            InternalRenderingError::LimitExceeded(ref l) => write!(f, "{l}"),
173
            InternalRenderingError::InvalidTransform => write!(f, "invalid transform"),
174
            InternalRenderingError::CircularReference(ref c) => {
175
                write!(f, "circular reference in element {c}")
176
            }
177
            InternalRenderingError::IdNotFound => write!(f, "element id not found"),
178
            InternalRenderingError::InvalidId(ref s) => write!(f, "invalid id: {s:?}"),
179
            InternalRenderingError::OutOfMemory(ref s) => write!(f, "out of memory: {s}"),
180
            InternalRenderingError::Cancelled => write!(f, "rendering cancelled"),
181
        }
182
    }
183
}
184

            
185
impl From<cairo::Error> for InternalRenderingError {
186
    fn from(e: cairo::Error) -> InternalRenderingError {
187
        InternalRenderingError::Rendering(format!("{e:?}"))
188
    }
189
}
190

            
191
/// Indicates that a transform is not invertible.
192
///
193
/// This generally represents an error from [`crate::transform::ValidTransform::try_from`], which is what we use
194
/// to check affine transforms for validity.
195
1
#[derive(Debug, PartialEq)]
196
pub struct InvalidTransform;
197

            
198
/// Errors from [`crate::document::AcquiredNodes`].
199
pub enum AcquireError {
200
    /// An element with the specified id was not found.
201
    LinkNotFound(NodeId),
202

            
203
    InvalidLinkType(NodeId),
204

            
205
    /// A circular reference was detected; non-fatal error.
206
    ///
207
    /// Callers are expected to treat the offending element as invalid, for example
208
    /// if a graphic element uses a pattern fill, but the pattern in turn includes
209
    /// another graphic element that references the same pattern.
210
    ///
211
    /// ```xml
212
    /// <pattern id="foo">
213
    ///   <rect width="1" height="1" fill="url(#foo)"/>
214
    /// </pattern>
215
    /// ```
216
    CircularReference(Node),
217

            
218
    /// Too many referenced objects were resolved; fatal error.
219
    ///
220
    /// Callers are expected to exit as early as possible and return an error to
221
    /// the public API.  See [`ImplementationLimit::TooManyReferencedElements`] for details.
222
    MaxReferencesExceeded,
223
}
224

            
225
impl fmt::Display for AcquireError {
226
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227
        match *self {
228
            AcquireError::LinkNotFound(ref frag) => write!(f, "link not found: {frag}"),
229

            
230
            AcquireError::InvalidLinkType(ref frag) => {
231
                write!(f, "link \"{frag}\" is to object of invalid type")
232
            }
233

            
234
            AcquireError::CircularReference(ref node) => {
235
                write!(f, "circular reference in node {node}")
236
            }
237

            
238
            AcquireError::MaxReferencesExceeded => {
239
                write!(f, "maximum number of references exceeded")
240
            }
241
        }
242
    }
243
}
244

            
245
/// Helper for converting `Result<O, E>` into `Result<O, ElementError>`
246
///
247
/// A `ElementError` requires a `QualName` that corresponds to the attribute to which the
248
/// error refers, plus the actual `ValueErrorKind` that describes the error.  However,
249
/// parsing functions for attribute value types will want to return their own kind of
250
/// error, instead of `ValueErrorKind`.  If that particular error type has an `impl
251
/// From<FooError> for ValueErrorKind`, then this trait helps assign attribute values in
252
/// `set_atts()` methods as follows:
253
///
254
/// ```
255
/// # use rsvg::doctest_only::AttributeResultExt;
256
/// # use rsvg::doctest_only::ValueErrorKind;
257
/// # use rsvg::doctest_only::ElementError;
258
/// # use markup5ever::{QualName, Prefix, Namespace, LocalName};
259
/// # type FooError = ValueErrorKind;
260
/// fn parse_foo(value: &str) -> Result<(), FooError>
261
/// # { Err(ValueErrorKind::value_error("test")) }
262
///
263
/// // It is assumed that there is an impl From<FooError> for ValueErrorKind
264
/// # let attr = QualName::new(
265
/// #     Some(Prefix::from("")),
266
/// #     Namespace::from(""),
267
/// #     LocalName::from(""),
268
/// # );
269
/// let result = parse_foo("value").attribute(attr);
270
/// assert!(result.is_err());
271
/// # Ok::<(), ElementError>(())
272
/// ```
273
///
274
/// The call to `.attribute(attr)` converts the `Result` from `parse_foo()` into a full
275
/// `ElementError` with the provided `attr`.
276
pub trait AttributeResultExt<O> {
277
    fn attribute(self, attr: QualName) -> Result<O, ElementError>;
278
}
279

            
280
impl<O, E: Into<ValueErrorKind>> AttributeResultExt<O> for Result<O, E> {
281
1646
    fn attribute(self, attr: QualName) -> Result<O, ElementError> {
282
1646
        self.map_err(|e| e.into())
283
1646
            .map_err(|err| ElementError { attr, err })
284
1646
    }
285
}
286

            
287
/// Turns a short-lived `ParseError` into a long-lived `ElementError`
288
impl<'i, O> AttributeResultExt<O> for Result<O, ParseError<'i>> {
289
7575
    fn attribute(self, attr: QualName) -> Result<O, ElementError> {
290
7585
        self.map_err(|e| {
291
            // FIXME: eventually, here we'll want to preserve the location information
292

            
293
            let ParseError {
294
10
                kind,
295
10
                location: _location,
296
10
            } = e;
297

            
298
10
            match kind {
299
8
                ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(tok)) => {
300
8
                    let mut s = String::from("unexpected token '");
301
8
                    tok.to_css(&mut s).unwrap(); // FIXME: what do we do with a fmt::Error?
302
8
                    s.push('\'');
303

            
304
8
                    ElementError {
305
8
                        attr,
306
8
                        err: ValueErrorKind::Parse(s),
307
                    }
308
8
                }
309

            
310
1
                ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => ElementError {
311
1
                    attr,
312
1
                    err: ValueErrorKind::parse_error("unexpected end of input"),
313
1
                },
314

            
315
                ParseErrorKind::Basic(_) => {
316
                    unreachable!("attribute parsers should not return errors for CSS rules")
317
                }
318

            
319
1
                ParseErrorKind::Custom(err) => ElementError { attr, err },
320
            }
321
10
        })
322
7575
    }
323
}
324

            
325
/// Errors returned when resolving an URL
326
#[derive(Debug, Clone)]
327
pub enum AllowedUrlError {
328
    /// parsing error from `Url::parse()`
329
108
    UrlParseError(url::ParseError),
330

            
331
    /// A base file/uri was not set
332
    BaseRequired,
333

            
334
    /// Cannot reference a file with a different URI scheme from the base file
335
    DifferentUriSchemes,
336

            
337
    /// Some scheme we don't allow loading
338
    DisallowedScheme,
339

            
340
    /// The requested file is not in the same directory as the base file,
341
    /// or in one directory below the base file.
342
    NotSiblingOrChildOfBaseFile,
343

            
344
    /// Loaded file:// URLs cannot have a query part, e.g. `file:///foo?blah`
345
    NoQueriesAllowed,
346

            
347
    /// URLs may not have fragment identifiers at this stage
348
    NoFragmentIdentifierAllowed,
349

            
350
    /// Error when obtaining the file path or the base file path
351
    InvalidPath,
352

            
353
    /// The base file cannot be the root of the file system
354
    BaseIsRoot,
355

            
356
    /// Error when canonicalizing either the file path or the base file path
357
    CanonicalizationError,
358
}
359

            
360
impl fmt::Display for AllowedUrlError {
361
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362
        use AllowedUrlError::*;
363
        match *self {
364
            UrlParseError(e) => write!(f, "URL parse error: {e}"),
365
            BaseRequired => write!(f, "base required"),
366
            DifferentUriSchemes => write!(f, "different URI schemes"),
367
            DisallowedScheme => write!(f, "disallowed scheme"),
368
            NotSiblingOrChildOfBaseFile => write!(f, "not sibling or child of base file"),
369
            NoQueriesAllowed => write!(f, "no queries allowed"),
370
            NoFragmentIdentifierAllowed => write!(f, "no fragment identifier allowed"),
371
            InvalidPath => write!(f, "invalid path"),
372
            BaseIsRoot => write!(f, "base is root"),
373
            CanonicalizationError => write!(f, "canonicalization error"),
374
        }
375
    }
376
}
377

            
378
/// Errors returned when creating a `NodeId` out of a string
379
#[derive(Debug, Clone)]
380
pub enum NodeIdError {
381
    NodeIdRequired,
382
}
383

            
384
impl From<NodeIdError> for ValueErrorKind {
385
2
    fn from(e: NodeIdError) -> ValueErrorKind {
386
        match e {
387
            NodeIdError::NodeIdRequired => {
388
2
                ValueErrorKind::value_error("fragment identifier required")
389
            }
390
        }
391
2
    }
392
}
393

            
394
/// Errors that can happen while loading an SVG document.
395
///
396
/// All of these codes are for unrecoverable errors that keep an SVG document from being
397
/// fully loaded and parsed.  Note that SVG is very lenient with respect to document
398
/// structure and the syntax of CSS property values; most errors there will not lead to a
399
/// `LoadingError`.  To see those errors, you may want to set the `RSVG_LOG=1` environment
400
/// variable.
401
///
402
/// I/O errors get reported in the `Glib` variant, since librsvg uses GIO internally for
403
/// all input/output.
404
#[non_exhaustive]
405
196
#[derive(Debug, Clone)]
406
pub enum LoadingError {
407
    /// XML syntax error.
408
179
    XmlParseError(String),
409

            
410
    /// Not enough memory to load the document.
411
    OutOfMemory(String),
412

            
413
    /// A malformed or disallowed URL was used.
414
    BadUrl,
415

            
416
    /// An invalid stylesheet was used.
417
    BadCss,
418

            
419
    /// There is no `<svg>` root element in the XML.
420
    NoSvgRoot,
421

            
422
    /// I/O error.
423
    Io(String),
424

            
425
    /// A particular implementation-defined limit was exceeded.
426
1
    LimitExceeded(ImplementationLimit),
427

            
428
    /// Catch-all for loading errors.
429
16
    Other(String),
430
}
431

            
432
/// Errors for implementation-defined limits, to mitigate malicious SVG documents.
433
///
434
/// These get emitted as [`LoadingError::LimitExceeded`] or [`RenderingError::LimitExceeded`].
435
/// The limits are present to mitigate malicious SVG documents which may try to exhaust
436
/// all available memory, or which would use large amounts of CPU time.
437
#[non_exhaustive]
438
1
#[derive(Debug, Copy, Clone)]
439
pub enum ImplementationLimit {
440
    /// Document exceeded the maximum number of times that elements
441
    /// can be referenced through URL fragments.
442
    ///
443
    /// This is a mitigation for malicious documents that attempt to
444
    /// consume exponential amounts of CPU time by creating millions
445
    /// of references to SVG elements.  For example, the `<use>` and
446
    /// `<pattern>` elements allow referencing other elements, which
447
    /// can in turn reference other elements.  This can be used to
448
    /// create documents which would require exponential amounts of
449
    /// CPU time to be rendered.
450
    ///
451
    /// Librsvg deals with both cases by placing a limit on how many
452
    /// references will be resolved during the SVG rendering process,
453
    /// that is, how many `url(#foo)` will be resolved.
454
    ///
455
    /// These malicious documents are similar to the XML
456
    /// [billion laughs attack], but done with SVG's referencing features.
457
    ///
458
    /// See issues
459
    /// [#323](https://gitlab.gnome.org/GNOME/librsvg/issues/323) and
460
    /// [#515](https://gitlab.gnome.org/GNOME/librsvg/issues/515) for
461
    /// examples for the `<use>` and `<pattern>` elements,
462
    /// respectively.
463
    ///
464
    /// [billion laughs attack]: https://bitbucket.org/tiran/defusedxml
465
    TooManyReferencedElements,
466

            
467
    /// Document exceeded the maximum number of elements that can be loaded.
468
    ///
469
    /// This is a mitigation for SVG files which create millions of
470
    /// elements in an attempt to exhaust memory.  Librsvg does not't
471
    /// allow loading more than a certain number of elements during
472
    /// the initial loading process.
473
    TooManyLoadedElements,
474

            
475
    /// Document exceeded the number of attributes that can be attached to
476
    /// an element.
477
    ///
478
    /// This is here because librsvg uses u16 to address attributes. It should
479
    /// be essentially impossible to actually hit this limit, because the
480
    /// number of attributes that the SVG standard ascribes meaning to are
481
    /// lower than this limit.
482
    TooManyAttributes,
483

            
484
    /// Document exceeded the maximum nesting level while rendering.
485
    ///
486
    /// Rendering is a recursive process, and there is a limit of how deep layers can
487
    /// nest.  This is to avoid malicious SVGs which try to have layers that are nested
488
    /// extremely deep, as this could cause stack exhaustion.
489
    MaximumLayerNestingDepthExceeded,
490
}
491

            
492
impl error::Error for LoadingError {}
493

            
494
impl fmt::Display for LoadingError {
495
3
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
496
3
        match *self {
497
3
            LoadingError::XmlParseError(ref s) => write!(f, "XML parse error: {s}"),
498
            LoadingError::OutOfMemory(ref s) => write!(f, "out of memory: {s}"),
499
            LoadingError::BadUrl => write!(f, "invalid URL"),
500
            LoadingError::BadCss => write!(f, "invalid CSS"),
501
            LoadingError::NoSvgRoot => write!(f, "XML does not have <svg> root"),
502
            LoadingError::Io(ref s) => write!(f, "I/O error: {s}"),
503
            LoadingError::LimitExceeded(ref l) => write!(f, "{l}"),
504
            LoadingError::Other(ref s) => write!(f, "{s}"),
505
        }
506
3
    }
507
}
508

            
509
impl From<glib::Error> for LoadingError {
510
    fn from(e: glib::Error) -> LoadingError {
511
        // FIXME: this is somewhat fishy; not all GError are I/O errors, but in librsvg
512
        // most GError do come from gio.  Some come from GdkPixbufLoader, though.
513
        LoadingError::Io(format!("{e}"))
514
    }
515
}
516

            
517
impl From<IoError> for LoadingError {
518
    fn from(e: IoError) -> LoadingError {
519
        match e {
520
            IoError::BadDataUrl => LoadingError::BadUrl,
521
            IoError::Glib(e) => LoadingError::Io(format!("{e}")),
522
        }
523
    }
524
}
525

            
526
impl fmt::Display for ImplementationLimit {
527
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528
        match *self {
529
            ImplementationLimit::TooManyReferencedElements => write!(
530
                f,
531
                "exceeded more than {} referenced elements",
532
                limits::MAX_REFERENCED_ELEMENTS
533
            ),
534

            
535
            ImplementationLimit::TooManyLoadedElements => write!(
536
                f,
537
                "cannot load more than {} XML elements",
538
                limits::MAX_LOADED_ELEMENTS
539
            ),
540

            
541
            ImplementationLimit::TooManyAttributes => write!(
542
                f,
543
                "cannot load more than {} XML attributes",
544
                limits::MAX_LOADED_ATTRIBUTES
545
            ),
546

            
547
            ImplementationLimit::MaximumLayerNestingDepthExceeded => write!(
548
                f,
549
                "maximum depth of {} nested layers has been exceeded",
550
                limits::MAX_LAYER_NESTING_DEPTH,
551
            ),
552
        }
553
    }
554
}