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
342
    pub fn parse_error(s: &str) -> ValueErrorKind {
43
342
        ValueErrorKind::Parse(s.to_string())
44
342
    }
45

            
46
12
    pub fn value_error(s: &str) -> ValueErrorKind {
47
12
        ValueErrorKind::Value(s.to_string())
48
12
    }
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(Debug, 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
    /// Tried to reference an SVG element that does not exist.
139
    IdNotFound,
140

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

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

            
148
impl From<DefsLookupErrorKind> for InternalRenderingError {
149
    fn from(e: DefsLookupErrorKind) -> InternalRenderingError {
150
        match e {
151
            DefsLookupErrorKind::NotFound => InternalRenderingError::IdNotFound,
152
            _ => InternalRenderingError::InvalidId(format!("{e}")),
153
        }
154
    }
155
}
156

            
157
impl From<InvalidTransform> for InternalRenderingError {
158
2
    fn from(_: InvalidTransform) -> InternalRenderingError {
159
2
        InternalRenderingError::InvalidTransform
160
2
    }
161
}
162

            
163
impl fmt::Display for InternalRenderingError {
164
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165
        match *self {
166
            InternalRenderingError::Rendering(ref s) => write!(f, "rendering error: {s}"),
167
            InternalRenderingError::LimitExceeded(ref l) => write!(f, "{l}"),
168
            InternalRenderingError::InvalidTransform => write!(f, "invalid transform"),
169
            InternalRenderingError::IdNotFound => write!(f, "element id not found"),
170
            InternalRenderingError::InvalidId(ref s) => write!(f, "invalid id: {s:?}"),
171
            InternalRenderingError::OutOfMemory(ref s) => write!(f, "out of memory: {s}"),
172
        }
173
    }
174
}
175

            
176
impl From<cairo::Error> for InternalRenderingError {
177
    fn from(e: cairo::Error) -> InternalRenderingError {
178
        InternalRenderingError::Rendering(format!("{e:?}"))
179
    }
180
}
181

            
182
/// Indicates that a transform is not invertible.
183
///
184
/// This generally represents an error from [`crate::transform::ValidTransform::try_from`], which is what we use
185
/// to check affine transforms for validity.
186
1
#[derive(Debug, PartialEq)]
187
pub struct InvalidTransform;
188

            
189
/// Errors from [`crate::document::AcquiredNodes`].
190
pub enum AcquireError {
191
    /// An element with the specified id was not found.
192
    LinkNotFound(NodeId),
193

            
194
    InvalidLinkType(NodeId),
195

            
196
    /// A circular reference was detected; non-fatal error.
197
    ///
198
    /// Callers are expected to treat the offending element as invalid, for example
199
    /// if a graphic element uses a pattern fill, but the pattern in turn includes
200
    /// another graphic element that references the same pattern.
201
    ///
202
    /// ```xml
203
    /// <pattern id="foo">
204
    ///   <rect width="1" height="1" fill="url(#foo)"/>
205
    /// </pattern>
206
    /// ```
207
    CircularReference(Node),
208

            
209
    /// Too many referenced objects were resolved; fatal error.
210
    ///
211
    /// Callers are expected to exit as early as possible and return an error to
212
    /// the public API.  See [`ImplementationLimit::TooManyReferencedElements`] for details.
213
    MaxReferencesExceeded,
214
}
215

            
216
impl fmt::Display for AcquireError {
217
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218
        match *self {
219
            AcquireError::LinkNotFound(ref frag) => write!(f, "link not found: {frag}"),
220

            
221
            AcquireError::InvalidLinkType(ref frag) => {
222
                write!(f, "link \"{frag}\" is to object of invalid type")
223
            }
224

            
225
            AcquireError::CircularReference(ref node) => {
226
                write!(f, "circular reference in node {node}")
227
            }
228

            
229
            AcquireError::MaxReferencesExceeded => {
230
                write!(f, "maximum number of references exceeded")
231
            }
232
        }
233
    }
234
}
235

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

            
271
impl<O, E: Into<ValueErrorKind>> AttributeResultExt<O> for Result<O, E> {
272
542
    fn attribute(self, attr: QualName) -> Result<O, ElementError> {
273
542
        self.map_err(|e| e.into())
274
542
            .map_err(|err| ElementError { attr, err })
275
542
    }
276
}
277

            
278
/// Turns a short-lived `ParseError` into a long-lived `ElementError`
279
impl<'i, O> AttributeResultExt<O> for Result<O, ParseError<'i>> {
280
7565
    fn attribute(self, attr: QualName) -> Result<O, ElementError> {
281
7568
        self.map_err(|e| {
282
            // FIXME: eventually, here we'll want to preserve the location information
283

            
284
            let ParseError {
285
3
                kind,
286
3
                location: _location,
287
3
            } = e;
288

            
289
3
            match kind {
290
1
                ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(tok)) => {
291
1
                    let mut s = String::from("unexpected token '");
292
1
                    tok.to_css(&mut s).unwrap(); // FIXME: what do we do with a fmt::Error?
293
1
                    s.push('\'');
294

            
295
1
                    ElementError {
296
1
                        attr,
297
1
                        err: ValueErrorKind::Parse(s),
298
                    }
299
1
                }
300

            
301
1
                ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => ElementError {
302
1
                    attr,
303
1
                    err: ValueErrorKind::parse_error("unexpected end of input"),
304
1
                },
305

            
306
                ParseErrorKind::Basic(_) => {
307
                    unreachable!("attribute parsers should not return errors for CSS rules")
308
                }
309

            
310
1
                ParseErrorKind::Custom(err) => ElementError { attr, err },
311
            }
312
3
        })
313
7565
    }
314
}
315

            
316
/// Errors returned when resolving an URL
317
#[derive(Debug, Clone)]
318
pub enum AllowedUrlError {
319
    /// parsing error from `Url::parse()`
320
2
    UrlParseError(url::ParseError),
321

            
322
    /// A base file/uri was not set
323
    BaseRequired,
324

            
325
    /// Cannot reference a file with a different URI scheme from the base file
326
    DifferentUriSchemes,
327

            
328
    /// Some scheme we don't allow loading
329
    DisallowedScheme,
330

            
331
    /// The requested file is not in the same directory as the base file,
332
    /// or in one directory below the base file.
333
    NotSiblingOrChildOfBaseFile,
334

            
335
    /// Loaded file:// URLs cannot have a query part, e.g. `file:///foo?blah`
336
    NoQueriesAllowed,
337

            
338
    /// URLs may not have fragment identifiers at this stage
339
    NoFragmentIdentifierAllowed,
340

            
341
    /// Error when obtaining the file path or the base file path
342
    InvalidPath,
343

            
344
    /// The base file cannot be the root of the file system
345
    BaseIsRoot,
346

            
347
    /// Error when canonicalizing either the file path or the base file path
348
    CanonicalizationError,
349
}
350

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

            
369
/// Errors returned when creating a `NodeId` out of a string
370
#[derive(Debug, Clone)]
371
pub enum NodeIdError {
372
    NodeIdRequired,
373
}
374

            
375
impl From<NodeIdError> for ValueErrorKind {
376
1
    fn from(e: NodeIdError) -> ValueErrorKind {
377
        match e {
378
            NodeIdError::NodeIdRequired => {
379
1
                ValueErrorKind::value_error("fragment identifier required")
380
            }
381
        }
382
1
    }
383
}
384

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

            
401
    /// Not enough memory to load the document.
402
    OutOfMemory(String),
403

            
404
    /// A malformed or disallowed URL was used.
405
    BadUrl,
406

            
407
    /// An invalid stylesheet was used.
408
    BadCss,
409

            
410
    /// There is no `<svg>` root element in the XML.
411
    NoSvgRoot,
412

            
413
    /// I/O error.
414
    Io(String),
415

            
416
    /// A particular implementation-defined limit was exceeded.
417
1
    LimitExceeded(ImplementationLimit),
418

            
419
    /// Catch-all for loading errors.
420
2
    Other(String),
421
}
422

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

            
458
    /// Document exceeded the maximum number of elements that can be loaded.
459
    ///
460
    /// This is a mitigation for SVG files which create millions of
461
    /// elements in an attempt to exhaust memory.  Librsvg does not't
462
    /// allow loading more than a certain number of elements during
463
    /// the initial loading process.
464
    TooManyLoadedElements,
465

            
466
    /// Document exceeded the number of attributes that can be attached to
467
    /// an element.
468
    ///
469
    /// This is here because librsvg uses u16 to address attributes. It should
470
    /// be essentially impossible to actually hit this limit, because the
471
    /// number of attributes that the SVG standard ascribes meaning to are
472
    /// lower than this limit.
473
    TooManyAttributes,
474
}
475

            
476
impl error::Error for LoadingError {}
477

            
478
impl fmt::Display for LoadingError {
479
3
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
480
3
        match *self {
481
3
            LoadingError::XmlParseError(ref s) => write!(f, "XML parse error: {s}"),
482
            LoadingError::OutOfMemory(ref s) => write!(f, "out of memory: {s}"),
483
            LoadingError::BadUrl => write!(f, "invalid URL"),
484
            LoadingError::BadCss => write!(f, "invalid CSS"),
485
            LoadingError::NoSvgRoot => write!(f, "XML does not have <svg> root"),
486
            LoadingError::Io(ref s) => write!(f, "I/O error: {s}"),
487
            LoadingError::LimitExceeded(ref l) => write!(f, "{l}"),
488
            LoadingError::Other(ref s) => write!(f, "{s}"),
489
        }
490
3
    }
491
}
492

            
493
impl From<glib::Error> for LoadingError {
494
    fn from(e: glib::Error) -> LoadingError {
495
        // FIXME: this is somewhat fishy; not all GError are I/O errors, but in librsvg
496
        // most GError do come from gio.  Some come from GdkPixbufLoader, though.
497
        LoadingError::Io(format!("{e}"))
498
    }
499
}
500

            
501
impl From<IoError> for LoadingError {
502
    fn from(e: IoError) -> LoadingError {
503
        match e {
504
            IoError::BadDataUrl => LoadingError::BadUrl,
505
            IoError::Glib(e) => LoadingError::Io(format!("{e}")),
506
        }
507
    }
508
}
509

            
510
impl fmt::Display for ImplementationLimit {
511
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512
        match *self {
513
            ImplementationLimit::TooManyReferencedElements => write!(
514
                f,
515
                "exceeded more than {} referenced elements",
516
                limits::MAX_REFERENCED_ELEMENTS
517
            ),
518

            
519
            ImplementationLimit::TooManyLoadedElements => write!(
520
                f,
521
                "cannot load more than {} XML elements",
522
                limits::MAX_LOADED_ELEMENTS
523
            ),
524

            
525
            ImplementationLimit::TooManyAttributes => write!(
526
                f,
527
                "cannot load more than {} XML attributes",
528
                limits::MAX_LOADED_ATTRIBUTES
529
            ),
530
        }
531
    }
532
}