e506ea4f99326fac78ae2f261b15c1b6db934034
[bluesky.git] / libs3-1.4 / src / response_headers_handler.c
1 /** **************************************************************************
2  * response_headers_handler.c
3  * 
4  * Copyright 2008 Bryan Ischo <bryan@ischo.com>
5  * 
6  * This file is part of libs3.
7  * 
8  * libs3 is free software: you can redistribute it and/or modify it under the
9  * terms of the GNU General Public License as published by the Free Software
10  * Foundation, version 3 of the License.
11  *
12  * In addition, as a special exception, the copyright holders give
13  * permission to link the code of this library and its programs with the
14  * OpenSSL library, and distribute linked combinations including the two.
15  *
16  * libs3 is distributed in the hope that it will be useful, but WITHOUT ANY
17  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License version 3
22  * along with libs3, in a file named COPYING.  If not, see
23  * <http://www.gnu.org/licenses/>.
24  *
25  ************************************************************************** **/
26
27 #include <ctype.h>
28 #include <string.h>
29 #include "response_headers_handler.h"
30
31
32 void response_headers_handler_initialize(ResponseHeadersHandler *handler)
33 {
34     handler->responseProperties.requestId = 0;
35     handler->responseProperties.requestId2 = 0;
36     handler->responseProperties.contentType = 0;
37     handler->responseProperties.contentLength = 0;
38     handler->responseProperties.server = 0;
39     handler->responseProperties.eTag = 0;
40     handler->responseProperties.lastModified = -1;
41     handler->responseProperties.metaDataCount = 0;
42     handler->responseProperties.metaData = 0;
43     handler->done = 0;
44     string_multibuffer_initialize(handler->responsePropertyStrings);
45     string_multibuffer_initialize(handler->responseMetaDataStrings);
46 }
47
48
49 void response_headers_handler_add(ResponseHeadersHandler *handler,
50                                   char *header, int len)
51 {
52     S3ResponseProperties *responseProperties = &(handler->responseProperties);
53     char *end = &(header[len]);
54     
55     // Curl might call back the header function after the body has been
56     // received, for 'chunked encoded' contents.  We don't handle this as of
57     // yet, and it's not clear that it would ever be useful.
58     if (handler->done) {
59         return;
60     }
61
62     // If we've already filled up the response headers, ignore this data.
63     // This sucks, but it shouldn't happen - S3 should not be sending back
64     // really long headers.
65     if (handler->responsePropertyStringsSize == 
66         (sizeof(handler->responsePropertyStrings) - 1)) {
67         return;
68     }
69
70     // It should not be possible to have a header line less than 3 long
71     if (len < 3) {
72         return;
73     }
74
75     // Skip whitespace at beginning of header; there never should be any,
76     // but just to be safe
77     while (is_blank(*header)) {
78         header++;
79     }
80
81     // The header must end in \r\n, so skip back over it, and also over any
82     // trailing whitespace
83     end -= 3;
84     while ((end > header) && is_blank(*end)) {
85         end--;
86     }
87     if (!is_blank(*end)) {
88         end++;
89     }
90
91     if (end == header) {
92         // totally bogus
93         return;
94     }
95
96     *end = 0;
97     
98     // Find the colon to split the header up
99     char *c = header;
100     while (*c && (*c != ':')) {
101         c++;
102     }
103     
104     int namelen = c - header;
105
106     // Now walk c past the colon
107     c++;
108     // Now skip whitespace to the beginning of the value
109     while (is_blank(*c)) {
110         c++;
111     }
112
113     int valuelen = (end - c) + 1, fit;
114
115     if (!strncmp(header, "x-amz-request-id", namelen)) {
116         responseProperties->requestId = 
117             string_multibuffer_current(handler->responsePropertyStrings);
118         string_multibuffer_add(handler->responsePropertyStrings, c, 
119                                valuelen, fit);
120     }
121     else if (!strncmp(header, "x-amz-id-2", namelen)) {
122         responseProperties->requestId2 = 
123             string_multibuffer_current(handler->responsePropertyStrings);
124         string_multibuffer_add(handler->responsePropertyStrings, c, 
125                                valuelen, fit);
126     }
127     else if (!strncmp(header, "Content-Type", namelen)) {
128         responseProperties->contentType = 
129             string_multibuffer_current(handler->responsePropertyStrings);
130         string_multibuffer_add(handler->responsePropertyStrings, c, 
131                                valuelen, fit);
132     }
133     else if (!strncmp(header, "Content-Length", namelen)) {
134         handler->responseProperties.contentLength = 0;
135         while (*c) {
136             handler->responseProperties.contentLength *= 10;
137             handler->responseProperties.contentLength += (*c++ - '0');
138         }
139     }
140     else if (!strncmp(header, "Server", namelen)) {
141         responseProperties->server = 
142             string_multibuffer_current(handler->responsePropertyStrings);
143         string_multibuffer_add(handler->responsePropertyStrings, c, 
144                                valuelen, fit);
145     }
146     else if (!strncmp(header, "ETag", namelen)) {
147         responseProperties->eTag = 
148             string_multibuffer_current(handler->responsePropertyStrings);
149         string_multibuffer_add(handler->responsePropertyStrings, c, 
150                                valuelen, fit);
151     }
152     else if (!strncmp(header, S3_METADATA_HEADER_NAME_PREFIX, 
153                       sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1)) {
154         // Make sure there is room for another x-amz-meta header
155         if (handler->responseProperties.metaDataCount ==
156             sizeof(handler->responseMetaData)) {
157             return;
158         }
159         // Copy the name in
160         char *metaName = &(header[sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1]);
161         int metaNameLen = 
162             (namelen - (sizeof(S3_METADATA_HEADER_NAME_PREFIX) - 1));
163         char *copiedName = 
164             string_multibuffer_current(handler->responseMetaDataStrings);
165         string_multibuffer_add(handler->responseMetaDataStrings, metaName,
166                                metaNameLen, fit);
167         if (!fit) {
168             return;
169         }
170
171         // Copy the value in
172         char *copiedValue = 
173             string_multibuffer_current(handler->responseMetaDataStrings);
174         string_multibuffer_add(handler->responseMetaDataStrings,
175                                c, valuelen, fit);
176         if (!fit) {
177             return;
178         }
179
180         if (!handler->responseProperties.metaDataCount) {
181             handler->responseProperties.metaData = 
182                 handler->responseMetaData;
183         }
184
185         S3NameValue *metaHeader = 
186             &(handler->responseMetaData
187               [handler->responseProperties.metaDataCount++]);
188         metaHeader->name = copiedName;
189         metaHeader->value = copiedValue;
190     }
191 }
192
193
194 void response_headers_handler_done(ResponseHeadersHandler *handler, CURL *curl)
195 {
196     // Now get the last modification time from curl, since it's easiest to let
197     // curl parse it
198     time_t lastModified;
199     if (curl_easy_getinfo
200         (curl, CURLINFO_FILETIME, &lastModified) == CURLE_OK) {
201         handler->responseProperties.lastModified = lastModified;
202     }
203     
204     handler->done = 1;
205 }