35 CurlOperation(handler, url, timeout, logger, callout, header_callout),
36 m_response_info(set_response_info),
37 m_host_addr(host_addr)
46 curl_easy_setopt(
m_curl.get(), CURLOPT_WRITEFUNCTION, CurlListdirOp::WriteCallback);
47 curl_easy_setopt(
m_curl.get(), CURLOPT_WRITEDATA,
this);
48 curl_easy_setopt(
m_curl.get(), CURLOPT_CUSTOMREQUEST,
"PROPFIND");
57 if (
m_curl ==
nullptr)
return;
58 curl_easy_setopt(
m_curl.get(), CURLOPT_WRITEFUNCTION,
nullptr);
59 curl_easy_setopt(
m_curl.get(), CURLOPT_WRITEDATA,
nullptr);
60 curl_easy_setopt(
m_curl.get(), CURLOPT_CUSTOMREQUEST,
nullptr);
61 curl_easy_setopt(
m_curl.get(), CURLOPT_HTTPHEADER,
nullptr);
66CurlListdirOp::WriteCallback(
char *buffer,
size_t size,
size_t nitems,
void *this_ptr)
69 if (size * nitems + me->m_response.size() > 10'000'000) {
70 return me->FailCallback(
kXR_ServerError,
"Response too large for PROPFIND operation");
73 me->m_response.append(buffer, size * nitems);
77bool CurlListdirOp::ParseProp(DavEntry &entry, TiXmlElement *prop)
79 for (
auto child = prop->FirstChildElement();
child !=
nullptr;
child =
child->NextSiblingElement()) {
80 if (!strcasecmp(
child->Value(),
"D:resourcetype") || !strcasecmp(
child->Value(),
"lp1:resourcetype")) {
81 auto collection =
child->FirstChildElement(
"D:collection");
82 entry.m_isdir = collection !=
nullptr;
83 if (entry.m_isdir && entry.m_size < 0) {
86 }
else if (!strcasecmp(
child->Value(),
"D:getcontentlength") || !strcasecmp(
child->Value(),
"lp1:getcontentlength")) {
87 auto size =
child->GetText();
88 if (size ==
nullptr) {
92 entry.m_size = std::stoll(size);
93 }
catch (std::invalid_argument &e) {
96 }
else if (!strcasecmp(
child->Value(),
"D:getlastmodified") || !strcasecmp(
child->Value(),
"lp1:getlastmodified")) {
97 auto lastmod =
child->GetText();
98 if (lastmod ==
nullptr) {
102 if (strptime(lastmod,
"%a, %d %b %Y %H:%M:%S", &tm) ==
nullptr) {
105 entry.m_lastmodified = timegm(&tm);
106 }
else if (strcasecmp(
child->Value(),
"D:href") == 0) {
107 auto href =
child->GetText();
108 if (href ==
nullptr) {
112 }
else if (!strcasecmp(
child->Value(),
"D:executable") || !strcasecmp(
child->Value(),
"lp1:executable")) {
113 auto val =
child->GetText();
114 if (val ==
nullptr) {
117 if (strcasecmp(val,
"T") == 0) {
118 entry.m_isexec =
true;
125std::pair<CurlListdirOp::DavEntry, bool>
126CurlListdirOp::ParseResponse(TiXmlElement *response)
129 bool success =
false;
130 for (
auto child = response->FirstChildElement();
child !=
nullptr;
child =
child->NextSiblingElement()) {
131 if (!strcasecmp(
child->Value(),
"D:href")) {
132 auto href =
child->GetText();
133 if (href ==
nullptr) {
134 return {entry,
false};
138 std::string_view href_str(href);
139 auto first_non_slash = href_str.find_last_not_of(
'/');
140 if (first_non_slash != std::string_view::npos) {
141 href_str = href_str.substr(0, first_non_slash + 1);
143 auto last_slash = href_str.find_last_of(
'/');
144 if (last_slash != std::string_view::npos) {
145 entry.m_name = href_str.substr(last_slash + 1);
151 if (strcasecmp(
child->Value(),
"D:propstat")) {
154 for (
auto propstat =
child->FirstChildElement(); propstat !=
nullptr; propstat = propstat->NextSiblingElement()) {
155 if (strcasecmp(propstat->Value(),
"D:prop")) {
158 success = ParseProp(entry, propstat);
160 return {entry, success};
164 return {entry, success};
176 doc.Parse(m_response.c_str());
178 m_logger->Error(
kLogXrdClHttp,
"Failed to parse XML response: %s", m_response.substr(0, 1024).c_str());
183 auto elem = doc.RootElement();
184 if (strcasecmp(elem->Value(),
"D:multistatus")) {
190 for (
auto response = elem->FirstChildElement(); response !=
nullptr; response = response->NextSiblingElement()) {
191 if (strcasecmp(response->Value(),
"D:response")) {
195 auto [entry, success] = ParseResponse(response);
197 m_logger->Error(
kLogXrdClHttp,
"Failed to parse response element in XML response: %s", m_response.substr(0, 1024).c_str());
209 if (entry.m_isexec) {
216 m_logger->Debug(
kLogXrdClHttp,
"Successful propfind directory listing operation on %s (%u items)",
m_url.c_str(),
static_cast<unsigned>(dirlist->GetSize()));
219 if (m_response_info) {
223 obj->Set(dirlist.release());
bool Setup(CURL *curl, CurlWorker &) override
CurlListdirOp(XrdCl::ResponseHandler *handler, const std::string &url, const std::string &host_addr, bool response_info, struct timespec timeout, XrdCl::Log *logger, CreateConnCalloutType callout, HeaderCallout *header_callout)
void ReleaseHandle() override
void SetDone(bool has_failed)
std::unique_ptr< ResponseInfo > MoveResponseInfo()
std::unique_ptr< CURL, void(*)(CURL *)> m_curl
virtual void ReleaseHandle()
void UpdateBytes(uint64_t bytes)
std::vector< std::pair< std::string, std::string > > m_headers_list
virtual void Fail(uint16_t errCode, uint32_t errNum, const std::string &)
XrdCl::ResponseHandler * m_handler
CurlOperation(XrdCl::ResponseHandler *handler, const std::string &url, struct timespec timeout, XrdCl::Log *log, CreateConnCalloutType, HeaderCallout *header_callout)
virtual bool Setup(CURL *curl, CurlWorker &)
void SetResponseInfo(std::unique_ptr< ResponseInfo > info)
Handle an async response.
@ IsReadable
Read access is allowed.
@ IsDir
This is a directory.
@ XBitSet
Executable/searchable bit set.
ConnectionCallout *(*)(const std::string &, const ResponseInfo &) CreateConnCalloutType
const uint64_t kLogXrdClHttp
const uint16_t errErrorResponse