Skip to content

Commit 562a004

Browse files
committed
Bad parsing fixed
1 parent 4f833f5 commit 562a004

File tree

4 files changed

+273
-41
lines changed

4 files changed

+273
-41
lines changed

Diff for: calendar.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ func ParseCalendar(r io.Reader) (*Calendar, error) {
431431
c := &Calendar{}
432432
cs := NewCalendarStream(r)
433433
cont := true
434-
for i := 0; cont; i++ {
434+
for ln := 0; cont; ln++ {
435435
l, err := cs.ReadLine()
436436
if err != nil {
437437
switch err {
@@ -444,9 +444,12 @@ func ParseCalendar(r io.Reader) (*Calendar, error) {
444444
if l == nil || len(*l) == 0 {
445445
continue
446446
}
447-
line := ParseProperty(*l)
447+
line, err := ParseProperty(*l)
448+
if err != nil {
449+
return nil, fmt.Errorf("parsing line %d: %w", ln, err)
450+
}
448451
if line == nil {
449-
return nil, errors.New("Error parsing line")
452+
return nil, fmt.Errorf("parsing line %d", ln)
450453
}
451454
switch state {
452455
case "begin":

Diff for: components.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ func ParseGeneralComponent(cs *CalendarStream, startLine *BaseProperty) *General
608608
func ParseComponent(cs *CalendarStream, startLine *BaseProperty) (ComponentBase, error) {
609609
cb := ComponentBase{}
610610
cont := true
611-
for i := 0; cont; i++ {
611+
for ln := 0; cont; ln++ {
612612
l, err := cs.ReadLine()
613613
if err != nil {
614614
switch err {
@@ -621,17 +621,20 @@ func ParseComponent(cs *CalendarStream, startLine *BaseProperty) (ComponentBase,
621621
if l == nil || len(*l) == 0 {
622622
continue
623623
}
624-
line := ParseProperty(*l)
624+
line, err := ParseProperty(*l)
625+
if err != nil {
626+
return cb, fmt.Errorf("parsing component property %d: %w", ln, err)
627+
}
625628
if line == nil {
626-
return cb, errors.New("Error parsing line")
629+
return cb, errors.New("parsing line")
627630
}
628631
switch line.IANAToken {
629632
case "END":
630633
switch line.Value {
631634
case startLine.Value:
632635
return cb, nil
633636
default:
634-
return cb, errors.New("Unbalanched end!")
637+
return cb, errors.New("unbalanced end")
635638
}
636639
case "BEGIN":
637640
co, err := GeneralParseComponent(cs, line)
@@ -645,5 +648,5 @@ func ParseComponent(cs *CalendarStream, startLine *BaseProperty) (ComponentBase,
645648
cb.Properties = append(cb.Properties, IANAProperty{*line})
646649
}
647650
}
648-
return cb, errors.New("Ran out of lines")
651+
return cb, errors.New("ran out of lines")
649652
}

Diff for: property.go

+88-25
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,9 @@ type IANAProperty struct {
127127
}
128128

129129
var (
130-
propertyIanaTokenReg *regexp.Regexp
131-
propertyParamNameReg *regexp.Regexp
132-
propertyParamValueReg *regexp.Regexp
133-
propertyValueTextReg *regexp.Regexp
130+
propertyIanaTokenReg *regexp.Regexp
131+
propertyParamNameReg *regexp.Regexp
132+
propertyValueTextReg *regexp.Regexp
134133
)
135134

136135
func init() {
@@ -140,10 +139,6 @@ func init() {
140139
log.Panicf("Failed to build regex: %v", err)
141140
}
142141
propertyParamNameReg = propertyIanaTokenReg
143-
propertyParamValueReg, err = regexp.Compile("^(?:\"(?:[^\"\\\\]|\\[\"nrt])*\"|[^,;\\\\:\"]*)")
144-
if err != nil {
145-
log.Panicf("Failed to build regex: %v", err)
146-
}
147142
propertyValueTextReg, err = regexp.Compile("^.*")
148143
if err != nil {
149144
log.Panicf("Failed to build regex: %v", err)
@@ -152,41 +147,46 @@ func init() {
152147

153148
type ContentLine string
154149

155-
func ParseProperty(contentLine ContentLine) *BaseProperty {
150+
func ParseProperty(contentLine ContentLine) (*BaseProperty, error) {
156151
r := &BaseProperty{
157152
ICalParameters: map[string][]string{},
158153
}
159154
tokenPos := propertyIanaTokenReg.FindIndex([]byte(contentLine))
160155
if tokenPos == nil {
161-
return nil
156+
return nil, nil
162157
}
163158
p := 0
164159
r.IANAToken = string(contentLine[p+tokenPos[0] : p+tokenPos[1]])
165160
p += tokenPos[1]
166161
for {
167162
if p >= len(contentLine) {
168-
return nil
163+
return nil, nil
169164
}
170165
switch rune(contentLine[p]) {
171166
case ':':
172-
return parsePropertyValue(r, string(contentLine), p+1)
167+
return parsePropertyValue(r, string(contentLine), p+1), nil
173168
case ';':
174169
var np int
175-
r, np = parsePropertyParam(r, string(contentLine), p+1)
170+
var err error
171+
t := r.IANAToken
172+
r, np, err = parsePropertyParam(r, string(contentLine), p+1)
173+
if err != nil {
174+
return nil, fmt.Errorf("parsing property %s: %w", t, err)
175+
}
176176
if r == nil {
177-
return nil
177+
return nil, nil
178178
}
179179
p = np
180180
default:
181-
return nil
181+
return nil, nil
182182
}
183183
}
184184
}
185185

186-
func parsePropertyParam(r *BaseProperty, contentLine string, p int) (*BaseProperty, int) {
186+
func parsePropertyParam(r *BaseProperty, contentLine string, p int) (*BaseProperty, int, error) {
187187
tokenPos := propertyParamNameReg.FindIndex([]byte(contentLine[p:]))
188188
if tokenPos == nil {
189-
return nil, p
189+
return nil, p, nil
190190
}
191191
k, v := "", ""
192192
k = string(contentLine[p : p+tokenPos[1]])
@@ -195,26 +195,89 @@ func parsePropertyParam(r *BaseProperty, contentLine string, p int) (*BaseProper
195195
case '=':
196196
p += 1
197197
default:
198-
return nil, p
198+
return nil, p, fmt.Errorf("missing property value for %s in %s", k, r.IANAToken)
199199
}
200200
for {
201201
if p >= len(contentLine) {
202-
return nil, p
202+
return nil, p, nil
203203
}
204-
tokenPos = propertyParamValueReg.FindIndex([]byte(contentLine[p:]))
205-
if tokenPos == nil {
206-
return nil, p
204+
var err error
205+
v, p, err = parsePropertyParamValue(contentLine, p)
206+
if err != nil {
207+
return nil, 0, fmt.Errorf("parse error: %w %s in %s", err, k, r.IANAToken)
207208
}
208-
v = string(contentLine[p+tokenPos[0] : p+tokenPos[1]])
209-
p += tokenPos[1]
210209
r.ICalParameters[k] = append(r.ICalParameters[k], v)
211210
switch rune(contentLine[p]) {
212211
case ',':
213212
p += 1
214213
default:
215-
return r, p
214+
return r, p, nil
215+
}
216+
}
217+
}
218+
219+
func parsePropertyParamValue(s string, p int) (string, int, error) {
220+
/*
221+
quoted-string = DQUOTE *QSAFE-CHAR DQUOTE
222+
223+
QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII
224+
; Any character except CONTROL and DQUOTE
225+
226+
SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E
227+
/ NON-US-ASCII
228+
; Any character except CONTROL, DQUOTE, ";", ":", ","
229+
230+
text = *(TSAFE-CHAR / ":" / DQUOTE / ESCAPED-CHAR)
231+
; Folded according to description above
232+
233+
ESCAPED-CHAR = "\\" / "\;" / "\," / "\N" / "\n")
234+
; \\ encodes \, \N or \n encodes newline
235+
; \; encodes ;, \, encodes ,
236+
237+
TSAFE-CHAR = %x20-21 / %x23-2B / %x2D-39 / %x3C-5B
238+
%x5D-7E / NON-US-ASCII
239+
; Any character except CTLs not needed by the current
240+
; character set, DQUOTE, ";", ":", "\", ","
241+
242+
CONTROL = %x00-08 / %x0A-1F / %x7F
243+
; All the controls except HTAB
244+
245+
*/
246+
r := make([]byte, 0, len(s))
247+
quoted := false
248+
done := false
249+
ip := p
250+
for ; p < len(s) && !done; p++ {
251+
switch s[p] {
252+
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08:
253+
return "", 0, fmt.Errorf("unexpected char ascii:%d in property param value", s[p])
254+
case 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
255+
0x1C, 0x1D, 0x1E, 0x1F:
256+
return "", 0, fmt.Errorf("unexpected char ascii:%d in property param value", s[p])
257+
case '\\':
258+
r = append(r, []byte(FromText(string(s[p+1:p+2])))...)
259+
p++
260+
continue
261+
case ';', ':', ',':
262+
if !quoted {
263+
done = true
264+
p--
265+
continue
266+
}
267+
case '"':
268+
if p == ip {
269+
quoted = true
270+
continue
271+
}
272+
if quoted {
273+
done = true
274+
continue
275+
}
276+
return "", 0, fmt.Errorf("unexpected double quote in property param value")
216277
}
278+
r = append(r, s[p])
217279
}
280+
return string(r), p, nil
218281
}
219282

220283
func parsePropertyValue(r *BaseProperty, contentLine string, p int) *BaseProperty {

0 commit comments

Comments
 (0)