- Page restrictions apply
- Added by Automation System, last edited by Automation System on Oct 11, 2012 (view change)
URI.cpp
00001 /**********************************************************\ 00002 Original Author: Dan Weatherford 00003 00004 Imported into FireBreath: Oct 4, 2010 00005 License: Dual license model; choose one of two: 00006 New BSD License 00007 http://www.opensource.org/licenses/bsd-license.php 00008 - or - 00009 GNU Lesser General Public License, version 2.1 00010 http://www.gnu.org/licenses/lgpl-2.1.html 00011 00012 Copyright 2010 Dan Weatherford and Facebook, Inc 00013 \**********************************************************/ 00014 00015 #ifdef _WIN32 00016 #include "win_targetver.h" 00017 #endif 00018 #include <boost/lexical_cast.hpp> 00019 #include <boost/algorithm/string.hpp> 00020 #include <boost/algorithm/string/trim.hpp> 00021 #include <boost/logic/tribool.hpp> 00022 #include <vector> 00023 #include <sstream> 00024 #include <boost/asio.hpp> 00025 #include "precompiled_headers.h" // On windows, everything above this line in PCH 00026 00027 #include "URI.h" 00028 00029 using namespace boost::algorithm; 00030 using namespace boost::logic; 00031 using std::string; 00032 using std::vector; 00033 using FB::URI; 00034 00035 URI::StringStringMap URI::m_lhMap; 00036 00037 std::string URI::url_encode(const std::string& in) { 00038 std::stringstream res; 00039 for (size_t i = 0; i < in.size(); ++i) { 00040 char c = in[i]; 00041 if (c > 0 && (isalnum(c) || c == '+' || 00042 c == '$' || c == '-' || c == '_' || c == '.' || c == '!' || 00043 c == '*' || c == '\''|| c == '(' || c == ')' || c == ',' || c == '/')) res << c; 00044 else { 00045 char buf[4]; 00046 sprintf(buf, "%%%.2x", c & 0xff); 00047 res << buf; 00048 } 00049 } 00050 return res.str(); 00051 } 00052 00053 std::string URI::url_decode(const std::string& in) { 00054 std::stringstream res; 00055 for (size_t i = 0; i < in.size(); ++i) { 00056 if (in[i] == '%' && (i + 2) < in.size() && isxdigit(in[i+1]) && isxdigit(in[i+2])) { 00057 char buf[3]; 00058 ++i; 00059 buf[0] = in[i++]; buf[1] = in[i]; buf[2] = '\0'; 00060 res << ((char)strtol(buf, NULL, 16)); 00061 } else res << in[i]; 00062 } 00063 return res.str(); 00064 } 00065 00066 std::string URI::toString(bool include_host_part) const { 00067 std::stringstream res; 00068 if (include_host_part) { 00069 res << protocol << string("://"); 00070 if (!login.empty()) res << login << "@"; 00071 res << domain; 00072 if (port) res << ":" << boost::lexical_cast<string>(port); 00073 } 00074 res << url_encode(path); 00075 if (!query_data.empty()) { 00076 char separator = '?'; 00077 for (std::map<std::string, std::string>::const_iterator it = query_data.begin(); it != query_data.end(); ++it) { 00078 res << separator; 00079 separator = '&'; 00080 res << url_encode(it->first); 00081 res << '='; 00082 res << url_encode(it->second); 00083 } 00084 } 00085 if (!fragment.empty()) 00086 res << "#" << fragment; 00087 return res.str(); 00088 } 00089 00090 URI URI::fromString(const std::string& in_str) { 00091 return URI(in_str); 00092 } 00093 00094 URI::URI(const std::string& in_str) : port(0) { 00095 string w = in_str; 00096 00097 size_t l = w.find("://"); 00098 if (l != std::string::npos) { 00099 protocol = w.substr(0, l); 00100 std::transform(protocol.begin(), protocol.end(), protocol.begin(), ::tolower); 00101 w = w.substr(l + 3); 00102 } 00103 // validate protocol -- should only contain [a-z0-9] 00104 for (l = 0; l < protocol.size(); ++l) { 00105 if (!isalnum(protocol[l])) throw std::runtime_error("URI: invalid characters in protocol part"); 00106 } 00107 00108 if (protocol != "file") { // file has neither a domain nor a port 00109 l = w.find_first_of("/\\"); 00110 // chomp at the '/' (if it exists) so parsing the login/domain/port is easier 00111 string domain_str; 00112 if (l == std::string::npos) { 00113 domain_str = w; 00114 w = "/"; 00115 } else { 00116 domain_str = w.substr(0, l); 00117 w = w.substr(l); 00118 } 00119 00120 // check for login info 00121 l = domain_str.find("@"); 00122 if (l != std::string::npos) { 00123 login = domain_str.substr(0, l); 00124 domain_str = domain_str.substr(l + 1); 00125 } 00126 00127 // split port, if it exists 00128 size_t p = domain_str.find(":"); 00129 if (p != std::string::npos && p < l) { 00130 domain = domain_str.substr(0, p); 00131 string port_str = domain_str.substr(p + 1); 00132 port = boost::lexical_cast<int>(port_str); 00133 } else { 00134 domain = domain_str; 00135 } 00136 // domains are case insensitive; transform to lower case for convenience. 00137 std::transform(domain.begin(), domain.end(), domain.begin(), ::tolower); 00138 } 00139 00140 l = w.find('#'); 00141 if (l != std::string::npos) { 00142 fragment = w.substr(l + 1); 00143 w = w.substr(0, l); 00144 } 00145 l = w.find('?'); 00146 if (l != std::string::npos) { 00147 parse_query_data(w.substr(l + 1)); 00148 w = w.substr(0, l); 00149 } 00150 path = url_decode(w); 00151 } 00152 00153 bool URI::operator==(const URI& right) const { 00154 return ( (protocol == right.protocol) && 00155 (login == right.login) && 00156 (domain == right.domain) && 00157 (port == right.port) && 00158 (path == right.path) && 00159 (fragment == right.fragment) && 00160 (query_data == right.query_data) 00161 ); 00162 } 00163 00164 void URI::appendPathComponent(const std::string& pc) { 00165 if (! pc.size()) return; 00166 // make sure we have exactly one '/' between the old path and new path component[s] 00167 if (path.size() && path[path.size() - 1] == '/') path.resize(path.size() - 1); 00168 if (pc[0] != '/') path.push_back('/'); 00169 path += pc; 00170 } 00171 00172 std::string URI::filename() const { 00173 if (path.empty() || path[path.size()-1] == '/') return string(); 00174 size_t loc = path.rfind("/"); 00175 if (loc == std::string::npos) return path; 00176 return path.substr(loc+1); 00177 } 00178 00179 void URI::resetValidLocalhost() 00180 { 00181 m_lhMap.clear(); 00182 } 00183 void URI::registerValidLocalhost(std::string domain, std::string ip) 00184 { 00185 if (ip.empty()) { 00186 ip = boost::asio::ip::address_v4::loopback().to_string(); 00187 } 00188 m_lhMap[domain] = ip; 00189 } 00190 00191 bool URI::isLocalhost() const { 00192 // To avoid a security breach by DNS poisioning, we make sure that the allowed 00193 // domain (either localhost or registered with registerValidLocalhost) actually 00194 // is pointing at localhost / the correct IP. 00195 StringStringMap tmp; 00196 StringStringMap::const_iterator fnd(m_lhMap.find(domain)); 00197 if (fnd != m_lhMap.end()) { 00198 if (fnd->first == boost::asio::ip::address_v4::loopback().to_string()) { 00199 return true; 00200 } 00201 static boost::tribool lastResult(boost::indeterminate); 00202 if (!boost::indeterminate(lastResult)) return lastResult; 00203 00204 boost::asio::io_service io_service; 00205 boost::asio::ip::tcp::resolver resolver(io_service); 00206 // The resolver wants a service name, that's what the "80" is. 00207 // The numeric_service flag tells it not to do anything with the service name anyway. 00208 boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), domain, "80", boost::asio::ip::resolver_query_base::numeric_service); 00209 boost::asio::ip::tcp::resolver::iterator it = resolver.resolve(query); 00210 if (it == boost::asio::ip::tcp::resolver::iterator()) return false; 00211 boost::asio::ip::tcp::endpoint ep = it->endpoint(); 00212 00213 lastResult = (bool) (ep.address() == boost::asio::ip::address_v4::loopback()); 00214 return lastResult; 00215 } 00216 return false; 00217 } 00218 00219 void URI::parse_query_data(const std::string& in_str) { 00220 std::vector<string> parts; 00221 split(parts, in_str, is_any_of("&")); 00222 for (std::vector<string>::iterator it = parts.begin(); it != parts.end(); ++it) { 00223 std::vector<string> kvp; 00224 split(kvp, *it, is_any_of("=")); 00225 if (kvp.empty()) continue; 00226 else if (kvp.size() == 1) query_data[url_decode(kvp[0])] = string(); 00227 else query_data[url_decode(kvp[0])] = url_decode(kvp[1]); 00228 } 00229 } 00230
Generated on 19 Jun 2013 for FireBreath by
1.6.1
Labels
