~ubuntu-branches/ubuntu/utopic/dovecot/utopic-proposed

« back to all changes in this revision

Viewing changes to src/lib-http/http-date.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (4.1.35 sid)
  • Revision ID: package-import@ubuntu.com-20140108093549-i72o93pux8p0dlaf
Tags: 1:2.2.9-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + Use the autotools-dev dh addon to update config.guess/config.sub for
    arm64.
* Dropped changes, included in Debian:
  - Update Dovecot name to reflect distribution in login greeting.
  - Update Drac plugin for >= 2.0.0 support.
* d/control: Drop dovecot-postfix package as its no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "str.h"
 
5
#include "utc-mktime.h"
 
6
#include "http-date.h"
 
7
 
 
8
#include <ctype.h>
 
9
 
 
10
/*
 
11
        Official specification is still RFC261, Section 3.3, but we anticipate
 
12
        HTTPbis and use the draft Part 2, Section 5.1 as reference for our
 
13
        parser:
 
14
 
 
15
        http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-20#section-5.1
 
16
 
 
17
        The defined syntax is as follows:
 
18
 
 
19
         HTTP-date    = rfc1123-date / obs-date
 
20
 
 
21
        Preferred format:
 
22
 
 
23
         rfc1123-date = day-name "," SP date1 SP time-of-day SP GMT
 
24
                        ; fixed length subset of the format defined in
 
25
                        ; Section 5.2.14 of [RFC1123]
 
26
         day-name     = %x4D.6F.6E ; "Mon", case-sensitive
 
27
                      / %x54.75.65 ; "Tue", case-sensitive
 
28
                      / %x57.65.64 ; "Wed", case-sensitive
 
29
                      / %x54.68.75 ; "Thu", case-sensitive
 
30
                      / %x46.72.69 ; "Fri", case-sensitive
 
31
                      / %x53.61.74 ; "Sat", case-sensitive
 
32
                      / %x53.75.6E ; "Sun", case-sensitive
 
33
         date1        = day SP month SP year
 
34
                        ; e.g., 02 Jun 1982
 
35
         day          = 2DIGIT
 
36
         month        = %x4A.61.6E ; "Jan", case-sensitive
 
37
                / %x46.65.62 ; "Feb", case-sensitive
 
38
                      / %x4D.61.72 ; "Mar", case-sensitive
 
39
                      / %x41.70.72 ; "Apr", case-sensitive
 
40
                      / %x4D.61.79 ; "May", case-sensitive
 
41
                      / %x4A.75.6E ; "Jun", case-sensitive
 
42
                      / %x4A.75.6C ; "Jul", case-sensitive
 
43
                      / %x41.75.67 ; "Aug", case-sensitive
 
44
                      / %x53.65.70 ; "Sep", case-sensitive
 
45
                      / %x4F.63.74 ; "Oct", case-sensitive
 
46
                      / %x4E.6F.76 ; "Nov", case-sensitive
 
47
                      / %x44.65.63 ; "Dec", case-sensitive
 
48
         year         = 4DIGIT
 
49
         GMT          = %x47.4D.54 ; "GMT", case-sensitive
 
50
         time-of-day  = hour ":" minute ":" second
 
51
                              ; 00:00:00 - 23:59:59
 
52
         hour         = 2DIGIT
 
53
         minute       = 2DIGIT
 
54
         second       = 2DIGIT
 
55
 
 
56
  The semantics of day-name, day, month, year, and time-of-day are the
 
57
  same as those defined for the RFC 5322 constructs with the
 
58
  corresponding name ([RFC5322], Section 3.3).
 
59
 
 
60
  Obsolete formats:
 
61
 
 
62
         obs-date     = rfc850-date / asctime-date
 
63
 
 
64
         rfc850-date  = day-name-l "," SP date2 SP time-of-day SP GMT
 
65
         date2        = day "-" month "-" 2DIGIT
 
66
                              ; day-month-year (e.g., 02-Jun-82)
 
67
         day-name-l   = %x4D.6F.6E.64.61.79 ; "Monday", case-sensitive
 
68
                      / %x54.75.65.73.64.61.79 ; "Tuesday", case-sensitive
 
69
                      / %x57.65.64.6E.65.73.64.61.79 ; "Wednesday", case-sensitive
 
70
                      / %x54.68.75.72.73.64.61.79 ; "Thursday", case-sensitive
 
71
                      / %x46.72.69.64.61.79 ; "Friday", case-sensitive
 
72
                      / %x53.61.74.75.72.64.61.79 ; "Saturday", case-sensitive
 
73
                      / %x53.75.6E.64.61.79 ; "Sunday", case-sensitive
 
74
 
 
75
         asctime-date = day-name SP date3 SP time-of-day SP year
 
76
         date3        = month SP ( 2DIGIT / ( SP 1DIGIT ))
 
77
                              ; month day (e.g., Jun  2)
 
78
 
 
79
 */
 
80
 
 
81
static const char *month_names[] = {
 
82
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 
83
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 
84
};
 
85
 
 
86
static const char *weekday_names[] = {
 
87
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
 
88
};
 
89
 
 
90
static const char *weekday_names_long[] = {
 
91
        "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
 
92
};
 
93
 
 
94
struct http_date_parser {
 
95
        const unsigned char *cur, *end;
 
96
 
 
97
        struct tm tm;
 
98
        int timezone_offset;
 
99
};
 
100
 
 
101
static inline int
 
102
http_date_parse_sp(struct http_date_parser *parser)
 
103
{
 
104
        if (parser->cur >= parser->end)
 
105
                return -1;
 
106
        if (parser->cur[0] != ' ')
 
107
                return 0;
 
108
        parser->cur++;
 
109
        return 1;
 
110
}
 
111
 
 
112
static inline int
 
113
http_date_parse_number(struct http_date_parser *parser,
 
114
                          int digits, int *number_r)
 
115
{
 
116
        int i;
 
117
 
 
118
        if (parser->cur >= parser->end || !i_isdigit(parser->cur[0]))
 
119
                return 0;
 
120
 
 
121
        *number_r = parser->cur[0] - '0';
 
122
        parser->cur++;
 
123
 
 
124
        for (i=0; i < digits-1; i++) {
 
125
                if (parser->cur >= parser->end || !i_isdigit(parser->cur[0]))
 
126
                        return -1;
 
127
                *number_r = ((*number_r) * 10) + parser->cur[0] - '0';
 
128
                parser->cur++;
 
129
        }
 
130
        return 1;
 
131
}
 
132
 
 
133
static inline int
 
134
http_date_parse_word(struct http_date_parser *parser,
 
135
                          int maxchars, string_t **word_r)
 
136
{
 
137
        string_t *word;
 
138
        int i;
 
139
 
 
140
        if (parser->cur >= parser->end || !i_isalpha(parser->cur[0]))
 
141
                return 0;
 
142
 
 
143
        word = t_str_new(maxchars);
 
144
        str_append_c(word, parser->cur[0]);
 
145
        parser->cur++;
 
146
 
 
147
        for (i=0; i < maxchars-1; i++) {
 
148
                if (parser->cur >= parser->end || !i_isalpha(parser->cur[0]))
 
149
                        break;
 
150
                str_append_c(word, parser->cur[0]);
 
151
                parser->cur++;
 
152
        }
 
153
        
 
154
        if (i_isalpha(parser->cur[0]))
 
155
                return -1;
 
156
        *word_r = word;
 
157
        return 1;
 
158
}
 
159
 
 
160
static inline int
 
161
http_date_parse_year(struct http_date_parser *parser)
 
162
{
 
163
        /* year = 4DIGIT */
 
164
        if (http_date_parse_number(parser, 4, &parser->tm.tm_year) <= 0)
 
165
                return -1;
 
166
        if (parser->tm.tm_year < 1900)
 
167
                return -1;
 
168
        parser->tm.tm_year -= 1900;
 
169
        return 1;
 
170
}
 
171
 
 
172
static inline int
 
173
http_date_parse_month(struct http_date_parser *parser)
 
174
{
 
175
        string_t *month;
 
176
        int i;
 
177
 
 
178
        if (http_date_parse_word(parser, 3, &month) <= 0 || str_len(month) != 3)
 
179
                return -1;
 
180
        
 
181
        for (i = 0; i < 12; i++) {
 
182
                if (strcmp(month_names[i], str_c(month)) == 0) {
 
183
                        break;
 
184
                }
 
185
        }
 
186
        if (i >= 12)
 
187
                return -1;
 
188
        
 
189
        parser->tm.tm_mon = i;
 
190
        return 1;
 
191
}
 
192
 
 
193
static inline int
 
194
http_date_parse_day(struct http_date_parser *parser)
 
195
{
 
196
        /* day = 2DIGIT */
 
197
        if (http_date_parse_number(parser, 2, &parser->tm.tm_mday) <= 0)
 
198
                return -1;
 
199
        return 1;
 
200
}
 
201
 
 
202
static int
 
203
http_date_parse_time_of_day(struct http_date_parser *parser)
 
204
{
 
205
        /* time-of-day  = hour ":" minute ":" second
 
206
                              ; 00:00:00 - 23:59:59
 
207
                 hour         = 2DIGIT
 
208
                 minute       = 2DIGIT
 
209
                 second       = 2DIGIT
 
210
         */
 
211
 
 
212
        /* hour = 2DIGIT */
 
213
        if (http_date_parse_number(parser, 2, &parser->tm.tm_hour) <= 0)
 
214
                return -1;
 
215
 
 
216
        /* ":" */
 
217
        if (parser->cur >= parser->end || parser->cur[0] != ':')
 
218
                return -1;
 
219
        parser->cur++;
 
220
 
 
221
        /* minute = 2DIGIT */
 
222
        if (http_date_parse_number(parser, 2, &parser->tm.tm_min) <= 0)
 
223
                return -1;
 
224
 
 
225
        /* ":" */
 
226
        if (parser->cur >= parser->end || parser->cur[0] != ':')
 
227
                return -1;
 
228
        parser->cur++;
 
229
 
 
230
        /* second = 2DIGIT */
 
231
        if (http_date_parse_number(parser, 2, &parser->tm.tm_sec) <= 0)
 
232
                return -1;
 
233
        return 1;
 
234
}
 
235
 
 
236
static inline int
 
237
http_date_parse_time_gmt(struct http_date_parser *parser)
 
238
{
 
239
        string_t *gmt;
 
240
 
 
241
        /* Remaining:    {...} SP time-of-day SP GMT
 
242
         */
 
243
 
 
244
        /* SP time-of-day */
 
245
        if (http_date_parse_sp(parser) <= 0)
 
246
                return -1;
 
247
        if (http_date_parse_time_of_day(parser) <= 0)
 
248
                return -1;
 
249
 
 
250
        /* SP GMT */
 
251
        if (http_date_parse_sp(parser) <= 0)
 
252
                return -1;
 
253
        if (http_date_parse_word(parser, 3, &gmt) <= 0 ||
 
254
                strcmp("GMT", str_c(gmt)) != 0)
 
255
                return -1;
 
256
        return 1;
 
257
}
 
258
 
 
259
static int
 
260
http_date_parse_format_rfc1123(struct http_date_parser *parser)
 
261
{
 
262
        /*
 
263
         rfc1123-date = day-name "," SP date1 SP time-of-day SP GMT
 
264
                        ; fixed length subset of the format defined in
 
265
                        ; Section 5.2.14 of [RFC1123]
 
266
         date1        = day SP month SP year
 
267
                        ; e.g., 02 Jun 1982
 
268
         
 
269
         Remaining:      {...} SP day SP month SP year SP time-of-day SP GMT
 
270
 
 
271
         */
 
272
 
 
273
        /* SP day */
 
274
        if (http_date_parse_sp(parser) <= 0)
 
275
                return -1;
 
276
        if (http_date_parse_day(parser) <= 0)
 
277
                return -1;
 
278
 
 
279
        /* SP month */
 
280
        if (http_date_parse_sp(parser) <= 0)
 
281
                return -1;
 
282
        if (http_date_parse_month(parser) <= 0)
 
283
                return -1;
 
284
 
 
285
        /* SP year */
 
286
        if (http_date_parse_sp(parser) <= 0)
 
287
                return -1;
 
288
        if (http_date_parse_year(parser) <= 0)
 
289
                return -1;
 
290
 
 
291
        /* SP time-of-day SP GMT */
 
292
        return http_date_parse_time_gmt(parser);
 
293
}
 
294
 
 
295
static int
 
296
http_date_parse_format_rfc850(struct http_date_parser *parser)
 
297
{
 
298
        /* 
 
299
         rfc850-date  = day-name-l "," SP date2 SP time-of-day SP GMT
 
300
         date2        = day "-" month "-" 2DIGIT
 
301
                              ; day-month-year (e.g., 02-Jun-82)
 
302
 
 
303
         Remaining: "," SP day "-" month "-" 2DIGIT SP time-of-day SP GMT
 
304
         */
 
305
 
 
306
        /* "," SP */
 
307
        if (parser->cur >= parser->end || parser->cur[0] != ',')
 
308
                return -1;
 
309
        parser->cur++;
 
310
        if (http_date_parse_sp(parser) <= 0)
 
311
                return -1;
 
312
 
 
313
        /* day */
 
314
        if (http_date_parse_day(parser) <= 0)
 
315
                return -1;      
 
316
 
 
317
        /* "-" */
 
318
        if (parser->cur >= parser->end || parser->cur[0] != '-')
 
319
                return -1;
 
320
        parser->cur++;
 
321
 
 
322
        /* month */
 
323
        if (http_date_parse_month(parser) <= 0)
 
324
                return -1;      
 
325
 
 
326
        /* "-" */
 
327
        if (parser->cur >= parser->end || parser->cur[0] != '-')
 
328
                return -1;
 
329
        parser->cur++;
 
330
 
 
331
        /* 2DIGIT */
 
332
        if (http_date_parse_number(parser, 2, &parser->tm.tm_year) <= 0)
 
333
                return -1;
 
334
        if (parser->tm.tm_year < 70)
 
335
                parser->tm.tm_year += 100;
 
336
 
 
337
        /* SP time-of-day SP GMT */
 
338
        return http_date_parse_time_gmt(parser);
 
339
}
 
340
 
 
341
static int
 
342
http_date_parse_format_asctime(struct http_date_parser *parser)
 
343
{
 
344
        int ret;
 
345
 
 
346
        /*
 
347
         asctime-date = day-name SP date3 SP time-of-day SP year
 
348
         date3        = month SP ( 2DIGIT / ( SP 1DIGIT ))
 
349
                              ; month day (e.g., Jun  2)
 
350
 
 
351
         Remaining: {...} month SP ( 2DIGIT / ( SP 1DIGIT )) SP time-of-day SP year
 
352
        */
 
353
 
 
354
        /* month */
 
355
        if (http_date_parse_month(parser) <= 0)
 
356
                return -1;
 
357
 
 
358
        /* SP */
 
359
        if (http_date_parse_sp(parser) <= 0)
 
360
                return -1;
 
361
 
 
362
        /* SP 1DIGIT / 2DIGIT */
 
363
        if ((ret=http_date_parse_sp(parser)) < 0)
 
364
                return -1;
 
365
        if (ret == 0) {
 
366
                if (http_date_parse_number(parser, 2, &parser->tm.tm_mday) <= 0)
 
367
                        return -1;
 
368
        } else {
 
369
                if (http_date_parse_number(parser, 1, &parser->tm.tm_mday) <= 0)
 
370
                        return -1;
 
371
        }
 
372
 
 
373
        /* SP time-of-day */
 
374
        if (http_date_parse_sp(parser) <= 0)
 
375
                return -1;
 
376
        if (http_date_parse_time_of_day(parser) <= 0)
 
377
                return -1;
 
378
 
 
379
        /* SP year */
 
380
        if (http_date_parse_sp(parser) <= 0)
 
381
                return -1;
 
382
 
 
383
        return http_date_parse_year(parser);
 
384
}
 
385
 
 
386
static int
 
387
http_date_parse_format_any(struct http_date_parser *parser)
 
388
{
 
389
        string_t *dayname;
 
390
        int i;
 
391
 
 
392
        /*
 
393
         HTTP-date    = rfc1123-date / obs-date
 
394
         rfc1123-date = day-name "," SP date1 SP time-of-day SP GMT
 
395
                        ; fixed length subset of the format defined in
 
396
                        ; Section 5.2.14 of [RFC1123]
 
397
         obs-date     = rfc850-date / asctime-date
 
398
         rfc850-date  = day-name-l "," SP date2 SP time-of-day SP GMT
 
399
         asctime-date = day-name SP date3 SP time-of-day SP year
 
400
         */
 
401
 
 
402
        if (http_date_parse_word(parser, 9, &dayname) <= 0)
 
403
                return -1;
 
404
        
 
405
        if (str_len(dayname) > 3) {
 
406
                /* rfc850-date */
 
407
                for (i = 0; i < 7; i++) {
 
408
                        if (strcmp(weekday_names_long[i], str_c(dayname)) == 0) {
 
409
                                break;
 
410
                        }
 
411
                }
 
412
                if (i >= 7)
 
413
                        return -1;
 
414
                return http_date_parse_format_rfc850(parser);
 
415
        }
 
416
 
 
417
        /* rfc1123-date / asctime-date */
 
418
        for (i = 0; i < 7; i++) {
 
419
                if (strcmp(weekday_names[i], str_c(dayname)) == 0) {
 
420
                        break;
 
421
                }
 
422
        }
 
423
 
 
424
        if (i >= 7 || parser->cur >= parser->end)
 
425
                return -1;
 
426
 
 
427
        if (parser->cur[0] == ' ') {
 
428
                /* asctime-date */
 
429
                parser->cur++;
 
430
                return http_date_parse_format_asctime(parser);
 
431
        }
 
432
 
 
433
        if (parser->cur[0] != ',')
 
434
                return -1;
 
435
 
 
436
        /* rfc1123-date */
 
437
        parser->cur++;
 
438
        return http_date_parse_format_rfc1123(parser);
 
439
}
 
440
 
 
441
bool http_date_parse(const unsigned char *data, size_t size,
 
442
                        time_t *timestamp_r)
 
443
{
 
444
        struct http_date_parser parser;
 
445
        time_t timestamp;
 
446
        
 
447
        memset(&parser, 0, sizeof(parser));
 
448
        parser.cur = data;
 
449
        parser.end = data + size;
 
450
 
 
451
        if (http_date_parse_format_any(&parser) <= 0)
 
452
                return FALSE;
 
453
 
 
454
        if (parser.cur != parser.end)
 
455
                return FALSE;
 
456
 
 
457
        parser.tm.tm_isdst = -1;
 
458
        timestamp = utc_mktime(&parser.tm);
 
459
        if (timestamp == (time_t)-1)
 
460
                return FALSE;
 
461
 
 
462
        *timestamp_r = timestamp;
 
463
        return TRUE;
 
464
}
 
465
 
 
466
bool http_date_parse_tm(const unsigned char *data, size_t size,
 
467
                           struct tm *tm_r)
 
468
{
 
469
        time_t timestamp;
 
470
        struct tm *tm;
 
471
 
 
472
        if (!http_date_parse(data, size, &timestamp))
 
473
                return FALSE;
 
474
 
 
475
        tm = gmtime(&timestamp);
 
476
        *tm_r = *tm;
 
477
        return TRUE;
 
478
}
 
479
 
 
480
const char *http_date_create_tm(struct tm *tm)
 
481
{
 
482
        return t_strdup_printf("%s, %02d %s %04d %02d:%02d:%02d GMT",
 
483
                               weekday_names[tm->tm_wday],
 
484
                               tm->tm_mday,
 
485
                               month_names[tm->tm_mon],
 
486
                               tm->tm_year+1900,
 
487
                               tm->tm_hour, tm->tm_min, tm->tm_sec);
 
488
}
 
489
 
 
490
const char *http_date_create(time_t timestamp)
 
491
{
 
492
        struct tm *tm;
 
493
        tm = gmtime(&timestamp);
 
494
 
 
495
        return http_date_create_tm(tm);
 
496
}
 
497