Skip to content

Conversation

@a13
Copy link
Contributor

@a13 a13 commented May 6, 2025

Hi, I'm trying to parse an ics file that contains long lines split by so called "line folding technique".

According to https://www.ietf.org/rfc/rfc2445.txt

Lines of text SHOULD NOT be longer than 75 octets, excluding the line
break. Long content lines SHOULD be split into a multiple line
representations using a line "folding" technique. That is, a long
line can be split between any two characters by inserting a CRLF
immediately followed by a single linear white space character (i.e.,
SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
of CRLF followed immediately by a single linear white space character
is ignored (i.e., removed) when processing the content type.

Unfortunately, while unfolding-line-seq*concatenates such lines properly, it doesn't skip the leading whitespace character, This can cause the parser to break.

To reproduce (see ical_test.clj):

(require '[tick.alpha.ical :as ical])

(def multiline
  "X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-SOME-HEADER-BREAKING-SOMEWHERE-AT-
  75=foobar;X-TITLE=Some Place:geo:44.815458,20.462758")

(deftest parse-line-folding-test
  (testing "Folded lines can be parsed"
    (is (= {:name "X-APPLE-STRUCTURED-LOCATION",
            :params
            {"VALUE" "URI",
             "X-SOME-HEADER-BREAKING-SOMEWHERE-AT-75" "foobar",
             "X-TITLE" "Some Place"},
            :value "geo:44.815458,20.462758",
            :string-value "geo:44.815458,20.462758"}
           (-> multiline
               char-array
               io/reader
               ical/unfolding-line-seq
               first
               ical/line->contentline)))))

By default results in:

1. Caused by clojure.lang.ExceptionInfo
   No name
   {:contentline
    "X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-SOME-HEADER-BREAKING-SOMEWHERE-AT- 75=foobar;X-TITLE=Some Place:geo:44.815458,20.462758"}
                  ical.clj:  286  tick.alpha.ical/line->contentline
                  ical.clj:  284  tick.alpha.ical/line->contentline

The fix skips the initial space char while concatenating.

@henryw374
Copy link
Collaborator

hi. seeing an error running tests

eption: clojure.lang.ExceptionInfo: No name
{:contentline "X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-SOME-HEADER-BREAKING-SOMEWHERE-AT- 75=foobar;X-TITLE=Some Place:geo:44.815458,20.462758"}
at tick.alpha.ical$line__GT_contentline.invokeStatic (ical.clj:286)
tick.alpha.ical$line__GT_contentline.invoke (ical.clj:284)
tick.alpha.ical_test$fn__8753.invokeStatic (ical_test.clj:57)
tick.alpha.ical_test/fn (ical_test.clj:48)
kaoch

@a13 a13 force-pushed the fix-multiline branch from b1f78a9 to 91bac50 Compare May 6, 2025 14:49
@a13
Copy link
Contributor Author

a13 commented May 6, 2025

@henryw374 Sorry, aggressive-indent-mode accidentally inserted an extra space in test data at some point, should be fine now. Here's the full test log on the branch.

-*- mode: compilation; default-directory: "~/git/tick/" -*-
Compilation started at Tue May  6 17:52:35

make test-clj
./bin/kaocha :clj
--- clj (clojure.test) ---------------------------
tick.alpha.ical-test
  parse-dtstart
  line->contentline-test
    Simple lines can be parsed to contentlines
    Lines with parameters can be parsed to contentlines
  stress-test
  parse-unicode
  parse-icalendar-test
  parse-line-folding-test
    Folded lines can be parsed

tick.addon-libs-test
  locale-test
  tz-test

tick.api-test
  period-functions-test
  range-test
  epoch-test
  month
  extraction-test
  predicates-test
  subtraction-test
  units-test
  in-test
  truncate-test
  fields-test
  day-of-week
  duration-functions-test
  zoned-date-time-test
  coincidence-test
    non-interval coincidence
  date-construction-test
    (noon (today))
    (noon (date))
  time-construction-test
    (time)
    (midnight)
    (noon)
  duration-test
  comparison-test-date
  between-test
    LocalDate
  offset-date-time-test
    offset date time basics
  comparison-test
    shifting inst
    max-min
    max-min key
    comparables not=
      comparables =
      comparables =
      comparables =
      comparables =
    ZonedDateTimes in different zones should be equals
    ZoneDateTimes and OffsetDateTime should be equals if represents the same point in time
    ZoneDateTimes and platform Date should be equals if represents the same point in time
    Instants and ZonedDateTimes should be equals if represents the same point in time
    durations
  constructor-test
  instant-test
    instant basics
  addition-test
  adjusters
  parse-test
  divide-test
  today-test
  formatting-test
    all predefined formatters exist
  clock-test
    clock
      (clock) return type
      Time shifting the clock back by 2 hours
      with instant
    Converting using with-clock
      inst to zoned-date-time
      date-time to zoned-date-time
      date-time to offset-date-time
    Creating a clock with a zone, and returning that zone
    Creation of clock with fixed instant
    Creation of clock with fixed offset

tick.internals-test
  parse-test
    (time "4pm")

tick.alpha.interval-test
  disjoint-test
  cannot-disturb-test
  difference-test
    Empty sets
  division-test
  unite-test
    Unite meeting intervals
    Unite overlapping intervals
    Unite containing intervals
    Unite finished-by intervals
  date-relation-test
  concur?-test
  normalize-test
  predicate-test
  am-test
  complement-test
    complement through max of type
    complement ordered disjoint intervals
    complement meeting intervals
    complement empty interval round trip
  exhaustive-test
  successive-intervals-meet
  intersection-test
    Empty sets
  ordered-disjoint-intervals?-test
  group-by-empty-test
  difference-invariant-test
  concur-test2
  division-test2
  group-by-test
  union-test
    counts
    union
  group-by-intervals-test
    p and s
    O
    M and e
    s
    f
    F
    d
    D
    o
  concur-test
  basic-relations-test
  flatten-test
  distinct-test

�[32m66 tests, 699 assertions, 0 failures.�[m

Compilation finished at Tue May  6 17:52:46

@a13 a13 marked this pull request as draft May 6, 2025 15:10
@a13 a13 marked this pull request as ready for review May 6, 2025 15:36
@a13
Copy link
Contributor Author

a13 commented May 6, 2025

While testing I found another edge case, ics files can contain empty lines, which causes

error: java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0

so I fixed this one too.

@henryw374 henryw374 merged commit 04c47a4 into juxt:master May 7, 2025
1 check passed
@henryw374
Copy link
Collaborator

thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants