Branch data Line data Source code
1 : : /* Copyright 2006-2011 Savarese Software Research Corporation
2 : : *
3 : : * Licensed under the Apache License, Version 2.0 (the "License");
4 : : * you may not use this file except in compliance with the License.
5 : : * You may obtain a copy of the License at
6 : : *
7 : : * https://www.savarese.com/software/ApacheLicense-2.0
8 : : *
9 : : * Unless required by applicable law or agreed to in writing, software
10 : : * distributed under the License is distributed on an "AS IS" BASIS,
11 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : * See the License for the specific language governing permissions and
13 : : * limitations under the License.
14 : : */
15 : :
16 : : #include <ssrc/wispers/fcgi/parse_query.h>
17 : : #include <ssrc/wispers/fcgi/URI.h>
18 : : #include <ssrc/wispers/utility/WebStrings.h>
19 : :
20 : : #include <boost/algorithm/string/trim.hpp>
21 : :
22 : : __BEGIN_NS_SSRC_WSPR_FCGI
23 : :
24 : : // Note, this is an approximation because a header value could contain:
25 : : // Foo-Header: foobar; bar=" foo=value"; foo=value
26 : : // In practice, that will not be the case for well-behaved programs.
27 : 12 : string parse_header_attribute(const string & header_value,
28 : : const string & attribute,
29 : : string::size_type pos,
30 : : string::size_type *end)
31 : : {
32 [ + - + - : 24 : string attr = std::move(string(" ").append(attribute).append("="));
+ - + - +
- + - ]
33 : 12 : string::size_type end_pos = 0;
34 : :
35 [ + - ]: 12 : pos = header_value.find(attr, pos);
36 : :
37 [ + + ]: 12 : if(pos != string::npos) {
38 : :
39 [ + - ]: 10 : pos+=attr.size();
40 : :
41 [ + - + - ]: 10 : if(pos < header_value.size()) {
42 [ + - + + ]: 10 : if(header_value[pos] == '"') {
43 : 8 : ++pos;
44 [ + - ]: 8 : end_pos = header_value.find('"', pos);
45 [ - + ]: 8 : if(end_pos == string::npos) {
46 : 0 : end_pos = pos;
47 : : }
48 : : } else {
49 [ + - ]: 2 : end_pos = header_value.find_first_of("; \r\n", pos + 1);
50 : :
51 [ + + ]: 2 : if(end_pos == string::npos) {
52 [ + - ]: 1 : end_pos = header_value.size();
53 : : }
54 : : }
55 : : } else {
56 : 0 : pos = 0;
57 : : }
58 : : } else {
59 : 2 : pos = 0;
60 : : }
61 : :
62 [ + + ]: 12 : if(end) {
63 : 3 : *end = end_pos;
64 : : }
65 : :
66 [ + - ]: 24 : return header_value.substr(pos, end_pos - pos);
67 : : }
68 : :
69 : 5 : void parse_query_string(const string & query_str, parameter_map & parameters) {
70 : : using NS_SSRC_WSPR_UTILITY::unescape_url;
71 : :
72 [ - + ]: 5 : if(query_str.empty())
73 : 5 : return;
74 : :
75 : 5 : string::size_type and_pos = query_str.find('&');
76 : 5 : string::size_type eq_pos = query_str.find('=');
77 : 5 : string::size_type pos = 0;
78 [ + - + - ]: 10 : string name, value;
79 : :
80 [ + + ]: 18 : while(eq_pos != string::npos) {
81 [ + + + + : 28 : while(and_pos < eq_pos || and_pos == string::npos) {
+ + ]
82 [ + + ]: 12 : if(and_pos == string::npos) {
83 [ + - ]: 5 : and_pos = query_str.size();
84 : : } else {
85 : 7 : pos = and_pos + 1;
86 [ + - ]: 7 : and_pos = query_str.find('&', pos);
87 : : }
88 : : }
89 : :
90 : 8 : const string::size_type key_size = eq_pos - pos;
91 : 8 : const string::size_type value_size = and_pos - eq_pos - 1;
92 : :
93 [ + + ]: 8 : if(key_size > 0) {
94 [ + - ]: 7 : name.assign(query_str, pos, key_size);
95 [ + - ]: 7 : value.assign(query_str, eq_pos + 1, value_size);
96 [ + - ]: 7 : unescape_url(name);
97 [ + - ]: 7 : unescape_url(value);
98 : :
99 : : // We do not allow leading or trailing whitespace in input values.
100 [ + - ]: 7 : boost::algorithm::trim(value);
101 : :
102 [ + - + - : 7 : parameters.insert(parameter_map::value_type(name, value));
+ - ]
103 : : }
104 : :
105 [ + - ]: 8 : eq_pos = query_str.find('=', and_pos);
106 : : }
107 : : }
108 : :
109 : 1 : void parse_multipart_data(const string & data, const string & boundary,
110 : : parameter_map & parameters)
111 : : {
112 : 1 : const string::size_type step = boundary.size();
113 : 1 : const string::size_type max_pos = data.size();
114 : 1 : string::size_type pos = data.find(boundary), start;
115 : :
116 [ + - ]: 1 : if(pos != string::npos) {
117 : 1 : pos+=step;
118 : :
119 [ + - + + : 5 : while(pos < max_pos && data[pos] != '-') {
+ + ]
120 : : // Since we don't handle file data, we don't bother looking
121 : : // for specific header fields. Instead, we assume only the
122 : : // Content-Disposition header contains a name attribute, thereby
123 : : // allowing us to search only for the next name attribute.
124 [ + - + - : 6 : const string && name = parse_header_attribute(data, "name", pos, &pos);
+ - ]
125 : :
126 [ + - - + ]: 3 : if(name.empty()) {
127 : : break;
128 : : }
129 : :
130 : : // Look for start of data.
131 [ + - - + ]: 3 : if((pos = data.find("\r\n\r\n", pos)) == string::npos) {
132 : : break;
133 : : }
134 : :
135 : 3 : start = pos + 4;
136 : :
137 : : // Look for end of data.
138 [ + - - + ]: 3 : if((pos = data.find(boundary, start)) == string::npos) {
139 : : break;
140 : : }
141 : :
142 [ + - ]: 3 : if(pos - 2 >= start) {
143 [ + - + - : 3 : parameters.insert(parameter_map::value_type(name, data.substr(start, pos - start - 2)));
+ - + - +
- ]
144 : : }
145 : :
146 [ + - ]: 6 : pos+=step;
147 : : }
148 : : }
149 : 1 : }
150 : :
151 : 0 : void parse_get_parameters(const FCGIRequest & request,
152 : : parameter_map & parameters)
153 : : {
154 : 0 : const char *query_str = request.fcgx_get_param("QUERY_STRING");
155 : :
156 [ # # # # ]: 0 : if(!query_str || *query_str == 0) {
157 : 0 : const char *request_uri = request.fcgx_get_param("REQUEST_URI");
158 [ # # ]: 0 : if(request_uri) {
159 [ # # # # : 0 : URI uri(request_uri);
# # ]
160 [ # # # # : 0 : parse_query_string(uri.query(), parameters);
# # ]
161 : 0 : }
162 : : } else
163 [ # # # # : 0 : parse_query_string(query_str, parameters);
# # ]
164 : 0 : }
165 : :
166 : 0 : void parse_post_parameters(const FCGIRequest & request,
167 : : parameter_map & parameters)
168 : : {
169 : 0 : const string && content_type = request.content_type();
170 : : const bool is_urlencoded =
171 [ # # ]: 0 : (content_type.find("application/x-www-form-urlencoded") == 0);
172 : : const bool is_multipart =
173 [ # # # # ]: 0 : (is_urlencoded ? false : (content_type.find( "multipart/form-data") == 0));
174 : :
175 : : // TODO: Check that content length is less than some maximum value first.
176 [ # # # # ]: 0 : if(is_urlencoded || is_multipart) {
177 [ # # ]: 0 : long length = request.content_length();
178 : :
179 [ # # ]: 0 : if(length > 0) {
180 [ # # # # ]: 0 : string data(length, ' ');
181 : 0 : int bytes_read = 0, total = 0;
182 : :
183 : : // Using &data[i] as a contiguous buffer may not work with some
184 : : // STL implementations, but C++ 0x requires the behavior.
185 [ # # ]: 0 : while(length > 0) {
186 [ # # # # ]: 0 : bytes_read = request.fcgx_get_str(&data[total], length);
187 [ # # ]: 0 : if(bytes_read <= 0)
188 : 0 : break;
189 : 0 : total+=bytes_read;
190 : 0 : length-=bytes_read;
191 : : }
192 : :
193 : : // Ignore short reads. The error will be handled elsewhere.
194 [ # # ]: 0 : if(length != 0)
195 [ # # ]: 0 : data.resize(total);
196 : :
197 [ # # ]: 0 : if(is_urlencoded) {
198 [ # # ]: 0 : parse_query_string(data, parameters);
199 : : } else {
200 [ # # # # ]: 0 : string boundary("--");
201 [ # # # # : 0 : boundary.append(parse_header_attribute(content_type, "boundary"));
# # # # #
# ]
202 [ # # # # ]: 0 : if(boundary.size() > 2) {
203 [ # # ]: 0 : parse_multipart_data(data, boundary, parameters);
204 : : }
205 : : }
206 : : }
207 : : }
208 : 0 : }
209 : :
210 : : __END_NS_SSRC_WSPR_FCGI
|