~ubuntu-branches/ubuntu/wily/dovecot/wily

« back to all changes in this revision

Viewing changes to src/lib-http/http-message-parser.c

  • Committer: Package Import Robot
  • Author(s): Jaldhar H. Vyas
  • Date: 2013-09-09 00:57:32 UTC
  • mfrom: (1.13.11)
  • mto: (4.8.5 experimental) (1.16.1)
  • mto: This revision was merged to the branch mainline in revision 97.
  • Revision ID: package-import@ubuntu.com-20130909005732-dn1eell8srqbhh0e
Tags: upstream-2.2.5
ImportĀ upstreamĀ versionĀ 2.2.5

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 "array.h"
 
5
#include "istream.h"
 
6
#include "http-parser.h"
 
7
#include "http-header-parser.h"
 
8
#include "http-date.h"
 
9
#include "http-transfer.h"
 
10
#include "http-message-parser.h"
 
11
 
 
12
#include <ctype.h>
 
13
 
 
14
void http_message_parser_init(struct http_message_parser *parser,
 
15
                              struct istream *input)
 
16
{
 
17
        memset(parser, 0, sizeof(*parser));
 
18
        parser->input = input;
 
19
}
 
20
 
 
21
void http_message_parser_deinit(struct http_message_parser *parser)
 
22
{
 
23
        if (parser->header_parser != NULL)
 
24
                http_header_parser_deinit(&parser->header_parser);
 
25
        if (parser->msg_pool != NULL)
 
26
                pool_unref(&parser->msg_pool);
 
27
        if (parser->payload != NULL)
 
28
                i_stream_unref(&parser->payload);
 
29
}
 
30
 
 
31
void http_message_parser_restart(struct http_message_parser *parser)
 
32
{
 
33
        i_assert(parser->payload == NULL);
 
34
 
 
35
        if (parser->header_parser == NULL)
 
36
                parser->header_parser = http_header_parser_init(parser->input);
 
37
        else
 
38
                http_header_parser_reset(parser->header_parser);
 
39
 
 
40
        if (parser->msg_pool != NULL)
 
41
                pool_unref(&parser->msg_pool);
 
42
        parser->msg_pool = pool_alloconly_create("http_message", 4096);
 
43
        memset(&parser->msg, 0, sizeof(parser->msg));
 
44
        parser->msg.date = (time_t)-1;
 
45
        p_array_init(&parser->msg.headers, parser->msg_pool, 32);
 
46
}
 
47
 
 
48
int http_message_parse_version(struct http_message_parser *parser)
 
49
{
 
50
        const unsigned char *p = parser->cur;
 
51
        const size_t size = parser->end - parser->cur;
 
52
 
 
53
        /* HTTP-version  = HTTP-name "/" DIGIT "." DIGIT
 
54
           HTTP-name     = %x48.54.54.50 ; "HTTP", case-sensitive
 
55
         */
 
56
        if (size < 8)
 
57
                return 0;
 
58
        if (memcmp(p, "HTTP/", 5) != 0 ||
 
59
            !i_isdigit(p[5]) || p[6] != '.' || !i_isdigit(p[7]))
 
60
                return -1;
 
61
        parser->msg.version_major = p[5] - '0';
 
62
        parser->msg.version_minor = p[7] - '0';
 
63
        parser->cur += 8;
 
64
        return 1;
 
65
}
 
66
 
 
67
int http_message_parse_finish_payload(struct http_message_parser *parser,
 
68
                                      const char **error_r)
 
69
{
 
70
        const unsigned char *data;
 
71
        size_t size;
 
72
        int ret;
 
73
 
 
74
        if (parser->payload == NULL)
 
75
                return 1;
 
76
 
 
77
        while ((ret = i_stream_read_data(parser->payload, &data, &size, 0)) > 0)
 
78
                i_stream_skip(parser->payload, size);
 
79
        if (ret == 0 || parser->payload->stream_errno != 0) {
 
80
                if (ret < 0)
 
81
                        *error_r = "Stream error while skipping payload";
 
82
                return ret;
 
83
        }
 
84
        i_stream_unref(&parser->payload);
 
85
        return 1;
 
86
}
 
87
 
 
88
static int
 
89
http_message_parse_header(struct http_message_parser *parser, const char *name,
 
90
                          const unsigned char *data, size_t size,
 
91
                          const char **error_r)
 
92
{
 
93
        struct http_response_header *hdr;
 
94
        struct http_parser hparser;
 
95
        void *value;
 
96
 
 
97
        hdr = array_append_space(&parser->msg.headers);
 
98
        hdr->key = p_strdup(parser->msg_pool, name);
 
99
        hdr->value = value = p_malloc(parser->msg_pool, size+1);
 
100
        memcpy(value, data, size);
 
101
        hdr->size = size;
 
102
 
 
103
        switch (name[0]) {
 
104
        case 'C': case 'c':
 
105
                if (strcasecmp(name, "Connection") == 0) {
 
106
                        const char *option;
 
107
 
 
108
                        /* Connection        = 1#connection-option
 
109
                                 connection-option = token
 
110
                        */
 
111
                        http_parser_init(&hparser, data, size);
 
112
                        for (;;) {
 
113
                                if (http_parse_token_list_next(&hparser, &option) <= 0)
 
114
                                        break;
 
115
                                if (strcasecmp(option, "close") == 0) {
 
116
                                        parser->msg.connection_close = TRUE;
 
117
                                        break; // not interested in any other options
 
118
                                }
 
119
                        }
 
120
                        return 0;
 
121
                }
 
122
                if (strcasecmp(name, "Content-Length") == 0) {
 
123
                        /* Content-Length = 1*DIGIT */
 
124
                        if (str_to_uoff(hdr->value, &parser->msg.content_length) < 0) {
 
125
                                *error_r = "Invalid Content-Length header";
 
126
                                return -1;
 
127
                        }
 
128
                        return 0;
 
129
                }
 
130
                break;
 
131
        case 'D': case 'd':
 
132
                if (strcasecmp(name, "Date") == 0) {
 
133
                        /* Date = HTTP-date */
 
134
                        (void)http_date_parse(data, size, &parser->msg.date);
 
135
                        return 0;
 
136
                }
 
137
                break;
 
138
        case 'L': case 'l':
 
139
                if (strcasecmp(name, "Location") == 0) {
 
140
                        /* Location = URI-reference (not parsed here) */
 
141
                        parser->msg.location = hdr->value;
 
142
                        return 0;
 
143
                }
 
144
                break;
 
145
        case 'T': case 't':
 
146
                if (strcasecmp(name, "Transfer-Encoding") == 0) {
 
147
                        /* Transfer-Encoding = 1#transfer-coding */
 
148
                        parser->msg.transfer_encoding = hdr->value;
 
149
                        return 0;
 
150
                }
 
151
                break;
 
152
        default:
 
153
                break;
 
154
        }
 
155
        return 0;
 
156
}
 
157
 
 
158
int http_message_parse_headers(struct http_message_parser *parser,
 
159
                               const char **error_r)
 
160
{
 
161
        const char *field_name, *error;
 
162
        const unsigned char *field_data;
 
163
        size_t field_size;
 
164
        int ret;
 
165
 
 
166
        /* *( header-field CRLF ) CRLF */
 
167
        while ((ret=http_header_parse_next_field
 
168
                (parser->header_parser, &field_name, &field_data, &field_size, &error)) > 0) {
 
169
                if (field_name == NULL) {
 
170
                        /* EOH */
 
171
                        return 1;
 
172
                }
 
173
                if (http_message_parse_header(parser, field_name, field_data,
 
174
                                              field_size, error_r) < 0)
 
175
                        return -1;
 
176
        }
 
177
 
 
178
        if (ret < 0) {
 
179
                *error_r = t_strdup_printf(
 
180
                        "Failed to parse response header: %s", error);
 
181
        }
 
182
        return ret;
 
183
}
 
184
 
 
185
int http_message_parse_body(struct http_message_parser *parser,
 
186
                            const char **error_r)
 
187
{
 
188
        struct http_parser hparser;
 
189
 
 
190
        if (parser->msg.content_length > 0) {
 
191
                /* Got explicit message size from Content-Length: header */
 
192
                parser->payload =
 
193
                        i_stream_create_limit(parser->input,
 
194
                                              parser->msg.content_length);
 
195
        } else if (parser->msg.transfer_encoding != NULL) {
 
196
                const char *tenc;
 
197
 
 
198
                /* Transfer-Encoding = 1#transfer-coding
 
199
                   transfer-coding    = "chunked" / "compress" / "deflate" / "gzip"
 
200
                                      / transfer-extension       ;  [FIXME]
 
201
                   transfer-extension = token *( OWS ";" OWS transfer-parameter )
 
202
                */
 
203
                http_parser_init(&hparser,
 
204
                                 (const unsigned char *)parser->msg.transfer_encoding,
 
205
                                 strlen(parser->msg.transfer_encoding));
 
206
                for (;;) {
 
207
                        if (http_parse_token_list_next(&hparser, &tenc) <= 0)
 
208
                                break;
 
209
                        if (strcasecmp(tenc, "chunked") == 0) {
 
210
                                parser->payload =
 
211
                                        http_transfer_chunked_istream_create(parser->input);
 
212
                                break; // FIXME
 
213
                        } else {
 
214
                                *error_r = t_strdup_printf(
 
215
                                        "Unknown Transfer-Encoding `%s'", tenc);
 
216
                                return -1;
 
217
                        }
 
218
                }
 
219
        }
 
220
        return 0;
 
221
}