@@ -74,36 +74,47 @@ olsonToTimeZoneSeries _ = Nothing
74-- it into a @TimeZoneSeries@ for use together with the types and
74-- it into a @TimeZoneSeries@ for use together with the types and
75-- fucntions of "Data.Time". This is the function from this module
75-- fucntions of "Data.Time". This is the function from this module
76-- for which you are most likely to have use.
76-- for which you are most likely to have use.
78+-- If the values in the Olson timezone file exceed the standard size
79+-- limits (see 'defaultLimits'), this function throws an
80+-- exception. For other behavior, use 'getOlson' and
81+-- 'Data.Binary.Get.runGet' directly.
77getTimeZoneSeriesFromOlsonFile :: FilePath -> IO TimeZoneSeries
82getTimeZoneSeriesFromOlsonFile :: FilePath -> IO TimeZoneSeries
78getTimeZoneSeriesFromOlsonFile fp = getOlsonFromFile fp >>=
83getTimeZoneSeriesFromOlsonFile fp = getOlsonFromFile fp >>=
79 maybe (throwOlson fp "no timezone found in OlsonData") return .
84 maybe (throwOlson fp "no timezone found in OlsonData") return .
80 olsonToTimeZoneSeries
85 olsonToTimeZoneSeries
82-- | Parse a binary Olson timezone file.
87-- | Parse a binary Olson timezone file.
89+-- If the values in the Olson timezone file exceed the standard size
90+-- limits (see 'defaultLimits'), this function throws an
91+-- exception. For other behavior, use 'getOlson' and
92+-- 'Data.Binary.Get.runGet' directly.
83getOlsonFromFile :: FilePath -> IO OlsonData
93getOlsonFromFile :: FilePath -> IO OlsonData
84getOlsonFromFile fp = do
94getOlsonFromFile fp = do
85− e <- try . fmap (runGet getOlson) $ L.readFile fp
95+ e <- try . fmap (runGet $ getOlson defaultLimits) $ L.readFile fp
86 either (formatError fp) return e
96 either (formatError fp) return e
88formatError :: FilePath -> ErrorCall -> IO a
98formatError :: FilePath -> ErrorCall -> IO a
89formatError fp e = throwOlson fp $ show e
99formatError fp e = throwOlson fp $ show e
91-- | A binary parser for binary Olson timezone files
101-- | A binary parser for binary Olson timezone files
92−getOlson :: Get OlsonData
102+getOlson :: SizeLimits -> Get OlsonData
93−getOlson = do
103+getOlson limits = do
94− (version, part1) <- getOlsonPart True get32bitInteger
104+ (version, part1) <- getOlsonPart True limits get32bitInteger
95 -- There is one part for Version 1 format data, and two parts and a POSIX
105 -- There is one part for Version 1 format data, and two parts and a POSIX
96 -- TZ string for Version 2 format data
106 -- TZ string for Version 2 format data
97 case version of
107 case version of
98 0 -> return part1
108 0 -> return part1
99− 50 -> do (_, part2) <- getOlsonPart False get64bitInteger
109+ 50 -> do (_, part2) <- getOlsonPart False limits get64bitInteger
100 posixTZ <- getPosixTZ
110 posixTZ <- getPosixTZ
101 return $ part1 `mappend` part2 `mappend` posixTZ
111 return $ part1 `mappend` part2 `mappend` posixTZ
102 _ -> verify (const False) "invalid version number" undefined
112 _ -> verify (const False) "invalid version number" undefined
104-- Parse the part of an Olson file that contains timezone data
114-- Parse the part of an Olson file that contains timezone data
105−getOlsonPart :: Integral a => Bool -> Get a -> Get (Word8, OlsonData)
115+getOlsonPart :: Integral a => Bool -> SizeLimits -> Get a ->
116+ Get (Word8, OlsonData)
106−getOlsonPart verifyMagic getTime = do
117+getOlsonPart verifyMagic limits getTime = do
107 magic <- fmap (toASCII . B.unpack) $ getByteString 4
118 magic <- fmap (toASCII . B.unpack) $ getByteString 4
108 when verifyMagic $ verify_ (== "TZif") "missing magic number" magic
119 when verifyMagic $ verify_ (== "TZif") "missing magic number" magic
109 version <- getWord8
120 version <- getWord8
@@ -111,9 +122,15 @@ getOlsonPart verifyMagic getTime = do
111 tzh_ttisgmtcnt <- get32bitInt
122 tzh_ttisgmtcnt <- get32bitInt
112 tzh_ttisstdcnt <- get32bitInt
123 tzh_ttisstdcnt <- get32bitInt
113 tzh_leapcnt <- get32bitInt
124 tzh_leapcnt <- get32bitInt
125+ >>= verify (withinLimit maxLeaps) "too many leap second specifications"
114 tzh_timecnt <- get32bitInt
126 tzh_timecnt <- get32bitInt
127+ >>= verify (withinLimit maxTimes) "too many timezone transitions"
115 tzh_typecnt <- get32bitInt
128 tzh_typecnt <- get32bitInt
129+ >>= verify (withinLimit maxTypes) "too many timezone type specifications"
130+ verify (withinLimit maxTypes) "too many isgmt specifiers" tzh_ttisgmtcnt
131+ verify (withinLimit maxTypes) "too many isstd specifiers" tzh_ttisstdcnt
116 tzh_charcnt <- get32bitInt
132 tzh_charcnt <- get32bitInt
133+ >>= verify (withinLimit maxAbbrChars) "too many tilezone specifiers"
117 times <- fmap (map toInteger) $ replicateM tzh_timecnt getTime
134 times <- fmap (map toInteger) $ replicateM tzh_timecnt getTime
118 indexes <- replicateM tzh_timecnt get8bitInt
135 indexes <- replicateM tzh_timecnt get8bitInt
119 ttinfos <- replicateM tzh_typecnt getTtInfo
136 ttinfos <- replicateM tzh_typecnt getTtInfo
@@ -131,6 +148,7 @@ getOlsonPart verifyMagic getTime = do
151+ withinLimit limit value = maybe True (value <=) $ limit limits
134 lookupAbbr (TtInfo gmtoff isdst ttype abbrind) =
152 lookupAbbr (TtInfo gmtoff isdst ttype abbrind) =
135 TtInfo gmtoff isdst ttype . takeWhile (/= '\NUL') . drop abbrind
153 TtInfo gmtoff isdst ttype . takeWhile (/= '\NUL') . drop abbrind
136 setTtype ttinfo ttype = ttinfo {tt_ttype = ttype}
154 setTtype ttinfo ttype = ttinfo {tt_ttype = ttype}