|
1 // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include "chttpserverheaderreader.h" |
|
17 |
|
18 #include <http/rhttpsession.h> |
|
19 #include <httpstringconstants.h> |
|
20 #include <httperr.h> |
|
21 #include <inetprottextutils.h> |
|
22 |
|
23 _LIT8(KCommaSpaceSep,", "); |
|
24 _LIT8(KFieldSeparator, "\n"); |
|
25 _LIT8(KCodecSpace, " "); |
|
26 _LIT8(KSemiSpaceSep," ;"); |
|
27 |
|
28 const TUint KCommaChar = ','; |
|
29 const TUint KSemiColonChar = ';'; |
|
30 const TUint KEqualsChar = '='; |
|
31 |
|
32 |
|
33 CHttpServerHeaderReader* CHttpServerHeaderReader::NewL(RStringPool aStringPool) |
|
34 { |
|
35 return new (ELeave) CHttpServerHeaderReader(aStringPool); |
|
36 } |
|
37 |
|
38 CHttpServerHeaderReader::~CHttpServerHeaderReader() |
|
39 { |
|
40 } |
|
41 |
|
42 CHttpServerHeaderReader::CHttpServerHeaderReader(RStringPool aStringPool) |
|
43 : CHttpHeaderReader(aStringPool) |
|
44 { |
|
45 } |
|
46 |
|
47 // Helper to DoAcceptL(), DoAcceptCharsetL(), DoAcceptLanguageL() and DoAcceptEncodingL() |
|
48 // for comma-separated lists incorporating q-values.. |
|
49 // name : value, value; q= 2.2, value,... |
|
50 void CHttpServerHeaderReader::DecodeGenericAcceptL(RHeaderField& aHeader, TInt aErrorCode) const |
|
51 { |
|
52 |
|
53 TPtrC8 rawData; |
|
54 aHeader.RawDataL(rawData); |
|
55 TInt remaining = rawData.Length(); |
|
56 TPtrC8 token; |
|
57 TInt tokensFound = 0; |
|
58 while (remaining > 0) |
|
59 { |
|
60 remaining -= InetProtTextUtils::ExtractNextTokenFromList(rawData, token, KCommaChar); |
|
61 |
|
62 TInt pos = token.Locate(KSemiColonChar); |
|
63 if (pos < 0) |
|
64 { |
|
65 // No parameters. Just store the field value |
|
66 InetProtTextUtils::RemoveWhiteSpace(token, InetProtTextUtils::ERemoveBoth); |
|
67 SetNewFStringPartL(aHeader, tokensFound, token); |
|
68 } |
|
69 else if (pos==0) |
|
70 { |
|
71 // No valid q-value. Just store the parameter. |
|
72 User::Leave(aErrorCode); |
|
73 } |
|
74 else |
|
75 { |
|
76 // parameter value(s) exist. |
|
77 |
|
78 if (pos==token.Length()) |
|
79 // if no field value exists. i.e. an invalid header |
|
80 User::Leave(aErrorCode); |
|
81 |
|
82 // store the field |
|
83 TPtrC8 fieldValue(token.Left(pos)); |
|
84 TPtrC8 parameters(token.Mid(pos+1)); |
|
85 InetProtTextUtils::RemoveWhiteSpace(fieldValue, InetProtTextUtils::ERemoveBoth); |
|
86 |
|
87 CHeaderFieldPart* part = SetNewFStringPartL(aHeader, tokensFound, fieldValue); |
|
88 |
|
89 TPtrC8 thisParam; |
|
90 do { |
|
91 // check if there is another parameter |
|
92 pos = parameters.Locate(KSemiColonChar); |
|
93 if (pos > 0) |
|
94 { |
|
95 if (pos==token.Length()) |
|
96 // if no field value exists. i.e. an invalid header |
|
97 User::Leave(aErrorCode); |
|
98 |
|
99 thisParam.Set(parameters.Left(pos)); |
|
100 parameters.Set(parameters.Mid(pos+1)); |
|
101 } |
|
102 else |
|
103 thisParam.Set(parameters); |
|
104 |
|
105 |
|
106 TInt pPos = thisParam.Locate(KEqualsChar); |
|
107 if (pPos <= 0 || pPos==thisParam.Length()) |
|
108 // Invalid parameter, missing '=' char, or missing field value. |
|
109 User::Leave(aErrorCode); |
|
110 |
|
111 TPtrC8 paramField(thisParam.Left(pPos)); |
|
112 TPtrC8 paramData(thisParam.Mid(pPos + 1)); |
|
113 |
|
114 SetNewFStringParamL(*part, paramField, paramData); |
|
115 |
|
116 } while (pos > 0); |
|
117 } |
|
118 ++tokensFound; |
|
119 } |
|
120 |
|
121 |
|
122 } |
|
123 |
|
124 void CHttpServerHeaderReader::DecodeAuthorizationL(RHeaderField& aHeader) const |
|
125 { |
|
126 // RFC2616, section 14.8; RFC2617, section |
|
127 // Authorization = "Authorization" ":" credentials |
|
128 // credentials = auth-scheme #auth-param |
|
129 // credentials = "Basic" basic-credentials |
|
130 // basic-credentials = base64-user-pass |
|
131 // base64-user-pass = <base64 [4] encoding of user-pass, except not limited to 76 char/line> |
|
132 // user-pass = userid ":" password |
|
133 // userid = *<TEXT excluding ":"> |
|
134 // password = *TEXT |
|
135 |
|
136 // Parts are encoded with no punctuation after them and parameters are comma |
|
137 // separated with the value in quotes. This means that for basic |
|
138 // authentication, part 1 must be 'Basic' and part 2 is the |
|
139 // credentials. For digest, part1 is 'Digest', and the digest |
|
140 // response is stored in parameters. |
|
141 |
|
142 TPtrC8 buffer; |
|
143 aHeader.RawDataL(buffer); |
|
144 |
|
145 TInt totalBytesConsumed = 0; |
|
146 TInt numParam = 0; |
|
147 CHeaderFieldPart* part = NULL; |
|
148 TBool done = EFalse; |
|
149 |
|
150 TPtrC8 token; |
|
151 TInt bytesConsumed = InetProtTextUtils::ExtractNextTokenFromList(buffer, token, KCommaSpaceSep); |
|
152 part = SetNewFStringPartL(aHeader, numParam, token); //first assignment |
|
153 ++numParam; |
|
154 |
|
155 TBool equalsPresent = (token.Locate('=') != KErrNotFound); |
|
156 if ((totalBytesConsumed == bytesConsumed) && equalsPresent) |
|
157 { |
|
158 // The first token has an equals sign in it. That |
|
159 // can't be as it has to be in an Authorization scheme |
|
160 User::Leave(KErrHttpDecodeAuthorization); |
|
161 } |
|
162 |
|
163 RStringF basic = iStrPool.StringF(HTTP::EBasic , RHTTPSession::GetTable()); |
|
164 |
|
165 if (token==basic.DesC()) |
|
166 { |
|
167 // Get next part and store away |
|
168 bytesConsumed = InetProtTextUtils::ExtractNextTokenFromList(buffer, token, KCommaSpaceSep); |
|
169 ++numParam; |
|
170 if (bytesConsumed==0) |
|
171 { |
|
172 // Need a second value if "Basic" |
|
173 User::Leave(KErrHttpDecodeAuthorization); |
|
174 } |
|
175 |
|
176 // Remove surrounding quotes |
|
177 TPtrC8 val; |
|
178 if (token[0] == '"') |
|
179 { |
|
180 bytesConsumed += |
|
181 InetProtTextUtils::ExtractQuotedStringL(token,val); |
|
182 } |
|
183 else |
|
184 { |
|
185 val.Set(token); |
|
186 } |
|
187 SetNewFStringPartL(aHeader, numParam - 1, val); |
|
188 } |
|
189 else // Get Digest parameters. Should check if it is digest |
|
190 { |
|
191 while (!done) |
|
192 { |
|
193 TPtrC8 token; |
|
194 TInt bytesConsumed = InetProtTextUtils::ExtractNextTokenFromList(buffer, token, KCommaSpaceSep); |
|
195 |
|
196 done = (bytesConsumed == 0); |
|
197 if (done) |
|
198 { |
|
199 if (numParam == 0)// if we didn't find _anything_ at all... |
|
200 User::Leave(KErrHttpDecodeAuthorization); |
|
201 } |
|
202 else if (token.Length() > 0) |
|
203 { |
|
204 totalBytesConsumed += bytesConsumed; |
|
205 |
|
206 if (token.Locate('=') == KErrNotFound) |
|
207 { |
|
208 // Got a new part. Add it. |
|
209 ++numParam; |
|
210 part = SetNewFStringPartL(aHeader, numParam - 1, token); |
|
211 } |
|
212 else |
|
213 { |
|
214 TPtrC8 paramName; |
|
215 TPtrC8 paramVal; |
|
216 bytesConsumed += GetParamNameAndValueL(token, paramName, paramVal, KErrHttpDecodeBasicAuth); |
|
217 SetNewStringParamL(*part, paramName, paramVal); |
|
218 } |
|
219 } |
|
220 } |
|
221 } |
|
222 } |
|
223 |
|
224 void CHttpServerHeaderReader::DecodeHostL(RHeaderField& aHeader) const |
|
225 { |
|
226 // RFC2616, section 14.23 |
|
227 // Host = "Host" ":" host [ ":" port ] ; Section 3.2.2 |
|
228 |
|
229 // Our convention will be that a string called HTTP::EPort will be used to |
|
230 // set a parameter holding the integer port number |
|
231 |
|
232 // We are looking for a hostname and optional port number, 0 or more values |
|
233 |
|
234 TPtrC8 rawData; |
|
235 aHeader.RawDataL(rawData); |
|
236 TInt remaining = rawData.Length(); |
|
237 TPtrC8 token; |
|
238 TInt tokensFound =0; |
|
239 |
|
240 remaining -= InetProtTextUtils::ExtractNextTokenFromList(rawData, token, KFieldSeparator); |
|
241 |
|
242 if (remaining) |
|
243 { |
|
244 // Should not be more data |
|
245 } |
|
246 |
|
247 // Check for colon in our token |
|
248 TInt colonPos = token.Locate(':'); |
|
249 InetProtTextUtils::RemoveWhiteSpace(token, InetProtTextUtils::ERemoveBoth); |
|
250 if (colonPos == KErrNotFound) |
|
251 { |
|
252 // Just a host name |
|
253 SetNewFStringPartL(aHeader, tokensFound, token); |
|
254 } |
|
255 else |
|
256 { |
|
257 //.. also has a port number. |
|
258 |
|
259 TPtrC8 host(token.Left(colonPos)); |
|
260 CHeaderFieldPart* part = SetNewFStringPartL(aHeader, tokensFound, host); |
|
261 |
|
262 if (colonPos==token.Length()) |
|
263 // No port number supplied |
|
264 User::Leave(KErrHttpGeneralHeaderMissingHost); |
|
265 |
|
266 TPtrC8 port(token.Mid(colonPos + 1)); |
|
267 TPtrC8 name(iStrPool.StringF(HTTP::EPort,RHTTPSession::GetTable()).DesC()); |
|
268 SetNewFStringParamL(*part, name ,port); |
|
269 } |
|
270 } |
|
271 |
|
272 void CHttpServerHeaderReader::DecodeExpectL(RHeaderField& aHeader) const |
|
273 { |
|
274 // Expect = "Expect" ":" 1#expectation |
|
275 // |
|
276 // expectation = "100-continue" | expectation-extension |
|
277 // expectation-extension = token [ "=" ( token | quoted-string ) |
|
278 // *expect-params ] |
|
279 // expect-params = ";" token [ "=" ( token | quoted-string ) ] |
|
280 |
|
281 TPtrC8 buffer; |
|
282 aHeader.RawDataL(buffer); |
|
283 |
|
284 TInt numParam = 0; |
|
285 |
|
286 TPtrC8 token; |
|
287 InetProtTextUtils::ExtractNextTokenFromList(buffer, token, KSemiSpaceSep); |
|
288 ++numParam; |
|
289 |
|
290 SetNewFStringPartL(aHeader, 0, buffer); |
|
291 } |
|
292 |
|
293 void CHttpServerHeaderReader::DecodeTEL(RHeaderField& aHeader) const |
|
294 { |
|
295 // TE = "TE" ":" #( t-codings ) |
|
296 // t-codings = "trailers" | ( transfer-extension [ accept-params ] ) |
|
297 // Examples of its use are: |
|
298 // TE: deflate |
|
299 // TE: |
|
300 // TE: trailers, deflate;q=0.5 |
|
301 |
|
302 DecodeGenericAcceptL(aHeader, KErrHttpDecodeTE); |
|
303 } |
|
304 |
|
305 /* |
|
306 * Methods from CHeaderReader |
|
307 */ |
|
308 |
|
309 void CHttpServerHeaderReader::DecodeHeaderL(RHeaderField& aHeader) |
|
310 { |
|
311 RStringF fieldStr = iStrPool.StringF(aHeader.Name()); |
|
312 switch( fieldStr.Index(RHTTPSession::GetTable()) ) |
|
313 { |
|
314 case HTTP::EAccept: |
|
315 DecodeGenericAcceptL(aHeader, KErrHttpDecodeAccept); |
|
316 break; |
|
317 case HTTP::EAcceptCharset: |
|
318 DecodeGenericAcceptL(aHeader, KErrHttpDecodeAcceptCharset); |
|
319 break; |
|
320 case HTTP::EAuthorization: |
|
321 DecodeAuthorizationL(aHeader); |
|
322 break; |
|
323 case HTTP::EAcceptLanguage: |
|
324 DecodeGenericAcceptL(aHeader, KErrHttpDecodeAcceptLanguage); |
|
325 break; |
|
326 case HTTP::EAcceptEncoding: |
|
327 DecodeGenericAcceptL(aHeader, KErrHttpDecodeAcceptEncoding); |
|
328 break; |
|
329 case HTTP::EHost: |
|
330 DecodeHostL(aHeader); |
|
331 break; |
|
332 case HTTP::EUserAgent: |
|
333 DecodeTokenListHeaderL(aHeader, KCodecSpace()); |
|
334 break; |
|
335 case HTTP::EIfMatch: |
|
336 DecodeTokenListHeaderL(aHeader, KCommaSpaceSep()); |
|
337 break; |
|
338 case HTTP::EIfNoneMatch: |
|
339 DecodeTokenListHeaderL(aHeader, KCommaSpaceSep()); |
|
340 break; |
|
341 case HTTP::EIfModifiedSince: |
|
342 DecodeDateL(aHeader); |
|
343 break; |
|
344 case HTTP::EIfUnmodifiedSince: |
|
345 DecodeDateL(aHeader); |
|
346 break; |
|
347 case HTTP::EExpect: |
|
348 DecodeExpectL(aHeader); |
|
349 break; |
|
350 case HTTP::ETE: |
|
351 DecodeTEL(aHeader); |
|
352 break; |
|
353 default: |
|
354 User::Leave(KErrNotSupported); |
|
355 } |
|
356 } |