|
1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: URI parser specific to symbian |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include "turi.h" |
|
20 |
|
21 |
|
22 |
|
23 const TInt KMinSizeOfEqualsAndValue = 2; |
|
24 const TUint16 KCharColon = ':'; |
|
25 const TUint16 KCharSemiColon = ';'; |
|
26 const TUint16 KCharForwardSlash = '/'; |
|
27 const TUint16 KCharEquals = '='; |
|
28 const TUint16 KCharQuestionMark = '?'; |
|
29 _LIT(KSlashes, "//"); |
|
30 _LIT(KIPv6PrefixForIPv4MappedAddress, "::ffff:"); |
|
31 _LIT(KParamValueTrue, "true"); |
|
32 _LIT(KParamValueFalse, "false"); |
|
33 |
|
34 |
|
35 |
|
36 // ---------------------------------------------------------------------------- |
|
37 // TUri |
|
38 // |
|
39 |
|
40 /** |
|
41 There is not much else to distinguish the most basic URI from other strings, |
|
42 so all we test here is if it contains a colon. |
|
43 */ |
|
44 TBool TUri::IsUri(const TDesC16& aName) |
|
45 { |
|
46 return (aName.Locate(KCharColon) > 0); |
|
47 } |
|
48 |
|
49 |
|
50 |
|
51 /** |
|
52 Parses a URI, based on the format detailed in RFC 2396, e.g. |
|
53 <scheme>://<authority><path>?<query> |
|
54 or more generally: |
|
55 <scheme>:<scheme-specific-part> |
|
56 if aCheckForSlashes is ETrue and scheme-specific-part |
|
57 doesn't start with 2 slashes, Parse returns KErrArgument |
|
58 */ |
|
59 TInt TUri::Parse(const TDesC16& aDes , TBool aCheckForSlashes) |
|
60 { |
|
61 iScheme.Set(KNullDesC()); |
|
62 iSchemeData.Set(KNullDesC()); |
|
63 iHost.Set(KNullDesC()); |
|
64 iPort.Set(KNullDesC()); |
|
65 iPath.Set(KNullDesC()); |
|
66 iParameters.Set(KNullDesC()); |
|
67 iQuery.Set(KNullDesC()); |
|
68 |
|
69 // Set this descriptor to equal the given URI |
|
70 Set(aDes); |
|
71 // Ensure that the colon is present and determine the scheme and scheme-specific |
|
72 // part |
|
73 TInt colonIndex = Locate(KCharColon); |
|
74 // The scheme must not be empty |
|
75 if (colonIndex <= 0) |
|
76 { |
|
77 return KErrArgument; |
|
78 } |
|
79 iScheme.Set(Left(colonIndex)); |
|
80 iSchemeData.Set(Mid(colonIndex + 1)); |
|
81 |
|
82 // Parse the scheme data further - we assume it is going to start with 2 slashes |
|
83 // and may contain stuff like a host, port, path, parameters. If it does not |
|
84 // have 2 slashes we do no more parsing. |
|
85 if (iSchemeData.Find(KSlashes()) == 0) |
|
86 { |
|
87 TPtrC16 portionToParse(iSchemeData.Mid(KSlashes().Length())); |
|
88 |
|
89 // Parse location (host and port) |
|
90 TInt pathStartIndex = portionToParse.Locate(KCharForwardSlash); |
|
91 TInt parametersStartIndex = portionToParse.Locate(KCharSemiColon); |
|
92 TInt queryStartIndex = portionToParse.Locate(KCharQuestionMark); |
|
93 // Find the length of the location portion of the URI |
|
94 TInt locationLength = portionToParse.Length(); |
|
95 if (pathStartIndex != KErrNotFound) |
|
96 { |
|
97 locationLength = pathStartIndex; |
|
98 } |
|
99 else if (parametersStartIndex != KErrNotFound) |
|
100 { |
|
101 locationLength = parametersStartIndex; |
|
102 } |
|
103 else if (queryStartIndex != KErrNotFound) |
|
104 { |
|
105 locationLength = queryStartIndex; |
|
106 } |
|
107 // Extract the host and port from the location portion, separated by a colon |
|
108 const TPtrC16 location(portionToParse.Left(locationLength)); |
|
109 // handle the case of an IPv4-mapped address, like ::ffff:10.10.10.10 |
|
110 TPtrC16 IPv4Location; |
|
111 TInt prefixLength = 0; |
|
112 if (location.FindF(KIPv6PrefixForIPv4MappedAddress()) == 0) |
|
113 { |
|
114 prefixLength = KIPv6PrefixForIPv4MappedAddress().Length(); |
|
115 IPv4Location.Set(location.Mid(prefixLength)); |
|
116 } |
|
117 else |
|
118 { |
|
119 IPv4Location.Set(location); |
|
120 } |
|
121 TInt locationColonIndex = IPv4Location.Locate(KCharColon); |
|
122 if (locationColonIndex != KErrNotFound) |
|
123 { |
|
124 iHost.Set(location.Left(prefixLength + locationColonIndex)); |
|
125 iPort.Set(IPv4Location.Mid(locationColonIndex + 1)); |
|
126 } |
|
127 else |
|
128 { |
|
129 iHost.Set(location); |
|
130 } |
|
131 |
|
132 // Parse path (and Query) |
|
133 if (pathStartIndex != KErrNotFound) |
|
134 { |
|
135 // Skip past the '/' character that marks the start of the path |
|
136 portionToParse.Set(portionToParse.Mid(locationLength + 1)); |
|
137 } |
|
138 queryStartIndex = portionToParse.Locate(KCharQuestionMark); |
|
139 if (queryStartIndex != KErrNotFound) |
|
140 { |
|
141 iQuery.Set(portionToParse.Mid(queryStartIndex + 1)); |
|
142 portionToParse.Set(portionToParse.Left(queryStartIndex)); |
|
143 } |
|
144 if (pathStartIndex != KErrNotFound) |
|
145 { |
|
146 iPath.Set(portionToParse); |
|
147 } |
|
148 |
|
149 // Parse parameters |
|
150 parametersStartIndex = portionToParse.Locate(KCharSemiColon); |
|
151 if (parametersStartIndex != KErrNotFound) |
|
152 { |
|
153 // Do not skip the ';' character that marks the start of the parameters |
|
154 iParameters.Set(portionToParse.Mid(parametersStartIndex)); |
|
155 } |
|
156 |
|
157 } |
|
158 else |
|
159 { |
|
160 if (aCheckForSlashes) |
|
161 return KErrArgument; |
|
162 else |
|
163 { |
|
164 //not checking for slashes, but do expect there to be parameters |
|
165 //used for CommConnection URIs |
|
166 TInt parametersStartIndex = iSchemeData.Locate(KCharSemiColon); |
|
167 if (parametersStartIndex != KErrNotFound) |
|
168 { |
|
169 iPort.Set(iSchemeData.Left(parametersStartIndex)); |
|
170 // Do not skip the ';' character that marks the start of the parameters |
|
171 iParameters.Set(iSchemeData.Mid(parametersStartIndex)); |
|
172 } |
|
173 else |
|
174 { |
|
175 iPort.Set(iSchemeData); |
|
176 } |
|
177 } |
|
178 } |
|
179 return KErrNone; |
|
180 } |
|
181 |
|
182 |
|
183 |
|
184 TInt TUri::PortAsUint(TUint& aPortNum) const |
|
185 { |
|
186 if (iPort.Length() == 0) |
|
187 { |
|
188 return KErrNotFound; |
|
189 } |
|
190 TLex16 portParser(iPort); |
|
191 TInt error = portParser.Val(aPortNum); |
|
192 // Check that the whole port string was parsed to a number |
|
193 if (error != KErrNone || !portParser.Eos()) |
|
194 { |
|
195 return KErrArgument; |
|
196 } |
|
197 return KErrNone; |
|
198 } |
|
199 |
|
200 |
|
201 |
|
202 /** |
|
203 Looks for the boolean value of the named parameter. |
|
204 @param aParamName The name of the boolean parameter. |
|
205 @param aValue The value that is set to the value of the parameter. This is |
|
206 not changed if an error code is returned. |
|
207 @return KErrNotFound if the parameter is not defined, KErrArgument if there |
|
208 is a parsing error, otherwise KErrNone. |
|
209 */ |
|
210 TInt TUri::GetParameterValue(const TDesC16& aParamName, TBool& aValue) const |
|
211 { |
|
212 // Assign default value |
|
213 TBool value; |
|
214 TPtrC16 paramValue; |
|
215 TInt error = GetParameterValue(aParamName, paramValue); |
|
216 if (error != KErrNone) |
|
217 { |
|
218 return error; |
|
219 } |
|
220 if (paramValue.CompareF(KParamValueTrue()) == 0) |
|
221 { |
|
222 value = ETrue; |
|
223 } |
|
224 else if (paramValue.CompareF(KParamValueFalse()) == 0) |
|
225 { |
|
226 value = EFalse; |
|
227 } |
|
228 else |
|
229 { |
|
230 return KErrArgument; |
|
231 } |
|
232 aValue = value; |
|
233 return KErrNone; |
|
234 } |
|
235 |
|
236 |
|
237 |
|
238 /** |
|
239 Looks for the integer value of the named parameter. If the parameter is |
|
240 not found, it dos not change the value of aValue. |
|
241 @return KErrNotFound if the parameter is not defined, KErrArgument if there |
|
242 is a parsing error, otherwise KErrNone. |
|
243 */ |
|
244 TInt TUri::GetParameterValueInt(const TDesC16& aParamName, TInt& aValue) const |
|
245 { |
|
246 TPtrC16 paramValue; |
|
247 TInt error = GetParameterValue(aParamName, paramValue); |
|
248 if (error != KErrNone) |
|
249 { |
|
250 return error; |
|
251 } |
|
252 TLex16 numberParser(paramValue); |
|
253 TInt value; |
|
254 error = numberParser.Val(value); |
|
255 if (error != KErrNone) |
|
256 { |
|
257 return KErrArgument; |
|
258 } |
|
259 aValue = value; |
|
260 return KErrNone; |
|
261 } |
|
262 |
|
263 |
|
264 |
|
265 TInt TUri::GetParameterValue(const TDesC16& aParamName, TPtrC16& aValue) const |
|
266 { |
|
267 __ASSERT_DEBUG(aParamName.Length() > 0, User::Invariant()); |
|
268 |
|
269 TInt index = FindParamNameIndex(aParamName, iParameters); |
|
270 if (index < KErrNone) |
|
271 { |
|
272 return index; |
|
273 } |
|
274 // We know the value is defined in the parameter list |
|
275 // Ensure that the parameter is defined only once in the list |
|
276 TPtrC16 restOfParameterList(iParameters.Mid(index + aParamName.Length())); |
|
277 TInt secondParamIndex = FindParamNameIndex(aParamName, restOfParameterList); |
|
278 if (secondParamIndex >= KErrNone) |
|
279 { |
|
280 // Parameter is defined twice, which is invalid |
|
281 return KErrArgument; |
|
282 } |
|
283 // Skip over the name and equals sign |
|
284 index += aParamName.Length(); |
|
285 ++index; |
|
286 // Extract the value itself |
|
287 const TPtrC16 remainder(iParameters.Mid(index)); |
|
288 TInt nextSemiColonIndex = remainder.Locate(KCharSemiColon); |
|
289 TInt paramValueLength = (nextSemiColonIndex != KErrNotFound) ? |
|
290 nextSemiColonIndex : iParameters.Length(); |
|
291 aValue.Set(remainder.Left(paramValueLength)); |
|
292 return KErrNone; |
|
293 } |
|
294 |
|
295 |
|
296 |
|
297 /** |
|
298 Finds the index of the parameter name. Checks that there are enough characters |
|
299 after the name for the equals sign and a value of length 1, and that the name |
|
300 has a semi-colon in front and an equals after it. |
|
301 */ |
|
302 TInt TUri::FindParamNameIndex(const TDesC16& aParamName, |
|
303 const TDesC16& aParametersSegment) const |
|
304 { |
|
305 TPtrC16 parameters(aParametersSegment); |
|
306 TInt nameIndex = KErrNotFound; |
|
307 TInt paramSegmentIndex = 0; |
|
308 do |
|
309 { |
|
310 // DEBUG( "TUri::GetParameterValue(): finding param name: %S", &aParamName ); |
|
311 // FindF is used because the case is not relevant for data within the URI |
|
312 TInt index = parameters.FindF(aParamName); |
|
313 if (index == KErrNotFound) |
|
314 { |
|
315 return KErrNotFound; |
|
316 } |
|
317 |
|
318 TInt endOfParamNameIndex = index + aParamName.Length(); |
|
319 // Check there are enough extra characters after the parameter name for the |
|
320 // equals and value |
|
321 TPtrC16 afterParamNameString(parameters.Mid(endOfParamNameIndex)); |
|
322 if (afterParamNameString.Length() < KMinSizeOfEqualsAndValue) |
|
323 { |
|
324 return KErrArgument; |
|
325 } |
|
326 |
|
327 // Check the parameter name found is a complete name and not part of a |
|
328 // larger string, i.e. it has a semi-colon in front and an equals after |
|
329 if (index == 0 || parameters[index - 1] != KCharSemiColon || |
|
330 parameters[endOfParamNameIndex] != KCharEquals) |
|
331 { |
|
332 paramSegmentIndex += endOfParamNameIndex; |
|
333 parameters.Set(parameters.Mid(endOfParamNameIndex)); |
|
334 } |
|
335 else |
|
336 { |
|
337 nameIndex = paramSegmentIndex + index; |
|
338 } |
|
339 } |
|
340 while (nameIndex == KErrNotFound); |
|
341 return nameIndex; |
|
342 } |
|
343 |
|
344 |
|
345 |
|
346 /** |
|
347 Iterates through the parameters of the URI, checking that each parameter name is |
|
348 present in the supplied list of valid names. |
|
349 @return KErrNone if all parameter names match the legal ones, or if there are |
|
350 no parameters in the URI, as parameters are optional anyway. |
|
351 */ |
|
352 TInt TUri::CheckParameterValidity(const RPointerArray<TDesC16>& aLegalParams) const |
|
353 { |
|
354 if (iParameters.Length() == 0) |
|
355 { |
|
356 return KErrNone; |
|
357 } |
|
358 TPtrC16 remainder(iParameters); |
|
359 TInt semiColonIndex = 0; |
|
360 do |
|
361 { |
|
362 remainder.Set(remainder.Mid(semiColonIndex + 1)); |
|
363 TInt error = CheckParameter(aLegalParams, remainder); |
|
364 if (error != KErrNone) |
|
365 { |
|
366 return error; |
|
367 } |
|
368 semiColonIndex = remainder.Locate(KCharSemiColon); |
|
369 } |
|
370 while (semiColonIndex != KErrNotFound); |
|
371 return KErrNone; |
|
372 } |
|
373 |
|
374 |
|
375 |
|
376 /** |
|
377 Checks that the parameter name at the start of aRemainder is present in the |
|
378 list of legal parameters. |
|
379 */ |
|
380 TInt TUri::CheckParameter(const RPointerArray<TDesC16>& aLegalParams, |
|
381 const TDesC16& aRemainder) const |
|
382 { |
|
383 // Find the end of the parameter name |
|
384 TInt equalsIndex = aRemainder.Locate(KCharEquals); |
|
385 if (equalsIndex == KErrNotFound) |
|
386 { |
|
387 return KErrArgument; |
|
388 } |
|
389 // Search for the name in the legal names list |
|
390 TPtrC16 paramName(aRemainder.Left(equalsIndex)); |
|
391 TIdentityRelation<TDesC16> relation(&TUri::MatchParamName); |
|
392 TInt nameIndex = aLegalParams.Find(¶mName, relation); |
|
393 if (nameIndex == KErrNotFound) |
|
394 { |
|
395 return KErrArgument; |
|
396 } |
|
397 return KErrNone; |
|
398 } |
|
399 |
|
400 |
|
401 |
|
402 /** |
|
403 Utility method for use with RArray::Find(). |
|
404 */ |
|
405 TBool TUri::MatchParamName(const TDesC16& aName1, const TDesC16& aName2) |
|
406 { |
|
407 return (aName1.CompareF(aName2) == 0); |
|
408 } |
|
409 |
|
410 |
|
411 |
|
412 /** |
|
413 Check there are no duplicate parameters |
|
414 */ |
|
415 TInt TUri::CheckNoDuplicateParameters() const |
|
416 { |
|
417 if (iParameters.Length() == 0) |
|
418 { |
|
419 return KErrNone; |
|
420 } |
|
421 |
|
422 RArray<TPtrC16> parameters; |
|
423 TPtrC16 remainder(iParameters); |
|
424 TInt semiColonIndex = 0; |
|
425 do |
|
426 { |
|
427 remainder.Set(remainder.Mid(semiColonIndex + 1)); |
|
428 |
|
429 TInt equalsIndex = remainder.Locate(KCharEquals); |
|
430 if (equalsIndex == KErrNotFound) |
|
431 { |
|
432 parameters.Close(); |
|
433 return KErrArgument; |
|
434 } |
|
435 |
|
436 TPtrC16 paramName(remainder.Left(equalsIndex)); |
|
437 |
|
438 TIdentityRelation<TPtrC16> relation(&TUri::MatchParamNamePtr); |
|
439 TInt nameIndex = parameters.Find(paramName, relation); |
|
440 |
|
441 TInt error = KErrNone; |
|
442 if (nameIndex == KErrNotFound) |
|
443 { |
|
444 error = parameters.Append(paramName); |
|
445 } |
|
446 else if (nameIndex >= 0) |
|
447 { |
|
448 error = KErrAlreadyExists; |
|
449 } |
|
450 else |
|
451 { |
|
452 error = nameIndex; |
|
453 } |
|
454 |
|
455 if (error != KErrNone) |
|
456 { |
|
457 parameters.Close(); |
|
458 return error; |
|
459 } |
|
460 semiColonIndex = remainder.Locate(KCharSemiColon); |
|
461 } |
|
462 while (semiColonIndex != KErrNotFound); |
|
463 |
|
464 parameters.Close(); |
|
465 return KErrNone; |
|
466 } |
|
467 |
|
468 |
|
469 /** |
|
470 Utility method for use with RArray::Find(). |
|
471 */ |
|
472 TBool TUri::MatchParamNamePtr(const TPtrC16& aName1, const TPtrC16& aName2) |
|
473 { |
|
474 return (aName1.CompareF(aName2) == 0); |
|
475 } |