1
use crate::common::{ParticipationStatusUnknown, PropertyKind};
2
use crate::model::param::{Param, ParticipationStatusParam, SentByParam, TimeZoneIdParam};
3
use crate::parser::param_value_participation_status;
4
use crate::parser::Error;
5
use crate::validate::error::ParamError;
6
use crate::validate::{
7
    param_name, ICalendarErrorSeverity, OccurrenceExpectation, PropertyInfo, PropertyLocation,
8
    ValueType,
9
};
10
use std::collections::HashMap;
11

            
12
macro_rules! check_property_param_occurrence {
13
    ($errors:ident, $seen:ident, $param:ident, $index:ident, $occur:expr) => {
14
        let name = $crate::validate::param_name($param);
15
        $crate::validate::add_to_seen($seen, name);
16
        if let Some(message) = $crate::validate::check_occurrence(&$seen, name, $occur.clone()) {
17
            $errors.push($crate::validate::ParamError {
18
                index: $index,
19
                severity: ICalendarErrorSeverity::Error,
20
                name: name.to_string(),
21
                message,
22
            });
23
        }
24
    };
25
}
26

            
27
1768
pub(super) fn validate_params(params: &[Param], property_info: PropertyInfo) -> Vec<ParamError> {
28
1768
    let mut errors = Vec::new();
29
1768

            
30
1768
    let mut seen = HashMap::<String, u32>::new();
31
2986
    for (index, param) in params.iter().enumerate() {
32
1284
        match param {
33
44
            Param::CommonName { .. } => {
34
44
                validate_common_name_param(&mut errors, &mut seen, param, index, &property_info);
35
44
            }
36
1320
            Param::Other { name, .. } | Param::Others { name, .. } if name == "CN" => {
37
2
                validate_common_name_param(&mut errors, &mut seen, param, index, &property_info);
38
2
            }
39
26
            Param::CalendarUserType { .. } => {
40
26
                validate_calendar_user_type_param(
41
26
                    &mut errors,
42
26
                    &mut seen,
43
26
                    param,
44
26
                    index,
45
26
                    &property_info,
46
26
                );
47
26
            }
48
1318
            Param::Other { name, .. } | Param::Others { name, .. } if name == "CUTYPE" => {
49
2
                validate_calendar_user_type_param(
50
2
                    &mut errors,
51
2
                    &mut seen,
52
2
                    param,
53
2
                    index,
54
2
                    &property_info,
55
2
                );
56
2
            }
57
20
            Param::DelegatedFrom { .. } => {
58
20
                validate_delegated_from_param(&mut errors, &mut seen, param, index, &property_info);
59
20
            }
60
1316
            Param::Other { name, .. } | Param::Others { name, .. } if name == "DELEGATED-FROM" => {
61
2
                validate_delegated_from_param(&mut errors, &mut seen, param, index, &property_info);
62
2
            }
63
20
            Param::DelegatedTo { .. } => {
64
20
                validate_delegated_to_param(&mut errors, &mut seen, param, index, &property_info);
65
20
            }
66
1314
            Param::Other { name, .. } | Param::Others { name, .. } if name == "DELEGATED-TO" => {
67
2
                validate_delegated_to_param(&mut errors, &mut seen, param, index, &property_info);
68
2
            }
69
44
            Param::DirectoryEntryReference { .. } => {
70
44
                validate_dir_param(&mut errors, &mut seen, param, index, &property_info);
71
44
            }
72
1312
            Param::Other { name, .. } | Param::Others { name, .. } if name == "DIR" => {
73
2
                validate_dir_param(&mut errors, &mut seen, param, index, &property_info);
74
2
            }
75
46
            Param::ValueType { .. } => {
76
46
                validate_value_type_param(&mut errors, &mut seen, param, index, &property_info);
77
46
            }
78
1310
            Param::Other { name, .. } if name == "VALUE" => {
79
2
                validate_value_type_param(&mut errors, &mut seen, param, index, &property_info);
80
2
            }
81
8
            Param::Encoding { .. } => {
82
8
                // Nothing further to validate
83
8
            }
84
20
            Param::FormatType { .. } => {
85
20
                validate_fmt_type_param(&mut errors, &mut seen, param, index, &property_info);
86
20
                // Format type is not further validated by this program
87
20
            }
88
1308
            Param::Other { name, .. } if name == "FMTTYPE" => {
89
2
                validate_fmt_type_param(&mut errors, &mut seen, param, index, &property_info);
90
2
            }
91
            Param::Others { name, .. } if name == "FMTTYPE" => {
92
                errors.push(ParamError {
93
                    index,
94
                    name: param_name(param).to_string(),
95
                    severity: ICalendarErrorSeverity::Error,
96
                    message: "FMTTYPE may not have multiple values".to_string(),
97
                });
98
            }
99
8
            Param::FreeBusyTimeType { .. } => {
100
8
                validate_free_busy_time_type_param(
101
8
                    &mut errors,
102
8
                    &mut seen,
103
8
                    param,
104
8
                    index,
105
8
                    &property_info,
106
8
                );
107
8
            }
108
1306
            Param::Other { name, .. } if name == "FBTYPE" => {
109
2
                validate_free_busy_time_type_param(
110
2
                    &mut errors,
111
2
                    &mut seen,
112
2
                    param,
113
2
                    index,
114
2
                    &property_info,
115
2
                );
116
2
            }
117
212
            Param::Language { .. } => {
118
212
                validate_language_param(&mut errors, &mut seen, param, index, &property_info);
119
212
                // Language is not further validated by this program
120
212
            }
121
1304
            Param::Other { name, .. } if name == "LANGUAGE" => {
122
2
                validate_language_param(&mut errors, &mut seen, param, index, &property_info);
123
2
            }
124
20
            Param::Members { .. } => {
125
20
                validate_member_param(&mut errors, &mut seen, param, index, &property_info);
126
20
            }
127
1302
            Param::Other { name, .. } | Param::Others { name, .. } if name == "MEMBER" => {
128
2
                validate_member_param(&mut errors, &mut seen, param, index, &property_info);
129
2
            }
130
24
            Param::ParticipationStatus(ParticipationStatusParam { status }) => {
131
24
                validate_part_stat_param(
132
24
                    &mut errors,
133
24
                    &mut seen,
134
24
                    param,
135
24
                    status,
136
24
                    index,
137
24
                    &property_info,
138
24
                );
139
24
            }
140
1300
            Param::Other { name, value } if name == "PARTSTAT" => {
141
2
                let mut v = value.as_bytes().to_vec();
142
2
                v.push(b';');
143
2
                match param_value_participation_status::<Error>(&v) {
144
2
                    Ok((_, status)) => {
145
2
                        validate_part_stat_param(
146
2
                            &mut errors,
147
2
                            &mut seen,
148
2
                            param,
149
2
                            &status,
150
2
                            index,
151
2
                            &property_info,
152
2
                        );
153
2
                    }
154
                    Err(_) => {
155
                        errors.push(ParamError {
156
                            index,
157
                            name: param_name(param).to_string(),
158
                            severity: ICalendarErrorSeverity::Error,
159
                            message: "Invalid participation status (PARTSTAT) value".to_string(),
160
                        });
161
                    }
162
                }
163
            }
164
20
            Param::Range { .. } => {
165
20
                // The parser should reject wrong values for this param and the builder won't let you
166
20
                // specify a wrong value, so not useful to validate the value in this context.
167
20

            
168
20
                validate_range_param(&mut errors, &mut seen, param, index, &property_info);
169
20
            }
170
1298
            Param::Other { name, .. } if name == "RANGE" => {
171
2
                validate_range_param(&mut errors, &mut seen, param, index, &property_info);
172
2
            }
173
8
            Param::TriggerRelationship { .. } => {
174
8
                validate_related_param(&mut errors, &mut seen, param, index, &property_info);
175
8
            }
176
1296
            Param::Other { name, .. } | Param::Others { name, .. } if name == "RELATED" => {
177
2
                validate_related_param(&mut errors, &mut seen, param, index, &property_info);
178
2
            }
179
20
            Param::RelationshipType { .. } => {
180
20
                validate_relationship_type_param(
181
20
                    &mut errors,
182
20
                    &mut seen,
183
20
                    param,
184
20
                    index,
185
20
                    &property_info,
186
20
                );
187
20
            }
188
1294
            Param::Other { name, .. } if name == "RELTYPE" => {
189
2
                validate_relationship_type_param(
190
2
                    &mut errors,
191
2
                    &mut seen,
192
2
                    param,
193
2
                    index,
194
2
                    &property_info,
195
2
                );
196
2
            }
197
26
            Param::Role { .. } => {
198
26
                validate_role_param(&mut errors, &mut seen, param, index, &property_info);
199
26
            }
200
1292
            Param::Other { name, .. } if name == "ROLE" => {
201
2
                validate_role_param(&mut errors, &mut seen, param, index, &property_info);
202
2
            }
203
26
            Param::Rsvp { .. } => {
204
26
                validate_rsvp_param(&mut errors, &mut seen, param, index, &property_info);
205
26
            }
206
1290
            Param::Other { name, .. } if name == "RSVP" => {
207
2
                validate_rsvp_param(&mut errors, &mut seen, param, index, &property_info);
208
2
            }
209
46
            Param::SentBy(SentByParam { address }) => {
210
46
                validate_sent_by_param(
211
46
                    &mut errors,
212
46
                    &mut seen,
213
46
                    param,
214
46
                    address,
215
46
                    index,
216
46
                    &property_info,
217
46
                );
218
46
            }
219
1288
            Param::Other { name, value } if name == "SENT-BY" => {
220
2
                validate_sent_by_param(&mut errors, &mut seen, param, value, index, &property_info);
221
2
            }
222
112
            Param::TimeZoneId(TimeZoneIdParam { tz_id, unique }) => {
223
112
                validate_time_zone_id_param(
224
112
                    &mut errors,
225
112
                    &mut seen,
226
112
                    param,
227
112
                    tz_id,
228
112
                    *unique,
229
112
                    index,
230
112
                    &property_info,
231
112
                );
232
112
            }
233
1286
            Param::Other { name, value } if name == "TZID" => {
234
2
                let (value, unique) = match value.chars().next() {
235
2
                    Some('/') => (value.splitn(2, '/').last().unwrap().to_string(), true),
236
                    _ => (value.clone(), false),
237
                };
238

            
239
2
                validate_time_zone_id_param(
240
2
                    &mut errors,
241
2
                    &mut seen,
242
2
                    param,
243
2
                    &value,
244
2
                    unique,
245
2
                    index,
246
2
                    &property_info,
247
2
                );
248
            }
249
120
            Param::AltRep { .. } => {
250
120
                validate_alt_rep_param(&mut errors, &mut seen, param, index, &property_info);
251
120
            }
252
1284
            Param::Other { name, .. } | Param::Others { name, .. } if name == "ALTREP" => {
253
                validate_alt_rep_param(&mut errors, &mut seen, param, index, &property_info);
254
            }
255
1284
            Param::Other { .. } | Param::Others { .. } => {
256
1284
                // Permit unknown parameters
257
1284
            }
258
        }
259
    }
260

            
261
1768
    errors
262
1768
}
263

            
264
// RFC 5545, Section 3.2.1
265
120
fn validate_alt_rep_param(
266
120
    errors: &mut Vec<ParamError>,
267
120
    seen: &mut HashMap<String, u32>,
268
120
    param: &Param,
269
120
    index: usize,
270
120
    property_info: &PropertyInfo,
271
120
) {
272
120
    if !property_info.is_other && property_info.value_type != ValueType::Text {
273
        errors.push(ParamError {
274
            index,
275
            name: param_name(param).to_string(),
276
            severity: ICalendarErrorSeverity::Error,
277
            message: "Alternate text representation (ALTREP) is not allowed for this property type"
278
                .to_string(),
279
        });
280
        return;
281
120
    }
282

            
283
120
    let occurrence_expectation = match property_info.property_kind {
284
        PropertyKind::Comment
285
        | PropertyKind::Description
286
        | PropertyKind::Location
287
        | PropertyKind::Resources
288
        | PropertyKind::Summary
289
120
        | PropertyKind::Contact => OccurrenceExpectation::OptionalOnce,
290
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
291
        _ => OccurrenceExpectation::Never,
292
    };
293
120
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
294
120
}
295

            
296
// RFC 5545, Section 3.2.2
297
46
fn validate_common_name_param(
298
46
    errors: &mut Vec<ParamError>,
299
46
    seen: &mut HashMap<String, u32>,
300
46
    param: &Param,
301
46
    index: usize,
302
46
    property_info: &PropertyInfo,
303
46
) {
304
46
    if !property_info.is_other && property_info.value_type != ValueType::CalendarAddress {
305
4
        errors.push(ParamError {
306
4
            index,
307
4
            name: param_name(param).to_string(),
308
4
            severity: ICalendarErrorSeverity::Error,
309
4
            message: "Common name (CN) is not allowed for this property type".to_string(),
310
4
        });
311
4
        return;
312
42
    }
313

            
314
42
    let occurrence_expectation = match property_info.property_kind {
315
18
        PropertyKind::Attendee => attendee_common_expectation(property_info),
316
24
        PropertyKind::Organizer => OccurrenceExpectation::OptionalOnce,
317
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
318
        _ => OccurrenceExpectation::Never,
319
    };
320
42
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
321
46
}
322

            
323
// RFC 5545, Section 3.2.3
324
28
fn validate_calendar_user_type_param(
325
28
    errors: &mut Vec<ParamError>,
326
28
    seen: &mut HashMap<String, u32>,
327
28
    param: &Param,
328
28
    index: usize,
329
28
    property_info: &PropertyInfo,
330
28
) {
331
28
    if !property_info.is_other && property_info.value_type != ValueType::CalendarAddress {
332
4
        errors.push(ParamError {
333
4
            index,
334
4
            name: param_name(param).to_string(),
335
4
            severity: ICalendarErrorSeverity::Error,
336
4
            message: "Calendar user type (CUTYPE) is not allowed for this property type"
337
4
                .to_string(),
338
4
        });
339
4
        return;
340
24
    }
341

            
342
24
    let occurrence_expectation = match property_info.property_kind {
343
24
        PropertyKind::Attendee => attendee_common_expectation(property_info),
344
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
345
        _ => OccurrenceExpectation::Never,
346
    };
347
24
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
348
28
}
349

            
350
// RFC 5545, Section 3.2.4
351
22
fn validate_delegated_from_param(
352
22
    errors: &mut Vec<ParamError>,
353
22
    seen: &mut HashMap<String, u32>,
354
22
    param: &Param,
355
22
    index: usize,
356
22
    property_info: &PropertyInfo,
357
22
) {
358
22
    if !property_info.is_other && property_info.value_type != ValueType::CalendarAddress {
359
4
        errors.push(ParamError {
360
4
            index,
361
4
            name: param_name(param).to_string(),
362
4
            severity: ICalendarErrorSeverity::Error,
363
4
            message: "Delegated from (DELEGATED-FROM) is not allowed for this property type"
364
4
                .to_string(),
365
4
        });
366
4
        return;
367
18
    }
368

            
369
18
    let occurrence_expectation = match property_info.property_kind {
370
18
        PropertyKind::Attendee => attendee_common_expectation(property_info),
371
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
372
        _ => OccurrenceExpectation::Never,
373
    };
374
18
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
375
22
}
376

            
377
// RFC 5545, Section 3.2.5
378
22
fn validate_delegated_to_param(
379
22
    errors: &mut Vec<ParamError>,
380
22
    seen: &mut HashMap<String, u32>,
381
22
    param: &Param,
382
22
    index: usize,
383
22
    property_info: &PropertyInfo,
384
22
) {
385
22
    if !property_info.is_other && property_info.value_type != ValueType::CalendarAddress {
386
4
        errors.push(ParamError {
387
4
            index,
388
4
            name: param_name(param).to_string(),
389
4
            severity: ICalendarErrorSeverity::Error,
390
4
            message: "Delegated to (DELEGATED-TO) is not allowed for this property type"
391
4
                .to_string(),
392
4
        });
393
4
        return;
394
18
    }
395

            
396
18
    let occurrence_expectation = match property_info.property_kind {
397
18
        PropertyKind::Attendee => attendee_common_expectation(property_info),
398
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
399
        _ => OccurrenceExpectation::Never,
400
    };
401
18
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
402
22
}
403

            
404
// RFC 5545, Section 3.2.6
405
46
fn validate_dir_param(
406
46
    errors: &mut Vec<ParamError>,
407
46
    seen: &mut HashMap<String, u32>,
408
46
    param: &Param,
409
46
    index: usize,
410
46
    property_info: &PropertyInfo,
411
46
) {
412
46
    if !property_info.is_other && property_info.value_type != ValueType::CalendarAddress {
413
4
        errors.push(ParamError {
414
4
            index,
415
4
            name: param_name(param).to_string(),
416
4
            severity: ICalendarErrorSeverity::Error,
417
4
            message: "Directory entry reference (DIR) is not allowed for this property type"
418
4
                .to_string(),
419
4
        });
420
4
        return;
421
42
    }
422

            
423
42
    let occurrence_expectation = match property_info.property_kind {
424
18
        PropertyKind::Attendee => attendee_common_expectation(property_info),
425
24
        PropertyKind::Organizer => OccurrenceExpectation::OptionalOnce,
426
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
427
        _ => OccurrenceExpectation::Never,
428
    };
429
42
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
430
46
}
431

            
432
// RFC 5545, Section 3.2.8
433
22
fn validate_fmt_type_param(
434
22
    errors: &mut Vec<ParamError>,
435
22
    seen: &mut HashMap<String, u32>,
436
22
    param: &Param,
437
22
    index: usize,
438
22
    property_info: &PropertyInfo,
439
22
) {
440
22
    let occurrence_expectation = match property_info.property_kind {
441
18
        PropertyKind::Attach => OccurrenceExpectation::OptionalOnce,
442
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
443
4
        _ => OccurrenceExpectation::Never,
444
    };
445
22
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
446
22
}
447

            
448
// RFC 5545, Section 3.2.9
449
10
fn validate_free_busy_time_type_param(
450
10
    errors: &mut Vec<ParamError>,
451
10
    seen: &mut HashMap<String, u32>,
452
10
    param: &Param,
453
10
    index: usize,
454
10
    property_info: &PropertyInfo,
455
10
) {
456
10
    let occurrence_expectation = match property_info.property_kind {
457
6
        PropertyKind::FreeBusyTime => OccurrenceExpectation::OptionalOnce,
458
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
459
4
        _ => OccurrenceExpectation::Never,
460
    };
461
10
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
462
10
}
463

            
464
// RFC 5545, Section 3.2.10
465
214
fn validate_language_param(
466
214
    errors: &mut Vec<ParamError>,
467
214
    seen: &mut HashMap<String, u32>,
468
214
    param: &Param,
469
214
    index: usize,
470
214
    property_info: &PropertyInfo,
471
214
) {
472
214
    let occurrence_expectation = match property_info.property_kind {
473
        PropertyKind::Categories
474
        | PropertyKind::Comment
475
        | PropertyKind::Description
476
        | PropertyKind::Location
477
        | PropertyKind::Resources
478
        | PropertyKind::Summary
479
        | PropertyKind::TimeZoneName
480
        | PropertyKind::Attendee
481
        | PropertyKind::Contact
482
        | PropertyKind::Organizer
483
210
        | PropertyKind::RequestStatus => OccurrenceExpectation::OptionalOnce,
484
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
485
4
        _ => OccurrenceExpectation::Never,
486
    };
487
214
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
488
214
}
489

            
490
// RFC 5545, Section 3.2.11
491
22
fn validate_member_param(
492
22
    errors: &mut Vec<ParamError>,
493
22
    seen: &mut HashMap<String, u32>,
494
22
    param: &Param,
495
22
    index: usize,
496
22
    property_info: &PropertyInfo,
497
22
) {
498
22
    if !property_info.is_other && property_info.value_type != ValueType::CalendarAddress {
499
4
        errors.push(ParamError {
500
4
            index,
501
4
            name: param_name(param).to_string(),
502
4
            severity: ICalendarErrorSeverity::Error,
503
4
            message: "Group or list membership (MEMBER) is not allowed for this property type"
504
4
                .to_string(),
505
4
        });
506
4
        return;
507
18
    }
508

            
509
18
    let occurrence_expectation = match property_info.property_kind {
510
18
        PropertyKind::Attendee => attendee_common_expectation(property_info),
511
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
512
        _ => OccurrenceExpectation::Never,
513
    };
514
18
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
515
22
}
516

            
517
// RFC 5545, Section 3.2.12
518
26
fn validate_part_stat_param(
519
26
    errors: &mut Vec<ParamError>,
520
26
    seen: &mut HashMap<String, u32>,
521
26
    param: &Param,
522
26
    status: &ParticipationStatusUnknown,
523
26
    index: usize,
524
26
    property_info: &PropertyInfo,
525
26
) {
526
26
    if !property_info.is_other && property_info.value_type != ValueType::CalendarAddress {
527
4
        errors.push(ParamError {
528
4
            index,
529
4
            name: param_name(param).to_string(),
530
4
            severity: ICalendarErrorSeverity::Error,
531
4
            message: "Participation status (PARTSTAT) is not allowed for this property type"
532
4
                .to_string(),
533
4
        });
534
4
        return;
535
22
    }
536
22

            
537
22
    match &property_info.property_location {
538
        PropertyLocation::Event => {
539
8
            match status {
540
                ParticipationStatusUnknown::NeedsAction
541
                | ParticipationStatusUnknown::Accepted
542
                | ParticipationStatusUnknown::Declined
543
                | ParticipationStatusUnknown::Tentative
544
                | ParticipationStatusUnknown::Delegated
545
                | ParticipationStatusUnknown::XName(_)
546
6
                | ParticipationStatusUnknown::IanaToken(_) => {
547
6
                    // Valid values
548
6
                }
549
2
                _ => {
550
2
                    errors.push(ParamError {
551
2
                        index,
552
2
                        name: param_name(param).to_string(),
553
2
                        severity: ICalendarErrorSeverity::Error,
554
2
                        message: format!("Invalid participation status (PARTSTAT) value [{status:?}] in a VEVENT component context"),
555
2
                    });
556
2
                }
557
            }
558
        }
559
6
        PropertyLocation::ToDo => {
560
6
            // This component type permits all recognized values
561
6
        }
562
        PropertyLocation::Journal => {
563
8
            match status {
564
                ParticipationStatusUnknown::NeedsAction
565
                | ParticipationStatusUnknown::Accepted
566
                | ParticipationStatusUnknown::Declined
567
                | ParticipationStatusUnknown::XName(_)
568
6
                | ParticipationStatusUnknown::IanaToken(_) => {
569
6
                    // Valid values
570
6
                }
571
2
                _ => {
572
2
                    errors.push(ParamError {
573
2
                        index,
574
2
                        name: param_name(param).to_string(),
575
2
                        severity: ICalendarErrorSeverity::Error,
576
2
                        message: format!("Invalid participation status (PARTSTAT) value [{status:?}] in a VJOURNAL component context"),
577
2
                    });
578
2
                }
579
            }
580
        }
581
        PropertyLocation::Other => {
582
            // Permit in "other", we don't know how it's being used.
583
        }
584
        _ => {
585
            // Expect other validation for occurrences to catch this if it's wrong
586
        }
587
    }
588

            
589
22
    let occurrence_expectation = match property_info.property_kind {
590
22
        PropertyKind::Attendee => attendee_common_expectation(property_info),
591
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
592
        _ => OccurrenceExpectation::Never,
593
    };
594
22
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
595
26
}
596

            
597
// RFC 5545, Section 3.2.13
598
22
fn validate_range_param(
599
22
    errors: &mut Vec<ParamError>,
600
22
    seen: &mut HashMap<String, u32>,
601
22
    param: &Param,
602
22
    index: usize,
603
22
    property_info: &PropertyInfo,
604
22
) {
605
22
    let occurrence_expectation = match property_info.property_kind {
606
18
        PropertyKind::RecurrenceId => OccurrenceExpectation::OptionalOnce,
607
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
608
4
        _ => OccurrenceExpectation::Never,
609
    };
610
22
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
611
22
}
612

            
613
// RFC 5545, Section 3.2.14
614
10
fn validate_related_param(
615
10
    errors: &mut Vec<ParamError>,
616
10
    seen: &mut HashMap<String, u32>,
617
10
    param: &Param,
618
10
    index: usize,
619
10
    property_info: &PropertyInfo,
620
10
) {
621
10
    if !property_info.is_other && property_info.value_type != ValueType::Duration {
622
4
        errors.push(ParamError {
623
4
            index,
624
4
            name: param_name(param).to_string(),
625
4
            severity: ICalendarErrorSeverity::Error,
626
4
            message: "Related (RELATED) is not allowed for this property type".to_string(),
627
4
        });
628
4
        return;
629
6
    }
630

            
631
6
    let occurrence_expectation = match property_info.property_kind {
632
        PropertyKind::Trigger => {
633
6
            if property_info.value_type == ValueType::Duration {
634
6
                OccurrenceExpectation::OptionalOnce
635
            } else {
636
                OccurrenceExpectation::Never
637
            }
638
        }
639
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
640
        _ => OccurrenceExpectation::Never,
641
    };
642
6
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
643
10
}
644

            
645
// RFC 5545, Section 3.2.15
646
22
fn validate_relationship_type_param(
647
22
    errors: &mut Vec<ParamError>,
648
22
    seen: &mut HashMap<String, u32>,
649
22
    param: &Param,
650
22
    index: usize,
651
22
    property_info: &PropertyInfo,
652
22
) {
653
22
    if !property_info.is_other && property_info.value_type != ValueType::Text {
654
4
        errors.push(ParamError {
655
4
            index,
656
4
            name: param_name(param).to_string(),
657
4
            severity: ICalendarErrorSeverity::Error,
658
4
            message: "Relationship type (RELTYPE) is not allowed for this property type"
659
4
                .to_string(),
660
4
        });
661
4
        return;
662
18
    }
663

            
664
18
    let occurrence_expectation = match property_info.property_kind {
665
18
        PropertyKind::Related => OccurrenceExpectation::OptionalOnce,
666
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
667
        _ => OccurrenceExpectation::Never,
668
    };
669
18
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
670
22
}
671

            
672
// RFC 5545, Section 3.2.16
673
28
fn validate_role_param(
674
28
    errors: &mut Vec<ParamError>,
675
28
    seen: &mut HashMap<String, u32>,
676
28
    param: &Param,
677
28
    index: usize,
678
28
    property_info: &PropertyInfo,
679
28
) {
680
28
    if !property_info.is_other && property_info.value_type != ValueType::CalendarAddress {
681
4
        errors.push(ParamError {
682
4
            index,
683
4
            name: param_name(param).to_string(),
684
4
            severity: ICalendarErrorSeverity::Error,
685
4
            message: "Participation role (ROLE) is not allowed for this property type".to_string(),
686
4
        });
687
4
        return;
688
24
    }
689

            
690
24
    let occurrence_expectation = match property_info.property_kind {
691
24
        PropertyKind::Attendee => attendee_common_expectation(property_info),
692
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
693
        _ => OccurrenceExpectation::Never,
694
    };
695
24
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
696
28
}
697

            
698
// RFC 5545, Section 3.2.17
699
28
fn validate_rsvp_param(
700
28
    errors: &mut Vec<ParamError>,
701
28
    seen: &mut HashMap<String, u32>,
702
28
    param: &Param,
703
28
    index: usize,
704
28
    property_info: &PropertyInfo,
705
28
) {
706
28
    if !property_info.is_other && property_info.value_type != ValueType::CalendarAddress {
707
4
        errors.push(ParamError {
708
4
            index,
709
4
            name: param_name(param).to_string(),
710
4
            severity: ICalendarErrorSeverity::Error,
711
4
            message: "RSVP expectation (RSVP) is not allowed for this property type".to_string(),
712
4
        });
713
4
        return;
714
24
    }
715

            
716
24
    let occurrence_expectation = match property_info.property_kind {
717
24
        PropertyKind::Attendee => attendee_common_expectation(property_info),
718
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
719
        _ => OccurrenceExpectation::Never,
720
    };
721
24
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
722
28
}
723

            
724
// RFC 5545, Section 3.2.18
725
48
fn validate_sent_by_param(
726
48
    errors: &mut Vec<ParamError>,
727
48
    seen: &mut HashMap<String, u32>,
728
48
    param: &Param,
729
48
    address: &str,
730
48
    index: usize,
731
48
    property_info: &PropertyInfo,
732
48
) {
733
48
    if !property_info.is_other && property_info.value_type != ValueType::CalendarAddress {
734
4
        errors.push(ParamError {
735
4
            index,
736
4
            name: param_name(param).to_string(),
737
4
            severity: ICalendarErrorSeverity::Error,
738
4
            message: "Sent by (SENT-BY) is not allowed for this property type".to_string(),
739
4
        });
740
4
        return;
741
44
    }
742
44

            
743
44
    if !address.starts_with("mailto:") {
744
2
        errors.push(ParamError {
745
2
            index,
746
2
            name: param_name(param).to_string(),
747
2
            severity: ICalendarErrorSeverity::Error,
748
2
            message: "Sent by (SENT-BY) must be a 'mailto:' URI".to_string(),
749
2
        });
750
44
    }
751

            
752
44
    let occurrence_expectation = match property_info.property_kind {
753
18
        PropertyKind::Attendee => attendee_common_expectation(property_info),
754
26
        PropertyKind::Organizer => OccurrenceExpectation::OptionalOnce,
755
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
756
        _ => OccurrenceExpectation::Never,
757
    };
758
44
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
759
48
}
760

            
761
// RFC 5545, Section 3.2.19
762
114
fn validate_time_zone_id_param(
763
114
    errors: &mut Vec<ParamError>,
764
114
    seen: &mut HashMap<String, u32>,
765
114
    param: &Param,
766
114
    tz_id: &String,
767
114
    unique: bool,
768
114
    index: usize,
769
114
    property_info: &PropertyInfo,
770
114
) {
771
114
    if property_info.value_type == ValueType::Date {
772
2
        errors.push(ParamError {
773
2
            index,
774
2
            name: param_name(param).to_string(),
775
2
            severity: ICalendarErrorSeverity::Error,
776
2
            message: "Time zone ID (TZID) is not allowed for the property value type DATE"
777
2
                .to_string(),
778
2
        });
779
2
        return;
780
112
    }
781
112

            
782
112
    if !unique && !property_info.calendar_info.time_zone_ids.contains(tz_id) {
783
2
        errors.push(ParamError {
784
2
            index,
785
2
            name: param_name(param).to_string(),
786
2
            severity: ICalendarErrorSeverity::Error,
787
2
            message: format!("Required time zone ID [{tz_id}] is not defined in the calendar"),
788
2
        });
789
110
    }
790

            
791
112
    if let Some(true) = property_info.value_is_utc {
792
2
        errors.push(ParamError {
793
2
            index,
794
2
            name: param_name(param).to_string(),
795
2
            severity: ICalendarErrorSeverity::Error,
796
2
            message: "Time zone ID (TZID) cannot be specified on a property with a UTC time"
797
2
                .to_string(),
798
2
        });
799
110
    }
800

            
801
112
    let occurrence_expectation = match property_info.property_kind {
802
30
        PropertyKind::DateTimeStart => match property_info.property_location {
803
            PropertyLocation::TimeZoneComponent => OccurrenceExpectation::Never,
804
30
            _ => OccurrenceExpectation::OptionalOnce,
805
        },
806
        PropertyKind::DateTimeEnd
807
        | PropertyKind::DateTimeDue
808
        | PropertyKind::RecurrenceId
809
        | PropertyKind::ExceptionDateTimes
810
78
        | PropertyKind::RecurrenceDateTimes => OccurrenceExpectation::OptionalOnce,
811
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
812
4
        _ => OccurrenceExpectation::Never,
813
    };
814
112
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
815
114
}
816

            
817
// RFC 5545, Section 3.2.20
818
48
fn validate_value_type_param(
819
48
    errors: &mut Vec<ParamError>,
820
48
    seen: &mut HashMap<String, u32>,
821
48
    param: &Param,
822
48
    index: usize,
823
48
    property_info: &PropertyInfo,
824
48
) {
825
48
    let occurrence_expectation = match property_info.property_kind {
826
        PropertyKind::Attach
827
        | PropertyKind::DateTimeStart
828
        | PropertyKind::DateTimeEnd
829
        | PropertyKind::DateTimeDue
830
        | PropertyKind::RecurrenceId
831
        | PropertyKind::ExceptionDateTimes
832
        | PropertyKind::RecurrenceDateTimes
833
44
        | PropertyKind::Trigger => OccurrenceExpectation::OptionalOnce,
834
        PropertyKind::Other => OccurrenceExpectation::OptionalMany,
835
4
        _ => OccurrenceExpectation::Never,
836
    };
837
48
    check_property_param_occurrence!(errors, seen, param, index, occurrence_expectation);
838
48
}
839

            
840
202
fn attendee_common_expectation(property_info: &PropertyInfo) -> OccurrenceExpectation {
841
202
    match property_info.property_location {
842
        PropertyLocation::Event | PropertyLocation::ToDo | PropertyLocation::Journal => {
843
202
            OccurrenceExpectation::OptionalOnce
844
        }
845
        PropertyLocation::FreeBusy | PropertyLocation::Alarm => OccurrenceExpectation::Never,
846
        PropertyLocation::Other => OccurrenceExpectation::OptionalMany,
847
        _ => OccurrenceExpectation::Never,
848
    }
849
202
}