2121#include <stdbool.h>
2222#include <time.h>
2323
24+ #if defined(_WIN32 )
25+ #include <string.h>
26+ #include <ctype.h>
27+ // Windows does not provide strptime/strptime_l. Implement a tiny parser that
28+ // supports the three date formats used for cookie parsing in this package:
29+ // 1) "%a, %d %b %Y %H:%M:%S"
30+ // 2) "%a, %d-%b-%y %H:%M:%S"
31+ // 3) "%a %b %d %H:%M:%S %Y"
32+
33+ static int month_from_abbrev (const char * p ) {
34+ // Return 0-11 for Jan..Dec, or -1 on failure.
35+ if (!p ) return -1 ;
36+ switch (p [0 ]) {
37+ case 'J' :
38+ if (p [1 ] == 'a' && p [2 ] == 'n' ) return 0 ; // Jan
39+ if (p [1 ] == 'u' && p [2 ] == 'n' ) return 5 ; // Jun
40+ if (p [1 ] == 'u' && p [2 ] == 'l' ) return 6 ; // Jul
41+ break ;
42+ case 'F' :
43+ if (p [1 ] == 'e' && p [2 ] == 'b' ) return 1 ; // Feb
44+ break ;
45+ case 'M' :
46+ if (p [1 ] == 'a' && p [2 ] == 'r' ) return 2 ; // Mar
47+ if (p [1 ] == 'a' && p [2 ] == 'y' ) return 4 ; // May
48+ break ;
49+ case 'A' :
50+ if (p [1 ] == 'p' && p [2 ] == 'r' ) return 3 ; // Apr
51+ if (p [1 ] == 'u' && p [2 ] == 'g' ) return 7 ; // Aug
52+ break ;
53+ case 'S' :
54+ if (p [1 ] == 'e' && p [2 ] == 'p' ) return 8 ; // Sep
55+ break ;
56+ case 'O' :
57+ if (p [1 ] == 'c' && p [2 ] == 't' ) return 9 ; // Oct
58+ break ;
59+ case 'N' :
60+ if (p [1 ] == 'o' && p [2 ] == 'v' ) return 10 ; // Nov
61+ break ;
62+ case 'D' :
63+ if (p [1 ] == 'e' && p [2 ] == 'c' ) return 11 ; // Dec
64+ break ;
65+ }
66+ return -1 ;
67+ }
68+
69+ static int is_wkday_abbrev (const char * p ) {
70+ // Check for valid weekday abbreviation (Mon..Sun)
71+ // Expect exactly 3 ASCII letters.
72+ if (!p ) return 0 ;
73+ char a = p [0 ], b = p [1 ], c = p [2 ];
74+ if (!isalpha ((unsigned char )a ) || !isalpha ((unsigned char )b ) || !isalpha ((unsigned char )c )) return 0 ;
75+ // Accept common English abbreviations, case-sensitive as typically emitted.
76+ return (a == 'M' && b == 'o' && c == 'n' )|| (a == 'T' && b == 'u' && c == 'e' )|| (a == 'W' && b == 'e' && c == 'd' )||
77+ (a == 'T' && b == 'h' && c == 'u' )|| (a == 'F' && b == 'r' && c == 'i' )|| (a == 'S' && b == 'a' && c == 't' )||
78+ (a == 'S' && b == 'u' && c == 'n' );
79+ }
80+
81+ static int parse_1to2_digits (const char * * pp ) {
82+ const char * p = * pp ;
83+ if (!isdigit ((unsigned char )p [0 ])) return -1 ;
84+ int val = p [0 ]- '0' ;
85+ p ++ ;
86+ if (isdigit ((unsigned char )p [0 ])) {
87+ val = val * 10 + (p [0 ]- '0' );
88+ p ++ ;
89+ }
90+ * pp = p ;
91+ return val ;
92+ }
93+
94+ static int parse_fixed2 (const char * * pp ) {
95+ const char * p = * pp ;
96+ if (!isdigit ((unsigned char )p [0 ]) || !isdigit ((unsigned char )p [1 ])) return -1 ;
97+ int val = (p [0 ]- '0' )* 10 + (p [1 ]- '0' );
98+ p += 2 ;
99+ * pp = p ;
100+ return val ;
101+ }
102+
103+ static int parse_fixed4 (const char * * pp ) {
104+ const char * p = * pp ;
105+ for (int i = 0 ; i < 4 ; i ++ ) {
106+ if (!isdigit ((unsigned char )p [i ])) return -1 ;
107+ }
108+ int val = (p [0 ]- '0' )* 1000 + (p [1 ]- '0' )* 100 + (p [2 ]- '0' )* 10 + (p [3 ]- '0' );
109+ p += 4 ;
110+ * pp = p ;
111+ return val ;
112+ }
113+
114+ static int expect_char (const char * * pp , char c ) {
115+ if (* * pp != c ) return 0 ;
116+ (* pp )++ ;
117+ return 1 ;
118+ }
119+
120+ static int expect_space (const char * * pp ) {
121+ if (* * pp != ' ' ) return 0 ;
122+ (* pp )++ ;
123+ return 1 ;
124+ }
125+
126+ static int parse_time_hms (const char * * pp , int * h , int * m , int * s ) {
127+ int hh = parse_fixed2 (pp ); if (hh < 0 ) return 0 ;
128+ if (!expect_char (pp , ':' )) return 0 ;
129+ int mm = parse_fixed2 (pp ); if (mm < 0 ) return 0 ;
130+ if (!expect_char (pp , ':' )) return 0 ;
131+ int ss = parse_fixed2 (pp ); if (ss < 0 ) return 0 ;
132+ if (hh > 23 || mm > 59 || ss > 60 ) return 0 ; // allow leap second 60
133+ * h = hh ; * m = mm ; * s = ss ;
134+ return 1 ;
135+ }
136+
137+ static void init_tm_utc (struct tm * out ) {
138+ memset (out , 0 , sizeof (* out ));
139+ out -> tm_isdst = 0 ;
140+ }
141+
142+ static bool parse_cookie_format1 (const char * p , struct tm * out ) {
143+ // "%a, %d %b %Y %H:%M:%S"
144+ if (!is_wkday_abbrev (p )) return false;
145+ p += 3 ;
146+ if (!expect_char (& p , ',' )) return false;
147+ if (!expect_space (& p )) return false;
148+ int mday = parse_1to2_digits (& p ); if (mday < 1 || mday > 31 ) return false;
149+ if (!expect_space (& p )) return false;
150+ int mon = month_from_abbrev (p ); if (mon < 0 ) return false; p += 3 ;
151+ if (!expect_space (& p )) return false;
152+ int year = parse_fixed4 (& p ); if (year < 1601 ) return false;
153+ if (!expect_space (& p )) return false;
154+ int hh , mm , ss ; if (!parse_time_hms (& p , & hh , & mm , & ss )) return false;
155+ if (* p != '\0' ) return false;
156+ init_tm_utc (out );
157+ out -> tm_mday = mday ;
158+ out -> tm_mon = mon ;
159+ out -> tm_year = year - 1900 ;
160+ out -> tm_hour = hh ; out -> tm_min = mm ; out -> tm_sec = ss ;
161+ return true;
162+ }
163+
164+ static bool parse_cookie_format2 (const char * p , struct tm * out ) {
165+ // "%a, %d-%b-%y %H:%M:%S"
166+ if (!is_wkday_abbrev (p )) return false;
167+ p += 3 ;
168+ if (!expect_char (& p , ',' )) return false;
169+ if (!expect_space (& p )) return false;
170+ int mday = parse_1to2_digits (& p ); if (mday < 1 || mday > 31 ) return false;
171+ if (!expect_char (& p , '-' )) return false;
172+ int mon = month_from_abbrev (p ); if (mon < 0 ) return false; p += 3 ;
173+ if (!expect_char (& p , '-' )) return false;
174+ int y2 = parse_fixed2 (& p ); if (y2 < 0 ) return false;
175+ int year = (y2 >= 70 ) ? (1900 + y2 ) : (2000 + y2 );
176+ if (!expect_space (& p )) return false;
177+ int hh , mm , ss ; if (!parse_time_hms (& p , & hh , & mm , & ss )) return false;
178+ if (* p != '\0' ) return false;
179+ init_tm_utc (out );
180+ out -> tm_mday = mday ;
181+ out -> tm_mon = mon ;
182+ out -> tm_year = year - 1900 ;
183+ out -> tm_hour = hh ; out -> tm_min = mm ; out -> tm_sec = ss ;
184+ return true;
185+ }
186+
187+ static bool parse_cookie_format3 (const char * p , struct tm * out ) {
188+ // "%a %b %d %H:%M:%S %Y"
189+ if (!is_wkday_abbrev (p )) return false;
190+ p += 3 ;
191+ if (!expect_space (& p )) return false;
192+ int mon = month_from_abbrev (p ); if (mon < 0 ) return false; p += 3 ;
193+ if (!expect_space (& p )) return false;
194+ int mday = parse_1to2_digits (& p ); if (mday < 1 || mday > 31 ) return false;
195+ if (!expect_space (& p )) return false;
196+ int hh , mm , ss ; if (!parse_time_hms (& p , & hh , & mm , & ss )) return false;
197+ if (!expect_space (& p )) return false;
198+ int year = parse_fixed4 (& p ); if (year < 1601 ) return false;
199+ if (* p != '\0' ) return false;
200+ init_tm_utc (out );
201+ out -> tm_mday = mday ;
202+ out -> tm_mon = mon ;
203+ out -> tm_year = year - 1900 ;
204+ out -> tm_hour = hh ; out -> tm_min = mm ; out -> tm_sec = ss ;
205+ return true;
206+ }
207+
208+ static bool parse_cookie_timestamp_windows (const char * string , const char * format , struct tm * result ) {
209+ (void )format ; // format ignored: we try the three known patterns regardless.
210+ return parse_cookie_format1 (string , result ) ||
211+ parse_cookie_format2 (string , result ) ||
212+ parse_cookie_format3 (string , result );
213+ }
214+
215+ bool swiftahc_cshims_strptime (const char * string , const char * format , struct tm * result ) {
216+ return parse_cookie_timestamp_windows (string , format , result );
217+ }
218+
219+ bool swiftahc_cshims_strptime_l (const char * string , const char * format , struct tm * result , void * locale ) {
220+ (void )locale ; // locale is ignored on Windows; we always use POSIX month/weekday names.
221+ return parse_cookie_timestamp_windows (string , format , result );
222+ }
223+ #endif // _WIN32
224+
225+ #if !defined(_WIN32 )
24226bool swiftahc_cshims_strptime (const char * string , const char * format , struct tm * result ) {
25227 const char * firstNonProcessed = strptime (string , format , result );
26228 if (firstNonProcessed ) {
@@ -41,3 +243,4 @@ bool swiftahc_cshims_strptime_l(const char * string, const char * format, struct
41243 }
42244 return false;
43245}
246+ #endif // _WIN32
0 commit comments