+ // Allocate the curl multi handle
+ fMulti = curl_multi_init();
+
+ // Allocate the curl easy handle
+ fEasy = curl_easy_init();
+
+ if (!fMulti || !fEasy)
+ throw IOException("Failed to allocate libcurl handles.");
+
+ curl_easy_setopt(fEasy, CURLOPT_URL, fURL.c_str());
+
+ // Set up a way to recieve the data
+ curl_easy_setopt(fEasy, CURLOPT_WRITEDATA, this); // Pass this pointer to write function
+ curl_easy_setopt(fEasy, CURLOPT_WRITEFUNCTION, staticWriteCallback); // Our static write function
+
+ // Do redirects
+ curl_easy_setopt(fEasy, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(fEasy, CURLOPT_MAXREDIRS, 6);
+
+ // Default settings.
+ curl_easy_setopt(fEasy, CURLOPT_CONNECTTIMEOUT, 10);
+ curl_easy_setopt(fEasy, CURLOPT_TIMEOUT, 60);
+ curl_easy_setopt(fEasy, CURLOPT_HTTPAUTH, 0);
+ curl_easy_setopt(fEasy, CURLOPT_USERPWD, nullptr);
+ curl_easy_setopt(fEasy, CURLOPT_SSL_VERIFYHOST, 2);
+ curl_easy_setopt(fEasy, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_easy_setopt(fEasy, CURLOPT_CAINFO, nullptr);
+ curl_easy_setopt(fEasy, CURLOPT_SSL_CIPHER_LIST, "ALL:!aNULL:!LOW:!EXPORT:!SSLv2");
+ curl_easy_setopt(fEasy, CURLOPT_NOPROGRESS, 1);
+ curl_easy_setopt(fEasy, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(fEasy, CURLOPT_FAILONERROR, 1);
+ curl_easy_setopt(fEasy, CURLOPT_ENCODING, "");
+
+ // Install SSL callback.
+ curl_easy_setopt(fEasy, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_callback);
+ curl_easy_setopt(fEasy, CURLOPT_SSL_CTX_DATA, this);
+
+ fError[0] = 0;
+ curl_easy_setopt(fEasy, CURLOPT_ERRORBUFFER, fError);
+
+ // Check for cache tag.
+ if (fCacheTag) {
+ // Outgoing tag.
+ if (!fCacheTag->empty()) {
+ fHeaders = curl_slist_append(fHeaders, fCacheTag->c_str());
+ }
+ // Incoming tag.
+ curl_easy_setopt(fEasy, CURLOPT_HEADERFUNCTION, curl_header_hook);
+ curl_easy_setopt(fEasy, CURLOPT_HEADERDATA, fCacheTag);
+ }
+
+ // Add User-Agent as a header for now. TODO: Add private member to hold the
+ // value for the standard UA option.
+ string ua = string("User-Agent: ") + XMLToolingConfig::getConfig().user_agent +
+ " libcurl/" + LIBCURL_VERSION + ' ' + OPENSSL_VERSION_TEXT;
+ fHeaders = curl_slist_append(fHeaders, ua.c_str());
+
+ // Add User-Agent and cache headers.
+ curl_easy_setopt(fEasy, CURLOPT_HTTPHEADER, fHeaders);
+
+ if (e) {
+ const XMLCh* flag = e->getAttributeNS(nullptr, verifyHost);
+ if (flag && (*flag == chLatin_f || *flag == chDigit_0))
+ curl_easy_setopt(fEasy, CURLOPT_SSL_VERIFYHOST, 0);
+
+ // Process TransportOption elements.
+ bool success;
+ DOMElement* child = XMLHelper::getLastChildElement(e, TransportOption);
+ while (child) {
+ if (child->hasChildNodes() && XMLString::equals(child->getAttributeNS(nullptr,_provider), _OpenSSL)) {
+ auto_ptr_char option(child->getAttributeNS(nullptr,_option));
+ auto_ptr_char value(child->getFirstChild()->getNodeValue());
+ if (option.get() && value.get() && !strcmp(option.get(), "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION") &&
+ (*value.get()=='1' || *value.get()=='t')) {
+ // If the new option to enable buggy rengotiation is available, set it.
+ // Otherwise, signal false if this is newer than 0.9.8k, because that
+ // means it's 0.9.8l, which blocks renegotiation, and therefore will
+ // not honor this request. Older versions are buggy, so behave as though
+ // the flag was set anyway, so we signal true.
+#if defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)
+ fOpenSSLOps |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+ success = true;
+#elif (OPENSSL_VERSION_NUMBER > 0x009080bfL)
+ success = false;
+#else
+ success = true;
+#endif
+ }
+ else {
+ success = false;
+ }
+ if (!success)
+ fLog.error("failed to set OpenSSL transport option (%s)", option.get());
+ }
+ else if (child->hasChildNodes() && XMLString::equals(child->getAttributeNS(nullptr,_provider), _CURL)) {
+ auto_ptr_char option(child->getAttributeNS(nullptr,_option));
+ auto_ptr_char value(child->getFirstChild()->getNodeValue());
+ if (option.get() && *option.get() && value.get() && *value.get()) {
+ // For libcurl, the option is an enum and the value type depends on the option.
+ CURLoption opt = static_cast<CURLoption>(strtol(option.get(), nullptr, 10));
+ if (opt < CURLOPTTYPE_OBJECTPOINT)
+ success = (curl_easy_setopt(fEasy, opt, strtol(value.get(), nullptr, 10)) == CURLE_OK);
+#ifdef CURLOPTTYPE_OFF_T
+ else if (opt < CURLOPTTYPE_OFF_T) {
+ fSavedOptions.push_back(value.get());
+ success = (curl_easy_setopt(fEasy, opt, fSavedOptions.back().c_str()) == CURLE_OK);
+ }
+# ifdef HAVE_CURL_OFF_T
+ else if (sizeof(curl_off_t) == sizeof(long))
+ success = (curl_easy_setopt(fEasy, opt, strtol(value.get(), nullptr, 10)) == CURLE_OK);
+# else
+ else if (sizeof(off_t) == sizeof(long))
+ success = (curl_easy_setopt(fEasy, opt, strtol(value.get(), nullptr, 10)) == CURLE_OK);
+# endif
+ else
+ success = false;
+#else
+ else {
+ fSavedOptions.push_back(value.get());
+ success = (curl_easy_setopt(fEasy, opt, fSavedOptions.back().c_str()) == CURLE_OK);
+ }
+#endif
+ if (!success)
+ fLog.error("failed to set CURL transport option (%s)", option.get());
+ }
+ }
+ child = XMLHelper::getPreviousSiblingElement(child, TransportOption);
+ }
+ }
+
+ // Add easy handle to the multi stack
+ curl_multi_add_handle(fMulti, fEasy);
+
+ fLog.debug("libcurl trying to fetch %s", fURL.c_str());
+
+ // Start reading, to get the content type
+ while(fBufferHeadPtr == fBuffer) {
+ int runningHandles = 0;
+ try {
+ readMore(&runningHandles);
+ }
+ catch (XMLException&) {
+ curl_multi_remove_handle(fMulti, fEasy);
+ curl_easy_cleanup(fEasy);
+ fEasy = nullptr;
+ curl_multi_cleanup(fMulti);
+ fMulti = nullptr;
+ throw;
+ }
+ if(runningHandles == 0) break;
+ }
+
+ // Check for a response code.
+ if (curl_easy_getinfo(fEasy, CURLINFO_RESPONSE_CODE, &fStatusCode) == CURLE_OK) {
+ if (fStatusCode >= 300 ) {
+ // Short-circuit usual processing by storing a special XML document in the buffer.
+ ostringstream specialdoc;
+ specialdoc << '<' << URLInputSource::asciiStatusCodeElementName << " xmlns=\"http://www.opensaml.org/xmltooling\">"
+ << fStatusCode
+ << "</" << URLInputSource::asciiStatusCodeElementName << '>';
+ string specialxml = specialdoc.str();
+ fBufferTailPtr = fBuffer = reinterpret_cast<XMLByte*>(malloc(specialxml.length()));
+ if (!fBuffer) {
+ curl_multi_remove_handle(fMulti, fEasy);
+ curl_easy_cleanup(fEasy);
+ fEasy = nullptr;
+ curl_multi_cleanup(fMulti);
+ fMulti = nullptr;
+ throw bad_alloc();
+ }
+ memcpy(fBuffer, specialxml.c_str(), specialxml.length());
+ fBufferHeadPtr = fBuffer + specialxml.length();
+ }
+ }
+ else {
+ fStatusCode = 200; // reset to 200 to ensure no special processing occurs
+ }
+
+ // Find the content type
+ char* contentType8 = nullptr;
+ if(curl_easy_getinfo(fEasy, CURLINFO_CONTENT_TYPE, &contentType8) == CURLE_OK && contentType8)
+ fContentType = XMLString::transcode(contentType8);