aboutsummaryrefslogtreecommitdiff
path: root/bin/main.ml
diff options
context:
space:
mode:
Diffstat (limited to 'bin/main.ml')
-rw-r--r--bin/main.ml60
1 files changed, 58 insertions, 2 deletions
diff --git a/bin/main.ml b/bin/main.ml
index 6f3cbd5..6f64582 100644
--- a/bin/main.ml
+++ b/bin/main.ml
@@ -389,7 +389,38 @@ let readings_report ~overlays y = resolved_year_report ~line:readings_line ~over
file; XML: one document per year, the schema's own root is a single
year; ICS: one VCALENDAR per year, valid to concatenate for a
subscriber that reads multiple files). *)
+(* [--dtstamp] is the only user string that reaches [emit]/[publish] output
+ unescaped and unvalidated (it becomes an ICS DTSTAMP: property value
+ directly, Colitur_render.Emit_ics.year's own [dtstamp] parameter) --
+ every OTHER interpolated value in this project's output is either
+ escaped (Escape.apply) or engine-computed, never raw user input placed
+ straight into a line-oriented format. RFC 5545 section 3.3.5 defines
+ DATE-TIME's UTC form as exactly 8 digits, "T", 6 digits, "Z"
+ (e.g. "20270101T000000Z"); rejecting anything else is what stops
+ "--dtstamp hello" from silently emitting a malformed "DTSTAMP:hello" AND
+ what stops a value carrying its own CRLF (e.g. "X\r\nBEGIN:VEVENT\r\n...")
+ from being injected verbatim into every VEVENT -- a value shaped exactly
+ like the real form cannot contain either character. *)
+let dtstamp_well_formed s =
+ let is_digit c = c >= '0' && c <= '9' in
+ String.length s = 16
+ && String.for_all is_digit (String.sub s 0 8)
+ && s.[8] = 'T'
+ && String.for_all is_digit (String.sub s 9 6)
+ && s.[15] = 'Z'
+
+let check_dtstamp = function
+ | None -> ()
+ | Some s when dtstamp_well_formed s -> ()
+ | Some s ->
+ Printf.eprintf
+ "colitur: --dtstamp %S is not RFC 5545 UTC form (want 8 digits, 'T', 6 digits, 'Z', e.g. \
+ 20270101T000000Z)\n"
+ s;
+ exit 2
+
let emit_report ~format ~overlays ~dtstamp ~from_y ~to_y =
+ check_dtstamp dtstamp;
if from_y > to_y then begin
Printf.eprintf "colitur: --from %d is after --to %d\n" from_y to_y;
exit 2
@@ -695,6 +726,7 @@ let index_html ~from_y ~to_y =
Buffer.contents b
let publish_report ~from_y ~to_y ~out ~overlays ~dtstamp ~prune =
+ check_dtstamp dtstamp;
if from_y > to_y then begin
Printf.eprintf "colitur: --from %d is after --to %d\n" from_y to_y;
exit 2
@@ -978,7 +1010,9 @@ let print_help () =
let usage () =
prerr_endline
"colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur \
- readings <year> | colitur check FILE | colitur new-overlay (try: colitur --help)";
+ readings <year> | colitur emit --format FMT --from Y --to Y | colitur table --year Y --template \
+ FILE | colitur render --template FILE --year Y | colitur publish --from Y --to Y --out DIR | \
+ colitur check FILE | colitur new-overlay (try: colitur --help)";
exit 2
let with_year ys f =
@@ -1383,5 +1417,27 @@ let () =
exit 2
| Some from_ys, Some to_ys ->
with_year from_ys (fun from_y ->
- with_year to_ys (fun to_y -> publish_report ~from_y ~to_y ~out ~overlays ~dtstamp ~prune))))
+ with_year to_ys (fun to_y ->
+ (* [publish_report] writes many files across a whole
+ year range ([mkdir_p]/[write_file], both above) --
+ an unwritable [--out] parent (EACCES) or an [--out]
+ that names an existing plain file (ENOTDIR) raises
+ from deep inside that loop, same defect class as
+ the template read this project already guards
+ (commit 6bd741b): "--out" is user input too, and
+ the WHOLE call is guarded here rather than each
+ [write_file] site individually, for the same
+ reason that fix guarded the whole read and not
+ only the open. [Unix.mkdir] raises
+ [Unix.Unix_error] directly; [open_out_bin]
+ (stdlib, not the Unix module) wraps the same
+ underlying errno in [Sys_error] instead -- both
+ are real on this path, so both are caught. *)
+ try publish_report ~from_y ~to_y ~out ~overlays ~dtstamp ~prune with
+ | Unix.Unix_error (e, fn, arg) ->
+ Printf.eprintf "colitur: %s: %s: %s\n" fn arg (Unix.error_message e);
+ exit 2
+ | Sys_error e ->
+ Printf.eprintf "colitur: %s\n" e;
+ exit 2))))
| _ -> usage ())