1
use crate::{single, utf8_seq};
2
use lazy_static::lazy_static;
3
use nom::branch::alt;
4
use nom::bytes::complete::{take_while, take_while1, take_while_m_n};
5
use nom::bytes::streaming::tag_no_case;
6
use nom::character::streaming::{char, crlf};
7
use nom::combinator::{cut, recognize};
8
use nom::error::{ErrorKind, FromExternalError, ParseError, VerboseError, VerboseErrorKind};
9
use nom::multi::{fold_many0, many0, separated_list1};
10
use nom::sequence::{separated_pair, tuple};
11
use nom::{AsChar, IResult, Parser};
12
use std::str::FromStr;
13
use std::sync::Mutex;
14

            
15
mod component;
16
mod first_pass;
17
mod language_tag;
18
mod object;
19
mod param;
20
mod property;
21

            
22
/// Types produced by the parser.
23
///
24
/// These types represent the structure of the iCalendar format.
25
pub mod types;
26

            
27
use crate::parser::types::{ContentLine, ParamValue};
28
pub use first_pass::content_line_first_pass;
29
pub use object::{ical_object, ical_stream};
30
pub use param::value::*;
31
pub use param::{property_param, property_params};
32
pub use property::component::*;
33
pub use property::recur::prop_value_recur;
34
pub use property::uri::param_value_uri;
35
pub use property::value::*;
36

            
37
#[derive(Clone, Debug, PartialEq)]
38
pub struct Error<'a> {
39
    pub input: &'a [u8],
40
    pub error: InnerError,
41
}
42

            
43
#[derive(Clone, Debug, PartialEq)]
44
pub enum InnerError {
45
    Nom(ErrorKind),
46
    XNameTooShort,
47
    EncodingError(String, std::str::Utf8Error),
48
    InvalidDateNum,
49
    InvalidTimeNum,
50
    InvalidDurationNum,
51
    InvalidFloatNum,
52
    InvalidIntegerNum,
53
    InvalidRecurNum,
54
    InvalidRecurPart(String),
55
    InvalidOctet,
56
    InvalidIpv6,
57
    InvalidPort,
58
    MismatchedComponentEnd(Vec<u8>, Vec<u8>),
59
    UnknownParamName(String),
60
    InvalidValueParam,
61
    InvalidBinaryValueSpec,
62
}
63

            
64
impl<'a> Error<'a> {
65
    pub fn new(input: &'a [u8], error: InnerError) -> Error<'a> {
66
        Error { input, error }
67
    }
68
}
69

            
70
impl<'a> ParseError<&'a [u8]> for Error<'a> {
71
123452
    fn from_error_kind(input: &'a [u8], kind: ErrorKind) -> Self {
72
123452
        Error {
73
123452
            input,
74
123452
            error: InnerError::Nom(kind),
75
123452
        }
76
123452
    }
77

            
78
15470
    fn append(input: &'a [u8], kind: ErrorKind, _other: Self) -> Self {
79
15470
        Error {
80
15470
            input,
81
15470
            error: InnerError::Nom(kind),
82
15470
        }
83
15470
    }
84
}
85

            
86
// Enables use of `map_res` with nom::Err for the custom Error type.
87
impl<'a> FromExternalError<&'a [u8], nom::Err<Error<'a>>> for Error<'a> {
88
    fn from_external_error(input: &'a [u8], kind: ErrorKind, e: nom::Err<Error<'a>>) -> Self {
89
        match e {
90
            nom::Err::Error(e) | nom::Err::Failure(e) => Error {
91
                input: e.input,
92
                error: e.error,
93
            },
94
            nom::Err::Incomplete(_) => Error {
95
                input,
96
                error: InnerError::Nom(kind),
97
            },
98
        }
99
    }
100
}
101

            
102
impl<'a> From<(&'a [u8], ErrorKind)> for Error<'a> {
103
    fn from((input, kind): (&'a [u8], ErrorKind)) -> Self {
104
        Error {
105
            input,
106
            error: InnerError::Nom(kind),
107
        }
108
    }
109
}
110

            
111
lazy_static! {
112
    static ref ERROR_HOLD: Mutex<Vec<(usize, usize)>> = Mutex::new(Vec::new());
113
}
114

            
115
#[cfg(test)]
116
pub(crate) unsafe fn clear_errors() {
117
    for (ptr, len) in ERROR_HOLD.lock().unwrap().drain(..) {
118
        unsafe { String::from_raw_parts(ptr as *mut u8, len, len) };
119
    }
120
}
121

            
122
impl<'a> From<Error<'a>> for VerboseError<&'a [u8]> {
123
    fn from(value: Error<'a>) -> Self {
124
        let ctx = Box::leak(format!("{:?}", value.error).to_string().into_boxed_str());
125

            
126
        ERROR_HOLD
127
            .lock()
128
            .unwrap()
129
            .push((ctx.as_ptr() as usize, ctx.len()));
130

            
131
        VerboseError {
132
            errors: vec![(value.input, VerboseErrorKind::Context(ctx))],
133
        }
134
    }
135
}
136

            
137
/// All ASCII control characters except tab (%x09).
138
#[inline]
139
3200
const fn is_control(b: u8) -> bool {
140
3200
    matches!(b, b'\0'..=b'\x08' | b'\x0A'..=b'\x1F' | b'\x7F')
141
3200
}
142

            
143
674
fn param_text<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
144
674
where
145
674
    E: ParseError<&'a [u8]> + From<Error<'a>>,
146
674
{
147
3628
    take_while(|c| c != b'\"' && c != b';' && c != b':' && c != b',' && !is_control(c))(input)
148
674
}
149

            
150
634
fn quoted_string<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
151
634
where
152
634
    E: ParseError<&'a [u8]> + From<Error<'a>>,
153
634
{
154
634
    let (input, (_, content, _)) = tuple((char('"'), cut(safe_char), char('"')))(input)?;
155

            
156
12
    Ok((input, content))
157
634
}
158

            
159
634
fn param_value<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
160
634
where
161
634
    E: ParseError<&'a [u8]> + From<Error<'a>>,
162
634
{
163
634
    let (input, value) = alt((quoted_string, param_text))(input)?;
164

            
165
634
    Ok((input, value))
166
634
}
167

            
168
12
fn safe_char<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
169
12
where
170
12
    E: ParseError<&'a [u8]> + From<Error<'a>>,
171
12
{
172
258
    take_while(|c| c != b'\"' && !is_control(c))(input)
173
12
}
174

            
175
2710
fn iana_token<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
176
2710
where
177
2710
    E: ParseError<&'a [u8]> + From<Error<'a>>,
178
2710
{
179
34340
    take_while1(|c: u8| c.is_alphanum() || c == b'-')(input)
180
2710
}
181

            
182
966
fn x_name<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
183
966
where
184
966
    E: ParseError<&'a [u8]> + From<Error<'a>>,
185
966
{
186
966
    let (input, x_name) = recognize(tuple((
187
966
        tag_no_case("X-"),
188
1672
        cut(take_while1(|c: u8| c.is_alphanum() || c == b'-')),
189
966
    )))(input)?;
190

            
191
228
    Ok((input, x_name))
192
966
}
193

            
194
100
fn name<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
195
100
where
196
100
    E: ParseError<&'a [u8]> + From<Error<'a>>,
197
100
{
198
100
    alt((iana_token, x_name))(input)
199
100
}
200

            
201
608
fn param_name<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
202
608
where
203
608
    E: ParseError<&'a [u8]> + From<Error<'a>>,
204
608
{
205
608
    alt((iana_token, x_name))(input)
206
608
}
207

            
208
#[inline]
209
224
const fn is_reg_name_char(b: u8) -> bool {
210
224
    matches!(b, b'\x41'..=b'\x5A' | b'\x61'..=b'\x7A' | b'\x30'..=b'\x39' | b'\x21' | b'\x23' | b'\x24' | b'\x26' | b'\x2E' | b'\x2B' | b'\x2D' | b'\x5E' | b'\x5F')
211
224
}
212

            
213
// See https://www.rfc-editor.org/rfc/rfc4288 section 4.2
214
36
fn reg_name<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E>
215
36
where
216
36
    E: ParseError<&'a [u8]> + From<Error<'a>>,
217
36
{
218
36
    take_while_m_n(1, 127, is_reg_name_char)(input)
219
36
}
220

            
221
238
fn read_string<'a, E>(input: &'a [u8], context: &str) -> Result<String, nom::Err<E>>
222
238
where
223
238
    E: ParseError<&'a [u8]>,
224
238
    E: From<Error<'a>>,
225
238
{
226
238
    Ok(std::str::from_utf8(input)
227
238
        .map_err(|e| {
228
            nom::Err::Failure(
229
                Error::new(input, InnerError::EncodingError(context.to_string(), e)).into(),
230
            )
231
238
        })?
232
238
        .to_string())
233
238
}
234

            
235
150
fn read_int<'a, E, N>(input: &'a [u8]) -> Result<N, nom::Err<E>>
236
150
where
237
150
    E: ParseError<&'a [u8]>,
238
150
    E: From<Error<'a>>,
239
150
    N: FromStr,
240
150
{
241
150
    std::str::from_utf8(input)
242
150
        .map_err(|e| {
243
            nom::Err::Error(
244
                Error::new(
245
                    input,
246
                    InnerError::EncodingError("Invalid integer number text".to_string(), e),
247
                )
248
                .into(),
249
            )
250
150
        })?
251
150
        .parse()
252
150
        .map_err(|_| nom::Err::Error(Error::new(input, InnerError::InvalidIntegerNum).into()))
253
150
}
254

            
255
100
fn line_value<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Vec<u8>, E>
256
100
where
257
100
    E: ParseError<&'a [u8]> + From<Error<'a>>,
258
100
{
259
822
    let (input, v) = fold_many0(value_char, Vec::new, |mut acc, item| {
260
822
        acc.extend_from_slice(&item);
261
822
        acc
262
822
    })(input)?;
263

            
264
100
    Ok((input, v))
265
100
}
266

            
267
2520
fn value_char<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Vec<u8>, E>
268
2520
where
269
2520
    E: ParseError<&'a [u8]> + From<Error<'a>>,
270
2520
{
271
2520
    alt((
272
2520
        single(|b| matches!(b, b' ' | b'\t' | b'\x21'..=b'\x7E')).map(|c| vec![c]),
273
2520
        utf8_seq.map(|c| c.to_vec()),
274
2520
    ))(input)
275
2520
}
276

            
277
164
fn value<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Vec<u8>, E>
278
164
where
279
164
    E: ParseError<&'a [u8]> + From<Error<'a>>,
280
164
{
281
1434
    fold_many0(value_char, Vec::new, |mut acc, item| {
282
1434
        acc.extend_from_slice(&item);
283
1434
        acc
284
1434
    })(input)
285
164
}
286

            
287
16
fn param<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], ParamValue<'a>, E>
288
16
where
289
16
    E: ParseError<&'a [u8]> + From<Error<'a>>,
290
16
{
291
16
    let (input, (name, values)) = separated_pair(
292
16
        param_name,
293
16
        char('='),
294
16
        cut(separated_list1(char(','), param_value)),
295
16
    )(input)?;
296

            
297
    Ok((
298
16
        input,
299
16
        if values.len() == 1 {
300
16
            ParamValue::Other {
301
16
                name,
302
16
                value: values[0],
303
16
            }
304
        } else {
305
            ParamValue::Others { name, values }
306
        },
307
    ))
308
16
}
309

            
310
100
fn content_line<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], ContentLine<'a>, E>
311
100
where
312
100
    E: ParseError<&'a [u8]> + From<Error<'a>>,
313
100
{
314
100
    let (input, (property_name, params, _, value, _)) = tuple((
315
100
        name,
316
104
        many0(tuple((char(';'), cut(param))).map(|(_, p)| p)),
317
100
        char(':'),
318
100
        cut(line_value),
319
100
        crlf,
320
100
    ))(input)?;
321

            
322
100
    Ok((
323
100
        input,
324
100
        ContentLine {
325
100
            property_name,
326
100
            params,
327
100
            value,
328
100
        },
329
100
    ))
330
100
}
331

            
332
#[cfg(test)]
333
mod tests {
334
    use super::*;
335
    use crate::test_utils::check_rem;
336

            
337
    #[test]
338
2
    fn iana_token_desc() {
339
2
        let (rem, token) = iana_token::<Error>(b"DESCRIPTION").unwrap();
340
2
        check_rem(rem, 0);
341
2
        assert_eq!(b"DESCRIPTION", token);
342
2
    }
343

            
344
    #[test]
345
2
    fn simple_x_name() {
346
2
        let (rem, x_name) = x_name::<Error>(b"X-TEST ").unwrap();
347
2
        check_rem(rem, 1);
348
2
        assert_eq!(b"X-TEST", x_name);
349
2
    }
350

            
351
    #[test]
352
2
    fn simple_x_name_with_vendor() {
353
2
        let (rem, x_name) = x_name::<Error>(b"X-ESL-TEST ").unwrap();
354
2
        check_rem(rem, 1);
355
2
        assert_eq!(b"X-ESL-TEST", x_name);
356
2
    }
357

            
358
    #[test]
359
2
    fn simple_content_line() {
360
2
        let (rem, content_line) = content_line::<Error>(
361
2
            b"DESCRIPTION:This is a long description that exists on a long line.\r\nnext",
362
2
        )
363
2
        .unwrap();
364
2
        check_rem(rem, 4);
365
2
        assert_eq!(b"DESCRIPTION", content_line.property_name);
366
2
        assert_eq!(
367
2
            b"This is a long description that exists on a long line.",
368
2
            content_line.value.as_slice()
369
2
        );
370
2
    }
371

            
372
    #[test]
373
2
    fn simple_content_line_utf8() {
374
2
        let (rem, content_line) = content_line::<Error>(
375
2
            "DESCRIPTION:This is a long description of a happy face - 😁.\r\n;".as_bytes(),
376
2
        )
377
2
        .unwrap();
378
2
        check_rem(rem, 1);
379
2
        assert_eq!(b"DESCRIPTION", content_line.property_name);
380
2
        assert_eq!(
381
2
            "This is a long description of a happy face - 😁.".as_bytes(),
382
2
            content_line.value.as_slice()
383
2
        );
384
2
    }
385
}