1 /*
2  *  Copyright 2008 The WebRTC Project Authors. All rights reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "webrtc/base/urlencode.h"
12 
13 #include "webrtc/base/common.h"
14 #include "webrtc/base/stringutils.h"
15 
HexPairValue(const char * code)16 static int HexPairValue(const char * code) {
17   int value = 0;
18   for (const char * pch = code; pch < code + 2; ++pch) {
19     value <<= 4;
20     int digit = *pch;
21     if (digit >= '0' && digit <= '9') {
22       value += digit - '0';
23     }
24     else if (digit >= 'A' && digit <= 'F') {
25       value += digit - 'A' + 10;
26     }
27     else if (digit >= 'a' && digit <= 'f') {
28       value += digit - 'a' + 10;
29     }
30     else {
31       return -1;
32     }
33   }
34   return value;
35 }
36 
InternalUrlDecode(const char * source,char * dest,bool encode_space_as_plus)37 static int InternalUrlDecode(const char *source, char *dest,
38                              bool encode_space_as_plus) {
39   char * start = dest;
40 
41   while (*source) {
42     switch (*source) {
43     case '+':
44       if (encode_space_as_plus) {
45         *(dest++) = ' ';
46       } else {
47         *dest++ = *source;
48       }
49       break;
50     case '%':
51       if (source[1] && source[2]) {
52         int value = HexPairValue(source + 1);
53         if (value >= 0) {
54           *(dest++) = static_cast<char>(value);
55           source += 2;
56         }
57         else {
58           *dest++ = '?';
59         }
60       }
61       else {
62         *dest++ = '?';
63       }
64       break;
65     default:
66       *dest++ = *source;
67     }
68     source++;
69   }
70 
71   *dest = 0;
72   return static_cast<int>(dest - start);
73 }
74 
IsValidUrlChar(char ch,bool unsafe_only)75 static bool IsValidUrlChar(char ch, bool unsafe_only) {
76   if (unsafe_only) {
77     return !(ch <= ' ' || strchr("\\\"^&`<>[]{}", ch));
78   } else {
79     return isalnum(ch) || strchr("-_.!~*'()", ch);
80   }
81 }
82 
83 namespace rtc {
84 
UrlDecode(const char * source,char * dest)85 int UrlDecode(const char *source, char *dest) {
86   return InternalUrlDecode(source, dest, true);
87 }
88 
UrlDecodeWithoutEncodingSpaceAsPlus(const char * source,char * dest)89 int UrlDecodeWithoutEncodingSpaceAsPlus(const char *source, char *dest) {
90   return InternalUrlDecode(source, dest, false);
91 }
92 
InternalUrlEncode(const char * source,char * dest,unsigned int max,bool encode_space_as_plus,bool unsafe_only)93 int InternalUrlEncode(const char *source, char *dest, unsigned int max,
94                       bool encode_space_as_plus, bool unsafe_only) {
95   static const char *digits = "0123456789ABCDEF";
96   if (max == 0) {
97     return 0;
98   }
99 
100   char *start = dest;
101   while (static_cast<unsigned>(dest - start) < max && *source) {
102     unsigned char ch = static_cast<unsigned char>(*source);
103     if (*source == ' ' && encode_space_as_plus && !unsafe_only) {
104       *dest++ = '+';
105     } else if (IsValidUrlChar(ch, unsafe_only)) {
106       *dest++ = *source;
107     } else {
108       if (static_cast<unsigned>(dest - start) + 4 > max) {
109         break;
110       }
111       *dest++ = '%';
112       *dest++ = digits[(ch >> 4) & 0x0F];
113       *dest++ = digits[       ch & 0x0F];
114     }
115     source++;
116   }
117   ASSERT(static_cast<unsigned int>(dest - start) < max);
118   *dest = 0;
119 
120   return static_cast<int>(dest - start);
121 }
122 
UrlEncode(const char * source,char * dest,unsigned max)123 int UrlEncode(const char *source, char *dest, unsigned max) {
124   return InternalUrlEncode(source, dest, max, true, false);
125 }
126 
UrlEncodeWithoutEncodingSpaceAsPlus(const char * source,char * dest,unsigned max)127 int UrlEncodeWithoutEncodingSpaceAsPlus(const char *source, char *dest,
128                                         unsigned max) {
129   return InternalUrlEncode(source, dest, max, false, false);
130 }
131 
UrlEncodeOnlyUnsafeChars(const char * source,char * dest,unsigned max)132 int UrlEncodeOnlyUnsafeChars(const char *source, char *dest, unsigned max) {
133   return InternalUrlEncode(source, dest, max, false, true);
134 }
135 
136 std::string
InternalUrlDecodeString(const std::string & encoded,bool encode_space_as_plus)137 InternalUrlDecodeString(const std::string & encoded,
138                         bool encode_space_as_plus) {
139   size_t needed_length = encoded.length() + 1;
140   char* buf = STACK_ARRAY(char, needed_length);
141   InternalUrlDecode(encoded.c_str(), buf, encode_space_as_plus);
142   return buf;
143 }
144 
145 std::string
UrlDecodeString(const std::string & encoded)146 UrlDecodeString(const std::string & encoded) {
147   return InternalUrlDecodeString(encoded, true);
148 }
149 
150 std::string
UrlDecodeStringWithoutEncodingSpaceAsPlus(const std::string & encoded)151 UrlDecodeStringWithoutEncodingSpaceAsPlus(const std::string & encoded) {
152   return InternalUrlDecodeString(encoded, false);
153 }
154 
155 std::string
InternalUrlEncodeString(const std::string & decoded,bool encode_space_as_plus,bool unsafe_only)156 InternalUrlEncodeString(const std::string & decoded,
157                         bool encode_space_as_plus,
158                         bool unsafe_only) {
159   int needed_length = static_cast<int>(decoded.length()) * 3 + 1;
160   char* buf = STACK_ARRAY(char, needed_length);
161   InternalUrlEncode(decoded.c_str(), buf, needed_length,
162                     encode_space_as_plus, unsafe_only);
163   return buf;
164 }
165 
166 std::string
UrlEncodeString(const std::string & decoded)167 UrlEncodeString(const std::string & decoded) {
168   return InternalUrlEncodeString(decoded, true, false);
169 }
170 
171 std::string
UrlEncodeStringWithoutEncodingSpaceAsPlus(const std::string & decoded)172 UrlEncodeStringWithoutEncodingSpaceAsPlus(const std::string & decoded) {
173   return InternalUrlEncodeString(decoded, false, false);
174 }
175 
176 std::string
UrlEncodeStringForOnlyUnsafeChars(const std::string & decoded)177 UrlEncodeStringForOnlyUnsafeChars(const std::string & decoded) {
178   return InternalUrlEncodeString(decoded, false, true);
179 }
180 
181 }  // namespace rtc
182