diff --git a/libtorrent/src/upnp.cpp b/libtorrent/src/upnp.cpp index baac6ca4f..c677bb64e 100644 --- a/libtorrent/src/upnp.cpp +++ b/libtorrent/src/upnp.cpp @@ -727,68 +727,94 @@ void upnp::delete_port_mapping(rootdevice& d, int i) namespace { - struct parse_state + char tolower(char c) { - parse_state(): found_service(false), exit(false) {} - void reset(char const* st) - { - found_service = false; - exit = false; - service_type = st; - } - bool found_service; - bool exit; - std::string top_tag; - std::string control_url; - char const* service_type; - std::string model; - }; - - void find_control_url(int type, char const* string, parse_state& state) - { - if (state.exit) return; - - if (type == xml_start_tag) - { - if ((!state.top_tag.empty() && state.top_tag == "service") - || !strcmp(string, "service")) - { - state.top_tag = string; - } - else if (!strcmp(string, "modelName")) - { - state.top_tag = string; - } - } - else if (type == xml_end_tag) - { - if (!strcmp(string, "service")) - { - state.top_tag.clear(); - if (state.found_service) state.exit = true; - } - else if (!state.top_tag.empty() && state.top_tag != "service") - state.top_tag = "service"; - } - else if (type == xml_string) - { - if (state.top_tag == "serviceType") - { - if (!strcmp(string, state.service_type)) - state.found_service = true; - } - else if (state.top_tag == "controlURL") - { - state.control_url = string; - if (state.found_service) state.exit = true; - } - else if (state.top_tag == "modelName") - { - state.model = string; - } - } + if (c >= 'A' && c <= 'Z') return c + ('a' - 'A'); + return c; } + void copy_tolower(std::string& dst, char const* src) + { + dst.clear(); + while (*src) dst.push_back(tolower(*src++)); + } + + bool string_equal_nocase(char const* lhs, char const* rhs) + { + while (tolower(*lhs) == tolower(*rhs)) + { + if (*lhs == 0) return true; + ++lhs; + ++rhs; + } + return false; + } +} + +struct parse_state +{ + parse_state(): found_service(false) {} + void reset(char const* st) + { + found_service = false; + service_type = st; + tag_stack.clear(); + } + bool found_service; + std::list tag_stack; + std::string control_url; + char const* service_type; + std::string model; + std::string url_base; + bool top_tags(const char* str1, const char* str2) + { + std::list::reverse_iterator i = tag_stack.rbegin(); + if (i == tag_stack.rend()) return false; + if (!string_equal_nocase(i->c_str(), str2)) return false; + ++i; + if (i == tag_stack.rend()) return false; + if (!string_equal_nocase(i->c_str(), str1)) return false; + return true; + } +}; + +void find_control_url(int type, char const* string, parse_state& state) +{ + if (type == xml_start_tag) + { + std::string tag; + copy_tolower(tag, string); + state.tag_stack.push_back(tag); +// std::copy(state.tag_stack.begin(), state.tag_stack.end(), std::ostream_iterator(std::cout, " ")); +// std::cout << std::endl; + } + else if (type == xml_end_tag) + { + if (!state.tag_stack.empty()) + state.tag_stack.pop_back(); + } + else if (type == xml_string) + { + if (state.tag_stack.empty()) return; +// std::cout << " " << string << std::endl; + if (!state.found_service && state.top_tags("service", "servicetype")) + { + if (string_equal_nocase(string, state.service_type)) + state.found_service = true; + } + else if (state.found_service && state.top_tags("service", "controlurl")) + { + state.control_url = string; + } + else if (state.tag_stack.back() == "modelname") + { + state.model = string; + } + else if (state.tag_stack.back() == "urlbase") + { + state.url_base = string; + } + } } void upnp::on_upnp_xml(error_code const& e @@ -874,7 +900,8 @@ void upnp::on_upnp_xml(error_code const& e << " namespace: " << d.service_namespace << std::endl; #endif - d.control_url = s.control_url; + if (s.url_base.empty()) d.control_url = s.control_url; + else d.control_url = s.url_base + s.control_url; std::string protocol; std::string auth;