torrent_handle session::add_torrent(add_torrent_params const& params, error_code& ec) { ec.clear(); if (string_begins_no_case("magnet:", params.url.c_str())) { add_torrent_params p(params); p.url.clear(); return add_magnet_uri(*this, params.url, p, ec); } TORRENT_SYNC_CALL_RET2(torrent_handle, add_torrent, params, boost::ref(ec)); return r; }
torrent_handle session::add_torrent(add_torrent_params const& params) { if (string_begins_no_case("magnet:", params.url.c_str())) { add_torrent_params p(params); p.url.clear(); return add_magnet_uri(*this, params.url, p); } error_code ec; TORRENT_SYNC_CALL_RET2(torrent_handle, add_torrent, params, ec); if (ec) throw libtorrent_exception(ec); return r; }
boost::tuple<int, int> http_parser::incoming( buffer::const_interval recv_buffer, bool& error) { TORRENT_ASSERT(recv_buffer.left() >= m_recv_buffer.left()); boost::tuple<int, int> ret(0, 0); int start_pos = m_recv_buffer.left(); // early exit if there's nothing new in the receive buffer if (start_pos == recv_buffer.left()) return ret; m_recv_buffer = recv_buffer; if (m_state == error_state) { error = true; return ret; } char const* pos = recv_buffer.begin + m_recv_pos; restart_response: if (m_state == read_status) { TORRENT_ASSERT(!m_finished); char const* newline = std::find(pos, recv_buffer.end, '\n'); // if we don't have a full line yet, wait. if (newline == recv_buffer.end) { boost::get<1>(ret) += m_recv_buffer.left() - start_pos; return ret; } if (newline == pos) { m_state = error_state; error = true; return ret; } char const* line_end = newline; if (pos != line_end && *(line_end - 1) == '\r') --line_end; char const* line = pos; ++newline; int incoming = int(newline - pos); m_recv_pos += incoming; boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos); pos = newline; m_protocol = read_until(line, ' ', line_end); if (m_protocol.substr(0, 5) == "HTTP/") { m_status_code = atoi(read_until(line, ' ', line_end).c_str()); m_server_message = read_until(line, '\r', line_end); } else { m_method = m_protocol; std::transform(m_method.begin(), m_method.end(), m_method.begin(), &to_lower); // the content length is assumed to be 0 for requests m_content_length = 0; m_protocol.clear(); m_path = read_until(line, ' ', line_end); m_protocol = read_until(line, ' ', line_end); m_status_code = 0; } m_state = read_header; start_pos = pos - recv_buffer.begin; } if (m_state == read_header) { TORRENT_ASSERT(!m_finished); char const* newline = std::find(pos, recv_buffer.end, '\n'); std::string line; while (newline != recv_buffer.end && m_state == read_header) { // if the LF character is preceeded by a CR // charachter, don't copy it into the line string. char const* line_end = newline; if (pos != line_end && *(line_end - 1) == '\r') --line_end; line.assign(pos, line_end); ++newline; m_recv_pos += newline - pos; pos = newline; std::string::size_type separator = line.find(':'); if (separator == std::string::npos) { if (m_status_code == 100) { // for 100 Continue, we need to read another response header // before reading the body m_state = read_status; goto restart_response; } // this means we got a blank line, // the header is finished and the body // starts. m_state = read_body; // if this is a request (not a response) // we're done once we reach the end of the headers // if (!m_method.empty()) m_finished = true; // the HTTP header should always be < 2 GB TORRENT_ASSERT(m_recv_pos < INT_MAX); m_body_start_pos = int(m_recv_pos); break; } std::string name = line.substr(0, separator); std::transform(name.begin(), name.end(), name.begin(), &to_lower); ++separator; // skip whitespace while (separator < line.size() && (line[separator] == ' ' || line[separator] == '\t')) ++separator; std::string value = line.substr(separator, std::string::npos); m_header.insert(std::make_pair(name, value)); if (name == "content-length") { m_content_length = strtoll(value.c_str(), 0, 10); } else if (name == "content-range") { bool success = true; char const* ptr = value.c_str(); // apparently some web servers do not send the "bytes" // in their content-range. Don't treat it as an error // if we can't find it, just assume the byte counters // start immediately if (string_begins_no_case("bytes ", ptr)) ptr += 6; char* end; m_range_start = strtoll(ptr, &end, 10); if (end == ptr) success = false; else if (*end != '-') success = false; else { ptr = end + 1; m_range_end = strtoll(ptr, &end, 10); if (end == ptr) success = false; } if (!success || m_range_end < m_range_start) { m_state = error_state; error = true; return ret; } // the http range is inclusive m_content_length = m_range_end - m_range_start + 1; } else if (name == "transfer-encoding") { m_chunked_encoding = string_begins_no_case("chunked", value.c_str()); } TORRENT_ASSERT(m_recv_pos <= recv_buffer.left()); newline = std::find(pos, recv_buffer.end, '\n'); } boost::get<1>(ret) += newline - (m_recv_buffer.begin + start_pos); } if (m_state == read_body) { int incoming = recv_buffer.end - pos; if (m_chunked_encoding && (m_flags & dont_parse_chunks) == 0) { if (m_cur_chunk_end == -1) m_cur_chunk_end = m_body_start_pos; while (m_cur_chunk_end <= m_recv_pos + incoming && !m_finished && incoming > 0) { size_type payload = m_cur_chunk_end - m_recv_pos; if (payload > 0) { TORRENT_ASSERT(payload < INT_MAX); m_recv_pos += payload; boost::get<0>(ret) += int(payload); incoming -= int(payload); } buffer::const_interval buf(recv_buffer.begin + m_cur_chunk_end, recv_buffer.end); size_type chunk_size; int header_size; if (parse_chunk_header(buf, &chunk_size, &header_size)) { if (chunk_size > 0) { std::pair<size_type, size_type> chunk_range(m_cur_chunk_end + header_size , m_cur_chunk_end + header_size + chunk_size); m_chunked_ranges.push_back(chunk_range); } m_cur_chunk_end += header_size + chunk_size; if (chunk_size == 0) { m_finished = true; TORRENT_ASSERT(m_content_length < 0 || m_recv_pos - m_body_start_pos - m_chunk_header_size == m_content_length); } header_size -= m_partial_chunk_header; m_partial_chunk_header = 0; // fprintf(stderr, "parse_chunk_header(%d, -> %d, -> %d) -> %d\n" // " incoming = %d\n m_recv_pos = %d\n m_cur_chunk_end = %d\n" // " content-length = %d\n" // , buf.left(), int(chunk_size), header_size, 1, incoming, int(m_recv_pos) // , m_cur_chunk_end, int(m_content_length)); } else { m_partial_chunk_header += incoming; header_size = incoming; // fprintf(stderr, "parse_chunk_header(%d, -> %d, -> %d) -> %d\n" // " incoming = %d\n m_recv_pos = %d\n m_cur_chunk_end = %d\n" // " content-length = %d\n" // , buf.left(), int(chunk_size), header_size, 0, incoming, int(m_recv_pos) // , m_cur_chunk_end, int(m_content_length)); } m_chunk_header_size += header_size; m_recv_pos += header_size; boost::get<1>(ret) += header_size; incoming -= header_size; } if (incoming > 0) { m_recv_pos += incoming; boost::get<0>(ret) += incoming; // incoming = 0; } } else { size_type payload_received = m_recv_pos - m_body_start_pos + incoming; if (payload_received > m_content_length && m_content_length >= 0) { TORRENT_ASSERT(m_content_length - m_recv_pos + m_body_start_pos < INT_MAX); incoming = int(m_content_length - m_recv_pos + m_body_start_pos); } TORRENT_ASSERT(incoming >= 0); m_recv_pos += incoming; boost::get<0>(ret) += incoming; } if (m_content_length >= 0 && !m_chunked_encoding && m_recv_pos - m_body_start_pos >= m_content_length) { m_finished = true; } } return ret; }
void xml_parse(char* p, char* end, CallbackType callback) { for(;p != end; ++p) { char const* start = p; char const* val_start = 0; int token; // look for tag start for(; *p != '<' && p != end; ++p); if (p != start) { if (p != end) { TORRENT_ASSERT(*p == '<'); *p = 0; } token = xml_string; callback(token, start, val_start); if (p != end) *p = '<'; } if (p == end) break; // skip '<' ++p; if (p != end && p+8 < end && string_begins_no_case("![CDATA[", p)) { // CDATA. match '![CDATA[' p += 8; start = p; while (p != end && !string_begins_no_case("]]>", p-2)) ++p; // parse error if (p == end) { token = xml_parse_error; start = "unexpected end of file"; callback(token, start, val_start); break; } token = xml_string; char tmp = p[-2]; p[-2] = 0; callback(token, start, val_start); p[-2] = tmp; continue; } // parse the name of the tag. for (start = p; p != end && *p != '>' && !is_space(*p); ++p); char* tag_name_end = p; // skip the attributes for now for (; p != end && *p != '>'; ++p); // parse error if (p == end) { token = xml_parse_error; start = "unexpected end of file"; callback(token, start, val_start); break; } TORRENT_ASSERT(*p == '>'); // save the character that terminated the tag name // it could be both '>' and ' '. char save = *tag_name_end; *tag_name_end = 0; char* tag_end = p; if (*start == '/') { ++start; token = xml_end_tag; callback(token, start, val_start); } else if (*(p-1) == '/') { *(p-1) = 0; token = xml_empty_tag; callback(token, start, val_start); *(p-1) = '/'; tag_end = p - 1; } else if (*start == '?' && *(p-1) == '?') { *(p-1) = 0; ++start; token = xml_declaration_tag; callback(token, start, val_start); *(p-1) = '?'; tag_end = p - 1; } else if (start + 5 < p && std::memcmp(start, "!--", 3) == 0 && std::memcmp(p-2, "--", 2) == 0) { start += 3; *(p-2) = 0; token = xml_comment; callback(token, start, val_start); *(p-2) = '-'; tag_end = p - 2; } else { token = xml_start_tag; callback(token, start, val_start); } *tag_name_end = save; // parse attributes for (char* i = tag_name_end; i < tag_end; ++i) { // find start of attribute name for (; i != tag_end && is_space(*i); ++i); if (i == tag_end) break; start = i; // find end of attribute name for (; i != tag_end && *i != '=' && !is_space(*i); ++i); char* name_end = i; // look for equality sign for (; i != tag_end && *i != '='; ++i); if (i == tag_end) { token = xml_parse_error; val_start = 0; start = "garbage inside element brackets"; callback(token, start, val_start); break; } ++i; for (; i != tag_end && is_space(*i); ++i); // check for parse error (values must be quoted) if (i == tag_end || (*i != '\'' && *i != '\"')) { token = xml_parse_error; val_start = 0; start = "unquoted attribute value"; callback(token, start, val_start); break; } char quote = *i; ++i; val_start = i; for (; i != tag_end && *i != quote; ++i); // parse error (missing end quote) if (i == tag_end) { token = xml_parse_error; val_start = 0; start = "missing end quote on attribute"; callback(token, start, val_start); break; } save = *i; *i = 0; *name_end = 0; token = xml_attribute; callback(token, start, val_start); *name_end = '='; *i = save; } } }
TORRENT_EXTRA_EXPORT void xml_parse(char const* p, char const* end , boost::function<void(int,char const*,int,char const*,int)> callback) { for(;p != end; ++p) { char const* start = p; int token; // look for tag start for(; p != end && *p != '<'; ++p); if (p != start) { token = xml_string; const int name_len = p - start; callback(token, start, name_len, nullptr, 0); } if (p == end) break; // skip '<' ++p; if (p != end && p+8 < end && string_begins_no_case("![CDATA[", p)) { // CDATA. match '![CDATA[' p += 8; start = p; while (p != end && !string_begins_no_case("]]>", p-2)) ++p; // parse error if (p == end) { token = xml_parse_error; start = "unexpected end of file"; callback(token, start, int(strlen(start)), nullptr, 0); break; } token = xml_string; const int name_len = p - start - 2; callback(token, start, name_len, nullptr, 0); continue; } // parse the name of the tag. for (start = p; p != end && *p != '>' && !is_space(*p); ++p); char const* tag_name_end = p; // skip the attributes for now for (; p != end && *p != '>'; ++p); // parse error if (p == end) { token = xml_parse_error; start = "unexpected end of file"; callback(token, start, int(strlen(start)), nullptr, 0); break; } TORRENT_ASSERT(*p == '>'); char const* tag_end = p; if (*start == '/') { ++start; token = xml_end_tag; const int name_len = tag_name_end - start; callback(token, start, name_len, nullptr, 0); } else if (*(p-1) == '/') { token = xml_empty_tag; const int name_len = (std::min)(tag_name_end - start, p - start - 1); callback(token, start, name_len, nullptr, 0); tag_end = p - 1; } else if (*start == '?' && *(p-1) == '?') { ++start; token = xml_declaration_tag; const int name_len = (std::min)(tag_name_end - start, p - start - 1); callback(token, start, name_len, nullptr, 0); tag_end = p - 1; } else if (start + 5 < p && std::memcmp(start, "!--", 3) == 0 && std::memcmp(p-2, "--", 2) == 0) { start += 3; token = xml_comment; const int name_len = tag_name_end - start - 2; callback(token, start, name_len, nullptr, 0); tag_end = p - 2; continue; } else { token = xml_start_tag; const int name_len = tag_name_end - start; callback(token, start, name_len, nullptr, 0); } // parse attributes for (char const* i = tag_name_end; i < tag_end; ++i) { char const* val_start = nullptr; // find start of attribute name for (; i != tag_end && is_space(*i); ++i); if (i == tag_end) break; start = i; // find end of attribute name for (; i != tag_end && *i != '=' && !is_space(*i); ++i); const int name_len = i - start; // look for equality sign for (; i != tag_end && *i != '='; ++i); // no equality sign found. Report this as xml_tag_content // instead of a series of key value pairs if (i == tag_end) { token = xml_tag_content; callback(token, start, i - start, nullptr, 0); break; } ++i; for (; i != tag_end && is_space(*i); ++i); // check for parse error (values must be quoted) if (i == tag_end || (*i != '\'' && *i != '\"')) { token = xml_parse_error; start = "unquoted attribute value"; callback(token, start, int(strlen(start)), nullptr, 0); break; } char quote = *i; ++i; val_start = i; for (; i != tag_end && *i != quote; ++i); // parse error (missing end quote) if (i == tag_end) { token = xml_parse_error; start = "missing end quote on attribute"; callback(token, start, int(strlen(start)), nullptr, 0); break; } const int val_len = i - val_start; token = xml_attribute; callback(token, start, name_len, val_start, val_len); } } }
void xml_parse(string_view input , std::function<void(int, string_view, string_view)> callback) { char const* p = input.data(); char const* end = input.data() + input.size(); for (;p != end; ++p) { char const* start = p; // look for tag start for (; p != end && *p != '<'; ++p); if (p != start) { callback(xml_string, {start, std::size_t(p - start)}, {}); } if (p == end) break; // skip '<' ++p; if (p != end && p + 8 < end && string_begins_no_case("![CDATA[", p)) { // CDATA. match '![CDATA[' p += 8; start = p; while (p != end && !string_begins_no_case("]]>", p - 2)) ++p; // parse error if (p == end) { callback(xml_parse_error, "unexpected end of file", {}); break; } callback(xml_string, {start, std::size_t(p - start - 2)}, {}); continue; } // parse the name of the tag. for (start = p; p != end && *p != '>' && !is_space(*p); ++p); char const* tag_name_end = p; // skip the attributes for now for (; p != end && *p != '>'; ++p); // parse error if (p == end) { callback(xml_parse_error, "unexpected end of file", {}); break; } TORRENT_ASSERT(*p == '>'); char const* tag_end = p; if (*start == '/') { ++start; callback(xml_end_tag, {start, std::size_t(tag_name_end - start)}, {}); } else if (*(p - 1) == '/') { callback(xml_empty_tag, {start, std::size_t(std::min(tag_name_end - start, p - start - 1))}, {}); tag_end = p - 1; } else if (*start == '?' && *(p - 1) == '?') { ++start; callback(xml_declaration_tag, {start, std::size_t(std::min(tag_name_end - start, p - start - 1))}, {}); tag_end = p - 1; } else if (start + 5 < p && std::memcmp(start, "!--", 3) == 0 && std::memcmp(p - 2, "--", 2) == 0) { start += 3; callback(xml_comment, {start, std::size_t(tag_name_end - start - 2)}, {}); continue; } else { callback(xml_start_tag, {start, std::size_t(tag_name_end - start)}, {}); } // parse attributes for (char const* i = tag_name_end; i < tag_end; ++i) { char const* val_start = nullptr; // find start of attribute name while (i != tag_end && is_space(*i)) ++i; if (i == tag_end) break; start = i; // find end of attribute name while (i != tag_end && *i != '=' && !is_space(*i)) ++i; std::size_t const name_len = std::size_t(i - start); // look for equality sign for (; i != tag_end && *i != '='; ++i); // no equality sign found. Report this as xml_tag_content // instead of a series of key value pairs if (i == tag_end) { callback(xml_tag_content, {start, std::size_t(i - start)}, {}); break; } ++i; while (i != tag_end && is_space(*i)) ++i; // check for parse error (values must be quoted) if (i == tag_end || (*i != '\'' && *i != '\"')) { callback(xml_parse_error, "unquoted attribute value", {}); break; } char quote = *i; ++i; val_start = i; for (; i != tag_end && *i != quote; ++i); // parse error (missing end quote) if (i == tag_end) { callback(xml_parse_error, "missing end quote on attribute", {}); break; } callback(xml_attribute, {start, name_len}, {val_start, std::size_t(i - val_start)}); } } }