| /* | 
 |  *  Copyright 2008 The WebRTC Project Authors. All rights reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "webrtc/base/urlencode.h" | 
 |  | 
 | #include "webrtc/base/common.h" | 
 | #include "webrtc/base/stringutils.h" | 
 |  | 
 | static int HexPairValue(const char * code) { | 
 |   int value = 0; | 
 |   for (const char * pch = code; pch < code + 2; ++pch) { | 
 |     value <<= 4; | 
 |     int digit = *pch; | 
 |     if (digit >= '0' && digit <= '9') { | 
 |       value += digit - '0'; | 
 |     } | 
 |     else if (digit >= 'A' && digit <= 'F') { | 
 |       value += digit - 'A' + 10; | 
 |     } | 
 |     else if (digit >= 'a' && digit <= 'f') { | 
 |       value += digit - 'a' + 10; | 
 |     } | 
 |     else { | 
 |       return -1; | 
 |     } | 
 |   } | 
 |   return value; | 
 | } | 
 |  | 
 | static int InternalUrlDecode(const char *source, char *dest, | 
 |                              bool encode_space_as_plus) { | 
 |   char * start = dest; | 
 |  | 
 |   while (*source) { | 
 |     switch (*source) { | 
 |     case '+': | 
 |       if (encode_space_as_plus) { | 
 |         *(dest++) = ' '; | 
 |       } else { | 
 |         *dest++ = *source; | 
 |       } | 
 |       break; | 
 |     case '%': | 
 |       if (source[1] && source[2]) { | 
 |         int value = HexPairValue(source + 1); | 
 |         if (value >= 0) { | 
 |           *(dest++) = static_cast<char>(value); | 
 |           source += 2; | 
 |         } | 
 |         else { | 
 |           *dest++ = '?'; | 
 |         } | 
 |       } | 
 |       else { | 
 |         *dest++ = '?'; | 
 |       } | 
 |       break; | 
 |     default: | 
 |       *dest++ = *source; | 
 |     } | 
 |     source++; | 
 |   } | 
 |  | 
 |   *dest = 0; | 
 |   return static_cast<int>(dest - start); | 
 | } | 
 |  | 
 | static bool IsValidUrlChar(char ch, bool unsafe_only) { | 
 |   if (unsafe_only) { | 
 |     return !(ch <= ' ' || strchr("\\\"^&`<>[]{}", ch)); | 
 |   } else { | 
 |     return isalnum(ch) || strchr("-_.!~*'()", ch); | 
 |   } | 
 | } | 
 |  | 
 | namespace rtc { | 
 |  | 
 | int UrlDecode(const char *source, char *dest) { | 
 |   return InternalUrlDecode(source, dest, true); | 
 | } | 
 |  | 
 | int UrlDecodeWithoutEncodingSpaceAsPlus(const char *source, char *dest) { | 
 |   return InternalUrlDecode(source, dest, false); | 
 | } | 
 |  | 
 | int InternalUrlEncode(const char *source, char *dest, unsigned int max, | 
 |                       bool encode_space_as_plus, bool unsafe_only) { | 
 |   static const char *digits = "0123456789ABCDEF"; | 
 |   if (max == 0) { | 
 |     return 0; | 
 |   } | 
 |  | 
 |   char *start = dest; | 
 |   while (static_cast<unsigned>(dest - start) < max && *source) { | 
 |     unsigned char ch = static_cast<unsigned char>(*source); | 
 |     if (*source == ' ' && encode_space_as_plus && !unsafe_only) { | 
 |       *dest++ = '+'; | 
 |     } else if (IsValidUrlChar(ch, unsafe_only)) { | 
 |       *dest++ = *source; | 
 |     } else { | 
 |       if (static_cast<unsigned>(dest - start) + 4 > max) { | 
 |         break; | 
 |       } | 
 |       *dest++ = '%'; | 
 |       *dest++ = digits[(ch >> 4) & 0x0F]; | 
 |       *dest++ = digits[       ch & 0x0F]; | 
 |     } | 
 |     source++; | 
 |   } | 
 |   ASSERT(static_cast<unsigned int>(dest - start) < max); | 
 |   *dest = 0; | 
 |  | 
 |   return static_cast<int>(dest - start); | 
 | } | 
 |  | 
 | int UrlEncode(const char *source, char *dest, unsigned max) { | 
 |   return InternalUrlEncode(source, dest, max, true, false); | 
 | } | 
 |  | 
 | int UrlEncodeWithoutEncodingSpaceAsPlus(const char *source, char *dest, | 
 |                                         unsigned max) { | 
 |   return InternalUrlEncode(source, dest, max, false, false); | 
 | } | 
 |  | 
 | int UrlEncodeOnlyUnsafeChars(const char *source, char *dest, unsigned max) { | 
 |   return InternalUrlEncode(source, dest, max, false, true); | 
 | } | 
 |  | 
 | std::string | 
 | InternalUrlDecodeString(const std::string & encoded, | 
 |                         bool encode_space_as_plus) { | 
 |   size_t needed_length = encoded.length() + 1; | 
 |   char* buf = STACK_ARRAY(char, needed_length); | 
 |   InternalUrlDecode(encoded.c_str(), buf, encode_space_as_plus); | 
 |   return buf; | 
 | } | 
 |  | 
 | std::string | 
 | UrlDecodeString(const std::string & encoded) { | 
 |   return InternalUrlDecodeString(encoded, true); | 
 | } | 
 |  | 
 | std::string | 
 | UrlDecodeStringWithoutEncodingSpaceAsPlus(const std::string & encoded) { | 
 |   return InternalUrlDecodeString(encoded, false); | 
 | } | 
 |  | 
 | std::string | 
 | InternalUrlEncodeString(const std::string & decoded, | 
 |                         bool encode_space_as_plus, | 
 |                         bool unsafe_only) { | 
 |   int needed_length = static_cast<int>(decoded.length()) * 3 + 1; | 
 |   char* buf = STACK_ARRAY(char, needed_length); | 
 |   InternalUrlEncode(decoded.c_str(), buf, needed_length, | 
 |                     encode_space_as_plus, unsafe_only); | 
 |   return buf; | 
 | } | 
 |  | 
 | std::string | 
 | UrlEncodeString(const std::string & decoded) { | 
 |   return InternalUrlEncodeString(decoded, true, false); | 
 | } | 
 |  | 
 | std::string | 
 | UrlEncodeStringWithoutEncodingSpaceAsPlus(const std::string & decoded) { | 
 |   return InternalUrlEncodeString(decoded, false, false); | 
 | } | 
 |  | 
 | std::string | 
 | UrlEncodeStringForOnlyUnsafeChars(const std::string & decoded) { | 
 |   return InternalUrlEncodeString(decoded, false, true); | 
 | } | 
 |  | 
 | }  // namespace rtc |